diff --git a/mozilla/directory/xpcom/base/public/nsILDAPService.idl b/mozilla/directory/xpcom/base/public/nsILDAPService.idl index c78eac0ba2f..33a881b92f0 100644 --- a/mozilla/directory/xpcom/base/public/nsILDAPService.idl +++ b/mozilla/directory/xpcom/base/public/nsILDAPService.idl @@ -18,6 +18,7 @@ * Rights Reserved. * * Contributor(s): Leif Hedstrom + * Dan Mosedale * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the @@ -177,4 +178,34 @@ interface nsILDAPService : nsISupports { */ void reconnectConnection(in wstring aKey, in nsILDAPMessageListener aListener); + + /** + * Generates and returns an LDAP search filter by substituting + * aValue, aAttr, aPrefix, and aSuffix into aPattern. + * + * The only good documentation I'm aware of for this function is + * at + * and + * + * Unfortunately, this does not currently seem to be available + * under any open source license, so I can't include that + * documentation here in the doxygen comments. + * + * @param aMaxSize maximum size (in PRUnichars) of string to be + * created and returned (including final \0) + * @param aPattern pattern to be used for the filter + * @param aPrefix prefix to prepend to the filter + * @param aSuffix suffix to be appended to the filer + * @param aAttr replacement for %a in the pattern + * @param aValue replacement for %v in the pattern + * + * @exception NS_ERROR_INVALID_ARG invalid parameter passed in + * @exception NS_ERROR_OUT_OF_MEMORY allocation failed + * @exception NS_ERROR_NOT_AVAILABLE filter longer than maxsiz chars + * @exception NS_ERROR_UNEXPECTED ldap_create_filter returned + * unexpected error code + */ + AString createFilter(in unsigned long aMaxSize, in AString aPattern, + in AString aPrefix, in AString aSuffix, + in AString aAttr, in AString aValue); }; diff --git a/mozilla/directory/xpcom/base/src/nsLDAPService.cpp b/mozilla/directory/xpcom/base/src/nsLDAPService.cpp index 4caf207be8f..c693f138dce 100644 --- a/mozilla/directory/xpcom/base/src/nsLDAPService.cpp +++ b/mozilla/directory/xpcom/base/src/nsLDAPService.cpp @@ -41,7 +41,7 @@ #include "nsIConsoleService.h" #include "nsILDAPURL.h" #include "nsAutoLock.h" - +#include "nsCRT.h" // Constants for CIDs used here. // @@ -849,3 +849,158 @@ nsresult nsLDAPService::EstablishConnection(nsLDAPServiceEntry *aEntry, return NS_OK; } + +/* AString createFilter (in unsigned long aMaxSize, in AString aPattern, in AString aPrefix, in AString aSuffix, in AString aAttr, in AString aValue); */ +NS_IMETHODIMP nsLDAPService::CreateFilter(PRUint32 aMaxSize, + const nsAReadableString & aPattern, + const nsAReadableString & aPrefix, + const nsAReadableString & aSuffix, + const nsAReadableString & aAttr, + const nsAReadableString & aValue, + nsAWritableString & _retval) +{ + if (!aMaxSize) { + return NS_ERROR_INVALID_ARG; + } + + // prepare to tokenize |value| for %vM ... %vN + // + nsReadingIterator iter, iterEnd; // setup the iterators + aValue.BeginReading(iter); + aValue.EndReading(iterEnd); + + // figure out how big of an array we're going to need for the tokens, + // including a trailing NULL, and allocate space for it. + // + PRUint32 numTokens = CountTokens(iter, iterEnd); + char **valueWords; + valueWords = NS_STATIC_CAST(char **, + nsMemory::Alloc((numTokens + 1) * + sizeof(char *))); + if (!valueWords) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // build the array of values + // + PRUint32 curToken = 0; + while (iter != iterEnd && curToken < numTokens ) { + valueWords[curToken] = NextToken(iter, iterEnd); + if ( !valueWords[curToken] ) { + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(curToken, valueWords); + return NS_ERROR_OUT_OF_MEMORY; + } + curToken++; + } + valueWords[numTokens] = 0; // end of array signal to LDAP C SDK + + // make buffer to be used for construction + // + char *buffer = NS_STATIC_CAST(char *, + nsMemory::Alloc(aMaxSize * sizeof(char))); + if (!buffer) { + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(numTokens, valueWords); + return NS_ERROR_OUT_OF_MEMORY; + } + + // create the filter itself + // + nsresult rv; + int result = ldap_create_filter(buffer, aMaxSize, + NS_CONST_CAST(char *, NS_ConvertUCS2toUTF8(aPattern).get()), + NS_CONST_CAST(char *, NS_ConvertUCS2toUTF8(aPrefix).get()), + NS_CONST_CAST(char *, NS_ConvertUCS2toUTF8(aSuffix).get()), + NS_CONST_CAST(char *, NS_ConvertUCS2toUTF8(aAttr).get()), + NS_CONST_CAST(char *, NS_ConvertUCS2toUTF8(aValue).get()), + valueWords); + switch (result) { + case LDAP_SUCCESS: + rv = NS_OK; + break; + + case LDAP_SIZELIMIT_EXCEEDED: + PR_LOG(gLDAPLogModule, PR_LOG_DEBUG, + ("nsLDAPService::CreateFilter(): " + "filter longer than max size of %d generated", + aMaxSize)); + rv = NS_ERROR_NOT_AVAILABLE; + break; + + case LDAP_PARAM_ERROR: + rv = NS_ERROR_INVALID_ARG; + break; + + default: + NS_ERROR("nsLDAPService::CreateFilter(): ldap_create_filter() " + "returned unexpected error"); + rv = NS_ERROR_UNEXPECTED; + break; + } + + _retval = NS_ConvertUTF8toUCS2(buffer); + + // done with the array and the buffer + // + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(numTokens, valueWords); + nsMemory::Free(buffer); + + return rv; +} + +// Count the number of space-separated tokens between aIter and aIterEnd +// +PRUint32 +nsLDAPService::CountTokens(nsReadingIterator aIter, + nsReadingIterator aIterEnd) +{ + PRUint32 count(0); + + // keep iterating through the string until we hit the end + // + while (aIter != aIterEnd) { + + // move past any leading spaces + // + while (aIter != aIterEnd && nsCRT::IsAsciiSpace(*aIter)) { + aIter++; + } + + // move past all chars in this token + // + while (aIter != aIterEnd) { + + if (nsCRT::IsAsciiSpace(*aIter)) { + count++; // token finished; increment the count + aIter++; // move past the space + break; + } + aIter++; // move to next char and continue with this token + } + } + + return count; +} + +// return the next token in this iterator +// +char * +nsLDAPService::NextToken(nsReadingIterator & aIter, + nsReadingIterator & aIterEnd) +{ + nsAutoString token; + + // move past any leading whitespace + // + while ( aIter != aIterEnd && nsCRT::IsAsciiSpace(*aIter) ) { + aIter++; + } + + // copy the token into our local variable + // + while ( aIter != aIterEnd && !nsCRT::IsAsciiSpace(*aIter) ) { + token.Append(*aIter); + aIter++; + } + + return token.ToNewCString(); +} diff --git a/mozilla/directory/xpcom/base/src/nsLDAPService.h b/mozilla/directory/xpcom/base/src/nsLDAPService.h index b31fd2152a7..b5fbd6e7ab1 100644 --- a/mozilla/directory/xpcom/base/src/nsLDAPService.h +++ b/mozilla/directory/xpcom/base/src/nsLDAPService.h @@ -124,6 +124,20 @@ class nsLDAPService : public nsILDAPService, public nsILDAPMessageListener nsresult EstablishConnection(nsLDAPServiceEntry *, nsILDAPMessageListener *); + // kinda like strtok_r, but with iterators. for use by + // createFilter + // + char *NextToken(nsReadingIterator & aIter, + nsReadingIterator & aIterEnd); + + // count how many tokens are in this string; for use by + // createFilter; note that unlike with NextToken, these params + // are copies, not references. + // + PRUint32 CountTokens(nsReadingIterator aIter, + nsReadingIterator aIterEnd); + + PRLock *mLock; // Lock mechanism nsHashtable *mServers; // Hash table holding server entries nsHashtable *mConnections; // Hash table holding "reverse" diff --git a/mozilla/mailnews/compose/resources/content/MsgComposeCommands.js b/mozilla/mailnews/compose/resources/content/MsgComposeCommands.js index 6c31ea7c455..aa8e881b91a 100644 --- a/mozilla/mailnews/compose/resources/content/MsgComposeCommands.js +++ b/mozilla/mailnews/compose/resources/content/MsgComposeCommands.js @@ -1891,14 +1891,23 @@ function LoadIdentity(startup) // try { session2.outputFormat = - prefs.CopyUnicodePref(autocompleteDirectory + + prefs.CopyUnicharPref(autocompleteDirectory + ".autoComplete.outputFormat"); } catch (ex) { // if this pref isn't there, no big deal. just let // nsLDAPAutoCompleteSession use its default. } - session2.filterTemplate = "cn="; + // override default search filter template? + // + try { + session2.filterTemplate = + prefs.CopyUnicharPref(autocompleteDirectory + + ".autoComplete.filterTemplate"); + } catch (ex) { + // if this pref isn't there, no big deal. just let + // nsLDAPAutoCompleteSession use its default + } // override default maxHits (currently 100) // diff --git a/mozilla/xpfe/components/autocomplete/src/nsLDAPAutoCompleteSession.cpp b/mozilla/xpfe/components/autocomplete/src/nsLDAPAutoCompleteSession.cpp index 80951ff964b..269c98970c7 100644 --- a/mozilla/xpfe/components/autocomplete/src/nsLDAPAutoCompleteSession.cpp +++ b/mozilla/xpfe/components/autocomplete/src/nsLDAPAutoCompleteSession.cpp @@ -35,8 +35,8 @@ #include "nsIComponentManager.h" #include "nsIServiceManager.h" #include "nsIProxyObjectManager.h" -#include "nsString.h" #include "nsILDAPURL.h" +#include "nsILDAPService.h" #include "nsXPIDLString.h" #include "nspr.h" #include "nsLDAP.h" @@ -49,7 +49,9 @@ NS_IMPL_ISUPPORTS3(nsLDAPAutoCompleteSession, nsIAutoCompleteSession, nsILDAPMessageListener, nsILDAPAutoCompleteSession) nsLDAPAutoCompleteSession::nsLDAPAutoCompleteSession() : - mState(UNBOUND), mMaxHits(100), mMinStringLength(0) + mState(UNBOUND), + mFilterTemplate(NS_LITERAL_STRING("(|(cn=%v*)(mail=%v*)(sn=%v*))")), + mMaxHits(100), mMinStringLength(0) { NS_INIT_ISUPPORTS(); } @@ -852,18 +854,80 @@ nsLDAPAutoCompleteSession::StartLDAPSearch() return NS_ERROR_UNEXPECTED; } - // XXXdmose process the mFilterTemplate here; ensuring that it's been set. - // This is waiting on substring replacement code, bug 74896. Note that - // we need to vet the input text for special LDAP filter chars lik - // '|', '(', etc. + // get the search filter associated with the directory server url; + // it will be ANDed with the rest of the search filter that we're using. // - nsCAutoString searchFilter("(|(cn="); - searchFilter.Append(NS_ConvertUCS2toUTF8(mSearchString)); - searchFilter.Append("*)(sn="); - searchFilter.Append(NS_ConvertUCS2toUTF8(mSearchString)); - searchFilter.Append("*))"); + nsXPIDLCString urlFilter; + rv = mServerURL->GetFilter(getter_Copies(urlFilter)); + if ( NS_FAILED(rv) ){ + mState = BOUND; + FinishAutoCompleteLookup(nsIAutoCompleteStatus::failed); + return NS_ERROR_UNEXPECTED; + } - // create a result set + // get the LDAP service, since createFilter is called through it. + // + nsCOMPtr ldapSvc = do_GetService( + "@mozilla.org/network/ldap-service;1", &rv); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPAutoCompleteSession::StartLDAPSearch(): couldn't " + "get @mozilla.org/network/ldap-service;1"); + mState = BOUND; + FinishAutoCompleteLookup(nsIAutoCompleteStatus::failed); + return NS_ERROR_FAILURE; + } + + // if urlFilter is unset (or set to the default "objectclass=*"), there's + // no need to AND in an empty search term, so leave prefix and suffix empty + // + nsAutoString prefix, suffix; + if (urlFilter[0] != 0 && nsCRT::strcmp(urlFilter, "(objectclass=*)")) { + prefix = NS_LITERAL_STRING("(&") + NS_ConvertUTF8toUCS2(urlFilter); + suffix = PRUnichar(')'); + } + + // generate an LDAP search filter from mFilterTemplate. If it's unset, + // use the default. + // +#define MAX_AUTOCOMPLETE_FILTER_SIZE 1024 + nsAutoString searchFilter; + rv = ldapSvc->CreateFilter(MAX_AUTOCOMPLETE_FILTER_SIZE, mFilterTemplate, + prefix, suffix, NS_LITERAL_STRING(""), + mSearchString, searchFilter); + if (NS_FAILED(rv)) { + switch(rv) { + + case NS_ERROR_OUT_OF_MEMORY: + mState=BOUND; + FinishAutoCompleteLookup(nsIAutoCompleteStatus::failed); + return rv; + + case NS_ERROR_NOT_AVAILABLE: + PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, + ("nsLDAPAutoCompleteSession::StartLDAPSearch(): " + "createFilter generated filter longer than max filter " + "size of %d", MAX_AUTOCOMPLETE_FILTER_SIZE)); + mState=BOUND; + FinishAutoCompleteLookup(nsIAutoCompleteStatus::failed); + return rv; + + case NS_ERROR_INVALID_ARG: + case NS_ERROR_UNEXPECTED: + default: + + // all this stuff indicates code bugs + // + NS_ERROR("nsLDAPAutoCompleteSession::StartLDAPSearch(): " + "createFilter returned unexpected value"); + + mState=BOUND; + FinishAutoCompleteLookup(nsIAutoCompleteStatus::failed); + return NS_ERROR_UNEXPECTED; + } + + } + + // create a result set // mResults = do_CreateInstance(NS_AUTOCOMPLETERESULTS_CONTRACTID, &rv); @@ -918,8 +982,7 @@ nsLDAPAutoCompleteSession::StartLDAPSearch() // attributes. requires tweaking SearchExt. // rv = mOperation->SearchExt(NS_ConvertUTF8toUCS2(dn).get(), scope, - NS_ConvertUTF8toUCS2(searchFilter).get(), - 0, 0, 0, mMaxHits); + searchFilter.get(), 0, 0, 0, mMaxHits); if (NS_FAILED(rv)) { switch(rv) {