pjs/lib/libmsg/msglsrch.cpp

2025 строки
52 KiB
C++
Исходник Обычный вид История

1998-06-23 02:39:40 +04:00
/* -*- 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.
*/
// Implementation of search for LDAP directories
//
#include "rosetta.h"
#include "msg.h"
#include "xpgetstr.h"
#include "pmsgsrch.h" // private search API
#include "msgutils.h" // for msg_GetURL
#include "dirprefs.h"
#include "libi18n.h"
#include HG77677
#include "intl_csi.h"
#ifdef MOZ_LDAP
#define NEEDPROTOS
#define LDAP_REFERRALS
#include "lber.h"
#include "ldap.h"
#include "disptmpl.h"
#endif /* MOZ_LDAP */
extern "C"
{
extern int XP_PROGRESS_CONTACTHOST;
extern int XP_LDAP_SIZELIMIT_EXCEEDED;
extern int XP_LDAP_SERVER_SIZELIMIT_EXCEEDED;
extern int XP_LDAP_OPEN_ENTRY_FAILED;
extern int XP_LDAP_OPEN_SERVER_FAILED;
extern int XP_LDAP_BIND_FAILED;
extern int XP_LDAP_SEARCH_FAILED;
extern int XP_LDAP_MODIFY_FAILED;
extern int MK_MSG_SEARCH_STATUS;
extern int XP_ALERT_OFFLINE_MODE_SELECTED;
extern int MK_LDAP_AUTH_PROMPT;
}
#ifdef MOZ_LDAP
// This class holds data needed across the modify operations
class LdapModifyBucket
{
public:
LdapModifyBucket ()
{
dnValue = NULL;
modList[0] = &mod;
berValList[0] = &ber;
modList[1] = NULL;
berValList[1] = NULL;
}
~LdapModifyBucket ()
{
MSG_DestroySearchValue (dnValue);
XP_FREE (ber.bv_val);
}
LDAPMod *modList[2];
LDAPMod mod;
struct berval *berValList[2];
struct berval ber;
MSG_SearchValue *dnValue;
};
#endif /* MOZ_LDAP */
msg_SearchLdap::msg_SearchLdap (MSG_ScopeTerm *scope, MSG_SearchTermArray &termList) : msg_SearchAdapter (scope, termList)
{
#ifdef MOZ_LDAP
m_ldap = NULL;
m_message = NULL;
m_modifyBucket = NULL;
m_filter = NULL;
m_password = NULL;
m_authDn = NULL;
m_valueUsedToFindDn = NULL;
m_nextState = kInitialize;
#endif /* MOZ_LDAP */
}
msg_SearchLdap::~msg_SearchLdap ()
{
XP_FREEIF(m_filter);
XP_FREEIF(m_password);
XP_FREEIF(m_valueUsedToFindDn);
XP_FREEIF(m_authDn);
ldap_msgfree (m_message);
#ifdef MOZ_LDAP
UnbindRequest ();
#endif /* MOZ_LDAP */
}
MSG_SearchError msg_SearchLdap::ValidateTerms ()
{
XP_FREEIF(m_filter);
m_filter = NULL;
MSG_SearchError err = msg_SearchAdapter::ValidateTerms ();
if (SearchError_Success == err)
err = Encode (&m_filter);
return err;
}
MSG_SearchError msg_SearchLdap::Search ()
{
#ifndef MOZ_LDAP
return SearchError_NotImplemented;
#else
MSG_SearchError err = SearchError_Success;
switch (m_nextState)
{
case kInitialize:
err = Initialize();
break;
case kPreAuthBindRequest:
err = PreAuthBindRequest ();
break;
case kPreAuthBindResponse:
err = PreAuthBindResponse ();
break;
case kPreAuthSearchRequest:
err = PreAuthSearchRequest ();
break;
case kPreAuthSearchResponse:
err = PreAuthSearchResponse ();
break;
case kAnonymousBindRequest:
err = AnonymousBindRequest();
break;
case kAuthenticatedBindRequest:
err = AuthenticatedBindRequest ();
break;
case kBindResponse:
err = BindResponse ();
break;
case kSearchRequest:
if ( GetSearchType() == searchNormal
|| ( GetSearchType() != searchRootDSE
&& DIR_TestFlag(GetDirServer(), DIR_LDAP_ROOTDSE_PARSED)))
err = SearchRequest ();
else
err = SearchRootDSERequest ();
break;
case kSearchVLVSearchRequest:
err = SearchVLVSearchRequest ();
break;
case kSearchVLVIndexRequest:
err = SearchVLVIndexRequest ();
break;
case kSearchResponse:
if ( GetSearchType() == searchNormal
|| ( GetSearchType() != searchRootDSE
&& DIR_TestFlag(GetDirServer(), DIR_LDAP_ROOTDSE_PARSED)))
err = SearchResponse ();
else
err = SearchRootDSEResponse ();
break;
case kSearchVLVSearchResponse:
err = SearchVLVSearchResponse ();
break;
case kSearchVLVIndexResponse:
err = SearchVLVIndexResponse ();
break;
case kModifyRequest :
err = ModifyRequest ();
break;
case kModifyResponse:
err = ModifyResponse ();
break;
case kUnbindRequest:
err = UnbindRequest ();
break;
default:
XP_ASSERT(FALSE);
}
if (SearchError_ScopeDone == err)
{
UnbindRequest();
m_scope->m_frame->EndCylonMode();
}
return err;
#endif /* MOZ_LDAP */
}
DIR_Server *msg_SearchLdap::GetDirServer ()
{
return m_scope->m_server;
}
typedef struct AttribToIdMap
{
MSG_SearchAttribute attrib;
DIR_AttributeId id;
} AttribToIdMap;
// string overhead per encoded term is
// req'd: '=' + '(' + ')' + '\0' + optional: at most two '*' + at most one '!' + () for '!' terms
#define kLdapTermOverhead 9
MSG_SearchError msg_SearchLdap::EncodeCustomFilter (const char *value, const char *filter, char **outEncoding)
{
MSG_SearchError err = SearchError_Success;
*outEncoding = NULL;
// Find out how many occurrences of %s there are
const char *walkFilter = filter;
int substCount = 0;
while (*walkFilter)
{
if (!XP_STRNCASECMP(walkFilter, "%s", 2))
{
substCount++;
walkFilter += 2;
}
else
walkFilter++;
}
// Allocate a string large enough to substitute the value as many times as necessary
int valueLength = XP_STRLEN(value);
int length = XP_STRLEN(filter) + (substCount * valueLength);
*outEncoding = (char*) XP_ALLOC(length + 1);
// Build the encoding. ### it would be nice to allow %$1 formatting here
if (*outEncoding)
{
char *walkEncoding = *outEncoding;
walkFilter = filter;
while (*walkFilter)
{
if (!XP_STRNCASECMP(walkFilter, "%s", 2))
{
XP_STRCPY(walkEncoding, value);
walkEncoding += valueLength;
walkFilter += 2;
}
else
*walkEncoding++ = *walkFilter++;
}
*walkEncoding = '\0';
}
else
err = SearchError_OutOfMemory;
return err;
}
MSG_SearchError msg_SearchLdap::EncodeDwim (const char *mnemonic, const char *value,
char **ppOutEncoding, int *pEncodingLength)
{
MSG_SearchError err = SearchError_Success;
DIR_Server *dirServer = GetDirServer();
const char *filter = DIR_GetFilterString (dirServer);
if (TRUE /*!DIR_RepeatFilterForTokens (dirServer, filter)*/) // I'm not sure this makes sense anymore
{
char *wildValue = NULL;
XP_Bool valueContainedSpaces = FALSE;
if (value)
{
valueContainedSpaces = (XP_STRCHR(value, ' ') != NULL);
wildValue = TransformSpacesToStars (value);
if (!wildValue)
return SearchError_OutOfMemory;
}
if (filter)
err = EncodeCustomFilter (wildValue, filter, ppOutEncoding);
else
{
// There are a bunch of heuristics happening here. I don't know if this is all correct, but
// here's the current thinking:
// - If there's no value, don't try to sprintf it
// - If there's an empty value let it through
// - If the LDAP server supports VLV, we want a typedown style search, without a leading *
// - If the user typed a phrase separated by spaces, we want a typedown style search, without a leading *
// - If the user just typed one word, we'll drop down to a contains style search, with a leading *
if (!value)
*ppOutEncoding = PR_smprintf ("%s=*");
else if (GetSearchType()== searchLdapVLV || !*wildValue || valueContainedSpaces)
*ppOutEncoding = PR_smprintf ("%s=%s*", mnemonic, wildValue);
else
*ppOutEncoding = PR_smprintf ("%s=*%s*", mnemonic, wildValue);
}
XP_FREEIF(wildValue);
if (SearchError_Success == err && *ppOutEncoding)
*pEncodingLength += XP_STRLEN(*ppOutEncoding);
else
{
*pEncodingLength = 0;
err = SearchError_OutOfMemory;
}
return err;
}
char *token = NULL;
char *allocatedToken = NULL;
msg_StringArray commaSeparatedTokens (TRUE /*owns memory for substrings*/);
commaSeparatedTokens.ImportTokenList (value, ",;");
XPPtrArray *singleWordTokens = new XPPtrArray [commaSeparatedTokens.GetSize()];
if (singleWordTokens)
{
int cchSingleWords = 0;
int i;
for (i = 0; i < commaSeparatedTokens.GetSize(); i++)
{
char *subValue = XP_STRDUP (commaSeparatedTokens.GetAt(i)); // make a copy since strtok will change it
if (!subValue)
break;
token = XP_STRTOK(subValue, " ");
while (token)
{
if (!XP_STRCMP(token, "*"))
{
// A special case for the user typing just one star
allocatedToken = PR_smprintf ("(%s=%s)", mnemonic, token);
}
else
{
// In the general case, just encode the filter
const char *filterString = DIR_GetFilterString (GetDirServer());
if (filterString)
{
allocatedToken = NULL;
err = EncodeCustomFilter (token, filterString, &allocatedToken);
}
else
allocatedToken = PR_smprintf ("(%s=%s*)", mnemonic, token);
}
if (allocatedToken)
{
cchSingleWords += XP_STRLEN(allocatedToken) + 1;
singleWordTokens[i].Add(allocatedToken);
}
token = XP_STRTOK(NULL, " ");
}
XP_FREE(subValue);
}
*pEncodingLength = cchSingleWords + (commaSeparatedTokens.GetSize() * 4) + 4;
*ppOutEncoding = (char*) XP_ALLOC (*pEncodingLength);
if (*ppOutEncoding)
{
if (commaSeparatedTokens.GetSize() > 1)
XP_STRCPY (*ppOutEncoding, "(|");
else
*ppOutEncoding[0] = '\0';
for (i = 0; i < commaSeparatedTokens.GetSize(); i++)
{
if (singleWordTokens[i].GetSize() > 1)
XP_STRCAT (*ppOutEncoding, "(&");
for (int j = 0; j < singleWordTokens[i].GetSize(); j++)
{
char *word = (char*) /*hack*/ singleWordTokens[i].GetAt(j);
XP_STRCAT (*ppOutEncoding, word);
XP_FREE(word);
}
if (singleWordTokens[i].GetSize() > 1)
XP_STRCAT (*ppOutEncoding, ")");
}
if (commaSeparatedTokens.GetSize() > 1)
XP_STRCAT (*ppOutEncoding, ")");
}
else
err = SearchError_OutOfMemory;
delete [] singleWordTokens;
}
else
err = SearchError_OutOfMemory;
return err;
}
MSG_SearchError msg_SearchLdap::EncodeMnemonic (MSG_SearchTerm *pTerm, const char *whichMnemonic, char **ppOutEncoding, int *pEncodingLength)
{
MSG_SearchError err = SearchError_Success;
char *utf8String = NULL;
INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_scope->m_frame->GetContext());
*ppOutEncoding = NULL;
*pEncodingLength = 0;
if (IsStringAttribute(pTerm->m_attribute))
{
utf8String = DIR_ConvertToServerCharSet (m_scope->m_server, pTerm->m_value.u.string, INTL_GetCSIWinCSID(c));
if (!utf8String)
return SearchError_InvalidSearchTerm;
}
// If we're doing the simple, Address Book search, use a different encoding system
if (opLdapDwim == pTerm->m_operator)
err = EncodeDwim (whichMnemonic, utf8String, ppOutEncoding, pEncodingLength);
else
{
// Build the actual LDAP filter encoding for this attribute
*pEncodingLength = XP_STRLEN (utf8String) + XP_STRLEN (whichMnemonic) + kLdapTermOverhead;
*ppOutEncoding = (char*) XP_ALLOC (*pEncodingLength);
if (*ppOutEncoding)
{
// Figure out which operators apply. Our negative operators should
// be mapped to a prefix '!', and our contains operators should be
// mapped to '*' around the value string
XP_Bool useBang = FALSE;
XP_Bool useTilde = FALSE;
XP_Bool leadingStar = FALSE;
XP_Bool trailingStar = FALSE;
switch (pTerm->m_operator)
{
case opDoesntContain:
leadingStar = trailingStar = TRUE;
// intentional fall-through
case opIsnt:
useBang = TRUE;
break;
case opContains:
leadingStar = trailingStar = TRUE;
break;
case opIs:
// nothing req'd,
break;
case opSoundsLike:
useTilde = TRUE;
break;
case opBeginsWith:
trailingStar = TRUE;
break;
case opEndsWith:
leadingStar = TRUE;
break;
default:
err = SearchError_ScopeAgreement;
}
XP_STRCPY (*ppOutEncoding, "(");
if (useBang)
XP_STRCAT (*ppOutEncoding, "!(");
XP_STRCAT (*ppOutEncoding, whichMnemonic);
if (useTilde)
XP_STRCAT (*ppOutEncoding, "~");
XP_STRCAT (*ppOutEncoding, "=");
if ((leadingStar || trailingStar) && XP_STRCMP (utf8String, "*"))
{
char *wildValue = NULL;
if (pTerm->m_operator == opContains)
wildValue = TransformSpacesToStars(utf8String);
else
wildValue = XP_STRDUP(utf8String);
if (wildValue)
{
if (leadingStar)
XP_STRCAT (*ppOutEncoding, "*");
XP_STRCAT (*ppOutEncoding, wildValue);
if (trailingStar)
XP_STRCAT (*ppOutEncoding, "*");
XP_FREE(wildValue);
}
else
err = SearchError_OutOfMemory;
}
else
XP_STRCAT (*ppOutEncoding, utf8String);
XP_STRCAT (*ppOutEncoding, ")");
if (useBang)
XP_STRCAT (*ppOutEncoding, ")");
}
else
err = SearchError_OutOfMemory;
}
XP_FREEIF (utf8String);
return err;
}
MSG_SearchError msg_SearchLdap::EncodeTerm (MSG_SearchTerm *pTerm, char **ppOutEncoding, int *pEncodingLength)
{
// In the general case, there is only one LDAP attribute per search term. However, with
// site-configurable LDAP preferences, administrators can set up several attributes, such
// that saying "E-mail address" really means (|(mail=foo)(xmail=foo)(ymail=foo)). Dealing
// with that substitution is the role of this function.
MSG_SearchError err = SearchError_Success;
*pEncodingLength = 0;
DIR_Server *server = GetDirServer();
DIR_AttributeId id;
err = MSG_SearchAttribToDirAttrib (pTerm->m_attribute, &id);
if (SearchError_Success == err)
{
const char **mnemonicList = DIR_GetAttributeStrings (server, id);
int count = 0;
while (mnemonicList[count])
count++;
char **encodingsPerMnemonic = (char**) XP_ALLOC (count * sizeof (char*));
if (encodingsPerMnemonic)
{
int encodingLength = 0;
int totalEncodingLength = 0;
int i;
for (i = 0; i < count; i++)
{
err = EncodeMnemonic (pTerm, mnemonicList[i], &encodingsPerMnemonic[i], &encodingLength);
totalEncodingLength += encodingLength;
}
*pEncodingLength = totalEncodingLength + 1;
if (count > 1)
*pEncodingLength += 3;
*ppOutEncoding = (char*) XP_ALLOC (*pEncodingLength);
if (*ppOutEncoding)
{
*ppOutEncoding[0] = '\0';
if (count > 1)
XP_STRCAT (*ppOutEncoding, "(|");
for (i = 0; i < count; i++)
{
if (encodingsPerMnemonic[i])
{
XP_STRCAT(*ppOutEncoding, encodingsPerMnemonic[i]);
XP_FREE (encodingsPerMnemonic[i]);
}
}
XP_FREE (encodingsPerMnemonic);
if (count > 1)
XP_STRCAT(*ppOutEncoding, ")");
}
else
err = SearchError_OutOfMemory;
}
else
err = SearchError_OutOfMemory;
}
return err;
}
MSG_SearchError msg_SearchLdap::Encode (char **ppOutEncoding)
{
// Generate the LDAP search encodings as specified in rfc-1558.
if (!ppOutEncoding)
return SearchError_NullPointer;
MSG_SearchError err = SearchError_Success;
// list of encodings, one per term
int cchTermEncodings = 0;
char **termEncodings = (char**) XP_ALLOC (m_searchTerms.GetSize() * sizeof (char*));
if (!termEncodings)
return SearchError_OutOfMemory;
XP_Bool booleanAND = TRUE; // should the terms be ANDed or ORed together?
// Encode each term in the list into its string form
for (int i = 0; i < m_searchTerms.GetSize() && SearchError_Success == err; i++)
{
MSG_SearchTerm *pTerm = m_searchTerms.GetAt(i);
XP_ASSERT (pTerm->IsValid());
if (pTerm->IsValid())
{
booleanAND = pTerm->IsBooleanOpAND();
int cchThisEncoding = 0;
err = EncodeTerm (pTerm, &termEncodings[i], &cchThisEncoding);
cchTermEncodings += cchThisEncoding;
}
}
// Build up the string containing all the term encodings catenated together
int idx = 0;
char *pTotalEncoding = (char*) XP_ALLOC (cchTermEncodings + kLdapTermOverhead + 1); // +1 for '&' between terms
if (pTotalEncoding && SearchError_Success == err)
{
pTotalEncoding[0] = '\0';
if (m_searchTerms.GetSize() > 1)
{
// since a search term can have AND or OR boolean operators, check the first term to determine which
// symbol we need.
if (booleanAND)
XP_STRCPY (pTotalEncoding, "(&"); // use AND symbol
else
XP_STRCPY (pTotalEncoding, "(|"); // use OR symbol
for (idx = 0; idx < m_searchTerms.GetSize(); idx++)
XP_STRCAT (pTotalEncoding, termEncodings[idx]);
XP_STRCAT (pTotalEncoding, ")");
}
else
XP_STRCAT (pTotalEncoding, termEncodings[0]);
}
else
err = SearchError_OutOfMemory;
// Clean up the intermediate encodings
for (idx = 0; idx < m_searchTerms.GetSize(); idx++)
XP_FREEIF (termEncodings[idx]);
XP_FREE (termEncodings);
// Only return the total encoding if we're sure it's right
if (SearchError_Success == err)
*ppOutEncoding = pTotalEncoding;
else
{
XP_FREE (pTotalEncoding);
*ppOutEncoding = NULL;
}
return err;
}
MSG_SearchError msg_SearchLdap::BuildUrl (const char *dn, char **outUrl, XP_Bool forAddToAB)
{
MSG_SearchError err = SearchError_Success;
*outUrl = DIR_BuildUrl (m_scope->m_server, dn, forAddToAB);
if (NULL == *outUrl)
err = SearchError_OutOfMemory;
return err;
}
MSG_SearchError msg_SearchLdap::OpenResultElement(MSG_MessagePane *,
MSG_ResultElement *)
{
XP_ASSERT(FALSE); // shouldn't get here with a messagePane
return SearchError_NotImplemented;
}
MSG_SearchError msg_SearchLdap::OpenResultElement(MWContext *context, MSG_ResultElement *result)
{
MSG_SearchError err = SearchError_NotImplemented;
#ifdef MOZ_LDAP
// fire a URL at a browser context, and use mkldap.cpp to build HTML
MSG_SearchValue *dnValue = NULL;
err = result->GetValue (attribDistinguishedName, &dnValue);
if (SearchError_Success == err)
{
char *url = NULL;
err = BuildUrl (dnValue->u.string, &url, FALSE /*addToAB*/);
if (SearchError_Success == err)
{
URL_Struct *url_s = NET_CreateURLStruct (url, NET_NORMAL_RELOAD);
if (url_s)
{
msg_GetURL (context, url_s, FALSE);
XP_FREE(url);
}
}
MSG_DestroySearchValue (dnValue);
}
#endif /* MOZ_LDAP */
return err;
}
MSG_SearchError msg_SearchLdap::ModifyResultElement(MSG_ResultElement *result, MSG_SearchValue *value)
{
MSG_SearchError err = SearchError_Success;
#ifdef MOZ_LDAP
char *platformName = value->u.string + XP_STRLEN("file://");
XP_File file = XP_FileOpen (platformName, xpMailFolder, XP_FILE_READ_BIN);
if (file)
{
XP_FileSeek (file, 0, SEEK_END);
int length = XP_FileTell (file);
char *jpegBuf = (char*) XP_ALLOC (length + 1);
if (jpegBuf)
{
XP_FileSeek (file, 0, SEEK_SET);
XP_FileRead (jpegBuf, length, file);
XP_FileClose (file);
MSG_SearchValue *value = NULL;
err = result->GetValue (attribDistinguishedName, &value);
if (SearchError_Success == err)
{
m_modifyBucket = new LdapModifyBucket;
if (m_modifyBucket)
{
m_modifyBucket->dnValue = value; // deleted in bucket dtor
m_modifyBucket->ber.bv_len = length;
m_modifyBucket->ber.bv_val = jpegBuf;
m_modifyBucket->mod.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
m_modifyBucket->mod.mod_type = "jpegphoto";
m_modifyBucket->mod.mod_vals.modv_bvals = m_modifyBucket->berValList;
Initialize();
m_nextState = kPreAuthBindRequest;
CollectUserCredentials();
m_scope->m_frame->m_parallelScopes.Add(m_scope);
err = m_scope->m_frame->GetUrl ();
}
else
err = SearchError_OutOfMemory;
}
else
err = SearchError_ScopeAgreement;
}
else
err = SearchError_OutOfMemory;
}
#endif /* MOZ_LDAP */
return err;
}
#ifdef MOZ_LDAP
int msg_SearchLdap::PollConnection ()
{
struct timeval timeout;
XP_BZERO (&timeout, sizeof(timeout));
ldap_msgfree (m_message);
int err = ldap_result (m_ldap, m_currentMessage, 0, &timeout, &m_message);
if (err == 0)
err = LDAP_TIMEOUT;
else
if (err != -1 && err != LDAP_RES_SEARCH_ENTRY /*&& err != LDAP_RES_SEARCH_RESULT*/)
{
int err2 = ldap_result2error (m_ldap, m_message, 0);
if (err2 != 0)
err = err2;
}
return err;
}
MSG_SearchError msg_SearchLdap::Initialize ()
{
if (NET_IsOffline())
{
FE_Alert (m_scope->m_frame->GetContext(), XP_GetString(XP_ALERT_OFFLINE_MODE_SELECTED));
return SearchError_ScopeDone;
}
MSG_SearchError err = SearchError_Success;
m_scope->m_frame->UpdateStatusBar (XP_PROGRESS_CONTACTHOST);
m_scope->m_frame->BeginCylonMode();
m_ldap = ldap_init ((char*) GetHostName(), GetPort());
if (m_ldap)
{
XP_Bool wantReferrals = TRUE;
ldap_set_option (m_ldap, LDAP_OPT_REFERRALS, &wantReferrals);
// Set up the max number of entries to return here. I don't
// think we should have a time limit since we have a Stop button.
int maxHits = GetMaxHits();
ldap_set_option (m_ldap, LDAP_OPT_SIZELIMIT, &maxHits);
if (DIR_TestFlag(GetDirServer(), DIR_LDAP_VERSION3))
{
int version = LDAP_VERSION3;
ldap_set_option (m_ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
}
if (GetEnableAuth() && SearchError_Success == CollectUserCredentials())
{
if (m_authDn && m_password)
{
// Got the credentials directly from prefs; go right to auth
// bind since there's no need to go fish for the DN
m_nextState = kAuthenticatedBindRequest;
}
else
m_nextState = kPreAuthBindRequest;
}
else
m_nextState = kAnonymousBindRequest;
}
else
{
DisplayError (XP_LDAP_OPEN_SERVER_FAILED, GetHostDescription(), LDAP_SERVER_DOWN);
err = SearchError_ScopeDone;
}
return err;
}
MSG_SearchError msg_SearchLdap::PreAuthBindRequest ()
{
MSG_SearchError err = BindRequest (NULL, NULL); // turns out to mean the same thing
if (SearchError_Success == err)
m_nextState = kPreAuthBindResponse;
return err;
}
MSG_SearchError msg_SearchLdap::PreAuthBindResponse ()
{
MSG_SearchError err = BindResponse (); // turns out to mean the same thing
m_nextState = kPreAuthSearchRequest;
return err;
}
MSG_SearchError msg_SearchLdap::PreAuthSearchRequest ()
{
MSG_SearchError err = SearchError_Success;
char *attribs[2];
DIR_Server *server = GetDirServer();
attribs[0] = XP_STRDUP(DIR_GetFirstAttributeString (server, cn));
attribs[1] = NULL;
char *filter = PR_smprintf ("(%s=%s)", DIR_GetFirstAttributeString (server, auth), m_valueUsedToFindDn);
if (attribs[0] && filter)
m_currentMessage = ldap_search (m_ldap, (char*) GetSearchBase(), LDAP_SCOPE_SUBTREE, filter, &attribs[0], 0);
else
err = SearchError_OutOfMemory;
int i = 0;
while (attribs[i])
XP_FREE(attribs[i++]);
XP_FREEIF(filter);
m_nextState = kPreAuthSearchResponse;
return err;
}
MSG_SearchError msg_SearchLdap::PreAuthSearchResponse ()
{
MSG_SearchError err = SearchError_Success;
int msgId = PollConnection ();
switch (msgId)
{
case LDAP_TIMEOUT: // search still pending
break;
case LDAP_RES_SEARCH_ENTRY: // got a hit, find its DN
if (!m_authDn)
{
char *authDn = ldap_get_dn (m_ldap, m_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
HandleSizeLimitExceeded();
err = SearchError_ScopeDone;
}
break;
case LDAP_RES_SEARCH_RESULT: // search finished
m_nextState = m_authDn ? kAuthenticatedBindRequest : kUnbindRequest;
break;
case LDAP_SIZELIMIT_EXCEEDED:
// 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
HandleSizeLimitExceeded();
m_nextState = m_authDn ? kAuthenticatedBindRequest : kUnbindRequest;
err = SearchError_ScopeDone;
break;
default:
DisplayError (XP_LDAP_SEARCH_FAILED, GetHostDescription(), msgId);
err = SearchError_ScopeDone;
}
return err;
}
MSG_SearchError msg_SearchLdap::SaveCredentialsToPrefs ()
{
DIR_Server *server = GetDirServer();
if (m_authDn)
DIR_SetAuthDN (server, m_authDn);
if (server->savePassword && m_password)
DIR_SetPassword (server, m_password);
return SearchError_Success;
}
MSG_SearchError msg_SearchLdap::CollectUserCredentials ()
{
MSG_SearchError err = SearchError_Success;
DIR_Server *server = GetDirServer();
// 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 (server->authDn && XP_STRLEN(server->authDn))
m_authDn = XP_STRDUP (server->authDn);
if (server->savePassword && server->password && XP_STRLEN(server->password))
m_password = XP_STRDUP (server->password);
if (m_authDn && m_password)
return err;
char *username = NULL;
char *password = NULL;
const char *anonAttrName = DIR_GetAttributeName (server, auth);
char *prompt = PR_smprintf (XP_GetString(MK_LDAP_AUTH_PROMPT), anonAttrName, server->description);
if (prompt)
{
if (FE_PromptUsernameAndPassword (m_scope->m_frame->GetContext(), prompt, &username, &password))
{
if (password && username)
{
m_password = HG72267 (password);
XP_FREE(password);
m_valueUsedToFindDn = username;
}
else
m_nextState = kAnonymousBindRequest; // no credentials -- let 'em try it anonymously
}
else
m_nextState = kAnonymousBindRequest; // no credentials -- let 'em try it anonymously
XP_FREE(prompt);
}
else
err = SearchError_OutOfMemory;
return err;
}
MSG_SearchError msg_SearchLdap::AuthenticatedBindRequest ()
{
return BindRequest (m_authDn, m_password);
}
MSG_SearchError msg_SearchLdap::AnonymousBindRequest()
{
return BindRequest (NULL, NULL);
}
MSG_SearchError msg_SearchLdap::BindRequest (const char *dn, const char *password)
{
MSG_SearchError err = SearchError_Success;
char *unmungedPassword = NULL;
if (password)
unmungedPassword = HG72224 (password);
m_currentMessage = ldap_simple_bind (m_ldap, dn, unmungedPassword);
if (m_currentMessage > -1)
m_nextState = kBindResponse;
else
{
DisplayError (XP_LDAP_BIND_FAILED, GetHostDescription(), ldap_get_lderrno(m_ldap,NULL,NULL));
err = SearchError_ScopeDone;
}
XP_FREEIF(unmungedPassword);
return err;
}
MSG_SearchError msg_SearchLdap::BindResponse ()
{
MSG_SearchError err = SearchError_Success;
int msgId = PollConnection ();
switch (msgId)
{
case LDAP_TIMEOUT: // bind still pending
break;
case LDAP_RES_BIND: // bind succeeded
m_nextState = m_modifyBucket ? kModifyRequest : kSearchRequest;
break;
default:
DisplayError (XP_LDAP_BIND_FAILED, GetHostDescription(), msgId);
err = SearchError_ScopeDone;
}
return err;
}
MSG_SearchError msg_SearchLdap::SearchRootDSERequest ()
{
m_nextState = kSearchResponse;
char *attribs[1];
attribs[0] = NULL;
m_currentMessage = ldap_search (m_ldap, "", LDAP_SCOPE_BASE, "(objectClass=*)", &attribs[0], 0);
return SearchError_Success;
}
MSG_SearchError msg_SearchLdap::SearchVLVSearchRequest ()
{
m_nextState = kSearchVLVSearchResponse;
char *attribs[4];
attribs[0] = XP_STRDUP(DIR_GetFirstAttributeString (GetDirServer(), cn));
attribs[1] = "vlvBase";
attribs[2] = "vlvFilter";
attribs[3] = NULL;
m_currentPair = 0;
m_currentMessage = ldap_search (m_ldap, LDAP_VLV_BASE, LDAP_SCOPE_ONELEVEL, LDAP_VLV_SEARCHCLASS, &attribs[0], 0);
XP_FREE(attribs[0]);
return SearchError_Success;
}
MSG_SearchError msg_SearchLdap::SearchVLVIndexRequest ()
{
MSG_SearchError err = SearchError_Success;
m_nextState = kSearchVLVIndexResponse;
msg_LdapPair *pair = (msg_LdapPair *)m_pairList.GetAt(m_currentPair);
if (pair)
{
char *attribs[3];
attribs[0] = "vlvSort";
attribs[1] = "vlvEnabled";
attribs[2] = NULL;
char *base = PR_smprintf ("%s=%s,%s", DIR_GetFirstAttributeString (GetDirServer(), cn), pair->cn, LDAP_VLV_BASE);
m_currentMessage = ldap_search (m_ldap, base, LDAP_SCOPE_SUBTREE, LDAP_VLV_INDEXCLASS, &attribs[0], 0);
XP_FREE(base);
}
else
err = SearchError_ScopeDone;
return err;
}
MSG_SearchError msg_SearchLdap::SearchRequest ()
{
m_scope->m_frame->UpdateStatusBar (MK_MSG_SEARCH_STATUS);
// This array serves to prune the returned attributes to only the ones
// we're interested in. If the user wants to see the whole entry,
// he/she can double-click on the result element.
char *attribs[8];
DIR_Server *server = GetDirServer();
attribs[0] = XP_STRDUP(DIR_GetFirstAttributeString (server, cn));
attribs[1] = XP_STRDUP(DIR_GetFirstAttributeString (server, telephonenumber));
attribs[2] = XP_STRDUP(DIR_GetFirstAttributeString (server, mail));
attribs[3] = XP_STRDUP(DIR_GetFirstAttributeString (server, l));
attribs[4] = XP_STRDUP(DIR_GetFirstAttributeString (server, o));
attribs[5] = XP_STRDUP(DIR_GetFirstAttributeString (server, givenname));
attribs[6] = XP_STRDUP(DIR_GetFirstAttributeString (server, sn));
attribs[7] = NULL;
char *filter = m_filter;
LDAPControl *sort_request = NULL;
LDAPControl *vlv_request = NULL;
LDAPControl *pldapCtrlList[3] = { NULL, NULL, NULL };
if (IsValidVLVSearch())
{
char *sort = "cn";
LDAPsortkey **keylist = NULL;
LDAPVirtualList *pLdapVLV = GetLdapVLVData();
/* We use the extra data pointer for passing the search filter and
* sort order.
*/
if (pLdapVLV->ldvlist_extradata)
{
filter = (char *)pLdapVLV->ldvlist_extradata;
sort = XP_STRCHR(filter, ':');
sort++[0] = '\0';
for (int i = 0; sort[i]; i++)
if (sort[i] != '-' && !isalnum(sort[i]))
sort[i] = ' ';
pLdapVLV->ldvlist_extradata = NULL;
}
ldap_create_sort_keylist (&keylist, sort);
ldap_create_sort_control (m_ldap, keylist, 0, &sort_request);
ldap_free_sort_keylist (keylist);
ldap_create_virtuallist_control (m_ldap, pLdapVLV, &vlv_request);
pldapCtrlList[0] = sort_request;
pldapCtrlList[1] = vlv_request;
}
int msgid;
if (ldap_search_ext (m_ldap, (char *) GetSearchBase(), LDAP_SCOPE_SUBTREE,
filter, &attribs[0], 0,
(pldapCtrlList[0] ? pldapCtrlList : (LDAPControl **)NULL),
(LDAPControl **)NULL, (struct timeval *)NULL,
-1, &msgid) == LDAP_SUCCESS)
m_currentMessage = msgid;
else
m_currentMessage = -1;
if (sort_request)
ldap_control_free (sort_request);
if (vlv_request)
ldap_control_free (vlv_request);
if (filter != m_filter)
XP_FREE(filter);
int i = 0;
while (attribs[i])
XP_FREE(attribs[i++]);
m_nextState = kSearchResponse;
return SearchError_Success;
}
MSG_SearchError msg_SearchLdap::SearchRootDSEResponse ()
{
XP_Bool keepPolling;
MSG_SearchError err = SearchError_Success;
do
{
int msgId = PollConnection ();
keepPolling = FALSE;
switch (msgId)
{
case LDAP_TIMEOUT: // search still pending
break;
case LDAP_RES_SEARCH_ENTRY:
DIR_ParseRootDSE (GetDirServer(), m_ldap, m_message);
keepPolling = TRUE;
break;
case LDAP_RES_SEARCH_RESULT:// search complete
default: // some error occurred, consider search complete
DIR_SetFlag (GetDirServer(), DIR_LDAP_ROOTDSE_PARSED);
if (GetSearchType() == searchRootDSE)
{
m_nextState = kUnbindRequest;
err = SearchError_ScopeDone;
}
else if ( GetSearchType() == searchLdapVLV
&& !DIR_TestFlag(GetDirServer(), DIR_LDAP_VIRTUALLISTVIEW))
RestartFailedVLVSearch();
else
m_nextState = kSearchVLVSearchRequest;
}
} while (keepPolling);
return err;
}
MSG_SearchError msg_SearchLdap::SearchVLVSearchResponse ()
{
XP_Bool keepPolling;
MSG_SearchError err = SearchError_Success;
do
{
int msgId = PollConnection ();
keepPolling = FALSE;
switch (msgId)
{
case LDAP_TIMEOUT: // search still pending
break;
case LDAP_RES_SEARCH_ENTRY:
keepPolling = TRUE;
AddPair (ldap_first_entry (m_ldap, m_message));
break;
case LDAP_RES_SEARCH_RESULT:// search complete
if (m_pairList.GetSize() == 0)
{
DIR_ClearFlag(GetDirServer(), DIR_LDAP_VIRTUALLISTVIEW);
RestartFailedVLVSearch();
}
else
m_nextState = kSearchVLVIndexRequest;
break;
default: // some error occurred, consider search complete
err = SearchError_ScopeDone;
UnbindRequest();
break;
}
} while (keepPolling);
return err;
}
MSG_SearchError msg_SearchLdap::SearchVLVIndexResponse ()
{
XP_Bool keepPolling;
MSG_SearchError err = SearchError_Success;
do
{
int msgId = PollConnection ();
keepPolling = FALSE;
switch (msgId)
{
case LDAP_TIMEOUT: // search still pending
break;
case LDAP_RES_SEARCH_ENTRY:
keepPolling = TRUE;
AddSortToPair((msg_LdapPair *)(m_pairList[m_currentPair]), ldap_first_entry (m_ldap, m_message));
break;
case LDAP_RES_SEARCH_RESULT:// search complete
/* If there are more pairs to process, do so.
*/
if (m_pairList.GetSize() > ++m_currentPair)
{
m_nextState = kSearchVLVIndexRequest;
break;
}
/* We're done processing the VLV pairs, we can now build the VLV pair string.
*/
ParsePairList();
/* Fall-through */
default: // some error occurred, consider search complete
UnbindRequest();
err = Initialize();
}
} while (keepPolling);
return err;
}
MSG_SearchError msg_SearchLdap::SearchResponse ()
{
MSG_SearchError err = SearchError_Success;
// The "keepPolling" logic is designed to read as many entries as we can
// right now. However, on a high-speed connection to an idle server, it
// can spew entries so fast that the user is likely to be mad that we're
// not giving them control of their machine. So we'll read in chunks, or
// until we need to pause for read.
const int chunkSize = 50;
XP_Bool keepPolling;
int numEntriesReceived = 0;
do
{
keepPolling = FALSE;
int msgId = PollConnection ();
switch (msgId)
{
case LDAP_TIMEOUT: // search still pending
break;
case LDAP_RES_SEARCH_ENTRY: // got some hits, search continues
AddMessageToResults (ldap_first_entry (m_ldap, m_message));
keepPolling = ++numEntriesReceived < chunkSize;
break;
case LDAP_RES_SEARCH_RESULT: // search finished
SaveCredentialsToPrefs ();
if (IsValidVLVSearch())
ProcessVLVResults (m_message);
m_nextState = kUnbindRequest;
err = SearchError_ScopeDone;
break;
case LDAP_SIZELIMIT_EXCEEDED:
HandleSizeLimitExceeded();
m_nextState = kUnbindRequest;
err = SearchError_ScopeDone;
break;
default:
DisplayError (XP_LDAP_SEARCH_FAILED, GetHostDescription(), msgId);
err = SearchError_ScopeDone;
}
} while (keepPolling);
return err;
}
MSG_SearchError msg_SearchLdap::ModifyRequest ()
{
MSG_SearchError err = SearchError_Success;
XP_ASSERT(m_modifyBucket);
if (m_modifyBucket)
{
m_currentMessage = ldap_modify (m_ldap, m_modifyBucket->dnValue->u.string, m_modifyBucket->modList);
m_nextState = kModifyResponse;
}
else
err = SearchError_NullPointer;
return err;
}
MSG_SearchError msg_SearchLdap::ModifyResponse ()
{
MSG_SearchError err = SearchError_Success;
int msgId = PollConnection ();
switch (msgId)
{
case LDAP_TIMEOUT: // modify still pending
break;
case LDAP_RES_MODIFY: // modify is complete
m_nextState = kUnbindRequest;
err = SearchError_ScopeDone;
// Free memory shared across the async modify operation
delete m_modifyBucket;
m_modifyBucket = NULL;
break;
default:
DisplayError (XP_LDAP_MODIFY_FAILED, GetHostDescription(), msgId);
err = SearchError_ScopeDone;
}
return err;
}
MSG_SearchError msg_SearchLdap::UnbindRequest ()
{
if (NULL != m_ldap)
{
ldap_unbind (m_ldap);
m_ldap = NULL;
}
return SearchError_ScopeDone;
}
#if !defined(MOZ_NO_LDAP)
void msg_SearchLdap::AddMessageToResults (LDAPMessage *messageChain)
{
if (!messageChain)
return; // Stop recursing on this chain
MSG_ResultElement *result = new MSG_ResultElement (this);
if (result)
{
XP_ASSERT (result);
MSG_SearchValue *value = NULL;
char **scratchArray = NULL;
XP_Bool needOrg = TRUE;
XP_Bool needCn = TRUE;
DIR_Server *server = GetDirServer();
INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_scope->m_frame->GetContext());
int16 win_csid = INTL_GetCSIWinCSID(c);
scratchArray = ldap_get_values (m_ldap, messageChain, DIR_GetFirstAttributeString(server, cn));
if (scratchArray && scratchArray[0])
{
value = new MSG_SearchValue;
value->attribute = attribCommonName;
value->u.string = DIR_ConvertFromServerCharSet (m_scope->m_server, scratchArray[0], win_csid);
result->AddValue (value);
ldap_value_free (scratchArray);
needCn = FALSE;
}
scratchArray = ldap_get_values (m_ldap, messageChain, DIR_GetFirstAttributeString(server, givenname));
if (scratchArray && scratchArray[0])
{
value = new MSG_SearchValue;
value->attribute = attribGivenName;
value->u.string = DIR_ConvertFromServerCharSet (m_scope->m_server, scratchArray[0], win_csid);
result->AddValue (value);
ldap_value_free (scratchArray);
}
scratchArray = ldap_get_values (m_ldap, messageChain, DIR_GetFirstAttributeString(server, sn));
if (scratchArray && scratchArray[0])
{
value = new MSG_SearchValue;
value->attribute = attribSurname;
value->u.string = DIR_ConvertFromServerCharSet (m_scope->m_server, scratchArray[0], win_csid);
result->AddValue (value);
ldap_value_free (scratchArray);
}
scratchArray = ldap_get_values (m_ldap, messageChain, DIR_GetFirstAttributeString(server, mail));
if (scratchArray && scratchArray[0])
{
char *mailbox = MSG_ExtractRFC822AddressMailboxes (scratchArray[0]);
if (mailbox)
{
value = new MSG_SearchValue;
value->attribute = attrib822Address;
value->u.string = DIR_ConvertFromServerCharSet (m_scope->m_server, mailbox, win_csid);
result->AddValue (value);
XP_FREE(mailbox);
}
ldap_value_free (scratchArray);
}
scratchArray = ldap_get_values (m_ldap, messageChain, DIR_GetFirstAttributeString(server, telephonenumber));
if (scratchArray && scratchArray[0])
{
value = new MSG_SearchValue;
value->attribute = attribPhoneNumber;
value->u.string = DIR_ConvertFromServerCharSet (m_scope->m_server, scratchArray[0], win_csid);
result->AddValue (value);
ldap_value_free (scratchArray);
}
scratchArray = ldap_get_values (m_ldap, messageChain, DIR_GetFirstAttributeString(server, l));
if (scratchArray && scratchArray[0])
{
value = new MSG_SearchValue;
value->attribute = attribLocality;
value->u.string = DIR_ConvertFromServerCharSet (m_scope->m_server, scratchArray[0], win_csid);
result->AddValue (value);
ldap_value_free (scratchArray);
}
scratchArray = ldap_get_values (m_ldap, messageChain, DIR_GetFirstAttributeString(server, o));
if (scratchArray && scratchArray[0])
{
value = new MSG_SearchValue;
value->attribute = attribOrganization;
value->u.string = DIR_ConvertFromServerCharSet (m_scope->m_server, scratchArray[0], win_csid);
result->AddValue (value);
ldap_value_free (scratchArray);
needOrg = FALSE;
}
// Save off the DN so the server can get back to this entry quickly
// if we need to get more attributes
char *distinguishedName = ldap_get_dn (m_ldap, messageChain);
if (distinguishedName)
{
value = new MSG_SearchValue;
if (value)
{
value->attribute = attribDistinguishedName;
value->u.string = DIR_ConvertFromServerCharSet (m_scope->m_server, distinguishedName, win_csid);
result->AddValue (value);
}
// If we didn't get some attributes we wanted, try to pick them up from the DN
if (needOrg || needCn)
{
if ((scratchArray = ldap_explode_dn (distinguishedName, FALSE /*want types*/)) != NULL)
{
char *dnComponent = NULL;
int i = 0;
while ((dnComponent = scratchArray[i++]) != NULL)
{
if (needOrg && !strncasecomp (dnComponent, "o=", 2))
{
if ((value = new MSG_SearchValue) != NULL)
{
value->attribute = attribOrganization;
value->u.string = DIR_ConvertFromServerCharSet (m_scope->m_server, dnComponent+2, win_csid);
result->AddValue (value);
needOrg = FALSE;
}
}
if (needCn && !strncasecomp (dnComponent, "cn=", 3))
{
if ((value = new MSG_SearchValue) != NULL)
{
value->attribute = attribCommonName;
value->u.string = DIR_ConvertFromServerCharSet (m_scope->m_server, dnComponent+3, win_csid);
result->AddValue (value);
needCn = FALSE;
}
}
}
ldap_value_free (scratchArray);
}
}
ldap_memfree (distinguishedName);
}
// Add this result element to the list we show the user
m_scope->m_frame->AddResultElement (result);
}
// Recurse down other possible messages in this chain
AddMessageToResults (ldap_next_entry (m_ldap, messageChain));
}
void msg_SearchLdap::AddPair (LDAPMessage *messageChain)
{
if (!messageChain)
return; // Stop recursing on this chain
msg_LdapPair *pair = (msg_LdapPair *)XP_CALLOC(1, sizeof(msg_LdapPair));
if (pair)
{
XP_Bool baseMatches = FALSE;
char **scratchArray = NULL;
DIR_Server *server = GetDirServer();
INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_scope->m_frame->GetContext());
int16 win_csid = INTL_GetCSIWinCSID(c);
/* Ignore VLVs whose base does not match the search base in the server.
* Unfortunately we can't just compare the search base with strcmp, LDAP attribute
* values are insensitive to extra whitespace.
*/
scratchArray = ldap_get_values (m_ldap, messageChain, "vlvBase");
if (scratchArray && scratchArray[0])
{
int i;
char *base[2], *start, *end, *compend;
base[0] = XP_STRDUP(server->searchBase),
base[1] = DIR_ConvertFromServerCharSet (m_scope->m_server, scratchArray[0], win_csid);
ldap_value_free (scratchArray);
/* Strip any non-significant whitespace from the search base strings.
*/
for (i = 0; i < 2 && base[i]; i++)
{
for (start = end = base[i]; *end; )
{
/* Get rid of space at the beginning of the string and
* after '=' and ','.
*/
while (*end && XP_IS_SPACE(*end))
end++;
if (*end == '=' || *end == ',')
{
*start++ = *end++;
}
else if (*end)
{
/* Find the beginning of the next token/component
*/
for (compend = end; *compend && *compend != '=' && *compend != ','; )
compend++;
/* Back up to the last non-whitespace character.
*/
while (compend > end && XP_IS_SPACE(*(compend-1)))
compend--;
/* Copy the component.
*/
while (end < compend)
*start++ = *end++;
}
}
*start = '\0';
}
baseMatches = (XP_STRCASECMP(base[0], base[1]) == 0);
XP_FREE(base[0]);
XP_FREE(base[1]);
}
if (baseMatches)
{
int numAttributesLeft = 2;
scratchArray = ldap_get_values (m_ldap, messageChain, DIR_GetFirstAttributeString(server, cn));
if (scratchArray && scratchArray[0])
{
pair->cn = XP_STRDUP(DIR_ConvertFromServerCharSet (m_scope->m_server, scratchArray[0], win_csid));
ldap_value_free (scratchArray);
numAttributesLeft--;
}
scratchArray = ldap_get_values (m_ldap, messageChain, "vlvFilter");
if (scratchArray && scratchArray[0])
{
pair->filter = XP_STRDUP(DIR_ConvertFromServerCharSet (m_scope->m_server, scratchArray[0], win_csid));
ldap_value_free (scratchArray);
numAttributesLeft--;
}
if (numAttributesLeft > 0)
FreePair(pair);
}
if (baseMatches)
{
pair->sort = NULL;
m_pairList.Add(pair);
}
}
// Recurse down other possible messages in this chain
AddPair (ldap_next_entry (m_ldap, messageChain));
}
void msg_SearchLdap::AddSortToPair (msg_LdapPair *pair, LDAPMessage *messageChain)
{
if (!messageChain)
return; // Stop recursing on this chain
INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_scope->m_frame->GetContext());
int16 win_csid = INTL_GetCSIWinCSID(c);
XP_Bool enabled = FALSE;
char **scratchArray = NULL;
DIR_Server *server = GetDirServer();
scratchArray = ldap_get_values (m_ldap, messageChain, "vlvEnabled");
if (scratchArray && scratchArray[0])
{
enabled = (XP_Bool)XP_ATOI(scratchArray[0]);
ldap_value_free (scratchArray);
}
if (enabled)
{
/* Only the first vlvSort attribute is recognized by the server.
*/
scratchArray = ldap_get_values (m_ldap, messageChain, "vlvSort");
if (scratchArray && scratchArray[0])
{
char *sort;
char *newsort = DIR_ConvertFromServerCharSet (m_scope->m_server, scratchArray[0], win_csid);
if (pair->sort)
{
sort = PR_smprintf ("%s;%s:%s", pair->sort, pair->filter, newsort);
XP_FREE(pair->sort);
}
else
sort = PR_smprintf ("%s:%s", pair->filter, newsort);
pair->sort = sort;
ldap_value_free (scratchArray);
}
}
// Recurse down other possible messages in this chain
AddSortToPair (pair, ldap_next_entry (m_ldap, messageChain));
}
void msg_SearchLdap::FreePair (msg_LdapPair *pair)
{
XP_ASSERT(pair);
XP_FREEIF(pair->cn);
XP_FREEIF(pair->filter);
XP_FREEIF(pair->sort);
XP_FREE(pair);
}
void msg_SearchLdap::ParsePairList ()
{
uint32 i, offset, size;
msg_LdapPair *pair;
/* First allocate space for the complete VLV pair list.
*/
for (i = 0, size = 0; i < m_pairList.GetSize(); i++)
{
pair = (msg_LdapPair *)(m_pairList[i]);
if (pair->sort)
size += XP_STRLEN(pair->sort) + 1;
}
size++;
char *pairList = (char *)XP_ALLOC(size);
if (pairList)
{
/* coallesce the VLV pairs into one string.
*/
pairList[0] = '\0';
for (i = 0, offset = 0; i < m_pairList.GetSize(); i++)
{
pair = (msg_LdapPair *)(m_pairList[i]);
if (pair->sort)
offset += PR_snprintf(&pairList[offset], size - offset, "%s;", pair->sort);
}
/* Store the pair string in the DIR_Server
*/
DIR_Server *server = GetDirServer();
XP_FREEIF(server->searchPairList);
server->searchPairList = pairList;
DIR_SaveServerPreferences (FE_GetDirServers());
}
/* Finally, free the pair list.
*/
for (i = 0; i < m_pairList.GetSize(); i++)
FreePair((msg_LdapPair *)(m_pairList[i]));
m_pairList.SetSize(0);
}
LDAPVirtualList *msg_SearchLdap::GetLdapVLVData ()
{
return (LDAPVirtualList *)(m_scope->m_frame->GetSearchParam());
}
MSG_SearchType msg_SearchLdap::GetSearchType ()
{
return m_scope->m_frame->GetSearchType();
}
MSG_SearchError msg_SearchLdap::SetSearchParam (MSG_SearchType type, void *param)
{
return m_scope->m_frame->SetSearchParam(type, param);
}
XP_Bool msg_SearchLdap::IsValidVLVSearch ()
{
return DIR_TestFlag(m_scope->m_server, DIR_LDAP_VIRTUALLISTVIEW)
&& m_scope->m_frame->GetSearchType() == searchLdapVLV
&& m_scope->m_frame->GetSearchParam();
}
MSG_SearchError msg_SearchLdap::RestartFailedVLVSearch ()
{
MSG_SearchError err = SearchError_ScopeDone;
/* We attempted a VLV search on a server that doesn't support VLV, we need
* to remap the search term to the VLV attribute value.
*/
LDAPVirtualList *pLdapVLV = GetLdapVLVData();
if (pLdapVLV && pLdapVLV->ldvlist_attrvalue)
{
MSG_SearchTerm *term = m_scope->m_frame->m_termList.GetAt(0);
XP_ASSERT(m_scope->m_frame->m_termList.GetSize() == 1);
XP_ASSERT(term->m_value.u.string[0] == '\0');
term->m_value.u.string = XP_STRDUP(pLdapVLV->ldvlist_attrvalue);
SetSearchParam (searchNormal, NULL);
if (ValidateTerms() == SearchError_Success)
{
UnbindRequest();
err = Initialize();
}
}
if (err != SearchError_Success)
{
m_nextState = kUnbindRequest;
err = SearchError_ScopeDone;
}
return err;
}
void msg_SearchLdap::ProcessVLVResults (LDAPMessage *messageChain)
{
int rc;
if (( rc = ldap_result2error (m_ldap, messageChain, 0)) != LDAP_SUCCESS)
{
SetSearchParam (searchLdapVLV, NULL);
}
/* Check for the sort and VLV response controls
*/
LDAPControl **ldapCtrlList;
ldap_parse_result (m_ldap, messageChain, &rc, NULL, NULL, NULL,
&ldapCtrlList, FALSE);
int error;
unsigned long result = 0, index, size;
char *attrib;
if ( ldap_parse_sort_control (m_ldap, ldapCtrlList, &result, &attrib) == LDAP_SUCCESS
&& ldap_parse_virtuallist_control (m_ldap, ldapCtrlList, &index, &size, &error) == LDAP_SUCCESS)
{
LDAPVirtualList *pldapVL = GetLdapVLVData ();
if (pldapVL)
{
pldapVL->ldvlist_index = index;
pldapVL->ldvlist_size = size;
}
}
else
{
SetSearchParam (searchLdapVLV, NULL);
}
ldap_controls_free (ldapCtrlList);
}
#endif
void msg_SearchLdap::DisplayError (int templateId, const char *contextString, int ldapErrorId, XP_Bool /*isTemplate*/)
{
// 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;
m_scope->m_frame->m_handlingError = TRUE;
// if (isTemplate)
// ldapErrString = ldap_tmplerr2string (ldapErrorId);
// else
ldapErrString = ldap_err2string (ldapErrorId);
if (templateString && contextString && ldapErrString)
{
int len = XP_STRLEN(templateString) + XP_STRLEN(ldapErrString) + XP_STRLEN(contextString) + 10;
char *theBigString = (char*) XP_ALLOC (len);
if (theBigString)
{
PR_snprintf (theBigString, len, templateString, contextString, ldapErrString, ldapErrorId);
FE_Alert (m_scope->m_frame->GetContext(), theBigString);
XP_FREE(theBigString);
}
}
m_scope->m_frame->m_handlingError = FALSE;
}
#endif /* MOZ_LDAP */
int msg_SearchLdap::Abort ()
{
#ifdef MOZ_LDAP
int result = 0;
if (m_ldap)
result = ldap_abandon (m_ldap, m_currentMessage);
if (0 == result)
result = msg_SearchAdapter::Abort ();
m_scope->m_frame->EndCylonMode();
return result;
#else
return msg_SearchAdapter::Abort ();
#endif /* MOZ_LDAP */
}
const char *msg_SearchLdap::GetHostName()
{
return m_scope->m_server->serverName;
}
const char *msg_SearchLdap::GetSearchBase ()
{
return m_scope->m_server->searchBase;
}
int msg_SearchLdap::GetMaxHits ()
{
return m_scope->m_server->maxHits;
}
XP_Bool msg_SearchLdap::GetEnableAuth ()
{
return m_scope->m_server->enableAuth;
}
int msg_SearchLdap::GetPort ()
{
#ifdef MOZ_LDAP
if (m_scope->m_server->port)
return m_scope->m_server->port;
return GetStandardPort();
#else
return 0;
#endif /* MOZ_LDAP */
}
int msg_SearchLdap::GetStandardPort()
{
HG25167
}
HG98726
const char *msg_SearchLdap::GetUrlScheme (XP_Bool forAddToAB)
{
HG82798
return forAddToAB ? "addbook-ldap:" : "ldap:";
}
const char *msg_SearchLdap::GetHostDescription()
{
// since the description is optional, it might be null, so pick up
// the server's DNS name if necessary
char *desc = m_scope->m_server->description;
if (desc && *desc)
return desc;
return GetHostName();
}
void msg_SearchLdap::HandleSizeLimitExceeded ()
{
if (m_scope->m_frame->m_resultList.GetSize() < GetMaxHits())
{
// If we've exceeded a size limit less than our client-imposed limit,
// we must have blown through the "look-through limit" on the server
FE_Progress (m_scope->m_frame->GetContext(), XP_GetString (XP_LDAP_SERVER_SIZELIMIT_EXCEEDED));
}
else
{
// If we have actually gotten hits back, we've probably exceeded
// the client-imposed limit stored in the DIR_Server
char *prompt = PR_smprintf (XP_GetString (XP_LDAP_SIZELIMIT_EXCEEDED), GetMaxHits());
if (prompt)
{
FE_Progress (m_scope->m_frame->GetContext(), prompt);
XP_FREE (prompt);
}
}
}
void msg_SearchValidityManager::EnableLdapAttribute (MSG_SearchAttribute attrib, XP_Bool enableIt)
{
m_ldapTable->SetAvailable (attrib, opContains, enableIt);
m_ldapTable->SetEnabled (attrib, opContains, enableIt);
m_ldapTable->SetAvailable (attrib, opDoesntContain, enableIt);
m_ldapTable->SetEnabled (attrib, opDoesntContain, enableIt);
m_ldapTable->SetAvailable (attrib, opIs, enableIt);
m_ldapTable->SetEnabled (attrib, opIs, enableIt);
m_ldapTable->SetAvailable (attrib, opIsnt, enableIt);
m_ldapTable->SetEnabled (attrib, opIsnt, enableIt);
m_ldapTable->SetAvailable (attrib, opSoundsLike, enableIt);
m_ldapTable->SetEnabled (attrib, opSoundsLike, enableIt);
m_ldapTable->SetAvailable (attrib, opBeginsWith, enableIt);
m_ldapTable->SetEnabled (attrib, opBeginsWith, enableIt);
m_ldapTable->SetAvailable (attrib, opEndsWith, enableIt);
m_ldapTable->SetEnabled (attrib, opEndsWith, enableIt);
}
MSG_SearchError msg_SearchValidityManager::InitLdapTable (DIR_Server *server)
{
MSG_SearchError err = SearchError_Success;
if (NULL == m_ldapTable)
err = NewTable (&m_ldapTable);
if (SearchError_Success == err)
{
EnableLdapAttribute (attribCommonName);
EnableLdapAttribute (attrib822Address);
EnableLdapAttribute (attribPhoneNumber);
EnableLdapAttribute (attribOrganization);
EnableLdapAttribute (attribOrgUnit);
EnableLdapAttribute (attribLocality);
EnableLdapAttribute (attribStreetAddress);
if (server)
{
EnableLdapAttribute (attribCustom1, DIR_UseCustomAttribute (server, custom1));
EnableLdapAttribute (attribCustom2, DIR_UseCustomAttribute (server, custom2));
EnableLdapAttribute (attribCustom3, DIR_UseCustomAttribute (server, custom3));
EnableLdapAttribute (attribCustom4, DIR_UseCustomAttribute (server, custom4));
EnableLdapAttribute (attribCustom5, DIR_UseCustomAttribute (server, custom5));
}
}
return err;
}
MSG_SearchError msg_SearchValidityManager::PostProcessValidityTable (DIR_Server *server)
{
return InitLdapTable (server);
}