зеркало из https://github.com/mozilla/pjs.git
3310 строки
86 KiB
C++
3310 строки
86 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
//
|
|
// mkldap.cpp -- Handles LDAP-related URLs for the core Navigator, without
|
|
// requiring libmsg. mkldap is responsible for
|
|
// 1. basic async searching of LDAP servers, including authentication,
|
|
// capability negotiation with the server, and parsing results
|
|
// 2. Reading LDAP entries and converting them into HTML for display
|
|
// in the browsers
|
|
// 3. Reading an LDAP entry and converting it for use in the local
|
|
// Address Book
|
|
// 4. Replicating a large section of an LDAP directory into a local
|
|
// Address Book using changelog deltas
|
|
//
|
|
|
|
#include "rosetta.h"
|
|
#include "mkutils.h"
|
|
|
|
#include "mkgeturl.h"
|
|
#include "mkldap.h"
|
|
#include "xpgetstr.h"
|
|
#include "abcom.h"
|
|
#include "addrbook.h"
|
|
#include "msgcom.h"
|
|
#include "msgnet.h"
|
|
#include "msgpane.h"
|
|
#include "msgurlq.h"
|
|
#include "dirprefs.h"
|
|
#include "net.h"
|
|
#include "xp_str.h"
|
|
#include HG87373
|
|
#include "secnav.h"
|
|
#include "libi18n.h"
|
|
#include "intl_csi.h"
|
|
#include "pw_public.h"
|
|
|
|
|
|
#ifdef MOZ_LDAP
|
|
#ifdef MOZ_LI
|
|
#include "LILDAPAllOps.h"
|
|
#endif
|
|
|
|
#define NEEDPROTOS
|
|
#define LDAP_REFERRALS
|
|
#include "lber.h"
|
|
#include "ldap.h"
|
|
#endif
|
|
|
|
|
|
extern "C"
|
|
{
|
|
extern int MK_OUT_OF_MEMORY;
|
|
extern int MK_MSG_SEARCH_FAILED;
|
|
|
|
extern int MK_LDAP_COMMON_NAME;
|
|
extern int MK_LDAP_FAX_NUMBER;
|
|
extern int MK_LDAP_GIVEN_NAME;
|
|
extern int MK_LDAP_LOCALITY;
|
|
extern int MK_LDAP_PHOTOGRAPH;
|
|
extern int MK_LDAP_EMAIL_ADDRESS;
|
|
extern int MK_LDAP_MANAGER;
|
|
extern int MK_LDAP_ORGANIZATION;
|
|
extern int MK_LDAP_OBJECT_CLASS;
|
|
extern int MK_LDAP_ORG_UNIT;
|
|
extern int MK_LDAP_POSTAL_ADDRESS;
|
|
extern int MK_LDAP_SECRETARY;
|
|
extern int MK_LDAP_SURNAME;
|
|
extern int MK_LDAP_STREET;
|
|
extern int MK_LDAP_PHONE_NUMBER;
|
|
extern int MK_LDAP_CAR_LICENSE;
|
|
extern int MK_LDAP_BUSINESS_CAT;
|
|
extern int MK_LDAP_DEPT_NUMBER;
|
|
extern int MK_LDAP_DESCRIPTION;
|
|
extern int MK_LDAP_EMPLOYEE_TYPE;
|
|
extern int MK_LDAP_POSTAL_CODE;
|
|
extern int MK_LDAP_TITLE;
|
|
|
|
extern int MK_LDAP_ADD_SERVER_TO_PREFS;
|
|
extern int MK_MALFORMED_URL_ERROR;
|
|
HG73226
|
|
extern int MK_LDAP_HTML_TITLE;
|
|
|
|
extern int MK_LDAP_AUTH_PROMPT;
|
|
|
|
extern int XP_LDAP_OPEN_SERVER_FAILED;
|
|
extern int XP_LDAP_BIND_FAILED;
|
|
extern int XP_LDAP_SEARCH_FAILED;
|
|
}
|
|
|
|
|
|
#ifdef MOZ_LDAP
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// net_LdapConnectionData is the base class which knows how to run
|
|
// ldap URLs asynchronously and pull out search results.
|
|
//
|
|
// If you want to do something special with the results of an LDAP
|
|
// search, you probably want to derive from this class and use/make
|
|
// virtual functions.
|
|
//
|
|
|
|
class net_LdapConnectionData
|
|
{
|
|
public:
|
|
net_LdapConnectionData (ActiveEntry*, XP_Bool bInitLdap = TRUE);
|
|
virtual ~net_LdapConnectionData ();
|
|
|
|
virtual int Load (ActiveEntry *ce);
|
|
virtual int Process ();
|
|
virtual int Interrupt ();
|
|
|
|
protected:
|
|
typedef enum _LdapConnectionType
|
|
{
|
|
kBaseConnection,
|
|
kHtmlConnection,
|
|
kReplicatorConnection,
|
|
kAddressBookConnection,
|
|
kLIConnection
|
|
} LdapConnectionType;
|
|
|
|
virtual int Unbind ();
|
|
virtual int OnBindCompleted () { return 0; }
|
|
virtual int OnEntryFound (LDAPMessage *) { return 0; }
|
|
virtual int OnSearchCompleted () { return MK_CONNECTED; }
|
|
virtual XP_Bool OnError(int err) { return TRUE; }
|
|
virtual int16 GetLocalCharSet ();
|
|
virtual LdapConnectionType GetConnectionType() { return kBaseConnection; }
|
|
|
|
void AddLdapServerToPrefs (LDAPURLDesc* pDesc);
|
|
|
|
char *ConvertFromServerCharSet (char *s);
|
|
char *ConvertToServerCharSet (char *s);
|
|
// sub classes may have strings prefixed to the url...We need to filter them out here.
|
|
virtual char * ExtractLDAPEncoding(char * s);
|
|
|
|
XP_Bool ShouldAddUrlDescToPrefs (MWContext *context, XP_List *serverList, LDAPURLDesc *pDesc);
|
|
void ConvertUrlDescToDirServer (LDAPURLDesc *pDesc, DIR_Server **pServer);
|
|
|
|
const char *GetHostDescription ();
|
|
char *GetAttributeFromDN (char *dn, const char *name);
|
|
const char *GetAttributeString (DIR_AttributeId id);
|
|
|
|
void DisplayError (int templateId, const char *contextString, int ldapErrorId);
|
|
|
|
int16 m_csid;
|
|
LDAP *m_ldap;
|
|
int m_messageId;
|
|
ActiveEntry *m_ce;
|
|
DIR_Server *m_server;
|
|
};
|
|
|
|
net_LdapConnectionData::net_LdapConnectionData (ActiveEntry *ce, XP_Bool bInitLdap)
|
|
{
|
|
// We init LDAP with no server name and port since we expect all
|
|
// such information to be spedified by URLs in our case.
|
|
//
|
|
if (bInitLdap)
|
|
m_ldap = ldap_init(NULL,0);
|
|
else
|
|
m_ldap = NULL;
|
|
|
|
m_messageId = -1; // Init to an illegal value so we can detect errors
|
|
|
|
m_server = NULL;
|
|
|
|
m_csid = -1;
|
|
m_ce = ce;
|
|
m_ce->socket = NULL;
|
|
|
|
// Netlib magic so we get a timeslice without waiting for a read-ready socket.
|
|
// It would be nice to ditch this, but the LDAP SDK manages the sockets.
|
|
NET_SetCallNetlibAllTheTime(m_ce->window_id, "mkldap");
|
|
}
|
|
|
|
|
|
net_LdapConnectionData::~net_LdapConnectionData ()
|
|
{
|
|
// This is the normal-termination case to let go of the connection
|
|
Unbind();
|
|
|
|
// Netlib magic so we won't get called anymore for this URL
|
|
// It would be nice to ditch this, but the LDAP SDK manages the sockets.
|
|
NET_ClearCallNetlibAllTheTime(m_ce->window_id, "mkldap");
|
|
}
|
|
|
|
int16 net_LdapConnectionData::GetLocalCharSet ()
|
|
{
|
|
if (m_csid == -1)
|
|
m_csid = INTL_DefaultDocCharSetID(0);
|
|
return m_csid;
|
|
}
|
|
|
|
void net_LdapConnectionData::AddLdapServerToPrefs (LDAPURLDesc* pDesc)
|
|
{
|
|
// Takes an ActiveEntry from the current operation, a url descriptor from ldap_url_parse,
|
|
// determines (by asking the user) if we should add it to our LDAP prefs, and adds it if necessary.
|
|
|
|
// Do we want to add the server specified by the URL to our LDAP prefs?
|
|
XP_List *serverList = FE_GetDirServers();
|
|
if (serverList && ShouldAddUrlDescToPrefs(m_ce->window_id, serverList, pDesc))
|
|
{
|
|
ConvertUrlDescToDirServer (pDesc, &m_server);
|
|
if (m_server)
|
|
{
|
|
XP_ListAddObjectToEnd (serverList, m_server);
|
|
DIR_SaveServerPreferences (serverList);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int net_LdapConnectionData::Load (ActiveEntry *ce)
|
|
{
|
|
int ldapErr = 0;
|
|
LDAPURLDesc *pDesc = NULL;
|
|
|
|
XP_ASSERT(ce);
|
|
XP_ASSERT(ce->URL_s);
|
|
XP_ASSERT(ce->URL_s->address);
|
|
|
|
if (m_ldap)
|
|
{
|
|
// addbook and html have prefixes in front of them (i.e. addbook-ldap). We now want
|
|
// to parse that out of the utf8 encoding string that will be sent to the server.
|
|
// In addition, we want to convert the resulting encoding to utf8
|
|
|
|
// we do not need to free encoding because it is a part of the url passed in....
|
|
char * encoding = ExtractLDAPEncoding(ce->URL_s->address);
|
|
|
|
// now take the ldap encoding and convert to the server char set.
|
|
char *utf8Encoding = utf8Encoding = ConvertToServerCharSet (encoding);
|
|
|
|
if (utf8Encoding)
|
|
{
|
|
ldapErr = ldap_url_parse (utf8Encoding, &pDesc);
|
|
|
|
if (!pDesc || ldapErr != LDAP_SUCCESS)
|
|
ldapErr = MK_MALFORMED_URL_ERROR;
|
|
|
|
if (ldapErr == 0)
|
|
{
|
|
// RFC 1959 allows this syntax, but we don't really have a way
|
|
// to infer an LDAP server, and it's a pretty strange X.500-ism anyway
|
|
if (!pDesc->lud_host)
|
|
ldapErr = MK_MALFORMED_URL_ERROR;
|
|
|
|
AddLdapServerToPrefs (pDesc);
|
|
|
|
HG29237
|
|
if (ldapErr == 0)
|
|
{
|
|
// Initiate the search on the server
|
|
int msgId = ldap_url_search (m_ldap, utf8Encoding, 0);
|
|
if (msgId != -1)
|
|
m_messageId = msgId;
|
|
else
|
|
ldapErr = -1;
|
|
}
|
|
}
|
|
else
|
|
ldapErr = MK_MALFORMED_URL_ERROR;
|
|
|
|
XP_FREE(utf8Encoding);
|
|
}
|
|
else
|
|
ldapErr = -1;
|
|
}
|
|
else
|
|
XP_ASSERT(pDesc);
|
|
ldapErr = -1;
|
|
|
|
if (pDesc)
|
|
ldap_free_urldesc (pDesc);
|
|
|
|
return ldapErr;
|
|
}
|
|
|
|
|
|
// Interpret all data we get from the server as being UTF-8
|
|
char *net_LdapConnectionData::ConvertFromServerCharSet (char *s)
|
|
{
|
|
char *converted = NULL;
|
|
XP_ASSERT(s);
|
|
if (s)
|
|
{
|
|
// Use the win_csid for browsers but the GUI csid for the AB
|
|
int16 destCsid = GetLocalCharSet();
|
|
converted = DIR_ConvertFromServerCharSet (m_server, s, destCsid);
|
|
}
|
|
return converted;
|
|
}
|
|
|
|
char * net_LdapConnectionData::ExtractLDAPEncoding(char * url)
|
|
{
|
|
// for the base class, we don't know about stripping any prefixes off...
|
|
return url;
|
|
}
|
|
|
|
// Send all data to the server in UTF-8
|
|
char *net_LdapConnectionData::ConvertToServerCharSet (char *s)
|
|
{
|
|
char *converted = NULL;
|
|
XP_ASSERT(s);
|
|
if (s)
|
|
{
|
|
// Use the win_csid for browsers but the GUI csid for the AB
|
|
int16 srcCsid = GetLocalCharSet();
|
|
converted = DIR_ConvertToServerCharSet (m_server, s, srcCsid);
|
|
}
|
|
return converted;
|
|
}
|
|
|
|
const char *net_LdapConnectionData::GetHostDescription()
|
|
{
|
|
// since the description is optional, it might be null, so pick up
|
|
// the server's DNS name if necessary
|
|
return m_server->description ? m_server->description : m_server->serverName;
|
|
}
|
|
|
|
char *net_LdapConnectionData::GetAttributeFromDN (char *dn, const char *name)
|
|
{
|
|
// Pull an attribute out of the distinguished name
|
|
// e.g. the cn attribute from cn=John Smith,o=Netscape Communications Corp.,c=US
|
|
|
|
char *result = NULL;
|
|
char **explodedDn = ldap_explode_dn (dn, FALSE /* suppress types? */);
|
|
if (explodedDn)
|
|
{
|
|
int i = 0;
|
|
while (explodedDn[i] && !result)
|
|
{
|
|
const char *nameFromDn = XP_STRTOK (explodedDn[i], "=");
|
|
if (!XP_STRCASECMP(name, nameFromDn))
|
|
{
|
|
char *tmpResult = XP_STRDUP(explodedDn[i] + XP_STRLEN(nameFromDn) + 1);
|
|
result = ConvertFromServerCharSet (tmpResult);
|
|
XP_FREE(tmpResult);
|
|
}
|
|
i++;
|
|
}
|
|
ldap_value_free (explodedDn);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
const char *net_LdapConnectionData::GetAttributeString (DIR_AttributeId id)
|
|
{
|
|
// This function is a bottleneck for DIR_GetAttributeName. I think most
|
|
// of the time we'll have a DIR_Server, but I think it can also be NULL
|
|
// if the user clicked on an ldap: URL for which there's no server in
|
|
// the preferences
|
|
|
|
return DIR_GetFirstAttributeString (m_server, id);
|
|
}
|
|
|
|
void net_LdapConnectionData::DisplayError (int templateId, const char *contextString, int ldapErrorId)
|
|
{
|
|
// This builds up error dialogs which look like:
|
|
// "Failed to open connection to 'ldap.mcom.com' due to LDAP error 'bad DN name' (0x22)"
|
|
|
|
char *templateString = XP_GetString (templateId);
|
|
char *ldapErrString = NULL;
|
|
|
|
if (ldapErrorId != 0)
|
|
ldapErrString = ldap_err2string (ldapErrorId);
|
|
if (templateString && contextString && (ldapErrorId == 0 || ldapErrString))
|
|
{
|
|
int len = XP_STRLEN(templateString) + XP_STRLEN(contextString) + 10;
|
|
if (ldapErrString)
|
|
len += XP_STRLEN(ldapErrString);
|
|
char *theBigString = (char*) XP_ALLOC (len);
|
|
if (theBigString)
|
|
{
|
|
if (ldapErrorId)
|
|
PR_snprintf (theBigString, len, templateString, contextString, ldapErrString, ldapErrorId);
|
|
else
|
|
PR_snprintf (theBigString, len, templateString, contextString);
|
|
FE_Alert (m_ce->window_id, theBigString);
|
|
XP_FREE(theBigString);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
XP_Bool net_LdapConnectionData::ShouldAddUrlDescToPrefs (MWContext *context, XP_List *serverList, LDAPURLDesc *pDesc)
|
|
{
|
|
XP_Bool shouldAdd = TRUE;
|
|
|
|
// First look through the prefs to see if the server already exists there
|
|
for (int i = 1; i <= XP_ListCount(serverList) && shouldAdd; i++)
|
|
{
|
|
DIR_Server *server = (DIR_Server*) XP_ListGetObjectNum (serverList, i);
|
|
if (server && server->serverName && pDesc->lud_host)
|
|
{
|
|
if (!XP_STRCASECMP(pDesc->lud_host, server->serverName))
|
|
{
|
|
// The server does exist -- don't ask to add it again
|
|
shouldAdd = FALSE;
|
|
if (!m_server)
|
|
m_server = server;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the server doesn't exist, ask the user if they want to add it to their prefs
|
|
if (shouldAdd)
|
|
{
|
|
char *prompt = PR_smprintf (XP_GetString(MK_LDAP_ADD_SERVER_TO_PREFS), pDesc->lud_host);
|
|
if (prompt)
|
|
{
|
|
shouldAdd = FE_Confirm (context, prompt);
|
|
XP_FREE(prompt);
|
|
}
|
|
else
|
|
shouldAdd = FALSE;
|
|
}
|
|
|
|
return shouldAdd;
|
|
}
|
|
|
|
|
|
void net_LdapConnectionData::ConvertUrlDescToDirServer (LDAPURLDesc *pDesc, DIR_Server **ppServer)
|
|
{
|
|
*ppServer = (DIR_Server*) XP_ALLOC (sizeof(DIR_Server));
|
|
if (*ppServer)
|
|
{
|
|
XP_BZERO(*ppServer, sizeof(DIR_Server));
|
|
|
|
// These fields just map right over
|
|
(*ppServer)->serverName = XP_STRDUP (pDesc->lud_host);
|
|
(*ppServer)->searchBase = XP_STRDUP (pDesc->lud_dn ? pDesc->lud_dn : "");
|
|
(*ppServer)->port = pDesc->lud_port;
|
|
|
|
// These could probably be in a generic DIR_Server initializer. Do we need that?
|
|
(*ppServer)->efficientWildcards = TRUE;
|
|
(*ppServer)->saveResults = TRUE;
|
|
(*ppServer)->maxHits = 100;
|
|
|
|
// The address book needs a valid filename. Use the hostname to derive one.
|
|
char *fileName = XP_STRTOK(pDesc->lud_host, ".");
|
|
if (fileName)
|
|
(*ppServer)->fileName = WH_FileName (fileName, xpAddrBook);
|
|
else
|
|
(*ppServer)->fileName = WH_FileName (pDesc->lud_host, xpAddrBook);
|
|
|
|
// Try to derive a reasonable name for DIR_Server.description by looking
|
|
// at the distinguished name and picking out the most specific RDN
|
|
// (i.e. the last OU, or any OU, or any O, or any C).
|
|
if (pDesc->lud_dn)
|
|
{
|
|
char **explodedDn = ldap_explode_dn (pDesc->lud_dn, TRUE /*suppress types? */);
|
|
if (explodedDn)
|
|
{
|
|
if (explodedDn[0])
|
|
(*ppServer)->description = XP_STRDUP (explodedDn[0]);
|
|
|
|
ldap_value_free (explodedDn);
|
|
}
|
|
}
|
|
|
|
// If we couldn't find any reasonable-looking DN component, just use the host name
|
|
if (NULL == (*ppServer)->description)
|
|
(*ppServer)->description = XP_STRDUP ((*ppServer)->serverName);
|
|
}
|
|
}
|
|
|
|
|
|
int net_LdapConnectionData::Process ()
|
|
{
|
|
LDAPMessage *message = NULL;
|
|
struct timeval timeout;
|
|
XP_BZERO (&timeout, sizeof(timeout));
|
|
int result = ldap_result (m_ldap, m_messageId, 0, &timeout, &message);
|
|
|
|
switch (result)
|
|
{
|
|
case LDAP_RES_SEARCH_ENTRY:
|
|
result = OnEntryFound (message);
|
|
break;
|
|
case LDAP_RES_SEARCH_RESULT:
|
|
result = OnSearchCompleted ();
|
|
break;
|
|
case LDAP_RES_BIND:
|
|
result = OnBindCompleted ();
|
|
break;
|
|
case LDAP_TIMEOUT:
|
|
result = 0;
|
|
break;
|
|
case 0:
|
|
break;
|
|
default:
|
|
if (result > 0)
|
|
{
|
|
int err = ldap_result2error (m_ldap, message, 0);
|
|
result = ((err != 0) ? err : result);
|
|
}
|
|
|
|
if (OnError(result))
|
|
{
|
|
DisplayError (XP_LDAP_OPEN_SERVER_FAILED, GetHostDescription(), result);
|
|
result = -1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (message != NULL)
|
|
ldap_msgfree (message);
|
|
return result;
|
|
}
|
|
|
|
|
|
int net_LdapConnectionData::Interrupt ()
|
|
{
|
|
// The destructor will unbind.
|
|
int ldapErr = ldap_abandon (m_ldap, m_messageId);
|
|
return ldapErr;
|
|
}
|
|
|
|
|
|
int net_LdapConnectionData::Unbind ()
|
|
{
|
|
int ldapErr = 0;
|
|
if (m_ldap)
|
|
{
|
|
ldapErr = ldap_unbind(m_ldap);
|
|
m_ldap = NULL;
|
|
}
|
|
return ldapErr;
|
|
}
|
|
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// net_LdapToHtml does the work of converting LDAP entries into HTML
|
|
// which can be displayed in a browser window
|
|
//
|
|
|
|
const int kNumAttributeNames = 24;
|
|
|
|
class net_LdapToHtml : public net_LdapConnectionData
|
|
{
|
|
public:
|
|
net_LdapToHtml (ActiveEntry*);
|
|
|
|
virtual int OnEntryFound (LDAPMessage *entry);
|
|
virtual int OnSearchCompleted();
|
|
virtual int16 GetLocalCharSet ();
|
|
|
|
typedef struct
|
|
{
|
|
char *attributeName;
|
|
int resourceId;
|
|
} AttributeName;
|
|
|
|
protected:
|
|
virtual LdapConnectionType GetConnectionType() { return kHtmlConnection; }
|
|
|
|
int OpenStream ();
|
|
int CloseStream ();
|
|
int WriteLineToStream (const char *line, XP_Bool bold, XP_Bool lineBreak, XP_Bool isMailto = FALSE,
|
|
XP_Bool isUri = FALSE, XP_Bool aligntop = FALSE, XP_Bool isCell = FALSE);
|
|
NET_StreamClass *m_stream;
|
|
|
|
const char *LookupAttributeName (const char *attrib);
|
|
char *GetTableCaption (LDAPMessage*);
|
|
|
|
int WriteEntryToStream (LDAPMessage*);
|
|
int WriteValue (const char *, XP_Bool isMailto = FALSE, XP_Bool isUri = FALSE);
|
|
int WriteAttribute (const char *);
|
|
int WritePlainLine (const char *);
|
|
|
|
|
|
XP_Bool IsMailtoAttribute (const char *);
|
|
XP_Bool IsUriAttribute (const char *);
|
|
|
|
char *ConvertDnToUri (const char *);
|
|
char *EscapeSpaces (const char *);
|
|
char *InferUrlBase ();
|
|
|
|
static AttributeName m_names[kNumAttributeNames];
|
|
};
|
|
|
|
|
|
// Note that these are sorted. Someday I'll match attribs by binary search
|
|
//
|
|
// These are initialized to zero instead of their real resource IDs because
|
|
// the HP/UX compiler seems not to be capable of doing that
|
|
|
|
net_LdapToHtml::AttributeName net_LdapToHtml::m_names[kNumAttributeNames] = {
|
|
{ "carlicense", 0 },
|
|
{ "cn", 0 },
|
|
{ "businesscategory", 0 },
|
|
{ "departmentnumber", 0 },
|
|
{ "description", 0 },
|
|
{ "employeetype", 0 },
|
|
{ "facsimiletelephonenumber", 0 },
|
|
{ "givenname", 0 },
|
|
{ "l", 0 },
|
|
{ "jpegPhoto", 0 },
|
|
{ "mail", 0 },
|
|
{ "manager", 0 },
|
|
{ "o", 0 },
|
|
{ "objectclass", 0 },
|
|
{ "ou", 0 },
|
|
{ "postaladdress", 0 },
|
|
{ "postalcode", 0 },
|
|
{ "secretary", 0 },
|
|
{ "sn", 0 },
|
|
{ "street", 0 },
|
|
{ "telephonenumber", 0 },
|
|
{ "title", 0 }
|
|
HG38731
|
|
};
|
|
|
|
|
|
net_LdapToHtml::net_LdapToHtml (ActiveEntry *ce) : net_LdapConnectionData (ce)
|
|
{
|
|
m_stream = NULL;
|
|
}
|
|
|
|
|
|
int net_LdapToHtml::OnEntryFound (LDAPMessage *message)
|
|
{
|
|
int status = 0;
|
|
if (!m_stream)
|
|
status = OpenStream();
|
|
if (0 == status)
|
|
status = WriteEntryToStream (message);
|
|
return status;
|
|
}
|
|
|
|
int net_LdapToHtml::OnSearchCompleted ()
|
|
{
|
|
CloseStream();
|
|
return net_LdapConnectionData::OnSearchCompleted();
|
|
}
|
|
|
|
|
|
int16 net_LdapToHtml::GetLocalCharSet ()
|
|
{
|
|
if (m_csid == -1)
|
|
{
|
|
INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_ce->window_id);
|
|
m_csid = INTL_GetCSIWinCSID(c);
|
|
if (CS_DEFAULT == m_csid)
|
|
m_csid = INTL_DefaultWinCharSetID (0);
|
|
}
|
|
return m_csid;
|
|
}
|
|
|
|
|
|
int net_LdapToHtml::OpenStream ()
|
|
{
|
|
int err = 0;
|
|
|
|
if (NULL != m_stream)
|
|
return err;
|
|
|
|
if (!m_ce->URL_s->content_type)
|
|
StrAllocCopy(m_ce->URL_s->content_type, TEXT_HTML);
|
|
m_stream = NET_StreamBuilder(m_ce->format_out, m_ce->URL_s, m_ce->window_id);
|
|
if (!m_stream)
|
|
return MK_OUT_OF_MEMORY;
|
|
|
|
// Scribble HTML-starting stuff into the stream
|
|
char *htmlHeaders = PR_smprintf ("<HTML><HEAD><TITLE>%s</TITLE></HEAD>%s<BODY>%s", XP_GetString(MK_LDAP_HTML_TITLE), LINEBREAK, LINEBREAK);
|
|
if (htmlHeaders)
|
|
err = (*(m_stream)->put_block)(m_stream, htmlHeaders, XP_STRLEN(htmlHeaders));
|
|
else
|
|
err = MK_OUT_OF_MEMORY;
|
|
|
|
if (1 == err)
|
|
err = 0; //### which is it: 1 for success or 0 for success?
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
int net_LdapToHtml::CloseStream ()
|
|
{
|
|
int err = 0;
|
|
|
|
// Never got any hits for this operation
|
|
if (!m_stream)
|
|
{
|
|
if (((err = OpenStream()) == 0) && m_stream)
|
|
{
|
|
char *noMatches = PR_smprintf ("<H1>%s</H1>", XP_GetString(MK_MSG_SEARCH_FAILED));
|
|
if (noMatches)
|
|
{
|
|
err = (*(m_stream)->put_block)(m_stream, noMatches, XP_STRLEN(noMatches));
|
|
XP_FREE(noMatches);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Scribble HTML-ending stuff into the stream
|
|
if (m_stream)
|
|
{
|
|
char htmlFooters[32];
|
|
PR_snprintf (htmlFooters, sizeof(htmlFooters), "</BODY>%s</HTML>%s", LINEBREAK, LINEBREAK);
|
|
err = (*(m_stream)->put_block)(m_stream, htmlFooters, XP_STRLEN(htmlFooters));
|
|
if (1 == err)
|
|
err = 0; //### which is it: 1 for success or 0 for success?
|
|
|
|
// complete the stream
|
|
(*m_stream->complete) (m_stream);
|
|
XP_FREE(m_stream);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
const char *net_LdapToHtml::LookupAttributeName (const char *attrib)
|
|
{
|
|
// If we know about this attribute (it has a DIR_AttributeId and we
|
|
// have a DIR_Server to ask, get the name from the DIR_Server
|
|
DIR_AttributeId id;
|
|
if (0 == DIR_AttributeNameToId (attrib, &id) && m_server)
|
|
return DIR_GetAttributeName (m_server, id);
|
|
|
|
// If we didn't get the name from LDAP prefs, get it using our
|
|
// own hacky internal table. ### This should really move into the LDAP SDK.
|
|
|
|
// Fill these resource IDs in here since the HP/UX compiler can't do it
|
|
// in the initializer for the static array.
|
|
if (m_names[0].resourceId == 0)
|
|
{
|
|
m_names[0].resourceId = MK_LDAP_CAR_LICENSE;
|
|
m_names[1].resourceId = MK_LDAP_COMMON_NAME;
|
|
m_names[2].resourceId = MK_LDAP_BUSINESS_CAT;
|
|
m_names[3].resourceId = MK_LDAP_DEPT_NUMBER;
|
|
m_names[4].resourceId = MK_LDAP_DESCRIPTION;
|
|
m_names[5].resourceId = MK_LDAP_EMPLOYEE_TYPE;
|
|
m_names[6].resourceId = MK_LDAP_FAX_NUMBER;
|
|
m_names[7].resourceId = MK_LDAP_GIVEN_NAME;
|
|
m_names[8].resourceId = MK_LDAP_LOCALITY;
|
|
m_names[9].resourceId = MK_LDAP_PHOTOGRAPH;
|
|
m_names[10].resourceId = MK_LDAP_EMAIL_ADDRESS;
|
|
m_names[11].resourceId = MK_LDAP_MANAGER;
|
|
m_names[12].resourceId = MK_LDAP_ORGANIZATION;
|
|
m_names[13].resourceId = MK_LDAP_OBJECT_CLASS;
|
|
m_names[14].resourceId = MK_LDAP_ORG_UNIT;
|
|
m_names[15].resourceId = MK_LDAP_POSTAL_ADDRESS;
|
|
m_names[16].resourceId = MK_LDAP_POSTAL_CODE;
|
|
m_names[17].resourceId = MK_LDAP_SECRETARY;
|
|
m_names[18].resourceId = MK_LDAP_SURNAME;
|
|
m_names[19].resourceId = MK_LDAP_STREET;
|
|
m_names[20].resourceId = MK_LDAP_PHONE_NUMBER;
|
|
m_names[21].resourceId = MK_LDAP_TITLE;
|
|
m_names[22].resourceId = HG87272;
|
|
m_names[23].resourceId = HG73272;
|
|
}
|
|
|
|
for (int i = 0; i < kNumAttributeNames; i++)
|
|
{
|
|
if (!XP_STRCASECMP(attrib, m_names[i].attributeName))
|
|
return XP_GetString (m_names[i].resourceId);
|
|
}
|
|
|
|
// ### Boy, is this hacky. Maybe we can push this into default prefs now that we have excluded attributes
|
|
if (XP_STRCASECMP(attrib, "userpassword") && XP_STRCMP(attrib, "objectclass") &&
|
|
XP_STRCASECMP(attrib, "subtreeaci"))
|
|
return attrib;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int net_LdapToHtml::WriteLineToStream (const char *line, XP_Bool bold, XP_Bool lineBreak, XP_Bool isMailto, XP_Bool isUri, XP_Bool aligntop, XP_Bool isCell)
|
|
{
|
|
#define kMailtoTemplate "<A HREF=\"mailto:%s\">%s</A>"
|
|
#define kUriTemplate "<A HREF=\"%s\">%s</A>"
|
|
|
|
int err = 0;
|
|
int htmlLen = XP_STRLEN(line) + XP_STRLEN("<TD VALIGN=TOP></TD>");
|
|
if (bold)
|
|
htmlLen += XP_STRLEN ("<B></B>");
|
|
if (lineBreak)
|
|
htmlLen += XP_STRLEN(LINEBREAK);
|
|
if (isMailto)
|
|
htmlLen += XP_STRLEN(kMailtoTemplate) + XP_STRLEN(line);
|
|
if (isUri)
|
|
htmlLen += XP_STRLEN(kUriTemplate) + XP_STRLEN(line);
|
|
|
|
char *htmlLine = (char *) XP_ALLOC(htmlLen + 1);
|
|
if (htmlLine)
|
|
{
|
|
htmlLine[0] = '\0';
|
|
if (isCell)
|
|
{
|
|
if (aligntop)
|
|
XP_STRCAT (htmlLine, "<TD VALIGN=TOP>");
|
|
else
|
|
XP_STRCAT (htmlLine, "<TD>");
|
|
}
|
|
|
|
if (bold)
|
|
XP_STRCAT (htmlLine, "<B>");
|
|
|
|
if (isMailto)
|
|
{
|
|
char *mailtoHtml = PR_smprintf (kMailtoTemplate, line, line);
|
|
if (mailtoHtml)
|
|
{
|
|
XP_STRCAT(htmlLine, mailtoHtml);
|
|
XP_FREE(mailtoHtml);
|
|
}
|
|
}
|
|
else if (isUri)
|
|
{
|
|
// RFC 2079 sez the URL comes first, then zero or more spaces, then an optional label
|
|
char *label = NULL;
|
|
char *url = XP_STRDUP(line);
|
|
if (url)
|
|
{
|
|
int i = 0;
|
|
while (url[i] && url[i] != ' ')
|
|
i++;
|
|
if (url[i] == ' ')
|
|
{
|
|
url[i] = '\0';
|
|
|
|
int j = i + 1;
|
|
while (url[j] == ' ')
|
|
j++;
|
|
|
|
label = &url[j];
|
|
}
|
|
}
|
|
|
|
char *uriHtml = PR_smprintf (kUriTemplate, url ? url : line, label ? label : line);
|
|
if (uriHtml)
|
|
{
|
|
XP_STRCAT(htmlLine, uriHtml);
|
|
XP_FREE(uriHtml);
|
|
}
|
|
XP_FREEIF(url);
|
|
}
|
|
else
|
|
XP_STRCAT (htmlLine, line);
|
|
|
|
if (bold)
|
|
XP_STRCAT (htmlLine, "</B> ");
|
|
if (isCell)
|
|
XP_STRCAT (htmlLine, "</TD>");
|
|
if (lineBreak)
|
|
XP_STRCAT (htmlLine, LINEBREAK);
|
|
err = (*(m_stream)->put_block)(m_stream, htmlLine, XP_STRLEN(htmlLine));
|
|
XP_FREE (htmlLine);
|
|
}
|
|
else
|
|
err = MK_OUT_OF_MEMORY;
|
|
return err;
|
|
}
|
|
|
|
|
|
int net_LdapToHtml::WriteAttribute (const char *attrib)
|
|
{
|
|
return WriteLineToStream (attrib, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE);
|
|
}
|
|
|
|
|
|
int net_LdapToHtml::WriteValue (const char *value, XP_Bool isMailto, XP_Bool isUri)
|
|
{
|
|
return WriteLineToStream (value, TRUE, TRUE, isMailto, isUri, FALSE, TRUE);
|
|
}
|
|
|
|
|
|
int net_LdapToHtml::WritePlainLine (const char *line)
|
|
{
|
|
return WriteLineToStream (line, FALSE, TRUE);
|
|
}
|
|
|
|
|
|
XP_Bool net_LdapToHtml::IsMailtoAttribute (const char *attrib)
|
|
{
|
|
if (m_server)
|
|
{
|
|
const char **strings = DIR_GetAttributeStrings (m_server, mail);
|
|
if (strings)
|
|
{
|
|
int i = 0;
|
|
while (strings[i])
|
|
{
|
|
if (!XP_STRCASECMP(attrib, strings[i]))
|
|
return TRUE;
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (!XP_STRCASECMP(attrib, "mail"))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
XP_Bool net_LdapToHtml::IsUriAttribute (const char *attrib)
|
|
{
|
|
switch (XP_TO_LOWER(attrib[0]))
|
|
{
|
|
case 'l':
|
|
if (!XP_STRCASECMP(attrib, "labeleduri") || // preferred
|
|
!XP_STRCASECMP(attrib, "labeledurl")) // old-style
|
|
return TRUE;
|
|
break;
|
|
case 'u':
|
|
if (!XP_STRCASECMP(attrib, "url")) // apparently some servers do this. be lenient in what we accept
|
|
return TRUE;
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
char *net_LdapToHtml::InferUrlBase ()
|
|
{
|
|
// Look in Netlib's data structures to find the URL we're running right now
|
|
// and assume that new URLs have the same host, port, etc
|
|
|
|
// I'm guessing we can just look for everything up to the third slash. If this
|
|
// turns out to be not true, we can do the more expensive thing of running it
|
|
// through ldap_url_parse and then reforming it from the ldap_url_desc
|
|
|
|
char *url = m_ce->URL_s->address;
|
|
|
|
// Small memory wastage here, but this buf is so short-lived, it's probably
|
|
// not worth figuring out the real right size
|
|
char *urlBase = (char*) XP_ALLOC (XP_STRLEN(url));
|
|
|
|
if (urlBase)
|
|
{
|
|
char *tmpSrc = url;
|
|
char *tmpDst = urlBase;
|
|
int slashCount = 0;
|
|
|
|
while (*tmpSrc)
|
|
{
|
|
if (*tmpSrc == '/')
|
|
slashCount++;
|
|
if (slashCount == 3)
|
|
{
|
|
*tmpDst = '\0';
|
|
break;
|
|
}
|
|
else
|
|
*tmpDst++ = *tmpSrc;
|
|
tmpSrc++;
|
|
}
|
|
|
|
}
|
|
return urlBase;
|
|
|
|
}
|
|
|
|
|
|
char *net_LdapToHtml::EscapeSpaces (const char *string)
|
|
{
|
|
int length = XP_STRLEN (string) + 1; // +1 for trailing null
|
|
const char *tmp = string;
|
|
while (*tmp)
|
|
{
|
|
if (*tmp == 0x20)
|
|
length += 2; // From " " to "%20"
|
|
tmp++;
|
|
}
|
|
|
|
char *result = (char*) XP_ALLOC (length);
|
|
if (result)
|
|
{
|
|
tmp = string;
|
|
char *tmp2 = result;
|
|
|
|
while (*tmp)
|
|
{
|
|
if (*tmp == 0x20)
|
|
{
|
|
*tmp2++ = '%';
|
|
*tmp2++ = '2';
|
|
*tmp2++ = '0';
|
|
}
|
|
else
|
|
*tmp2++ = *tmp;
|
|
*tmp++;
|
|
}
|
|
*tmp2 = '\0';
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
char *net_LdapToHtml::ConvertDnToUri (const char *dn)
|
|
{
|
|
char *uri = NULL;
|
|
char **explodedDn = ldap_explode_dn (dn, TRUE /* suppress types? */);
|
|
if (explodedDn)
|
|
{
|
|
char *urlBase = InferUrlBase();
|
|
if (urlBase)
|
|
{
|
|
char *unescapedUrl = PR_smprintf ("%s/%s", urlBase, dn);
|
|
if (unescapedUrl)
|
|
{
|
|
char *escapedUrl = EscapeSpaces (unescapedUrl);
|
|
if (escapedUrl)
|
|
{
|
|
uri = PR_smprintf ("%s %s", escapedUrl, explodedDn[0]);
|
|
XP_FREE(escapedUrl);
|
|
}
|
|
XP_FREE(unescapedUrl);
|
|
}
|
|
XP_FREE(urlBase);
|
|
}
|
|
ldap_value_free (explodedDn);
|
|
}
|
|
|
|
return uri;
|
|
}
|
|
|
|
|
|
char *net_LdapToHtml::GetTableCaption (LDAPMessage *message)
|
|
{
|
|
// Here's a somewhat arbitrary way to decide what the table caption for an entry should be
|
|
|
|
char *caption = NULL;
|
|
|
|
// Look first for a "cn" attribute.
|
|
const char *cnAttrName = GetAttributeString (cn);
|
|
if (cnAttrName)
|
|
{
|
|
char **cnValues = ldap_get_values (m_ldap, message, cnAttrName);
|
|
if (cnValues)
|
|
{
|
|
if (cnValues[0])
|
|
caption = ConvertFromServerCharSet (cnValues[0]);
|
|
ldap_value_free (cnValues);
|
|
}
|
|
}
|
|
|
|
if (!caption)
|
|
{
|
|
// Use the left-most (most specific) component of the DN
|
|
char *dn = ldap_get_dn (m_ldap, message);
|
|
if (dn)
|
|
{
|
|
char **explodedDn = ldap_explode_dn (dn, TRUE /* suppress types? */);
|
|
if (explodedDn && explodedDn[0])
|
|
caption = ConvertFromServerCharSet (explodedDn[0]);
|
|
ldap_value_free (explodedDn);
|
|
ldap_memfree(dn);
|
|
}
|
|
}
|
|
|
|
return caption;
|
|
}
|
|
|
|
|
|
int net_LdapToHtml::WriteEntryToStream (LDAPMessage *message)
|
|
{
|
|
// Write an LDAP directory entry to the HTML stream
|
|
|
|
XP_ASSERT(message && m_stream);
|
|
if (!message || !m_stream)
|
|
return -1;
|
|
|
|
WritePlainLine ("<TABLE ALIGN=CENTER>");
|
|
|
|
char *caption = GetTableCaption (message);
|
|
if (caption)
|
|
{
|
|
char *captionHtml = PR_smprintf ("<TD><CAPTION><FONT COLOR=\"#1908B9\"><FONT SIZE=+2>%s</FONT></FONT> </CAPTION></TD>", caption);
|
|
if (captionHtml)
|
|
{
|
|
WritePlainLine (captionHtml);
|
|
XP_FREE(captionHtml);
|
|
}
|
|
XP_FREE(caption);
|
|
}
|
|
|
|
BerElement *ber;
|
|
char *attrib = ldap_first_attribute (m_ldap, message, &ber);
|
|
while (attrib)
|
|
{
|
|
const char *attribName = LookupAttributeName (attrib);
|
|
if (attribName && !DIR_IsAttributeExcludedFromHtml (m_server, attrib))
|
|
{
|
|
int valueIndex = 0;
|
|
|
|
HG73211
|
|
if (XP_STRCASECMP(attrib, "jpegPhoto") == 0 || XP_STRCASECMP(attrib, "audio") == 0)
|
|
{
|
|
struct berval **berImages = ldap_get_values_len (m_ldap, message, attrib);
|
|
while (berImages[valueIndex])
|
|
{
|
|
WritePlainLine ("<TR>");
|
|
if (valueIndex == 0)
|
|
WriteAttribute (attribName);
|
|
else
|
|
WritePlainLine("<TD></TD>");
|
|
|
|
// ### There are Win16 problems with this strategy. We could use libmime
|
|
// to stream the base64 bits one line at a time, but we'd still need to get the
|
|
// LDAP SDK to do something similar, or to use huge pointers.
|
|
|
|
char *base64Encoding = NET_Base64Encode (
|
|
berImages[valueIndex]->bv_val,
|
|
berImages[valueIndex]->bv_len);
|
|
if (base64Encoding)
|
|
{
|
|
char *htmlLine = NULL;
|
|
if (XP_STRCASECMP(attrib, "jpegPhoto") == 0)
|
|
htmlLine = PR_smprintf ("<TD><IMG SRC=\"data:image/jpeg;base64,%s\"></TD>", base64Encoding);
|
|
else
|
|
htmlLine = PR_smprintf ("<TD><A HREF=\"data:audio/basic;base64,%s\"><IMG SRC=\"/ns-icons/sound.gif\"></A></TD>", base64Encoding);
|
|
if (htmlLine)
|
|
{
|
|
WritePlainLine (htmlLine);
|
|
XP_FREE (htmlLine);
|
|
}
|
|
XP_FREE(base64Encoding);
|
|
}
|
|
WritePlainLine ("</TR>");
|
|
valueIndex++;
|
|
}
|
|
ldap_value_free_len (berImages);
|
|
}
|
|
else
|
|
{
|
|
char **values = ldap_get_values (m_ldap, message, attrib);
|
|
if (values)
|
|
{
|
|
XP_Bool isMailtoAttribute = IsMailtoAttribute (attrib);
|
|
XP_Bool isUriAttribute = IsUriAttribute (attrib);
|
|
XP_Bool isDnAttribute = DIR_IsDnAttribute (m_server, attrib);
|
|
XP_Bool isEscapedAttribute = DIR_IsEscapedAttribute (m_server, attrib);
|
|
|
|
while (values[valueIndex])
|
|
{
|
|
char *decodedUtf8 = ConvertFromServerCharSet (values[valueIndex]);
|
|
if (decodedUtf8)
|
|
{
|
|
WritePlainLine ("<TR>");
|
|
if (valueIndex == 0)
|
|
WriteAttribute (attribName);
|
|
else
|
|
WritePlainLine("<TD></TD>");
|
|
|
|
// This unescaping code is mandated by. DNs are one class of thing, and
|
|
// attributes which are "productions" of strings are another class of thing
|
|
// and plain old string attributes get left alone.
|
|
if (isDnAttribute)
|
|
{
|
|
// Here's some special code to make values which look like DNs into
|
|
// links to LDAP URLs. Cool, huh?
|
|
char *tmp = ConvertDnToUri (decodedUtf8);
|
|
if (tmp)
|
|
{
|
|
XP_FREE(decodedUtf8);
|
|
decodedUtf8 = tmp;
|
|
isUriAttribute = TRUE;
|
|
}
|
|
}
|
|
else if (isEscapedAttribute)
|
|
{
|
|
// Some attributes which are "productions" of a couple of strings have
|
|
// special encoding rules. Decode 'em into something useful here.
|
|
char *tmp = DIR_Unescape (decodedUtf8, TRUE /*makeHtml?*/);
|
|
if (tmp)
|
|
{
|
|
XP_FREE(decodedUtf8);
|
|
decodedUtf8 = tmp;
|
|
}
|
|
}
|
|
|
|
WriteValue (decodedUtf8, isMailtoAttribute, isUriAttribute);
|
|
XP_FREE(decodedUtf8);
|
|
WritePlainLine ("</TR>");
|
|
}
|
|
valueIndex++;
|
|
}
|
|
ldap_value_free (values);
|
|
}
|
|
}
|
|
}
|
|
ldap_memfree (attrib);
|
|
attrib = ldap_next_attribute (m_ldap, message, ber);
|
|
}
|
|
|
|
WritePlainLine ("</TABLE>");
|
|
WritePlainLine ("<HR>");
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// net_LdapReplicator is the class which knows how to read replication entries
|
|
// from the LDAP server to the local Address Book using the "change log" format
|
|
// specified in:
|
|
// http://ds.internic.net/internet-drafts/draft-good-ldap-changelog-00.txt
|
|
//
|
|
|
|
#include "garray.h"
|
|
|
|
extern "C"
|
|
{
|
|
extern int MK_OUT_OF_MEMORY;
|
|
extern int MK_LDAP_REPL_CANT_SYNC_REPLICA;
|
|
extern int MK_LDAP_REPL_DSE_BOGUS;
|
|
extern int MK_LDAP_REPL_CHANGELOG_BOGUS;
|
|
extern int MK_LDAP_REPL_PROGRESS_TITLE;
|
|
extern int MK_LDAP_REPL_CONNECTING;
|
|
extern int MK_LDAP_REPL_CHANGE_ENTRY;
|
|
extern int MK_LDAP_REPL_NEW_ENTRY;
|
|
extern int MK_LDAP_AUTHDN_LOOKUP_FAILED;
|
|
}
|
|
|
|
#define LDAP_REPL_CHUNK_SIZE 50
|
|
|
|
|
|
static void CancelReplication(void *closure)
|
|
{
|
|
MWContext *progressContext = (MWContext *)closure;
|
|
if (progressContext)
|
|
XP_InterruptContext(progressContext);
|
|
}
|
|
|
|
|
|
class net_LdapReplicator : public net_LdapConnectionData
|
|
{
|
|
public:
|
|
net_LdapReplicator(ActiveEntry *ce);
|
|
virtual ~net_LdapReplicator();
|
|
|
|
virtual int Interrupt ();
|
|
|
|
virtual int OnBindCompleted ();
|
|
virtual int OnEntryFound (LDAPMessage *message);
|
|
virtual int OnSearchCompleted ();
|
|
virtual XP_Bool OnError(int err);
|
|
|
|
protected:
|
|
|
|
typedef enum _LdapReplicationState
|
|
{
|
|
kInitializing,
|
|
kAuthorizing,
|
|
kBinding,
|
|
kParsingRootDSE,
|
|
kReplicatingChanges,
|
|
kReplicatingAdditions,
|
|
kReplicatingEntries
|
|
} LdapReplicationState;
|
|
|
|
virtual LdapConnectionType GetConnectionType() { return kReplicatorConnection; }
|
|
|
|
XP_Bool CollectUserCredentials ();
|
|
void SaveCredentialsToPrefs ();
|
|
void UpdateProgress ();
|
|
|
|
int Load (ActiveEntry *);
|
|
int Process ();
|
|
int Initialize ();
|
|
int Authorize ();
|
|
int Bind ();
|
|
int SearchRootDSE ();
|
|
int Replicate (XP_Bool initial = FALSE);
|
|
int ProcessAdditions ();
|
|
|
|
int ParseAuthorizationEntry (LDAPMessage *);
|
|
int ParseRootDSE (LDAPMessage *);
|
|
int ParseChangelogEntry (LDAPMessage *);
|
|
int ParseInitialEntry (LDAPMessage *);
|
|
void ParseReplicationUrl(const char *);
|
|
|
|
XP_Bool m_initialReplicationOkay;
|
|
XP_Bool m_changesReturned;
|
|
char *m_authDn;
|
|
char *m_authPwd;
|
|
char *m_valueUsedToFindDn;
|
|
char *m_dnOfChangelog; // Root DSE's setting for the DN suffix of changelog entries
|
|
char *m_dataVersion; // Root DSE's setting to scope change numbers. The data version
|
|
int32 m_firstChangeNumber; // Root DSE's setting for oldest change it knows
|
|
int32 m_lastChangeNumber; // Root DSE's setting for newest change it knows
|
|
// gets reset whenever the server's DB gets reloaded from LDIF
|
|
|
|
int32 m_progressEntry;
|
|
int32 m_progressTotal;
|
|
MWContext *m_progressContext;
|
|
pw_ptr m_progressWindow;
|
|
|
|
LdapReplicationState m_replState;
|
|
CXP_GrowableArray m_dnsToAdd;
|
|
|
|
AB_ContainerInfo *m_container;
|
|
|
|
static char *m_rootReplicationAttribs[5];
|
|
static char *m_entryReplicationAttribs[3];
|
|
};
|
|
|
|
char *net_LdapReplicator::m_rootReplicationAttribs[5] =
|
|
{ "changelog", "firstChangeNumber", "lastChangeNumber", "dataVersion", NULL };
|
|
char *net_LdapReplicator::m_entryReplicationAttribs[3] =
|
|
{ "targetdn", "changetype", NULL };
|
|
|
|
|
|
net_LdapReplicator::net_LdapReplicator (ActiveEntry *ce) : net_LdapConnectionData (ce, FALSE)
|
|
{
|
|
m_replState = kInitializing;
|
|
|
|
m_initialReplicationOkay = FALSE;
|
|
m_changesReturned = FALSE;
|
|
m_authDn = NULL;
|
|
m_authPwd = NULL;
|
|
m_valueUsedToFindDn = NULL;
|
|
m_dnOfChangelog = NULL;
|
|
m_dataVersion = NULL;
|
|
m_firstChangeNumber = 0;
|
|
m_lastChangeNumber = 0;
|
|
|
|
m_progressEntry = 0;
|
|
m_progressTotal = 0;
|
|
m_progressContext = NULL;
|
|
m_progressWindow = NULL;
|
|
|
|
m_container = NULL;
|
|
}
|
|
|
|
net_LdapReplicator::~net_LdapReplicator ()
|
|
{
|
|
int size;
|
|
|
|
XP_FREEIF(m_authDn);
|
|
XP_FREEIF(m_authPwd);
|
|
XP_FREEIF(m_dnOfChangelog);
|
|
XP_FREEIF(m_dataVersion);
|
|
|
|
size = m_dnsToAdd.Size();
|
|
if (size > 0)
|
|
{
|
|
while (--size >= 0)
|
|
XP_FREE(m_dnsToAdd[size]);
|
|
}
|
|
|
|
if (m_container)
|
|
{
|
|
AB_EndReplication(m_container);
|
|
m_container = NULL;
|
|
}
|
|
|
|
if (m_progressWindow)
|
|
{
|
|
PW_Hide(m_progressWindow);
|
|
PW_Destroy(m_progressWindow);
|
|
|
|
if (m_progressContext)
|
|
PW_DestroyProgressContext(m_progressContext);
|
|
}
|
|
}
|
|
|
|
|
|
int net_LdapReplicator::OnBindCompleted ()
|
|
{
|
|
return SearchRootDSE();
|
|
}
|
|
|
|
int net_LdapReplicator::OnEntryFound (LDAPMessage *message)
|
|
{
|
|
int status = 0;
|
|
|
|
switch (m_replState)
|
|
{
|
|
case kAuthorizing:
|
|
status = ParseAuthorizationEntry (message);
|
|
break;
|
|
case kParsingRootDSE:
|
|
status = ParseRootDSE (message);
|
|
break;
|
|
case kReplicatingChanges:
|
|
status = ParseChangelogEntry (message);
|
|
break;
|
|
case kReplicatingAdditions:
|
|
case kReplicatingEntries:
|
|
status = ParseInitialEntry (message);
|
|
break;
|
|
default:
|
|
XP_ASSERT(FALSE);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
int net_LdapReplicator::OnSearchCompleted ()
|
|
{
|
|
int status = 0;
|
|
|
|
switch (m_replState)
|
|
{
|
|
case kAuthorizing:
|
|
status = Bind();
|
|
break;
|
|
case kParsingRootDSE:
|
|
status = Replicate (TRUE);
|
|
break;
|
|
case kReplicatingAdditions:
|
|
m_server->replInfo->lastChangeNumber++;
|
|
/* fall-through */
|
|
case kReplicatingChanges:
|
|
status = ProcessAdditions ();
|
|
break;
|
|
case kReplicatingEntries:
|
|
if (m_initialReplicationOkay)
|
|
m_server->replInfo->lastChangeNumber = m_lastChangeNumber;
|
|
if (m_server->replInfo->lastChangeNumber >= 0)
|
|
{
|
|
SaveCredentialsToPrefs ();
|
|
DIR_SaveServerPreferences (FE_GetDirServers());
|
|
}
|
|
status = MK_CONNECTED;
|
|
break;
|
|
default:
|
|
XP_ASSERT(FALSE);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
XP_Bool net_LdapReplicator::OnError(int err)
|
|
{
|
|
/* If we were performing the initial replication, destroy the subset of
|
|
* entries that we had replicated so far.
|
|
*/
|
|
if (m_server->replInfo->lastChangeNumber < 0)
|
|
AB_RemoveReplicaEntries(m_container);
|
|
|
|
/* Because replication can be very expensive, we must try to save the data
|
|
* we have already replicated even in the event of an error.
|
|
*/
|
|
DIR_SaveServerPreferences (FE_GetDirServers());
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
XP_Bool net_LdapReplicator::CollectUserCredentials ()
|
|
{
|
|
/* First look in the DIR_Server we read out of the prefs. If it already
|
|
* knows the user's credentials, there's no need to ask again here.
|
|
*/
|
|
if (m_server->authDn && XP_STRLEN(m_server->authDn))
|
|
m_authDn = XP_STRDUP (m_server->authDn);
|
|
if (m_server->savePassword && m_server->password && XP_STRLEN(m_server->password))
|
|
m_authPwd = XP_STRDUP (m_server->password);
|
|
if (m_authDn && m_authPwd)
|
|
return TRUE;
|
|
|
|
XP_Bool rc = FALSE;
|
|
char *prompt = PR_smprintf (XP_GetString(MK_LDAP_AUTH_PROMPT),
|
|
DIR_GetAttributeName (m_server, auth),
|
|
m_server->description);
|
|
if (prompt)
|
|
{
|
|
/* TBD: Only prompt for password if we already have the DN
|
|
*/
|
|
char *username = NULL, *password = NULL;
|
|
if (m_authDn)
|
|
{
|
|
password = FE_PromptPassword (m_ce->window_id, prompt);
|
|
rc = (password != NULL);
|
|
}
|
|
else
|
|
rc = FE_PromptUsernameAndPassword (m_ce->window_id, prompt, &username, &password);
|
|
|
|
if (rc)
|
|
{
|
|
if (username)
|
|
m_valueUsedToFindDn = username;
|
|
if (password)
|
|
{
|
|
m_authPwd = SECNAV_MungeString (password);
|
|
XP_FREE(password);
|
|
}
|
|
}
|
|
|
|
XP_FREE(prompt);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
void net_LdapReplicator::SaveCredentialsToPrefs ()
|
|
{
|
|
if (m_authDn)
|
|
DIR_SetAuthDN (m_server, m_authDn);
|
|
|
|
if (m_server->savePassword && m_authPwd)
|
|
DIR_SetPassword (m_server, m_authPwd);
|
|
}
|
|
|
|
void net_LdapReplicator::UpdateProgress ()
|
|
{
|
|
++m_progressEntry;
|
|
|
|
if (!m_progressContext)
|
|
return;
|
|
|
|
if (m_replState == kReplicatingAdditions)
|
|
{
|
|
int percent = (m_progressEntry * 100) / m_progressTotal;
|
|
FE_SetProgressBarPercent (m_progressContext, percent);
|
|
}
|
|
|
|
if ((m_progressEntry % MAX(1, m_progressEntry / 10)) == 0)
|
|
{
|
|
char *status;
|
|
|
|
if (m_replState == kReplicatingAdditions)
|
|
status = PR_smprintf (XP_GetString(MK_LDAP_REPL_CHANGE_ENTRY), m_progressEntry);
|
|
else
|
|
status = PR_smprintf (XP_GetString(MK_LDAP_REPL_NEW_ENTRY), m_progressEntry);
|
|
|
|
if (status)
|
|
{
|
|
FE_Progress (m_progressContext, status);
|
|
XP_FREE(status);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int net_LdapReplicator::Load (ActiveEntry *ce)
|
|
{
|
|
XP_ASSERT(ce);
|
|
XP_ASSERT(ce->URL_s);
|
|
XP_ASSERT(ce->URL_s->address);
|
|
|
|
/* If this URL was not started via NET_ReplicateDirectory, we need to
|
|
* create a progress window for it.
|
|
*/
|
|
if (!ce->URL_s->internal_url)
|
|
{
|
|
m_progressContext = PW_CreateProgressContext();
|
|
if (m_progressContext)
|
|
{
|
|
m_progressWindow = PW_Create(NULL, pwStandard);
|
|
if (m_progressWindow)
|
|
{
|
|
PW_AssociateWindowWithContext(m_progressContext, m_progressWindow);
|
|
PW_SetWindowTitle(m_progressWindow, XP_GetString(MK_LDAP_REPL_PROGRESS_TITLE));
|
|
PW_SetLine1(m_progressWindow, NULL);
|
|
PW_SetLine2(m_progressWindow, NULL);
|
|
PW_SetProgressRange(m_progressWindow, 0, 0);
|
|
PW_SetCancelCallback(m_progressWindow, CancelReplication, m_progressContext);
|
|
PW_Show(m_progressWindow);
|
|
}
|
|
else
|
|
{
|
|
PW_DestroyProgressContext(m_progressContext);
|
|
m_progressContext = NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
m_progressContext = ce->window_id;
|
|
|
|
if (m_progressContext)
|
|
{
|
|
FE_Progress (m_progressContext, XP_GetString(MK_LDAP_REPL_CONNECTING));
|
|
FE_SetProgressBarPercent (m_progressContext, 0);
|
|
}
|
|
|
|
/* Get the DIR_Server that matches the hostname in the URL.
|
|
*/
|
|
ParseReplicationUrl (ce->URL_s->address);
|
|
if (m_server == NULL)
|
|
return -1;
|
|
|
|
/* Initialize the LDAP structure
|
|
*
|
|
* TBD: need to account for the case where one host name corresponds to
|
|
* more than one machine (e.g. directory.netscape.com).
|
|
*/
|
|
m_ldap = ldap_init(m_server->serverName, m_server->port);
|
|
if (!m_ldap)
|
|
return MK_OUT_OF_MEMORY;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int net_LdapReplicator::Process ()
|
|
{
|
|
int status = 0;
|
|
|
|
if (m_replState == kInitializing)
|
|
status = Initialize ();
|
|
else
|
|
status = net_LdapConnectionData::Process ();
|
|
|
|
return status;
|
|
}
|
|
|
|
int net_LdapReplicator::Initialize ()
|
|
{
|
|
int status = 0;
|
|
|
|
/* If we are supposed to authenticate with the server, attempt to bind to
|
|
* the server using authentication info from the prefs or the user.
|
|
*/
|
|
if (m_server->enableAuth)
|
|
{
|
|
if (CollectUserCredentials())
|
|
{
|
|
/* If we don't have an authorization DN but we have an email id,
|
|
* then try to look up the authorization DN.
|
|
*/
|
|
if (!m_authDn && m_valueUsedToFindDn)
|
|
return Authorize();
|
|
else
|
|
return Bind();
|
|
}
|
|
}
|
|
|
|
return SearchRootDSE();
|
|
}
|
|
|
|
int net_LdapReplicator::Authorize ()
|
|
{
|
|
int status = 0;
|
|
|
|
char *attribs[2];
|
|
attribs[0] = XP_STRDUP(DIR_GetFirstAttributeString (m_server, cn));
|
|
attribs[1] = NULL;
|
|
|
|
char *filter = PR_smprintf ("(%s=%s)", DIR_GetFirstAttributeString (m_server, auth), m_valueUsedToFindDn);
|
|
|
|
int msgId = -1;
|
|
if (attribs[0] && filter)
|
|
msgId = ldap_search (m_ldap, m_server->searchBase, LDAP_SCOPE_SUBTREE, filter, &attribs[0], 0);
|
|
else
|
|
status = MK_OUT_OF_MEMORY;
|
|
|
|
XP_FREEIF(attribs[0]);
|
|
XP_FREEIF(filter);
|
|
|
|
if (msgId != -1)
|
|
{
|
|
m_replState = kAuthorizing;
|
|
m_messageId = msgId;
|
|
}
|
|
else if (status == 0)
|
|
{
|
|
DisplayError (XP_LDAP_SEARCH_FAILED, GetHostDescription(), ldap_get_lderrno(m_ldap,NULL,NULL));
|
|
status = -1;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
int net_LdapReplicator::Bind ()
|
|
{
|
|
char *upwd = SECNAV_UnMungeString (m_authPwd);
|
|
if (upwd)
|
|
{
|
|
int msgId = ldap_simple_bind(m_ldap, m_authDn, upwd);
|
|
XP_FREE(upwd);
|
|
|
|
if (msgId != -1)
|
|
{
|
|
m_replState = kBinding;
|
|
m_messageId = msgId;
|
|
}
|
|
else
|
|
{
|
|
DisplayError (XP_LDAP_BIND_FAILED, GetHostDescription(), ldap_get_lderrno(m_ldap,NULL,NULL));
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
return MK_OUT_OF_MEMORY;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int net_LdapReplicator::SearchRootDSE ()
|
|
{
|
|
/* Kick things off with a search of the Root DSE for the replication data.
|
|
*/
|
|
int msgId = ldap_search(m_ldap, "", LDAP_SCOPE_BASE, "objectclass=*",
|
|
m_rootReplicationAttribs, 0);
|
|
if (msgId != -1)
|
|
{
|
|
m_replState = kParsingRootDSE;
|
|
m_messageId = msgId;
|
|
}
|
|
else
|
|
{
|
|
DisplayError (XP_LDAP_SEARCH_FAILED, GetHostDescription(), ldap_get_lderrno(m_ldap,NULL,NULL));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int net_LdapReplicator::Replicate (XP_Bool initial)
|
|
{
|
|
int msgId = -1;
|
|
|
|
/* If the last completed change is -1, we need to perform an initial
|
|
* replication.
|
|
*/
|
|
if (m_server->replInfo->lastChangeNumber == -1)
|
|
{
|
|
/* We're going to replicate every entry.
|
|
*/
|
|
m_replState = kReplicatingEntries;
|
|
|
|
/* Delete the old replica database and start a fresh one.
|
|
*/
|
|
AB_RemoveReplicaEntries(m_container);
|
|
|
|
/* Try to do a search which will return every entry (limited by the
|
|
* filter preference) on the server.
|
|
*
|
|
* TBD: This fails even on thehole unless Directory Manager is used.
|
|
* We must have a special replication dn or find some other way
|
|
* to initialize the database.
|
|
*/
|
|
msgId = ldap_search(m_ldap, m_server->searchBase, LDAP_SCOPE_SUBTREE,
|
|
DIR_GetReplicationFilter(m_server), AB_GetReplicaAttributeNames(m_container), 0);
|
|
}
|
|
else if (m_lastChangeNumber > m_server->replInfo->lastChangeNumber)
|
|
{
|
|
/* We're going to replicate only the changes.
|
|
*/
|
|
m_replState = kReplicatingChanges;
|
|
|
|
if (initial)
|
|
m_progressTotal = m_lastChangeNumber - m_server->replInfo->lastChangeNumber;
|
|
|
|
char *filter = PR_smprintf ("(&(changenumber>=%d)(changenumber<=%d))",
|
|
m_server->replInfo->lastChangeNumber + 1,
|
|
MIN(m_lastChangeNumber,
|
|
m_server->replInfo->lastChangeNumber + LDAP_REPL_CHUNK_SIZE));
|
|
if (filter)
|
|
{
|
|
msgId = ldap_search(m_ldap, m_dnOfChangelog, LDAP_SCOPE_ONELEVEL,
|
|
filter, m_entryReplicationAttribs, 0);
|
|
XP_FREE(filter);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!initial)
|
|
{
|
|
SaveCredentialsToPrefs ();
|
|
DIR_SaveServerPreferences (FE_GetDirServers());
|
|
}
|
|
return MK_CONNECTED;
|
|
}
|
|
|
|
if (msgId != -1)
|
|
m_messageId = msgId;
|
|
else
|
|
{
|
|
DisplayError (XP_LDAP_SEARCH_FAILED, GetHostDescription(), ldap_get_lderrno(m_ldap,NULL,NULL));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int net_LdapReplicator::ProcessAdditions()
|
|
{
|
|
int size = m_dnsToAdd.Size();
|
|
if (size > 0)
|
|
{
|
|
int msgId = -1;
|
|
char *targetDn = (char *)(m_dnsToAdd[size - 1]);
|
|
|
|
msgId = ldap_search(m_ldap, targetDn, LDAP_SCOPE_SUBTREE,
|
|
DIR_GetReplicationFilter(m_server), AB_GetReplicaAttributeNames(m_container), 0);
|
|
|
|
XP_FREE(targetDn);
|
|
m_dnsToAdd.SetSize(size - 1);
|
|
|
|
if (msgId != -1)
|
|
{
|
|
m_messageId = msgId;
|
|
m_replState = kReplicatingAdditions;
|
|
}
|
|
else
|
|
{
|
|
DisplayError (XP_LDAP_SEARCH_FAILED, GetHostDescription(), ldap_get_lderrno(m_ldap,NULL,NULL));
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* We must verify that the changelog search returned at least one
|
|
* entry. It may return none if the user has insufficient access.
|
|
* If so, we must abort the replication; otherwise, we will get stuck
|
|
* in an infinite loop.
|
|
*/
|
|
if (m_changesReturned)
|
|
{
|
|
return Replicate();
|
|
}
|
|
else
|
|
{
|
|
DisplayError (XP_LDAP_SEARCH_FAILED, GetHostDescription(), LDAP_INSUFFICIENT_ACCESS);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int net_LdapReplicator::Interrupt ()
|
|
{
|
|
/* Put the last processed change number into prefs
|
|
*/
|
|
if (m_server->replInfo && m_server->replInfo->lastChangeNumber >= 0)
|
|
DIR_SaveServerPreferences (FE_GetDirServers());
|
|
|
|
/* If we were performing the initial replication, destroy the subset of
|
|
* entries that we had replicated so far.
|
|
*/
|
|
else
|
|
AB_RemoveReplicaEntries(m_container);
|
|
|
|
if (m_ldap)
|
|
ldap_abandon (m_ldap, m_messageId);
|
|
|
|
return net_LdapConnectionData::Interrupt(); // base class will unbind
|
|
}
|
|
|
|
|
|
void net_LdapReplicator::ParseReplicationUrl(const char *ldap_url)
|
|
{
|
|
char *url = XP_STRDUP(ldap_url);
|
|
LDAPURLDesc *desc = (LDAPURLDesc *)XP_CALLOC(1, sizeof(LDAPURLDesc));
|
|
if (desc && url)
|
|
{
|
|
char *next;
|
|
char *fullurl = url;
|
|
|
|
/* Pull out the connection type (and fill in the default port numbers).
|
|
*/
|
|
if (XP_STRCASECMP(url, "repl-ldaps://") == 0)
|
|
{
|
|
url += 13;
|
|
desc->lud_options |= LDAP_URL_OPT_SECURE;
|
|
desc->lud_port = LDAPS_PORT;
|
|
}
|
|
else
|
|
{
|
|
url += 12;
|
|
desc->lud_port = LDAP_PORT;
|
|
}
|
|
|
|
/* Pull out the authorized DN and password if they are present and
|
|
* requested.
|
|
*/
|
|
char *authDn=NULL, *authPwd=NULL;
|
|
next = XP_STRCHR(url, '@');
|
|
if (next)
|
|
{
|
|
next[0] = '\0';
|
|
|
|
char *pwd = XP_STRCHR(url, ':');
|
|
if (pwd)
|
|
{
|
|
pwd[0] = '\0';
|
|
authPwd = pwd + 1;
|
|
}
|
|
authDn = url;
|
|
|
|
url = next + 1;
|
|
}
|
|
|
|
/* Pull out the host name, the port number and the base dn.
|
|
*/
|
|
next = XP_STRCHR(url, ':');
|
|
if (!next)
|
|
next = XP_STRCHR(url, '/');
|
|
if (!next)
|
|
desc->lud_host = url;
|
|
else
|
|
{
|
|
if (next[0] == ':')
|
|
desc->lud_port = 0;
|
|
next[0] = '\0';
|
|
|
|
desc->lud_host = url;
|
|
url = next + 1;
|
|
|
|
if (desc->lud_port == 0)
|
|
{
|
|
next = XP_STRCHR(url, '/');
|
|
if (next)
|
|
next[0] = '\0';
|
|
desc->lud_port = XP_ATOI(url);
|
|
}
|
|
|
|
if (next)
|
|
desc->lud_dn = next + 1;
|
|
}
|
|
|
|
/* The host name, port number and base dn must be present and valid.
|
|
*/
|
|
if (desc->lud_host && desc->lud_port != 0 && desc->lud_dn)
|
|
{
|
|
AddLdapServerToPrefs(desc);
|
|
|
|
if (m_server)
|
|
{
|
|
if (!m_server->authDn && authDn)
|
|
m_server->authDn = XP_STRDUP(authDn);
|
|
if (!m_server->password && authPwd)
|
|
m_server->password = XP_STRDUP(authPwd);
|
|
}
|
|
}
|
|
|
|
url = fullurl;
|
|
}
|
|
|
|
XP_FREEIF(desc);
|
|
XP_FREEIF(url);
|
|
}
|
|
|
|
|
|
int net_LdapReplicator::ParseAuthorizationEntry (LDAPMessage *message)
|
|
{
|
|
if (!m_authDn)
|
|
{
|
|
char *authDn = ldap_get_dn (m_ldap, message);
|
|
m_authDn = XP_STRDUP(authDn);
|
|
ldap_memfree(authDn);
|
|
}
|
|
else
|
|
{
|
|
/* Better not have more than one hit for this search.
|
|
* We don't have a way for the user to choose which "phil" they are.
|
|
*/
|
|
DisplayError (MK_LDAP_AUTHDN_LOOKUP_FAILED, GetHostDescription(), 0);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int net_LdapReplicator::ParseRootDSE (LDAPMessage *message)
|
|
{
|
|
int i;
|
|
char **values = NULL;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
values = ldap_get_values (m_ldap, message, m_rootReplicationAttribs[i]);
|
|
if (values && values[0])
|
|
{
|
|
switch (i) {
|
|
case 0:
|
|
m_dnOfChangelog = XP_STRDUP (values[0]);
|
|
break;
|
|
case 1:
|
|
m_firstChangeNumber = XP_ATOI (values[0]);
|
|
break;
|
|
case 2:
|
|
m_lastChangeNumber = XP_ATOI (values[0]);
|
|
break;
|
|
case 3:
|
|
m_dataVersion = XP_STRDUP (values[0]);
|
|
break;
|
|
}
|
|
|
|
ldap_value_free (values);
|
|
}
|
|
else
|
|
{
|
|
DisplayError (MK_LDAP_REPL_DSE_BOGUS, GetHostDescription(), 0);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Parse the root DSE. DIR_ValidateRootDSE can return three different
|
|
* types of values;
|
|
* <0 - There is no replication information available for this server;
|
|
* we should abort replication.
|
|
* 0 - Either our replication data matches what the server returned or
|
|
* we have never replicated. In the former case, perform an
|
|
* incremental update. In the later case, generate the replica
|
|
* from scratch.
|
|
* >0 - The server's replication data does not match ours; reinitialize
|
|
* our replica from scratch.
|
|
*/
|
|
int rc = DIR_ValidateRootDSE (m_server, m_dataVersion, m_firstChangeNumber, m_lastChangeNumber);
|
|
if (rc < 0)
|
|
{
|
|
DisplayError (MK_LDAP_REPL_DSE_BOGUS, GetHostDescription(), 0);
|
|
return -1;
|
|
}
|
|
|
|
/* Get access to the container info that will manage the interaction
|
|
* between ldap server connection and the replica database.
|
|
*
|
|
* NOTE: This must be called after DIR_ValidateRootDSE, because the later
|
|
* is what sets up the replication information in the DIR_Server
|
|
* which in turn is required by the container to replicate.
|
|
*/
|
|
m_container = AB_BeginReplication(m_progressContext, m_server);
|
|
if (m_container == NULL)
|
|
{
|
|
DisplayError (MK_LDAP_REPL_CANT_SYNC_REPLICA, GetHostDescription(), 0);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Here we get an entry from the changelog and try to figure out what kind of
|
|
// change happened to the entry given by the targetdn.
|
|
int net_LdapReplicator::ParseChangelogEntry (LDAPMessage *message)
|
|
{
|
|
char **values = NULL;
|
|
char *targetDn = NULL;
|
|
|
|
values = ldap_get_values (m_ldap, message, "targetdn");
|
|
if (values && values[0])
|
|
{
|
|
targetDn = XP_STRDUP(values[0]);
|
|
ldap_value_free (values);
|
|
}
|
|
else
|
|
{
|
|
DisplayError (MK_LDAP_REPL_CHANGELOG_BOGUS, GetHostDescription(), 0);
|
|
return -1;
|
|
}
|
|
|
|
values = ldap_get_values (m_ldap, message, "changetype");
|
|
if (values && values[0])
|
|
{
|
|
if (!XP_STRCASECMP(values[0], "add"))
|
|
{
|
|
m_dnsToAdd.Add (targetDn);
|
|
}
|
|
else if (!XP_STRCASECMP(values[0], "mod"))
|
|
{
|
|
AB_DeleteReplicaEntry (m_container,targetDn);
|
|
m_dnsToAdd.Add (targetDn);
|
|
}
|
|
else if (!XP_STRCASECMP(values[0], "del"))
|
|
{
|
|
AB_DeleteReplicaEntry (m_container,targetDn);
|
|
UpdateProgress();
|
|
}
|
|
ldap_value_free (values);
|
|
}
|
|
else
|
|
{
|
|
DisplayError (MK_LDAP_REPL_CHANGELOG_BOGUS, GetHostDescription(), 0);
|
|
return -1;
|
|
}
|
|
|
|
m_changesReturned = TRUE;
|
|
return 0;
|
|
}
|
|
|
|
/* ParseInitialEntry
|
|
*
|
|
* Method for adding an entry to the replication database.
|
|
*/
|
|
int net_LdapReplicator::ParseInitialEntry (LDAPMessage *message)
|
|
{
|
|
message = ldap_first_entry (m_ldap, message);
|
|
if (message)
|
|
{
|
|
int mapSize = AB_GetNumReplicaAttributes(m_container);
|
|
char **mapNames = AB_GetReplicaAttributeNames(m_container);
|
|
char **valueList = (char **)XP_ALLOC(sizeof(char *) * mapSize);
|
|
if (!valueList)
|
|
{
|
|
m_initialReplicationOkay = FALSE;
|
|
return MK_OUT_OF_MEMORY;
|
|
}
|
|
|
|
/* Copy each of the attribute values from the LDAP result into the index
|
|
* in the value array that corresponds to the same index in the attribute
|
|
* mapping structure.
|
|
*/
|
|
int i, indexCn = -1, indexOrg = -1;
|
|
char **scratchArray = NULL;
|
|
for (i = 0; i < mapSize; i++)
|
|
{
|
|
scratchArray = ldap_get_values (m_ldap, message, mapNames[i]);
|
|
if (scratchArray && scratchArray[0])
|
|
{
|
|
valueList[i] = DIR_ConvertFromServerCharSet (m_server, scratchArray[0], GetLocalCharSet());
|
|
ldap_value_free (scratchArray);
|
|
}
|
|
else
|
|
{
|
|
if (AB_ReplicaAttributeMatchesId(m_container, i, cn))
|
|
indexCn = i;
|
|
else if (AB_ReplicaAttributeMatchesId(m_container, i, o))
|
|
indexOrg = i;
|
|
|
|
valueList[i] = NULL;
|
|
}
|
|
}
|
|
|
|
/* If we didn't get some attributes we wanted, try to pick them up from the DN
|
|
*/
|
|
if (indexOrg != -1 || indexCn != -1)
|
|
{
|
|
char *scratch;
|
|
if (scratch = ldap_get_dn (m_ldap, message))
|
|
{
|
|
if ((scratchArray = ldap_explode_dn (scratch, FALSE)) != NULL)
|
|
{
|
|
char *dnComponent = NULL;
|
|
for (i = 0; (dnComponent = scratchArray[i]) != NULL; i++)
|
|
{
|
|
if (indexOrg != -1 && !XP_STRNCASECMP (dnComponent, "o=", 2))
|
|
{
|
|
valueList[indexOrg] = DIR_ConvertFromServerCharSet (m_server, dnComponent+2, GetLocalCharSet());
|
|
indexOrg = -1;
|
|
}
|
|
if (indexCn != -1 && !XP_STRNCASECMP (dnComponent, "cn=", 3))
|
|
{
|
|
valueList[indexCn] = DIR_ConvertFromServerCharSet (m_server, dnComponent+3, GetLocalCharSet());
|
|
indexCn = -1;
|
|
}
|
|
}
|
|
ldap_value_free (scratchArray);
|
|
}
|
|
ldap_memfree (scratch);
|
|
}
|
|
}
|
|
|
|
/* If the entry is valid, add it to the replica database. Also set the
|
|
* initial replication okay flag to true in case we are performing an
|
|
* initial replication. Finally, update the progress window.
|
|
*/
|
|
if (indexCn == -1 && AB_AddReplicaEntry (m_container, valueList) == AB_SUCCESS)
|
|
{
|
|
m_initialReplicationOkay = TRUE;
|
|
UpdateProgress();
|
|
}
|
|
|
|
for (i = 0; i < mapSize; i++)
|
|
XP_FREEIF(valueList[i]);
|
|
XP_FREE(valueList);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// net_OfflineLdapReplicator is the class which knows which directory replicas
|
|
// should be updated and how to replicate them in succession.
|
|
//
|
|
|
|
class net_OfflineLdapReplicator
|
|
{
|
|
public:
|
|
net_OfflineLdapReplicator(MSG_Pane *, DIR_Server *server = NULL);
|
|
~net_OfflineLdapReplicator();
|
|
|
|
XP_Bool ReplicateDirectory();
|
|
|
|
static void ReplicateNextDirectory(URL_Struct *, int, MWContext *);
|
|
|
|
protected:
|
|
MSG_Pane *m_pane;
|
|
XP_List *m_serverList;
|
|
MWContext *m_progressContext;
|
|
pw_ptr m_progressWindow;
|
|
};
|
|
|
|
|
|
net_OfflineLdapReplicator::net_OfflineLdapReplicator(MSG_Pane *pane, DIR_Server *server)
|
|
{
|
|
m_pane = pane;
|
|
|
|
m_progressContext = NULL;
|
|
m_progressWindow = NULL;
|
|
|
|
m_serverList = XP_ListNew();
|
|
if (m_serverList)
|
|
{
|
|
if (server)
|
|
{
|
|
if (DIR_TestFlag(server, DIR_REPLICATION_ENABLED))
|
|
{
|
|
/* Create a progress window.
|
|
*/
|
|
m_progressContext = PW_CreateProgressContext();
|
|
if (m_progressContext)
|
|
{
|
|
m_progressWindow = PW_Create(NULL, pwStandard);
|
|
if (m_progressWindow)
|
|
{
|
|
PW_AssociateWindowWithContext(m_progressContext, m_progressWindow);
|
|
PW_SetWindowTitle(m_progressWindow, XP_GetString(MK_LDAP_REPL_PROGRESS_TITLE));
|
|
PW_SetLine1(m_progressWindow, NULL);
|
|
PW_SetLine2(m_progressWindow, NULL);
|
|
PW_SetProgressRange(m_progressWindow, 0, 0);
|
|
PW_SetCancelCallback(m_progressWindow, CancelReplication, m_progressContext);
|
|
PW_Show(m_progressWindow);
|
|
}
|
|
else
|
|
{
|
|
PW_DestroyProgressContext(m_progressContext);
|
|
}
|
|
}
|
|
|
|
XP_ListAddObjectToEnd(m_serverList, server);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
XP_ASSERT(m_pane);
|
|
m_progressContext = m_pane->GetContext();
|
|
|
|
XP_List *serverList = FE_GetDirServers();
|
|
for (int i = 1; i <= XP_ListCount(serverList); i++)
|
|
{
|
|
server = (DIR_Server *)XP_ListGetObjectNum(serverList, i);
|
|
if (DIR_TestFlag(server, DIR_REPLICATION_ENABLED))
|
|
XP_ListAddObjectToEnd(m_serverList, server);
|
|
}
|
|
}
|
|
|
|
if (XP_ListCount(m_serverList) == 0)
|
|
{
|
|
XP_ListDestroy(m_serverList);
|
|
m_serverList = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
net_OfflineLdapReplicator::~net_OfflineLdapReplicator()
|
|
{
|
|
if (m_serverList)
|
|
XP_ListDestroy(m_serverList);
|
|
|
|
/* We destroy the progress window, but the pane destroys the context?
|
|
* This seems kind of weird, but it's the way things work.
|
|
*/
|
|
if (m_progressWindow)
|
|
{
|
|
PW_Hide(m_progressWindow);
|
|
PW_Destroy(m_progressWindow);
|
|
}
|
|
}
|
|
|
|
|
|
XP_Bool net_OfflineLdapReplicator::ReplicateDirectory()
|
|
{
|
|
/* Return FALSE iff there are no more servers to replicate.
|
|
*/
|
|
DIR_Server *server = (DIR_Server *)XP_ListRemoveTopObject(m_serverList);
|
|
if (!server)
|
|
return FALSE;
|
|
|
|
char *auth = NULL;
|
|
if (server->enableAuth && server->authDn)
|
|
{
|
|
if (server->password)
|
|
auth = PR_smprintf("%s:%s@", server->authDn, server->password);
|
|
else
|
|
auth = PR_smprintf("%s@", server->authDn);
|
|
}
|
|
|
|
char *url = PR_smprintf("repl-ldap%s://%s%s:%d/%s", server->isSecure ? "s" : "",
|
|
auth ? auth : "", server->serverName, server->port, server->searchBase);
|
|
if (url)
|
|
{
|
|
URL_Struct *URL_s = NET_CreateURLStruct(url, NET_DONT_RELOAD);
|
|
if(URL_s)
|
|
{
|
|
URL_s->owner_data = this;
|
|
URL_s->internal_url = TRUE;
|
|
URL_s->msg_pane = m_pane;
|
|
if (m_pane)
|
|
MSG_UrlQueue::AddUrlToPane(URL_s, net_OfflineLdapReplicator::ReplicateNextDirectory, m_pane, TRUE, FO_PRESENT);
|
|
else
|
|
NET_GetURL(URL_s, FO_PRESENT, m_progressContext, net_OfflineLdapReplicator::ReplicateNextDirectory);
|
|
}
|
|
XP_FREE(url);
|
|
}
|
|
XP_FREEIF(auth);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void net_OfflineLdapReplicator::ReplicateNextDirectory(URL_Struct *URL_s, int, MWContext *)
|
|
{
|
|
net_OfflineLdapReplicator *replicator = (net_OfflineLdapReplicator *)(URL_s->owner_data);
|
|
if (replicator)
|
|
{
|
|
/* ReplicateDirectory returns FALSE if there are no more directories
|
|
* left to replicate. It is our job to clean up the replicator object
|
|
* when we're done.
|
|
*/
|
|
if (!replicator->ReplicateDirectory())
|
|
delete replicator;
|
|
}
|
|
}
|
|
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// net_LdapToAddressBook does the work of converting LDAP entries into a format
|
|
// that can be stored in the local Address Book database.
|
|
//
|
|
class net_LdapToAddressBook : public net_LdapConnectionData
|
|
{
|
|
public:
|
|
net_LdapToAddressBook (ActiveEntry*);
|
|
virtual ~net_LdapToAddressBook();
|
|
|
|
// Base class overrides
|
|
virtual int OnEntryFound (LDAPMessage*);
|
|
|
|
// Address Book specific stuff
|
|
void ConvertEntryToPerson (LDAPMessage *, PersonEntry &);
|
|
void ConvertEntryToPerson (LDAPMessage *message, AB_AttributeValue ** valuesArray, uint16 * numItems); // new version for new AB
|
|
int WriteEntryToAddressBook (LDAPMessage*);
|
|
virtual char * ExtractLDAPEncoding(char * url);
|
|
|
|
protected:
|
|
virtual LdapConnectionType GetConnectionType() { return kAddressBookConnection; }
|
|
|
|
// used to take an ldap attribute, value pair and conver it to one or more address book attribute
|
|
// values, adding those values to the abAttributes list.
|
|
int AddLDAPAttribute(const char * dirAttrib, const char * dirValue, XP_List * abAttributes);
|
|
|
|
AB_AddressBookCopyInfo * m_abCopyInfo; // used for new address book...
|
|
};
|
|
|
|
// prototypes fo net_LdapToAddressBook helper functions...
|
|
AB_AttributeValue * findAttributeInList(AB_AttribID attrib, XP_List * abAttributes);
|
|
|
|
net_LdapToAddressBook::net_LdapToAddressBook (ActiveEntry *ce) : net_LdapConnectionData(ce)
|
|
{
|
|
if (ce && ce->URL_s)
|
|
m_abCopyInfo = (AB_AddressBookCopyInfo *) ce->URL_s->owner_data;
|
|
else
|
|
m_abCopyInfo = NULL;
|
|
}
|
|
|
|
net_LdapToAddressBook::~net_LdapToAddressBook()
|
|
{
|
|
// copy info freed by url exit function...
|
|
// if (m_abCopyInfo)
|
|
// AB_FreeAddressBookCopyInfo(m_abCopyInfo); // also destroys allocated memory..
|
|
m_abCopyInfo = NULL;
|
|
}
|
|
|
|
char * net_LdapToAddressBook::ExtractLDAPEncoding(char * url)
|
|
{
|
|
// we want to strip off the addbook- part for the LDAP encoding...
|
|
// caller should not be freeing this return string because it is part of the url..
|
|
return url+8; // 8 for "addbook-"
|
|
}
|
|
|
|
// helper function used to find an attribute in the list
|
|
AB_AttributeValue * findAttributeInList(AB_AttribID attrib, XP_List * abAttributes)
|
|
{
|
|
if (abAttributes)
|
|
{
|
|
for (int i = 1; i <= XP_ListCount(abAttributes); i++)
|
|
{
|
|
AB_AttributeValue *v = (AB_AttributeValue*) XP_ListGetObjectNum (abAttributes, i);
|
|
if (v && v->attrib == attrib)
|
|
return v;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int net_LdapToAddressBook::AddLDAPAttribute(const char * attrib, const char * dirValue, XP_List * abAttributes)
|
|
{
|
|
int status = 0;
|
|
AB_AttributeValue * value = (AB_AttributeValue *) XP_ALLOC(sizeof(AB_AttributeValue));
|
|
if (value)
|
|
{
|
|
status = -1;
|
|
// we can make a minor hack to optmize this...since all of
|
|
// them are string attributes, we only need to set the
|
|
// value->attrib field in the if statements...then if
|
|
// we found a match, set the value->u.string outside the
|
|
// if statements...
|
|
|
|
if (!XP_STRCASECMP(attrib, GetAttributeString(cn)))
|
|
{
|
|
value->attrib = AB_attribDisplayName;
|
|
status = 0;
|
|
}
|
|
if (!XP_STRCASECMP(attrib, GetAttributeString (givenname)))
|
|
{
|
|
value->attrib = AB_attribGivenName;
|
|
status = 0;
|
|
}
|
|
else if (!XP_STRCASECMP(attrib, GetAttributeString(sn)))
|
|
{
|
|
value->attrib = AB_attribFamilyName;
|
|
status = 0;
|
|
}
|
|
else if (!XP_STRCASECMP(attrib, GetAttributeString(o)))
|
|
{
|
|
value->attrib = AB_attribCompanyName;
|
|
status = 0;
|
|
}
|
|
else if (!XP_STRCASECMP(attrib, GetAttributeString(l)))
|
|
{
|
|
value->attrib = AB_attribLocality;
|
|
status = 0;
|
|
}
|
|
else if (!XP_STRCASECMP(attrib, GetAttributeString(mail)))
|
|
{
|
|
value->attrib = AB_attribEmailAddress;
|
|
status = 0;
|
|
}
|
|
else if (!XP_STRCASECMP(attrib, "title"))
|
|
{
|
|
value->attrib = AB_attribTitle;
|
|
status = 0;
|
|
}
|
|
else if (!XP_STRCASECMP(attrib, "postaladdress"))
|
|
{
|
|
value->attrib = AB_attribPOAddress;
|
|
status = 0;
|
|
}
|
|
else if (!XP_STRCASECMP(attrib, "st"))
|
|
{
|
|
value->attrib = AB_attribRegion;
|
|
status = 0;
|
|
}
|
|
else if (!XP_STRCASECMP(attrib, "postalcode"))
|
|
{
|
|
value->attrib = AB_attribZipCode;
|
|
status = 0;
|
|
}
|
|
else if (!XP_STRCASECMP(attrib, GetAttributeString(telephonenumber)))
|
|
{
|
|
value->attrib = AB_attribWorkPhone;
|
|
status = 0;
|
|
}
|
|
else if (!XP_STRCASECMP(attrib, "facsimiletelephonenumber"))
|
|
{
|
|
value->attrib = AB_attribFaxPhone;
|
|
status = 0;
|
|
}
|
|
else if (!XP_STRCASECMP(attrib, "dlshost"))
|
|
{
|
|
value->attrib = AB_attribCoolAddress;
|
|
// we also need to set the use server...
|
|
AB_AttributeValue * nextValue = (AB_AttributeValue *) XP_ALLOC(sizeof(AB_AttributeValue));
|
|
if (nextValue)
|
|
{
|
|
nextValue->attrib = AB_attribUseServer;
|
|
nextValue->u.shortValue = kSpecificDLS;
|
|
XP_ListAddObjectToEnd(abAttributes, (void *) nextValue);
|
|
}
|
|
}
|
|
else if (!XP_STRCASECMP(attrib, "host"))
|
|
{
|
|
value->attrib = AB_attribCoolAddress;
|
|
// we also need to set the use server...
|
|
AB_AttributeValue * nextValue = (AB_AttributeValue *) XP_ALLOC(sizeof(AB_AttributeValue));
|
|
if (nextValue)
|
|
{
|
|
nextValue->attrib = AB_attribUseServer;
|
|
nextValue->u.shortValue = kHostOrIPAddress;
|
|
XP_ListAddObjectToEnd(abAttributes, (void *) nextValue);
|
|
}
|
|
}
|
|
|
|
if (status == 0) // we found a match
|
|
{
|
|
value->u.string = XP_STRDUP(dirValue);
|
|
XP_ListAddObjectToEnd(abAttributes, (void *) value);
|
|
}
|
|
|
|
else // we didn't find a match....
|
|
XP_FREEIF(value);
|
|
}
|
|
else
|
|
status = MK_OUT_OF_MEMORY;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
void net_LdapToAddressBook::ConvertEntryToPerson (LDAPMessage *message, AB_AttributeValue ** valuesArray, uint16 * numItems)
|
|
{
|
|
XP_List * abAttributes = XP_ListNew();
|
|
AB_AttributeValue * values = NULL;
|
|
uint16 numAttributes = 0;
|
|
|
|
if (abAttributes)
|
|
{
|
|
// loop over each attribute in the entry, and pick out the ones the address book
|
|
// is interested in. We are ignoring the case where the same attribute has multiple
|
|
// values. The AB doesn't have a way to deal with that.
|
|
|
|
char *tmpCn = NULL;
|
|
BerElement *ber;
|
|
char *attrib = ldap_first_attribute (m_ldap, message, &ber);
|
|
while (attrib)
|
|
{
|
|
char ** values = ldap_get_values(m_ldap, message, attrib);
|
|
if (m_server && values && values[0])
|
|
{
|
|
char *decodedUtf8 = ConvertFromServerCharSet (values[0]);
|
|
if (decodedUtf8)
|
|
{
|
|
AddLDAPAttribute(attrib, decodedUtf8, abAttributes);
|
|
if (!XP_STRCASECMP(attrib, GetAttributeString (cn)))
|
|
tmpCn = XP_STRDUP(decodedUtf8); // hang on to cn in case we need it later
|
|
}
|
|
|
|
XP_FREEIF (decodedUtf8);
|
|
}
|
|
if (values)
|
|
ldap_value_free (values);
|
|
ldap_memfree (attrib);
|
|
attrib = ldap_next_attribute (m_ldap, message, ber);
|
|
} // add the next attribute....
|
|
|
|
// now we have some fixing up to do...adding CSID,
|
|
AB_AttributeValue * abValue = (AB_AttributeValue *) XP_ALLOC(sizeof(AB_AttributeValue));
|
|
if (abValue)
|
|
{
|
|
abValue->attrib = AB_attribWinCSID;
|
|
abValue->u.shortValue = INTL_GetCSIWinCSID(LO_GetDocumentCharacterSetInfo(m_ce->window_id));
|
|
XP_ListAddObjectToEnd(abAttributes, (void *) abValue);
|
|
}
|
|
|
|
// If the entry doesn't have an 'o', get it from the DN
|
|
char *dn = NULL;
|
|
AB_AttributeValue * company = findAttributeInList(AB_attribCompanyName, abAttributes);
|
|
if (!company)
|
|
{
|
|
if (!dn)
|
|
dn = ldap_get_dn (m_ldap, message);
|
|
if (dn)
|
|
{
|
|
const char *oAttribName = GetAttributeString (o);
|
|
AB_AttributeValue * abValue = (AB_AttributeValue *) XP_ALLOC(sizeof(AB_AttributeValue));
|
|
if (abValue)
|
|
{
|
|
abValue->attrib = AB_attribCompanyName;
|
|
abValue->u.string = GetAttributeFromDN (dn, oAttribName);
|
|
XP_ListAddObjectToEnd(abAttributes, (void *) abValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the entry doesn't have 'givenname' or 'sn' try to use 'cn'
|
|
AB_AttributeValue * givenName = findAttributeInList(AB_attribGivenName, abAttributes);
|
|
AB_AttributeValue * familyName = findAttributeInList(AB_attribFamilyName, abAttributes);
|
|
if (!givenName /* || !familyName */)
|
|
{
|
|
char * useThisForGivenName = NULL;
|
|
// prefer CN as attribute to CN from dist name
|
|
if (tmpCn)
|
|
{
|
|
useThisForGivenName = tmpCn;
|
|
tmpCn = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (!dn)
|
|
dn = ldap_get_dn (m_ldap, message);
|
|
if (dn)
|
|
{
|
|
const char *cnAttribName = GetAttributeString (cn);
|
|
useThisForGivenName = GetAttributeFromDN (dn, cnAttribName);
|
|
}
|
|
}
|
|
|
|
if (useThisForGivenName)
|
|
{
|
|
AB_AttributeValue * abValue = (AB_AttributeValue *) XP_ALLOC(sizeof(AB_AttributeValue));
|
|
if (abValue)
|
|
{
|
|
abValue->attrib = AB_attribGivenName;
|
|
abValue->u.string = useThisForGivenName;
|
|
XP_ListAddObjectToEnd(abAttributes, (void *) abValue);
|
|
}
|
|
}
|
|
} // !givenName
|
|
|
|
if (dn)
|
|
ldap_memfree(dn);
|
|
if (tmpCn)
|
|
XP_FREE(tmpCn);
|
|
|
|
// our last step is to turn the XP_List into an array of attribute values...
|
|
numAttributes = XP_ListCount(abAttributes);
|
|
if (numAttributes)
|
|
{
|
|
values = (AB_AttributeValue *) XP_ALLOC(sizeof(AB_AttributeValue) * numAttributes);
|
|
if (valuesArray)
|
|
{
|
|
// iterate through the list adding each attribute to our static array
|
|
for (uint16 index = 1; index <= numAttributes; index++)
|
|
{
|
|
AB_AttributeValue * value = (AB_AttributeValue *) XP_ListGetObjectNum(abAttributes, index);
|
|
AB_CopyEntryAttributeValue(value, &values[index-1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// free the list attributes...
|
|
for (int i = 1; i <= XP_ListCount(abAttributes); i++)
|
|
{
|
|
AB_AttributeValue * value = (AB_AttributeValue *) XP_ListGetObjectNum(abAttributes, i);
|
|
AB_FreeEntryAttributeValue(value);
|
|
|
|
}
|
|
|
|
XP_ListDestroy (abAttributes);
|
|
|
|
} // if abAttributes
|
|
|
|
// set up the return values...
|
|
if (numItems)
|
|
*numItems = numAttributes;
|
|
if (valuesArray)
|
|
*valuesArray = values;
|
|
else
|
|
AB_FreeEntryAttributeValues(values, numAttributes);
|
|
}
|
|
|
|
|
|
#ifdef MOZ_NEWADDR
|
|
int net_LdapToAddressBook::WriteEntryToAddressBook (LDAPMessage *message)
|
|
{
|
|
int status = 0;
|
|
if (m_abCopyInfo && m_abCopyInfo->destContainer)
|
|
{
|
|
AB_AttributeValue * valuesArray = NULL;
|
|
uint16 numItems = 0;
|
|
ConvertEntryToPerson(message, &valuesArray, &numItems);
|
|
if (valuesArray)
|
|
{
|
|
// If there is only one URL in the queue, we bring up the UI, but
|
|
// if there is more than one, we add them silently. There could be
|
|
// a ton of URLs, and we don't have a Yes/No/All dialog
|
|
status = AB_AddUserAB2(m_abCopyInfo->destContainer, valuesArray, numItems, NULL /* we don't care about the entry id...*/);
|
|
AB_FreeEntryAttributeValues(valuesArray, numItems);
|
|
}
|
|
}
|
|
else
|
|
status = ABError_NullPointer;
|
|
|
|
return status;
|
|
}
|
|
#else
|
|
int net_LdapToAddressBook::WriteEntryToAddressBook (LDAPMessage *message)
|
|
{
|
|
// Add an entry we found on an LDAP directory into the local AB DB
|
|
|
|
int err = 0;
|
|
PersonEntry person;
|
|
person.Initialize ();
|
|
ConvertEntryToPerson (message, person);
|
|
|
|
XP_List *dirServers = FE_GetDirServers ();
|
|
XP_ASSERT(dirServers);
|
|
if (dirServers)
|
|
{
|
|
DIR_Server *pab = NULL;
|
|
DIR_GetPersonalAddressBook (dirServers, &pab);
|
|
XP_ASSERT(pab);
|
|
if (pab)
|
|
{
|
|
ABook *ab = FE_GetAddressBook (NULL /* don't have a pane? */);
|
|
AB_BreakApartFirstName (ab, &person);
|
|
|
|
// If there is only one URL in the queue, we bring up the UI, but
|
|
// if there is more than one, we add them silently. There could be
|
|
// a ton of URLs, and we don't have a Yes/No/All dialog
|
|
|
|
if (MSG_GetUrlQueueSize (m_ce->URL_s->address, m_ce->window_id) <= 1)
|
|
err = AB_AddUserWithUI (m_ce->window_id, &person, pab, TRUE);
|
|
else
|
|
{
|
|
// Don't allow any external URLs to add batches of people to the AB
|
|
if (m_ce->URL_s->internal_url)
|
|
{
|
|
ABID unusedId;
|
|
err = AB_AddUser (pab, ab, &person, &unusedId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
person.CleanUp();
|
|
return err;
|
|
}
|
|
#endif
|
|
|
|
int net_LdapToAddressBook::OnEntryFound (LDAPMessage *message)
|
|
{
|
|
return WriteEntryToAddressBook (message);
|
|
}
|
|
|
|
void net_LdapToAddressBook::ConvertEntryToPerson (LDAPMessage *message, PersonEntry &person)
|
|
{
|
|
// Loop over each attribute in the entry, and pick out the ones the
|
|
// Address Book is interested in. Note that we're ignoring cases where
|
|
// the same attribute has multiple values. The AB doesn't have a way to
|
|
// deal with that.
|
|
|
|
char *tmpCn = NULL;
|
|
BerElement *ber;
|
|
char *attrib = ldap_first_attribute (m_ldap, message, &ber);
|
|
while (attrib)
|
|
{
|
|
char **values = ldap_get_values (m_ldap, message, attrib);
|
|
|
|
// We better have a DIR_Server for this, otherwise GetAttributeString
|
|
// will crash. Since the only way to get to this code is through the Search
|
|
// Directory dialog, I think this is an ok restriction. We could certainly
|
|
// add default names in dirprefs.c if necessary.
|
|
XP_ASSERT(m_server && values && values[0]);
|
|
if (!m_server || !values || !values[0])
|
|
return;
|
|
|
|
char *decodedUtf8 = ConvertFromServerCharSet (values[0]);
|
|
if (!decodedUtf8)
|
|
return;
|
|
|
|
// ### maybe it would be nice to have a few more of these
|
|
// expressed as DIR_Prefs attributeIds???
|
|
|
|
if (!XP_STRCASECMP(attrib, GetAttributeString (cn)))
|
|
tmpCn = XP_STRDUP(decodedUtf8); // hang on to cn in case we need it later
|
|
if (!XP_STRCASECMP(attrib, GetAttributeString (givenname)))
|
|
person.pGivenName = XP_STRDUP(decodedUtf8);
|
|
else if (!XP_STRCASECMP(attrib, GetAttributeString(sn)))
|
|
person.pFamilyName = XP_STRDUP(decodedUtf8);
|
|
else if (!XP_STRCASECMP(attrib, GetAttributeString(o)))
|
|
person.pCompanyName = XP_STRDUP(decodedUtf8);
|
|
else if (!XP_STRCASECMP(attrib, GetAttributeString(l)))
|
|
person.pLocality = XP_STRDUP(decodedUtf8);
|
|
else if (!XP_STRCASECMP(attrib, GetAttributeString(mail)))
|
|
person.pEmailAddress = XP_STRDUP(decodedUtf8);
|
|
else if (!XP_STRCASECMP(attrib, "title"))
|
|
person.pTitle = XP_STRDUP(decodedUtf8);
|
|
else if (!XP_STRCASECMP(attrib, "postaladdress"))
|
|
person.pPOAddress = XP_STRDUP(decodedUtf8);
|
|
else if (!XP_STRCASECMP(attrib, "st"))
|
|
person.pRegion = XP_STRDUP(decodedUtf8);
|
|
else if (!XP_STRCASECMP(attrib, "postalcode"))
|
|
person.pZipCode = XP_STRDUP(decodedUtf8);
|
|
else if (!XP_STRCASECMP(attrib, GetAttributeString(telephonenumber)))
|
|
person.pWorkPhone = XP_STRDUP(decodedUtf8);
|
|
else if (!XP_STRCASECMP(attrib, "facsimiletelephonenumber"))
|
|
person.pFaxPhone = XP_STRDUP(decodedUtf8);
|
|
else if (!XP_STRCASECMP(attrib, "dlshost"))
|
|
{
|
|
person.UseServer = kSpecificDLS;
|
|
person.pCoolAddress = XP_STRDUP(decodedUtf8);
|
|
}
|
|
else if (!XP_STRCASECMP(attrib, "host"))
|
|
{
|
|
person.UseServer = kHostOrIPAddress;
|
|
person.pCoolAddress = XP_STRDUP(decodedUtf8);
|
|
}
|
|
|
|
|
|
ldap_value_free (values);
|
|
XP_FREE (decodedUtf8);
|
|
ldap_memfree (attrib);
|
|
|
|
attrib = ldap_next_attribute (m_ldap, message, ber);
|
|
}
|
|
|
|
// If the entry doesn't have an 'o', get it from the DN
|
|
char *dn = NULL;
|
|
if (!person.pCompanyName)
|
|
{
|
|
if (!dn)
|
|
dn = ldap_get_dn (m_ldap, message);
|
|
if (dn)
|
|
{
|
|
const char *oAttribName = GetAttributeString (o);
|
|
person.pCompanyName = GetAttributeFromDN (dn, oAttribName);
|
|
}
|
|
}
|
|
|
|
// If the entry doesn't have 'givenname' or 'sn' try to use 'cn'
|
|
if (!person.pGivenName || !person.pFamilyName)
|
|
{
|
|
// prefer CN as attribute to CN from dist name
|
|
if (tmpCn)
|
|
{
|
|
person.pGivenName = tmpCn;
|
|
tmpCn = NULL; // PersonEntry now owns that memory, so don't free it
|
|
}
|
|
else
|
|
{
|
|
if (!dn)
|
|
dn = ldap_get_dn (m_ldap, message);
|
|
if (dn)
|
|
{
|
|
const char *cnAttribName = GetAttributeString (cn);
|
|
person.pGivenName = GetAttributeFromDN (dn, cnAttribName);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dn)
|
|
ldap_memfree(dn);
|
|
if (tmpCn)
|
|
XP_FREE(tmpCn);
|
|
|
|
person.WinCSID = INTL_GetCSIWinCSID(LO_GetDocumentCharacterSetInfo(m_ce->window_id));
|
|
}
|
|
|
|
#ifdef MOZ_LI
|
|
|
|
/*
|
|
* Class net_LdapLIConnectionData
|
|
* ==============================
|
|
* This class is used for LI LDAP operations. It expects a LDAPOperation* to be
|
|
* passed in via the URL_s->owner_data. The operation to be performed is determined
|
|
* by URL_s->method. The results of the operations are returned in the LDAPOperation;
|
|
* the caller is responsible for creating and deleting the operation object.
|
|
*/
|
|
class net_LdapLIConnectionData : public net_LdapConnectionData
|
|
{
|
|
public:
|
|
net_LdapLIConnectionData(ActiveEntry *ce);
|
|
virtual ~net_LdapLIConnectionData (); // The base class handles it correctly.
|
|
|
|
virtual int Load (ActiveEntry *ce);
|
|
virtual int Process ();
|
|
virtual int Interrupt ();
|
|
|
|
virtual int Unbind ();
|
|
|
|
|
|
protected:
|
|
virtual LdapConnectionType GetConnectionType() { return kLIConnection; }
|
|
|
|
private:
|
|
XP_Bool m_bCallerProvidedConnection; // Did the caller provide the m_ldap connection?
|
|
LDAPOperation* m_pParams; // Parameters.
|
|
};
|
|
|
|
/* net_LdapLIConnectionData Constructor
|
|
* ====================================
|
|
* Create a connection data object class for an LI operation; set up a connection if
|
|
* the caller didn't provide one. We tell the base class not to initialize m_ldap since
|
|
* a LDAP* may be passed in to us.
|
|
*/
|
|
net_LdapLIConnectionData::net_LdapLIConnectionData(ActiveEntry *ce) : net_LdapConnectionData(ce,FALSE)
|
|
{
|
|
m_pParams = (LDAPOperation*)ce->URL_s->owner_data;
|
|
|
|
if(m_pParams && (m_pParams->getConnection() || (m_pParams->getURLMethod() == LDAP_LI_BIND_METHOD)))
|
|
{
|
|
// Set the connection in the base class.
|
|
// Free the LDAP* struct if created by the base class.
|
|
m_ldap = m_pParams->getConnection();
|
|
m_bCallerProvidedConnection = TRUE;
|
|
}
|
|
else
|
|
{
|
|
m_ldap = ldap_init(m_pParams->getURL()->getHost(),m_pParams->getURL()->getPort());
|
|
m_bCallerProvidedConnection = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
/* net_LdapLIConnectionData Destructor
|
|
* ====================================
|
|
* Remember not to unbind if the caller provided the connection.
|
|
*/
|
|
net_LdapLIConnectionData::~net_LdapLIConnectionData()
|
|
{
|
|
// Base class destructor will try to unbind, so we have to prevent that if
|
|
// the caller provided the connection.
|
|
if(m_bCallerProvidedConnection)
|
|
m_ldap = NULL;
|
|
}
|
|
|
|
/* Load()
|
|
* ======
|
|
* Called by netlib when starting an LI operation. This function is responsible to starting
|
|
* the LDAP operations.
|
|
*/
|
|
int net_LdapLIConnectionData::Load(ActiveEntry *ce)
|
|
{
|
|
int ldapErr = 0;
|
|
int msgId = -1;
|
|
char *attrs[2] = { "modifyTimeStamp" , NULL };
|
|
const LDAPURL* url = m_pParams->getURL();
|
|
char *utf8Encoding = NULL;
|
|
|
|
if(url->getParseError() != LDAP_SUCCESS)
|
|
{
|
|
ldapErr = MK_MALFORMED_URL_ERROR;
|
|
goto net_LdapLIConnectionDataLoadExit;
|
|
}
|
|
|
|
if(m_pParams->started())
|
|
{
|
|
ldapErr = LI_LDAP_ALREADY_STARTED;
|
|
goto net_LdapLIConnectionDataLoadExit;
|
|
}
|
|
|
|
XP_ASSERT(m_ce);
|
|
XP_ASSERT(m_ce->URL_s);
|
|
XP_ASSERT(m_ce->URL_s->address);
|
|
XP_ASSERT(m_ldap);
|
|
|
|
/* Convert the URL to UTF8 */
|
|
utf8Encoding = ConvertToServerCharSet (ce->URL_s->address);
|
|
|
|
if(!utf8Encoding)
|
|
return LI_LDAP_MEMORY_ERROR;
|
|
|
|
/* The URL is assumed to be valid (checked by
|
|
* LDAPOperation before calling NET_GetURL)
|
|
*/
|
|
|
|
/* This is where the real work begins */
|
|
|
|
// let the world know what we are doing.
|
|
FE_GraphProgressInit(ce->window_id, NULL, 1);
|
|
|
|
// Initiate the operation on the server
|
|
switch(m_pParams->getURLMethod())
|
|
{
|
|
case LDAP_LI_BIND_METHOD:
|
|
// Bind operation is not really async yet, the initial IP stuff is still synchronous in the LDAP API.
|
|
msgId = ldap_simple_bind(m_ldap,m_pParams->getURL()->getDN(),((LDAPBindOperation*)m_pParams)->getPassword());
|
|
break;
|
|
case LDAP_LI_SEARCH_METHOD:
|
|
msgId = ldap_search(m_ldap,
|
|
url->getDN(),
|
|
url->getScope(),
|
|
url->getFilter(),
|
|
url->getAttrs(),
|
|
((LDAPSearchOperation*)m_pParams)->getAttrsOnly());
|
|
break;
|
|
case LDAP_LI_ADD_METHOD:
|
|
case LDAP_LI_ADDGLM_METHOD:
|
|
// Note that since we take apart the URL, we end up ignoring everything but the DN.
|
|
msgId = ldap_add(m_ldap, url->getDN(), ((LDAPAddOperation*)m_pParams)->getLDAPModArray()->getLDAPModStarStar());
|
|
break;
|
|
case LDAP_LI_MOD_METHOD:
|
|
case LDAP_LI_MODGLM_METHOD:
|
|
// Note that since we take apart the URL, we end up ignoring everything but the DN.
|
|
msgId = ldap_modify(m_ldap, url->getDN(), ((LDAPModOperation*)m_pParams)->getLDAPModArray()->getLDAPModStarStar());
|
|
break;
|
|
case LDAP_LI_DEL_METHOD:
|
|
msgId = ldap_delete(m_ldap, url->getDN());
|
|
break;
|
|
case LDAP_LI_GETLASTMOD_METHOD:
|
|
{
|
|
msgId = ldap_search(m_ldap,
|
|
url->getDN(),
|
|
url->getScope(),
|
|
url->getFilter(),
|
|
attrs,
|
|
((LDAPSearchOperation*)m_pParams)->getAttrsOnly());
|
|
}
|
|
break;
|
|
case LDAP_SUCCESS:
|
|
break;
|
|
}
|
|
|
|
if (msgId != -1)
|
|
{
|
|
m_messageId = msgId;
|
|
} else {
|
|
m_pParams->setErrNo(ldap_get_lderrno(m_ldap,NULL,NULL));
|
|
ldapErr = LI_LDAP_ERROR;
|
|
}
|
|
|
|
net_LdapLIConnectionDataLoadExit:
|
|
|
|
XP_FREEIF(utf8Encoding);
|
|
|
|
return ldapErr;
|
|
}
|
|
|
|
/* Process()
|
|
* =========
|
|
* Process LI Gets/Puts, etc. Only one entry can be gotten/put at a time.
|
|
* The result of the process is a LDAPMessage* put into the LDAPOperation*
|
|
* in the URL_s->owner_data in the ActiveEntry.
|
|
* The caller of NET_GetURL is required to free this data via ldap_msgfree()
|
|
* if it is non-NULL in the LDAPOperation object.
|
|
*/
|
|
int net_LdapLIConnectionData::Process()
|
|
{
|
|
LDAPMessage *message = NULL;
|
|
struct timeval timeout;
|
|
|
|
// REMIND: put in the timeout stuff.
|
|
|
|
XP_BZERO (&timeout, sizeof(timeout));
|
|
int ldapErr = ldap_result (m_ldap, m_messageId, 1, &timeout, &message);
|
|
|
|
XP_ASSERT(m_pParams);
|
|
|
|
/* Check for network error */
|
|
// We don't set result to NULL here because it may have already been set to
|
|
// something, e.g. by the LDAP_LI_ADDGLM or LDAP_LI_MODGLM methods.
|
|
if(ldapErr == -1)
|
|
{
|
|
m_pParams->setErrNo(ldap_get_lderrno(m_ldap,NULL,NULL));
|
|
return LI_LDAP_ERROR;
|
|
}
|
|
|
|
switch (ldapErr)
|
|
{
|
|
/*
|
|
* We can only get one entry at a time in the current implementation, so we
|
|
* terminate when we have at least one.
|
|
*/
|
|
case LDAP_RES_SEARCH_ENTRY:
|
|
case LDAP_RES_SEARCH_RESULT:
|
|
case LDAP_RES_MODIFY:
|
|
case LDAP_RES_DELETE:
|
|
case LDAP_RES_ADD:
|
|
FE_GraphProgress(m_ce->window_id, NULL, 0, 1, -2); // -2 specifies that we want items not bytes
|
|
case LDAP_RES_BIND:
|
|
m_pParams->setResults(message);
|
|
ldapErr = LI_LDAP_DONE; /* Special value to indicate to netlib that we're done. */
|
|
/* We don't use -1 here so we can differentiate between */
|
|
/* a valid result and an ldap error */
|
|
m_pParams->setErrNo(ldap_result2error(m_ldap,message,0));
|
|
break;
|
|
case LDAP_SUCCESS:
|
|
break;
|
|
default:
|
|
if(message)
|
|
m_pParams->setResults(message);
|
|
m_pParams->setErrNo(ldap_result2error(m_ldap,message,0));
|
|
ldapErr = LI_LDAP_ERROR;
|
|
break;
|
|
}
|
|
|
|
/* If the ldapErr is -2, it means we're done and we've gotten something */
|
|
if(ldapErr == LI_LDAP_DONE) {
|
|
|
|
char *utf8Encoding = NULL;
|
|
|
|
// the last modified time is given by the modifyTimeStamp attribute.
|
|
char *attrs[2] = { "modifyTimeStamp" , NULL };
|
|
int msgID;
|
|
const LDAPURL* url = m_pParams->getURL();
|
|
|
|
// For LDAP_LI_MODGLM_METHOD and LDAP_LI_ADDGLM_METHOD, we have to
|
|
// do another search to get the last modified time if the first
|
|
// search completes successfully. To this, we simply start the search
|
|
// and don't return -1 so netlib will keep calling process(). We also
|
|
// change m_method to LDAP_LI_SEARCH_METHOD so that we won't end up in this
|
|
// if statement again. Note that we don't change it in m_ce->URL_s->method
|
|
// This is mainly to avoid netlib weirdness.
|
|
switch(m_pParams->getURLMethod()) {
|
|
case LDAP_LI_ADDGLM_METHOD:
|
|
case LDAP_LI_MODGLM_METHOD:
|
|
|
|
// Determine if the ADD part succeeded. We don't free the result because
|
|
// it's the policy of the URL handler to always return the result when
|
|
// it's available. We'll have to free if it the ADD/MOD succeed so that
|
|
// the GLM result can be returned.
|
|
if(m_pParams-> getErrNo() != LDAP_SUCCESS) {
|
|
// m_pParams->result and param->status already set above.
|
|
((LDAPModGLMOperation*)m_pParams)->setModSucceeded(FALSE);
|
|
break; // exit the ADDGLM and MODGLM stuff.
|
|
}
|
|
|
|
// setResults will free message, since setResults(message) was called earlier.
|
|
m_pParams->setResults(NULL);
|
|
message = NULL;
|
|
((LDAPModGLMOperation*)m_pParams)->setModSucceeded(TRUE);
|
|
|
|
msgID = ldap_search(m_ldap,url->getDN(),url->getScope(),"(objectclass=*)",attrs,0);
|
|
|
|
// each time we initiate a new ldap operation we need to increase the count
|
|
FE_GraphProgressInit(m_ce->window_id, NULL, 1);
|
|
|
|
// If msgID == -1, then there was a network error; so we set the error condition and
|
|
// leave ldapErr = -1 so netlib will exit the url handler.
|
|
if(msgID == -1) {
|
|
m_pParams->setErrNo(ldap_get_lderrno(m_ldap,NULL,NULL));
|
|
ldapErr = LI_LDAP_ERROR;
|
|
} else {
|
|
ldapErr = 1; // >0 indicates to netlib to continue processing
|
|
m_messageId = msgID;
|
|
((LDAPModGLMOperation*)m_pParams)->changeModToGLM();
|
|
}
|
|
break;
|
|
} // switch
|
|
FE_GraphProgressDestroy(m_ce->window_id, NULL, 1, 1);
|
|
} // if
|
|
|
|
return ldapErr;
|
|
}
|
|
|
|
/* Unbind()
|
|
* ========
|
|
* Unbind from the server if the caller didn't provide a connection.
|
|
*/
|
|
int net_LdapLIConnectionData::Unbind()
|
|
{
|
|
// If the caller did not provide the connection, we do the standard unbind.
|
|
// If the caller did provide the connection, we have a sticky situation
|
|
// because net_LdapConnectionData::Unbind() is called in the base class'
|
|
// destructor, and that destructor is guaranteed to be called. To avoid
|
|
// unbinding a caller provided connection, we just set m_ldap to NULL so that
|
|
// the base class' Unbind() won't do anything.
|
|
if(!m_bCallerProvidedConnection)
|
|
return net_LdapConnectionData::Unbind();
|
|
else
|
|
m_ldap = NULL;
|
|
|
|
return 0; // Don't know what else we can return.
|
|
}
|
|
|
|
/* Interrupt()
|
|
* ===========
|
|
* netlib wants to interrupt this connect; we rely on the base class to do most of
|
|
* the work and just set the appropriate error.
|
|
*/
|
|
int net_LdapLIConnectionData::Interrupt()
|
|
{
|
|
return net_LdapConnectionData::Interrupt();
|
|
}
|
|
|
|
#endif // MOZ_LI
|
|
#endif // MOZ_LDAP
|
|
|
|
|
|
//****************************************************************************
|
|
//
|
|
// Callbacks from NET_GetURL
|
|
//
|
|
|
|
extern "C" int32 net_LoadLdap (ActiveEntry *ce)
|
|
{
|
|
#ifdef MOZ_LDAP
|
|
if (NET_IsOffline())
|
|
return MK_OFFLINE;
|
|
|
|
net_LdapConnectionData *cd = NULL;
|
|
switch (NET_URL_Type (ce->URL_s->address))
|
|
{
|
|
case LDAP_TYPE_URL:
|
|
case SECURE_LDAP_TYPE_URL:
|
|
#ifdef MOZ_LI
|
|
// If we're doing an LDAP operation, we
|
|
// have to figure out if it's an LI operation
|
|
// and instantiate the right connection data object.
|
|
if((LDAP_SEARCH_METHOD < ce->URL_s->method) && (LDAP_LI_BIND_METHOD >= ce->URL_s->method))
|
|
cd = new net_LdapLIConnectionData(ce);
|
|
else
|
|
#endif
|
|
cd = new net_LdapToHtml (ce);
|
|
break;
|
|
case LDAP_QUERY_DSE_TYPE_URL: // I think we need to query the DSE for any operation, so this is in the base class
|
|
cd = new net_LdapToHtml (ce);
|
|
break;
|
|
case ADDRESS_BOOK_LDAP_TYPE_URL:
|
|
cd = new net_LdapToAddressBook (ce);
|
|
break;
|
|
case LDAP_REPLICATION_TYPE_URL:
|
|
cd = new net_LdapReplicator (ce);
|
|
break;
|
|
default:
|
|
XP_ASSERT(FALSE);
|
|
return MK_MALFORMED_URL_ERROR;
|
|
}
|
|
if (!cd)
|
|
return MK_OUT_OF_MEMORY;
|
|
|
|
ce->con_data = cd;
|
|
int err = cd->Load (ce);
|
|
if (err)
|
|
{
|
|
ce->status = err;
|
|
delete cd;
|
|
ce->con_data = NULL;
|
|
}
|
|
|
|
return err;
|
|
#else
|
|
return MK_MALFORMED_URL_ERROR;
|
|
#endif
|
|
}
|
|
|
|
extern "C" int32 net_ProcessLdap (ActiveEntry *ce)
|
|
{
|
|
#ifdef MOZ_LDAP
|
|
net_LdapConnectionData *cd = (net_LdapConnectionData*) ce->con_data;
|
|
|
|
int err = cd->Process();
|
|
if (err < 0 || err == MK_CONNECTED)
|
|
{
|
|
delete cd;
|
|
ce->con_data = NULL;
|
|
}
|
|
|
|
if(err == MK_CONNECTED)
|
|
err = -1;
|
|
|
|
return err;
|
|
#else
|
|
return MK_MALFORMED_URL_ERROR;
|
|
#endif
|
|
}
|
|
|
|
|
|
extern "C" int32 net_InterruptLdap (ActiveEntry * ce)
|
|
{
|
|
#ifdef MOZ_LDAP
|
|
net_LdapConnectionData *cd = (net_LdapConnectionData*) ce->con_data;
|
|
|
|
int err = cd->Interrupt ();
|
|
delete cd;
|
|
ce->con_data = NULL;
|
|
ce->status = MK_INTERRUPTED;
|
|
|
|
return err;
|
|
#else
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
extern "C" void
|
|
net_CleanupLdap(void)
|
|
{
|
|
}
|
|
|
|
extern "C" void
|
|
NET_InitLDAPProtocol(void)
|
|
{
|
|
static NET_ProtoImpl ldap_proto_impl;
|
|
|
|
ldap_proto_impl.init = net_LoadLdap;
|
|
ldap_proto_impl.process = net_ProcessLdap;
|
|
ldap_proto_impl.interrupt = net_InterruptLdap;
|
|
ldap_proto_impl.cleanup = net_CleanupLdap;
|
|
|
|
NET_RegisterProtocolImplementation(&ldap_proto_impl, LDAP_TYPE_URL);
|
|
NET_RegisterProtocolImplementation(&ldap_proto_impl, SECURE_LDAP_TYPE_URL);
|
|
NET_RegisterProtocolImplementation(&ldap_proto_impl, ADDRESS_BOOK_LDAP_TYPE_URL);
|
|
}
|
|
|
|
|
|
//****************************************************************************
|
|
//
|
|
// Helper APIs
|
|
//
|
|
|
|
extern "C" XP_Bool NET_ReplicateDirectory (MSG_Pane *pane, DIR_Server *server)
|
|
{
|
|
#ifdef MOZ_LDAP
|
|
net_OfflineLdapReplicator *replicator = new net_OfflineLdapReplicator(pane, server);
|
|
|
|
if (replicator->ReplicateDirectory())
|
|
return TRUE;
|
|
|
|
delete replicator;
|
|
#endif
|
|
return FALSE;
|
|
}
|