From d9d778c3294c030eb3933d4384415920dd4c4a92 Mon Sep 17 00:00:00 2001 From: "dmose%netscape.com" Date: Tue, 15 Nov 2005 20:08:36 +0000 Subject: [PATCH] Fix LDAP autocomplete to show display names (bug 79885) r=ducarroz@netscape.com, sr=mscott@netscape.com, a=dbaron@fas.harvard.edu --- .../public/nsILDAPAutoCompleteSession.idl | 13 +- .../src/nsLDAPAutoCompleteSession.cpp | 270 +++++++++++++++--- .../addrbook/src/nsLDAPAutoCompleteSession.h | 14 +- 3 files changed, 250 insertions(+), 47 deletions(-) diff --git a/mailnews/addrbook/public/nsILDAPAutoCompleteSession.idl b/mailnews/addrbook/public/nsILDAPAutoCompleteSession.idl index 8e32fb81c9a5..0d9896147d65 100644 --- a/mailnews/addrbook/public/nsILDAPAutoCompleteSession.idl +++ b/mailnews/addrbook/public/nsILDAPAutoCompleteSession.idl @@ -44,9 +44,16 @@ interface nsILDAPAutoCompleteSession : nsIAutoCompleteSession { attribute wstring filterTemplate; /** - * A template for formatting the autocompletion results. - * - * XXX syntax to-be-determined + * A template string for formatting the autocompletion results. + * + * Required attributes are delimited by curly braces, and optional + * attributes are determined by brackets. Backslash escapes any + * character, including itself. The default template is + * "[cn] <{mail}>", without the quotes. This will generate autocomplete + * items of the form "John Doe ", or, if the cn is not + * found, " ". This latter form is suboptimal, in that + * the space and angle brackets are a bit ugly, but should work. See + * bug 81961. * * @exception NS_ERROR_NULL_POINTER NULL pointer passed to getter * @exception NS_ERROR_OUT_OF_MEMORY Getter couldn't allocate string diff --git a/mailnews/addrbook/src/nsLDAPAutoCompleteSession.cpp b/mailnews/addrbook/src/nsLDAPAutoCompleteSession.cpp index b886300f0509..8d7ccff3bd10 100644 --- a/mailnews/addrbook/src/nsLDAPAutoCompleteSession.cpp +++ b/mailnews/addrbook/src/nsLDAPAutoCompleteSession.cpp @@ -21,7 +21,6 @@ * */ - // Work around lack of conditional build logic in codewarrior's // build system. The MOZ_LDAP_XPCOM preprocessor symbol is only // defined on Mac because noone else needs this weirdness; thus @@ -33,7 +32,6 @@ #include "nsLDAPAutoCompleteSession.h" #include "nsIComponentManager.h" -#include "nsIConsoleService.h" #include "nsIServiceManager.h" #include "nsIProxyObjectManager.h" #include "nsString.h" @@ -442,12 +440,6 @@ nsLDAPAutoCompleteSession::OnLDAPBind(nsILDAPMessage *aMessage) nsresult nsLDAPAutoCompleteSession::OnLDAPSearchEntry(nsILDAPMessage *aMessage) { - // ldap attribute names used to fill in fields in nsIAutoCompleteItem - // - // XXXdmose need to get this from outputFormat - // - const char *valueField="mail"; - nsresult rv; // temporary for return vals PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, @@ -460,6 +452,18 @@ nsLDAPAutoCompleteSession::OnLDAPSearchEntry(nsILDAPMessage *aMessage) "nsLDAPAutoCompleteSession::OnLDAPSearchEntry(): " "mResultsArrayItems is uninitialized"); + nsAutoString value; + rv = GenerateAutoCompleteValue(aMessage, value); + if (NS_FAILED(rv)) { + // Something went wrong lower down the stack; a message should have + // already been logged there. Return an error (which ultimately gets + // ignored, since this is being called through an async proxy). But + // the important thing is that we're bailing out here rather than + // trying to generate a bogus nsIAutoCompleteItem + // + return NS_ERROR_FAILURE; + } + // create an nsIAutoCompleteItem to hold the returned value // nsCOMPtr item = do_CreateInstance( @@ -476,42 +480,11 @@ nsLDAPAutoCompleteSession::OnLDAPSearchEntry(nsILDAPMessage *aMessage) // return NS_ERROR_FAILURE; } - - // get the attribute values for the field which will be used to fill in - // nsIAutoCompleteItem::value - // - PRUint32 numVals; - PRUnichar **values; - - rv = aMessage->GetValues(valueField, &numVals, &values); - if (NS_FAILED(rv)) { - - switch (rv) { - case NS_ERROR_LDAP_DECODING_ERROR: - // XXXdmose log error for non-debug builds? could mean that this - // entry didn't have an attr named "email". - // - NS_WARNING("nsLDAPAutoCompleteSession::OnLDAPSearchEntry(): " - "error decoding email attribute"); - return NS_ERROR_LDAP_DECODING_ERROR; - - case NS_ERROR_OUT_OF_MEMORY: - case NS_ERROR_UNEXPECTED: - return rv; - - default: - NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPSearchEntry(): " - "unexpected return code from aMessage->getValues()"); - return NS_ERROR_UNEXPECTED; - } - } // just use the first value for the email attribute; subsequent values - // are ignored + // are ignored. XXXdmose should do better than this; bug 76595. // - rv = item->SetValue(values[0]); - NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(numVals, values); - + rv = item->SetValue(value.get()); if (NS_FAILED(rv)) { NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPSearchEntry(): " "item->SetValue failed"); @@ -519,8 +492,6 @@ nsLDAPAutoCompleteSession::OnLDAPSearchEntry(nsILDAPMessage *aMessage) return NS_ERROR_FAILURE; } - // XXXdmose process outputFormat here - rv = mResultsArray->AppendElement(item); if (NS_FAILED(rv)) { NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPSearchEntry(): " @@ -535,6 +506,219 @@ nsLDAPAutoCompleteSession::OnLDAPSearchEntry(nsILDAPMessage *aMessage) return NS_OK; } +// use outputFormat to generate an autocomplete value from attributes. +// any errors (including failure to find a required attribute) return +// an NS_ERROR_* up the stack so that the caller doesn't try and generate +// an nsIAutoCompleteItem from this. +// +nsresult +nsLDAPAutoCompleteSession::GenerateAutoCompleteValue(nsILDAPMessage *aMessage, + nsAWritableString &aValue) +{ + nsresult rv; // temp for return values + + // if our outputFormat attribute hasn't been set, use the default + // + if (mOutputFormat.IsEmpty()) { + mOutputFormat = NS_LITERAL_STRING("[cn] <{mail}>"); + } + + // get some iterators to parse mOutputFormat + // + nsReadingIterator iter, iterEnd; + mOutputFormat.BeginReading(iter); + mOutputFormat.EndReading(iterEnd); + + // get the console service for error logging + // + nsCOMPtr consoleSvc = + do_GetService("@mozilla.org/consoleservice;1", &rv); + if (NS_FAILED(rv)) { + NS_WARNING("nsLDAPAutoCompleteSession::GenerateAutoCompleteValue(): " + "couldn't get console service"); + } + + PRBool attrRequired = PR_FALSE; // is this attr required or optional? + + // parse until we hit the end of the string + // + while (iter != iterEnd) { + + switch (*iter) { // process the next char + + case PRUnichar('{'): + + attrRequired = PR_TRUE; // this attribute is required + + /*FALLTHROUGH*/ + + case PRUnichar('['): + + rv = ProcessAttribute(iter, iterEnd, aMessage, attrRequired, + consoleSvc, aValue); + if ( NS_FAILED(rv) ) { + + // something unrecoverable happened; stop parsing and + // propagate the error up the stack + // + return NS_ERROR_FAILURE; + } + + attrRequired = PR_FALSE; // reset to the default for the next pass + + break; + + case PRUnichar('\\'): + + // advance the iterator and be sure we haven't run off the end + // + iter++; + if (iter == iterEnd) { + + // abort; missing escaped char + // + if (consoleSvc) { + consoleSvc->LogStringMessage( + NS_LITERAL_STRING( + "LDAP autocomplete session: error parsing outputFormat: premature end of string after \\ escape").get()); + + NS_ERROR("LDAP autocomplete session: error parsing " + "outputFormat: premature end of string after \\ " + "escape"); + } + + return NS_ERROR_FAILURE; + } + + /*FALLTHROUGH*/ + + default: + + // this character gets treated as a literal + // + aValue.Append(*iter); + + } + + iter++; // advance the iterator + } + + return NS_OK; +} + +nsresult +nsLDAPAutoCompleteSession::ProcessAttribute( + nsReadingIterator &aIter, // iterators for mOutputString + nsReadingIterator &aIterEnd, + nsILDAPMessage *aMessage, // message being processed + PRBool aAttrRequired, // required? or just optional? + nsCOMPtr &aConsoleSvc, // no need to reacquire this + nsAWritableString &aValue) // value being built +{ + nsCAutoString attrName; + + // reset attrname, and move past the opening brace + // + aIter++; + + // get the rest of the attribute name + // + do { + + // be sure we haven't run off the end + // + if (aIter == aIterEnd) { + + // abort; missing closing delimiter + // + if (aConsoleSvc) { + aConsoleSvc->LogStringMessage( + NS_LITERAL_STRING( + "LDAP autocomplete session: error parsing outputFormat: missing } or ]").get()); + + NS_ERROR("LDAP autocomplete session: error parsing " + "outputFormat: missing } or ]"); + } + + return NS_ERROR_FAILURE; + + } else if ( (aAttrRequired && *aIter == PRUnichar('}')) || + *aIter == PRUnichar(']') ) { + + // done with this attribute + // + break; + + } else { + + // this must be part of the attribute name + // + attrName.Append(char(*aIter)); + } + + aIter++; + + } while (1); + + // get the attribute values for the field which will be used + // to fill in nsIAutoCompleteItem::value + // + PRUint32 numVals; + PRUnichar **values; + + nsresult rv; + rv = aMessage->GetValues(attrName, &numVals, &values); + if (NS_FAILED(rv)) { + + switch (rv) { + case NS_ERROR_LDAP_DECODING_ERROR: + // this may not be an error, per se; it could just be that the + // requested attribute does not exist in this particular message, + // either because we didn't request it with the search operation, + // or because it doesn't exist on the server. + // + PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_WARNING, + ("nsLDAPAutoCompleteSession::OnLDAPSearchEntry(): " + "couldn't decode '%s' attribute", attrName.get())); + break; + + case NS_ERROR_OUT_OF_MEMORY: + case NS_ERROR_UNEXPECTED: + break; + + default: + NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPSearchEntry(): " + "unexpected return code from aMessage->getValues()"); + rv = NS_ERROR_UNEXPECTED; + break; + } + + // if this was a required attribute, don't append anything to aValue + // and return the error code + // + if (aAttrRequired) { + return rv; + } else { + // otherwise forget about this attribute, but return NS_OK, which + // will cause our caller to continue processing outputFormat in + // order to generate an nsIAutoCompleteItem. + // + return NS_OK; + } + } + + // append the value to our string; then free the array of results + // + aValue.Append(values[0]); + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(numVals, values); + + // if this attribute wasn't required, we fall through to here, and return + // ok + // + return NS_OK; + +} + nsresult nsLDAPAutoCompleteSession::OnLDAPSearchResult(nsILDAPMessage *aMessage) { diff --git a/mailnews/addrbook/src/nsLDAPAutoCompleteSession.h b/mailnews/addrbook/src/nsLDAPAutoCompleteSession.h index d3685f7696e8..80379e5d425a 100644 --- a/mailnews/addrbook/src/nsLDAPAutoCompleteSession.h +++ b/mailnews/addrbook/src/nsLDAPAutoCompleteSession.h @@ -29,6 +29,7 @@ #include "nsILDAPURL.h" #include "nsString.h" #include "nsISupportsArray.h" +#include "nsIConsoleService.h" // 964665d0-1dd1-11b2-aeae-897834fb00b9 // @@ -62,7 +63,7 @@ class nsLDAPAutoCompleteSession : public nsILDAPMessageListener, nsCOMPtr mServerURL; // URL for the directory to search PRInt32 mSizeLimit; // return at most this many entries PRUint32 mMinStringLength; // strings < this size are ignored - + // stopgap until nsLDAPService works nsresult InitConnection(); @@ -72,6 +73,17 @@ class nsLDAPAutoCompleteSession : public nsILDAPMessageListener, // add to the results set nsresult OnLDAPSearchEntry(nsILDAPMessage *aMessage); + // use outputFormat to generate an autocomplete value from attributes + nsresult GenerateAutoCompleteValue(nsILDAPMessage *aMessage, + nsAWritableString &aValue); + + // process a single attribute while parsing outputFormat + nsresult ProcessAttribute(nsReadingIterator & aIter, + nsReadingIterator & aIterEnd, + nsILDAPMessage *aMessage, PRBool aAttrRequired, + nsCOMPtr & aConsoleSvc, + nsAWritableString & aValue); + // all done; call OnAutoComplete nsresult OnLDAPSearchResult(nsILDAPMessage *aMessage);