From 8bb14539223c5c59e09e5154e84709110d85adf9 Mon Sep 17 00:00:00 2001 From: "dmose%netscape.com" Date: Fri, 17 Aug 2001 22:32:28 +0000 Subject: [PATCH] Fix RFC 822 quoting for LDAP autocompleted display names (bug 89198). Fix works by creating a new "formatter" interface and implementing an addressbook-specific version of it which uses the mailnews quoting code to fix it up. Also provides infrastructure for setting the LDAP autocomplete dropdown comment field, and fixes some error-handling issues in the compose window code for setting up LDAP autocompletion. r=leif@netscape.com (backend changes), srilatha@netscape.com (compose window changes); sr=bienvenu@netscape.com --- mailnews/addrbook/build/nsAbBaseCID.h | 11 + mailnews/addrbook/build/nsAbFactory.cpp | 9 +- mailnews/addrbook/public/Makefile.in | 6 +- mailnews/addrbook/public/makefile.win | 1 + .../public/nsIAbLDAPAutoCompFormatter.idl | 70 +++ mailnews/addrbook/src/Makefile.in | 2 + mailnews/addrbook/src/makefile.win | 2 + .../src/nsAbLDAPAutoCompFormatter.cpp | 521 ++++++++++++++++++ .../addrbook/src/nsAbLDAPAutoCompFormatter.h | 64 +++ .../resources/content/MsgComposeCommands.js | 156 ++++-- .../autocomplete/public/MANIFEST_IDL | 2 - .../autocomplete/public/Makefile.in | 5 +- .../autocomplete/public/makefile.win | 1 + .../public/nsILDAPAutoCompFormatter.idl | 65 +++ .../public/nsILDAPAutoCompleteSession.idl | 37 +- .../src/nsLDAPAutoCompleteSession.cpp | 433 ++------------- .../src/nsLDAPAutoCompleteSession.h | 25 +- 17 files changed, 939 insertions(+), 471 deletions(-) create mode 100644 mailnews/addrbook/public/nsIAbLDAPAutoCompFormatter.idl create mode 100644 mailnews/addrbook/src/nsAbLDAPAutoCompFormatter.cpp create mode 100644 mailnews/addrbook/src/nsAbLDAPAutoCompFormatter.h create mode 100644 xpfe/components/autocomplete/public/nsILDAPAutoCompFormatter.idl diff --git a/mailnews/addrbook/build/nsAbBaseCID.h b/mailnews/addrbook/build/nsAbBaseCID.h index 7b53450ff39..cde5bfc6105 100644 --- a/mailnews/addrbook/build/nsAbBaseCID.h +++ b/mailnews/addrbook/build/nsAbBaseCID.h @@ -346,4 +346,15 @@ {0x84, 0xac, 0x12, 0x48, 0x25, 0xc7, 0x78, 0xf8} \ } +// nsAbLDAPAutoCompFormatter + +// 4e276d6d-9981-46b4-9070-92f344ac5f5a +// +#define NS_ABLDAPAUTOCOMPFORMATTER_CID \ +{ 0x4e276d6d, 0x9981, 0x46b4, \ + { 0x90, 0x70, 0x92, 0xf3, 0x44, 0xac, 0x5f, 0x5a }} + +#define NS_ABLDAPAUTOCOMPFORMATTER_CONTRACTID \ + "@mozilla.org/ldap-autocomplete-formatter;1?type=addrbook" + #endif // nsAbBaseCID_h__ diff --git a/mailnews/addrbook/build/nsAbFactory.cpp b/mailnews/addrbook/build/nsAbFactory.cpp index b29fdb51898..1156d8d1c9d 100644 --- a/mailnews/addrbook/build/nsAbFactory.cpp +++ b/mailnews/addrbook/build/nsAbFactory.cpp @@ -33,7 +33,6 @@ #include "nsCRT.h" #include "nsCOMPtr.h" - /* Include all of the interfaces our factory can generate components for */ #include "nsDirectoryDataSource.h" @@ -70,6 +69,7 @@ #include "nsAbLDAPDirectory.h" #include "nsAbLDAPCard.h" #include "nsAbLDAPDirFactory.h" +#include "nsAbLDAPAutoCompFormatter.h" #endif NS_GENERIC_FACTORY_CONSTRUCTOR(nsAddressBook) @@ -108,6 +108,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsAbBooleanExpression) NS_GENERIC_FACTORY_CONSTRUCTOR(nsAbLDAPDirectory) NS_GENERIC_FACTORY_CONSTRUCTOR(nsAbLDAPCard) NS_GENERIC_FACTORY_CONSTRUCTOR(nsAbLDAPDirFactory) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsAbLDAPAutoCompFormatter) #endif NS_GENERIC_FACTORY_CONSTRUCTOR(nsAbDirectoryQueryProxy) @@ -251,6 +252,12 @@ static nsModuleComponentInfo components[] = NS_ABLDAPDIRFACTORY_CID, NS_ABLDAPDIRFACTORY_CONTRACTID, nsAbLDAPDirFactoryConstructor }, + + { "Address book LDAP autocomplete formatter", + NS_ABLDAPAUTOCOMPFORMATTER_CID, + NS_ABLDAPAUTOCOMPFORMATTER_CONTRACTID, + nsAbLDAPAutoCompFormatterConstructor }, + #endif { "The directory query proxy interface", diff --git a/mailnews/addrbook/public/Makefile.in b/mailnews/addrbook/public/Makefile.in index 68e2d38fd26..765390e1af0 100644 --- a/mailnews/addrbook/public/Makefile.in +++ b/mailnews/addrbook/public/Makefile.in @@ -52,9 +52,11 @@ XPIDLSRCS = \ nsIAbDirectorySearch.idl \ $(NULL) ifdef MOZ_LDAP_XPCOM -XPIDLSRCS += nsILDAPPrefsService.idl +XPIDLSRCS += \ + nsILDAPPrefsService.idl \ + nsIAbLDAPAutoCompFormatter.idl \ + $(NULL) endif - include $(topsrcdir)/config/rules.mk diff --git a/mailnews/addrbook/public/makefile.win b/mailnews/addrbook/public/makefile.win index 14f4b0c69db..d928844bbde 100644 --- a/mailnews/addrbook/public/makefile.win +++ b/mailnews/addrbook/public/makefile.win @@ -46,6 +46,7 @@ XPIDLSRCS = \ .\nsIAbDirectorySearch.idl \ !if !defined(DISABLE_LDAP) .\nsILDAPPrefsService.idl \ + .\nsIAbLDAPAutoCompFormatter.idl \ !endif $(NULL) diff --git a/mailnews/addrbook/public/nsIAbLDAPAutoCompFormatter.idl b/mailnews/addrbook/public/nsIAbLDAPAutoCompFormatter.idl new file mode 100644 index 00000000000..cf8f06177e7 --- /dev/null +++ b/mailnews/addrbook/public/nsIAbLDAPAutoCompFormatter.idl @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 2001 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Dan Mosedale (Original Author) + * + */ + +#include "nsILDAPAutoCompFormatter.idl" + +/** + * An interface for formatter LDAP messages to be used as nsAutoCompleteItems + * for use in RFC 2822 message headers. + */ +[scriptable, uuid(cde8836e-1dd1-11b2-baff-a6d918243e80)] +interface nsIAbLDAPAutoCompFormatter: nsILDAPAutoCompFormatter { + + /** + * All three Format attributes are templates, which work as follows: + * + * Required LDAP attributes are delimited by curly braces, and + * optional LDAP attributes are determined by brackets. Backslash + * escapes any character, including itself. Someday perhaps we'll rev + * this interface and change these to a more flexible format; perhaps + * something regexp based. + * + * If any of these are unset, components implementing this interface + * are free to choose reasonable defaults. As an example, the + *"@mozilla.org/ldap-autocomplete-formatter;1?type=addrbook" + * implementation currently happens to use the followingn defaults: + * + * nameFormat: [cn] + * addressFormat: {mail} + * commentFormat: [o] + * + * and generates autocomplete items like this: + * + * value: John Doe + * comment: Foo Organization + * + * Note that nsILDAPAutoCompFormatter::getAttributes() is only + * required to be called by the nsILDAPAutoCompleteSession + * implementation when the nsILDAPAutoCompleteSession::formatter + * IDL attribute is set. So if for some reason the format + * attributes exposed through this interface get changed (eg a + * user changes their prefs), it is the changing code's + * responsibly to re-set the nsILDAPAutoCompleteSession::formatter + * IDL attribute to the same object to force a new getAttributes() + * call. + */ + + attribute AString nameFormat; + attribute AString addressFormat; + attribute AString commentFormat; +}; diff --git a/mailnews/addrbook/src/Makefile.in b/mailnews/addrbook/src/Makefile.in index 7e079dd06a3..fdd1a24ef40 100644 --- a/mailnews/addrbook/src/Makefile.in +++ b/mailnews/addrbook/src/Makefile.in @@ -98,6 +98,7 @@ EXPORTS += nsAbLDAPDirectoryQuery.h \ nsAbLDAPDirectory.h \ nsAbLDAPDirFactory.h \ nsAbLDAPCard.h \ + nsAbLDAPAutoCompFormatter.h \ $(NULL) CPPSRCS += nsAbLDAPDirectory.cpp \ @@ -106,6 +107,7 @@ CPPSRCS += nsAbLDAPDirectory.cpp \ nsAbLDAPProperties.cpp \ nsAbLDAPDirectoryQuery.cpp \ nsAbBoolExprToLDAPFilter.cpp \ + nsAbLDAPAutoCompFormatter.cpp \ $(NULL) endif diff --git a/mailnews/addrbook/src/makefile.win b/mailnews/addrbook/src/makefile.win index 5fc9cc6a867..c65abc64a32 100644 --- a/mailnews/addrbook/src/makefile.win +++ b/mailnews/addrbook/src/makefile.win @@ -60,6 +60,7 @@ EXPORTS= \ nsAbLDAPDirectory.h \ nsAbLDAPDirFactory.h \ nsAbLDAPCard.h \ + nsAbLDAPAutoCompFormatter.h \ !endif nsAbDirectoryRDFResource.h \ nsAbBooleanExpression.h \ @@ -107,6 +108,7 @@ CPP_OBJS= \ .\$(OBJDIR)\nsAbLDAPProperties.obj \ .\$(OBJDIR)\nsAbLDAPDirectoryQuery.obj \ .\$(OBJDIR)\nsAbBoolExprToLDAPFilter.obj \ + .\$(OBJDIR)\nsAbLDAPAutoCompFormatter.obj \ !endif .\$(OBJDIR)\nsAbBooleanExpression.obj \ .\$(OBJDIR)\nsAbDirectoryRDFResource.obj \ diff --git a/mailnews/addrbook/src/nsAbLDAPAutoCompFormatter.cpp b/mailnews/addrbook/src/nsAbLDAPAutoCompFormatter.cpp new file mode 100644 index 00000000000..f095f968d3e --- /dev/null +++ b/mailnews/addrbook/src/nsAbLDAPAutoCompFormatter.cpp @@ -0,0 +1,521 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 2001 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Dan Mosedale (Original Author) + */ + +// 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 +// the XP_MAC check first. This conditional encloses the entire +// file, so that in the case where the ldap option is turned off +// in the mac build, a dummy (empty) object will be generated. +// +#if !defined(XP_MAC) || defined(MOZ_LDAP_XPCOM) + +#include "nsAbLDAPAutoCompFormatter.h" +#include "nsIAutoCompleteResults.h" +#include "nsIServiceManager.h" +#include "nsIMsgHeaderParser.h" +#include "nsLDAP.h" +#include "prlog.h" + +NS_IMPL_ISUPPORTS2(nsAbLDAPAutoCompFormatter, + nsILDAPAutoCompFormatter, + nsIAbLDAPAutoCompFormatter) + +nsAbLDAPAutoCompFormatter::nsAbLDAPAutoCompFormatter() : + mNameFormat(NS_LITERAL_STRING("[cn]")), + mAddressFormat(NS_LITERAL_STRING("{mail}")), + mCommentFormat(NS_LITERAL_STRING("[o]")) +{ + NS_INIT_ISUPPORTS(); +} + +nsAbLDAPAutoCompFormatter::~nsAbLDAPAutoCompFormatter() +{ +} + +NS_IMETHODIMP +nsAbLDAPAutoCompFormatter::Format(nsILDAPMessage *aMsg, + nsIAutoCompleteItem **aItem) +{ + nsresult rv; + + nsCOMPtr msgHdrParser = + do_GetService("@mozilla.org/messenger/headerparser;1", &rv); + if (NS_FAILED(rv)) { + NS_ERROR("nsAbLDAPAutoCompFormatter::Format(): do_GetService()" + " failed trying to obtain" + " '@mozilla.org/messenger/headerparser;1'"); + return NS_ERROR_NOT_AVAILABLE; + } + + // generate the appropriate string + // + nsCAutoString name; + rv = ProcessFormat(mNameFormat, aMsg, &name, 0); + if (NS_FAILED(rv)) { + // Something went wrong lower down the stack; a message should + // have already been logged there. Return an error, rather + // than trying to generate a bogus nsIAutoCompleteItem + // + return rv; + } + + nsCAutoString address; + rv = ProcessFormat(mAddressFormat, aMsg, &address, 0); + if (NS_FAILED(rv)) { + // Something went wrong lower down the stack; a message should have + // already been logged there. Return an error, rather than trying to + // generate a bogus nsIAutoCompleteItem. + // + return rv; + } + + nsXPIDLCString value; + /* As far as I can tell, the documentation in nsIMsgHdrParser that + * nsnull means "US-ASCII" is actually wrong, it appears to mean UTF8 + */ + rv = msgHdrParser->MakeFullAddress(nsnull, name.get(), address.get(), + getter_Copies(value)); + if (NS_FAILED(rv)) { + NS_ERROR("nsAbLDAPAutoCompFormatter::Format(): call to" + " MakeFullAddress() failed"); + return rv; + } + + // create an nsIAutoCompleteItem to hold the returned value + // + nsCOMPtr item = do_CreateInstance( + NS_AUTOCOMPLETEITEM_CONTRACTID, &rv); + + if (NS_FAILED(rv)) { + NS_ERROR("nsAbLDAPAutoCompFormatter::Format(): couldn't" + " create " NS_AUTOCOMPLETEITEM_CONTRACTID "\n"); + return NS_ERROR_NOT_AVAILABLE; + } + + // this is that part that actually gets autocompleted to + // + rv = item->SetValue(NS_ConvertUTF8toUCS2(value)); + if (NS_FAILED(rv)) { + NS_ERROR("nsAbLDAPAutoCompFormatter::Format(): " + "item->SetValue failed"); + return rv; + } + + // generate the appropriate string to appear as a comment off to the side + // + nsCAutoString comment; + rv = ProcessFormat(mCommentFormat, aMsg, &comment, 0); + if (NS_FAILED(rv)) { + // Something went wrong lower down the stack; a messagne should have + // already been logged there. Return an error rather than + // trying to generate a bogus nsIAutoCompleteItem. + // + return rv; + } + + rv = item->SetComment(NS_ConvertUTF8toUCS2(comment).get()); + if (NS_FAILED(rv)) { + NS_ERROR("nsAbLDAPAutoCompFormatter::Format():" + " item->SetComment failed"); + return rv; + } + + // all done; return the item + // + NS_IF_ADDREF(*aItem = item); + + return NS_OK; +} + +NS_IMETHODIMP +nsAbLDAPAutoCompFormatter::GetAttributes(PRUint32 *aCount, char ** *aAttrs) +{ + if (!aCount || !aAttrs) { + return NS_ERROR_INVALID_POINTER; + } + + nsCStringArray mSearchAttrs; + nsresult rv = ProcessFormat(mNameFormat, 0, 0, &mSearchAttrs); + if (NS_FAILED(rv)) { + NS_WARNING("nsAbLDAPAutoCompFormatter::SetNameFormat(): " + "ProcessFormat() failed"); + return rv; + } + rv = ProcessFormat(mAddressFormat, 0, 0, &mSearchAttrs); + if (NS_FAILED(rv)) { + NS_WARNING("nsAbLDAPAutoCompFormatter::SetNameFormat(): " + "ProcessFormat() failed"); + return rv; + } + rv = ProcessFormat(mCommentFormat, 0, 0, &mSearchAttrs); + if (NS_FAILED(rv)) { + NS_WARNING("nsAbLDAPAutoCompFormatter::SetNameFormat(): " + "ProcessFormat() failed"); + return rv; + } + + // none of the formatting templates require any LDAP attributes + // + PRUint32 count = mSearchAttrs.Count(); // size of XPCOM array we'll need + if (!count) { + NS_ERROR("nsAbLDAPAutoCompFormatter::GetAttributes(): " + "current output format (if set) requires no search " + "attributes"); + return NS_ERROR_NOT_INITIALIZED; + } + + // build up the raw XPCOM array to return + // + PRUint32 rawSearchAttrsSize = 0; // grown as XPCOM array is built + char **rawSearchAttrs = + NS_STATIC_CAST(char **, nsMemory::Alloc(count * sizeof(char *))); + if (!rawSearchAttrs) { + NS_ERROR("nsAbLDAPAutoCompFormatter::GetAttributes(): out of " + "memory"); + return NS_ERROR_OUT_OF_MEMORY; + } + + // Loop through the string array, and build up the C-array. + // + while (rawSearchAttrsSize < count) { + if (!(rawSearchAttrs[rawSearchAttrsSize] = + (mSearchAttrs.CStringAt(rawSearchAttrsSize))->ToNewCString())) { + NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(rawSearchAttrsSize, + rawSearchAttrs); + NS_ERROR("nsAbLDAPAutoCompFormatter::GetAttributes(): out " + "of memory"); + return NS_ERROR_OUT_OF_MEMORY; + } + rawSearchAttrsSize++; + } + + *aCount = rawSearchAttrsSize; + *aAttrs = rawSearchAttrs; + + return NS_OK; + +} +// parse and process a formatting attribute. If aStringArray is +// non-NULL, return a list of the attributes from mNameFormat in +// aStringArray. Otherwise, generate an autocomplete value from the +// information in aMessage and append it to aValue. Any errors +// (including failure to find a required attribute while building up aValue) +// return an NS_ERROR_* up the stack so that the caller doesn't try and +// generate an nsIAutoCompleteItem from this. +// +nsresult +nsAbLDAPAutoCompFormatter::ProcessFormat(const nsAReadableString & aFormat, + nsILDAPMessage *aMessage, + nsAWritableCString *aValue, + nsCStringArray *aAttrs) +{ + nsresult rv; // temp for return values + + // get some iterators to parse aFormat + // + nsReadingIterator iter, iterEnd; + aFormat.BeginReading(iter); + aFormat.EndReading(iterEnd); + + // get the console service for error logging + // + nsCOMPtr consoleSvc = + do_GetService("@mozilla.org/consoleservice;1", &rv); + if (NS_FAILED(rv)) { + NS_WARNING("nsAbLDAPAutoCompFormatter::ProcessFormat(): " + "couldn't get console service"); + } + + PRBool attrRequired = PR_FALSE; // is this attr required or optional? + nsCAutoString attrName; // current attr to get + + // 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 = ParseAttrName(iter, iterEnd, attrRequired, consoleSvc, + attrName); + if ( NS_FAILED(rv) ) { + + // something unrecoverable happened; stop parsing and + // propagate the error up the stack + // + return rv; + } + + // if we're building an array + if ( aAttrs ) { + + // and it doesn't already contain this string + if (aAttrs->IndexOfIgnoreCase(attrName) == -1) { + + // add it + if (!aAttrs->AppendCString(attrName)) { + + // current AppendCString always returns PR_TRUE; + // if we hit this error, something has changed in + // that code + // + NS_ERROR( + "nsAbLDAPAutoCompFormatter::ProcessFormat():" + " aAttrs->AppendCString(attrName) failed"); + return NS_ERROR_UNEXPECTED; + } + } + } else { + + // otherwise, append the first value of this attr to aValue + // XXXdmose should do better than this; bug 76595 + + rv = AppendFirstAttrValue(attrName, aMessage, attrRequired, + *aValue); + if ( NS_FAILED(rv) ) { + + // something unrecoverable happened; stop parsing and + // propagate the error up the stack + // + return rv; + } + } + + attrName.Truncate(); // clear out for next pass + 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 addressbook autocomplete formatter: error parsing format string: premature end of string after \\ escape").get()); + + NS_ERROR("LDAP addressbook autocomplete formatter: error " + "parsing format string: premature end of string " + "after \\ escape"); + } + + return NS_ERROR_ILLEGAL_VALUE; + } + + /*FALLTHROUGH*/ + + default: + + // if we're not just building an array of attribute names, append + // this character to the item we're generating. + // + if (!aAttrs) { + + // this character gets treated as a literal + // + (*aValue).Append(NS_STATIC_CAST(char, *iter)); + } + } + + ++iter; // advance the iterator + } + + return NS_OK; +} + +nsresult +nsAbLDAPAutoCompFormatter::ParseAttrName( + nsReadingIterator &aIter, // iterators for mOutputString + nsReadingIterator &aIterEnd, + PRBool aAttrRequired, // required? or just optional? + nsCOMPtr &aConsoleSvc, // no need to reacquire this + nsAWritableCString &aAttrName) // attribute token +{ + // 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 address book autocomplete formatter: error parsing format string: missing } or ]").get()); + + NS_ERROR("LDAP address book autocomplete formatter: error " + "parsing format string: missing } or ]"); + } + + return NS_ERROR_ILLEGAL_VALUE; + + } else if ( (aAttrRequired && *aIter == PRUnichar('}')) || + (!aAttrRequired && *aIter == PRUnichar(']')) ) { + + // done with this attribute + // + break; + + } else { + + // this must be part of the attribute name + // + aAttrName.Append(NS_STATIC_CAST(char,*aIter)); + } + + ++aIter; + + } while (1); + + return NS_OK; +} + +nsresult +nsAbLDAPAutoCompFormatter::AppendFirstAttrValue( + nsAReadableCString &aAttrName, // attr to get + nsILDAPMessage *aMessage, // msg to get values from + PRBool aAttrRequired, // is this a required value? + nsAWritableCString &aValue) +{ + // 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(PromiseFlatCString(aAttrName).get(), &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. + // + 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 nameFormat in + // order to generate an nsIAutoCompleteItem. + // + return NS_OK; + } + } + + // append the value to our string; then free the array of results + // + aValue.Append(NS_ConvertUCS2toUTF8(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; +} + +// attribute AString nameFormat; +NS_IMETHODIMP +nsAbLDAPAutoCompFormatter::GetNameFormat(nsAWritableString & aNameFormat) +{ + aNameFormat = mNameFormat; + return NS_OK; +} +NS_IMETHODIMP +nsAbLDAPAutoCompFormatter::SetNameFormat(const nsAReadableString & aNameFormat) +{ + mNameFormat = aNameFormat; + return NS_OK; +} + +// attribute AString addressFormat; +NS_IMETHODIMP +nsAbLDAPAutoCompFormatter::GetAddressFormat(nsAWritableString & aAddressFormat) +{ + aAddressFormat = mAddressFormat; + return NS_OK; +} +NS_IMETHODIMP +nsAbLDAPAutoCompFormatter::SetAddressFormat(const nsAReadableString & + aAddressFormat) +{ + mAddressFormat = aAddressFormat; + return NS_OK; +} + +// attribute AString commentFormat; +NS_IMETHODIMP +nsAbLDAPAutoCompFormatter::GetCommentFormat(nsAWritableString & aCommentFormat) +{ + aCommentFormat = mCommentFormat; + return NS_OK; +} +NS_IMETHODIMP +nsAbLDAPAutoCompFormatter::SetCommentFormat(const nsAReadableString & + aCommentFormat) +{ + mCommentFormat = aCommentFormat; + + return NS_OK; +} + +#endif diff --git a/mailnews/addrbook/src/nsAbLDAPAutoCompFormatter.h b/mailnews/addrbook/src/nsAbLDAPAutoCompFormatter.h new file mode 100644 index 00000000000..fedb927c043 --- /dev/null +++ b/mailnews/addrbook/src/nsAbLDAPAutoCompFormatter.h @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 2001 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Dan Mosedale (Original Author) + * + */ + +#include "nsString.h" +#include "nsIAbLDAPAutoCompFormatter.h" +#include "nsIConsoleService.h" +#include "nsCOMPtr.h" +#include "nsVoidArray.h" + +class nsAbLDAPAutoCompFormatter : public nsIAbLDAPAutoCompFormatter +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_NSILDAPAUTOCOMPFORMATTER + NS_DECL_NSIABLDAPAUTOCOMPFORMATTER + + nsAbLDAPAutoCompFormatter(); + virtual ~nsAbLDAPAutoCompFormatter(); + + protected: + nsString mNameFormat; // how to format these pieces + nsString mAddressFormat; + nsString mCommentFormat; + + // parse and process format + nsresult ProcessFormat(const nsAReadableString & aFormat, + nsILDAPMessage *aMessage, + nsAWritableCString *aValue, + nsCStringArray *aAttrs); + + // process a single attribute while parsing format + nsresult ParseAttrName(nsReadingIterator & aIter, + nsReadingIterator & aIterEnd, + PRBool aAttrRequired, + nsCOMPtr & aConsoleSvc, + nsAWritableCString & aAttrName); + + // append the first value associated with aAttrName in aMessage to aValue + nsresult AppendFirstAttrValue(nsAReadableCString &aAttrName, + nsILDAPMessage *aMessage, + PRBool aAttrRequired, + nsAWritableCString &aValue); +}; + diff --git a/mailnews/compose/resources/content/MsgComposeCommands.js b/mailnews/compose/resources/content/MsgComposeCommands.js index abb5e61544f..def3af44487 100644 --- a/mailnews/compose/resources/content/MsgComposeCommands.js +++ b/mailnews/compose/resources/content/MsgComposeCommands.js @@ -519,7 +519,13 @@ var messageComposeOfflineObserver = { else isOffline = false; MessageComposeOfflineStateChanged(isOffline); - setupLdapAutocompleteSession(); + + try { + setupLdapAutocompleteSession(); + } catch (ex) { + // catch the exception and ignore it, so that if LDAP setup + // fails, the entire compose window stuff doesn't get aborted + } } } @@ -560,7 +566,12 @@ function MessageComposeOfflineStateChanged(goingOffline) var directoryServerObserver = { Observe: function(subject, topic, value) { - setupLdapAutocompleteSession(); + try { + setupLdapAutocompleteSession(); + } catch (ex) { + // catch the exception and ignore it, so that if LDAP setup + // fails, the entire compose window doesn't get horked + } } } @@ -605,31 +616,33 @@ function RemoveDirectorySettingsObserver(prefstring) function setupLdapAutocompleteSession() { - gSetupLdapAutocomplete = true; var autocompleteLdap = false; var autocompleteDirectory = null; var prevAutocompleteDirectory = currentAutocompleteDirectory; - - try { - autocompleteLdap = prefs.GetBoolPref( - "ldap_2.autoComplete.useDirectory"); - if (autocompleteLdap) - autocompleteDirectory = prefs.CopyCharPref( - "ldap_2.autoComplete.directoryServer"); - } catch(ex) { - dump("ERROR: " + ex + "\n"); - } + + autocompleteLdap = prefs.GetBoolPref("ldap_2.autoComplete.useDirectory"); + if (autocompleteLdap) + autocompleteDirectory = prefs.CopyCharPref( + "ldap_2.autoComplete.directoryServer"); if(gCurrentIdentity.overrideGlobalPref) { autocompleteDirectory = gCurrentIdentity.directoryServer; } - try { - if (!gLDAPSession) - gLDAPSession = Components.classes["@mozilla.org/autocompleteSession;1?type=ldap"].createInstance() - .QueryInterface(Components.interfaces.nsILDAPAutoCompleteSession); - } catch (ex) { - dump ("ERROR:" + ex + "\n"); + + // use a temporary to do the setup so that we don't overwrite the + // global, then have some problem and throw an exception, and leave the + // global with a partially setup session. we'll assign the temp + // into the global after we're done setting up the session + // + var LDAPSession; + if (gLDAPSession) { + LDAPSession = gLDAPSession; + } else { + LDAPSession = Components.classes[ + "@mozilla.org/autocompleteSession;1?type=ldap"].createInstance() + .QueryInterface(Components.interfaces.nsILDAPAutoCompleteSession); } + if (autocompleteDirectory && !isOffline) { // Add observer on the directory server we are autocompleting against // only if current server is different from previous. @@ -643,13 +656,10 @@ function setupLdapAutocompleteSession() } else AddDirectorySettingsObserver(); - if (gLDAPSession) { - if (!sessionAdded) { - // add session for all the recipients - for (var i=1; i <= MAX_RECIPIENTS; i++) - document.getElementById("msgRecipient#" + i).addSession(gLDAPSession); - sessionAdded = true; - } + + // fill in the session params if there is a session + // + if (LDAPSession) { var serverURL = Components.classes[ "@mozilla.org/network/ldap-url;1"]. createInstance().QueryInterface( @@ -661,22 +671,69 @@ function setupLdapAutocompleteSession() } catch (ex) { dump("ERROR: " + ex + "\n"); } - gLDAPSession.serverURL = serverURL; + LDAPSession.serverURL = serverURL; // don't search on strings shorter than this // try { - gLDAPSession.minStringLength = prefs.GetIntPref( + LDAPSession.minStringLength = prefs.GetIntPref( autocompleteDirectory + ".autoComplete.minStringLength"); } catch (ex) { // if this pref isn't there, no big deal. just let // nsLDAPAutoCompleteSession use its default. } + // we don't try/catch here, because if this fails, we're outta luck + // + var ldapFormatter = Components.classes[ + "@mozilla.org/ldap-autocomplete-formatter;1?type=addrbook"] + .createInstance().QueryInterface( + Components.interfaces.nsIAbLDAPAutoCompFormatter); + + // override autocomplete name format? + // + try { + ldapFormatter.nameFormat = + prefs.CopyUnicharPref(autocompleteDirectory + + ".autoComplete.nameFormat"); + } catch (ex) { + // if this pref isn't there, no big deal. just let + // nsAbLDAPAutoCompFormatter use its default. + } + + // override autocomplete mail address format? + // + try { + ldapFormatter.addressFormat = + prefs.CopyUnicharPref(autocompleteDirectory + + ".autoComplete.addressFormat"); + } catch (ex) { + // if this pref isn't there, no big deal. just let + // nsAbLDAPAutoCompFormatter use its default. + } + + // override autocomplete entry comment format? + // + try { + ldapFormatter.commentFormat = + prefs.CopyUnicharPref(autocompleteDirectory + + ".autoComplete.commentFormat"); + } catch (ex) { + // if this pref isn't there, no big deal. just let + // nsAbLDAPAutoCompFormatter use its default. + } + + // set the session's formatter, which also happens to + // force a call to the formatter's getAttributes() method + // -- which is why this needs to happen after we've set the + // various formats + // + LDAPSession.formatter = ldapFormatter; + // override autocomplete entry formatting? // try { - gLDAPSession.outputFormat = + LDAPSession.outputFormat = prefs.CopyUnicharPref(autocompleteDirectory + ".autoComplete.outputFormat"); } catch (ex) { @@ -687,26 +744,35 @@ function setupLdapAutocompleteSession() // override default search filter template? // try { - gLDAPSession.filterTemplate = prefs.CopyUnicharPref( + LDAPSession.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) // try { // XXXdmose should really use .autocomplete.maxHits, // but there's no UI for that yet // - gLDAPSession.maxHits = + LDAPSession.maxHits = prefs.GetIntPref(autocompleteDirectory + ".maxHits"); } catch (ex) { // if this pref isn't there, or is out of range, no big deal. // just let nsLDAPAutoCompleteSession use its default. } + + if (!sessionAdded) { + // if we make it here, we know that session initialization has + // succeeded; add the session for all recipients, and + // remember that we've done so + for (var i=1; i <= MAX_RECIPIENTS; i++) + document.getElementById("msgRecipient#" + i). + addSession(LDAPSession); + sessionAdded = true; + } } } else { if (currentAutocompleteDirectory) { @@ -717,10 +783,14 @@ function setupLdapAutocompleteSession() } if (gLDAPSession && sessionAdded) { for (var i=1; i <= MAX_RECIPIENTS; i++) - document.getElementById("msgRecipient#" + i).removeSession(gLDAPSession); + document.getElementById("msgRecipient#" + i). + removeSession(gLDAPSession); sessionAdded = false; } } + + gLDAPSession = LDAPSession; + gSetupLdapAutocomplete = true; } function DoCommandClose() @@ -1983,8 +2053,14 @@ function LoadIdentity(startup) } AddDirectoryServerObserver(false); - if (!startup) - setupLdapAutocompleteSession(); + if (!startup) { + try { + setupLdapAutocompleteSession(); + } catch (ex) { + // catch the exception and ignore it, so that if LDAP setup + // fails, the entire compose window doesn't end up horked + } + } } } @@ -2003,8 +2079,14 @@ function setupAutocomplete() gAutocompleteSession = 1; } } - if (!gSetupLdapAutocomplete) - setupLdapAutocompleteSession(); + if (!gSetupLdapAutocomplete) { + try { + setupLdapAutocompleteSession(); + } catch (ex) { + // catch the exception and ignore it, so that if LDAP setup + // fails, the entire compose window doesn't end up horked + } + } } function subjectKeyPress(event) diff --git a/xpfe/components/autocomplete/public/MANIFEST_IDL b/xpfe/components/autocomplete/public/MANIFEST_IDL index 85ecd538fce..e69de29bb2d 100644 --- a/xpfe/components/autocomplete/public/MANIFEST_IDL +++ b/xpfe/components/autocomplete/public/MANIFEST_IDL @@ -1,2 +0,0 @@ -nsIAutoCompleteSession.idl -nsILDAPAutoCompleteSession.idl diff --git a/xpfe/components/autocomplete/public/Makefile.in b/xpfe/components/autocomplete/public/Makefile.in index 155958f7c42..c1e7bd0b13e 100644 --- a/xpfe/components/autocomplete/public/Makefile.in +++ b/xpfe/components/autocomplete/public/Makefile.in @@ -37,7 +37,10 @@ XPIDLSRCS = \ ifdef MOZ_LDAP_XPCOM REQUIRES += mozldap necko -XPIDLSRCS += nsILDAPAutoCompleteSession.idl +XPIDLSRCS += \ + nsILDAPAutoCompleteSession.idl \ + nsILDAPAutoCompFormatter.idl \ + $(NULL) endif include $(topsrcdir)/config/rules.mk diff --git a/xpfe/components/autocomplete/public/makefile.win b/xpfe/components/autocomplete/public/makefile.win index e4b52490076..cf67181977f 100644 --- a/xpfe/components/autocomplete/public/makefile.win +++ b/xpfe/components/autocomplete/public/makefile.win @@ -32,6 +32,7 @@ XPIDLSRCS=\ .\nsIAutoCompleteResults.idl \ .\nsIAutoCompleteSession.idl \ !if !defined(DISABLE_LDAP) + .\nsILDAPAutoCompFormatter.idl \ .\nsILDAPAutoCompleteSession.idl \ !endif $(NULL) diff --git a/xpfe/components/autocomplete/public/nsILDAPAutoCompFormatter.idl b/xpfe/components/autocomplete/public/nsILDAPAutoCompFormatter.idl new file mode 100644 index 00000000000..4dbfa97b6c2 --- /dev/null +++ b/xpfe/components/autocomplete/public/nsILDAPAutoCompFormatter.idl @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Netscape + * Communications Corporation. Portions created by Netscape are + * Copyright (C) 2001 Netscape Communications Corporation. All + * Rights Reserved. + * + * Contributor(s): Dan Mosedale (Original Author) + * + */ + +#include "nsISupports.idl" +#include "nsILDAPMessage.idl" + +interface nsIAutoCompleteItem; + +/** + * An interface to allow different users of nsILDAPAutoCompleteSession to + * format each nsILDAPMessage into an nsIAutoCompleteItem as it sees fit. + */ +[scriptable, uuid(cbec617c-22d2-4286-8308-0ef8bf57c0ce)] +interface nsILDAPAutoCompFormatter : nsISupports { + + /** + * Returns an nsIAutoCompleteItem generated from the data in + * the given nsILDAPMessage. + * + * @param aMessage message to be formatted + * @return resulting nsIAutoCompleteItem + */ + nsIAutoCompleteItem format(in nsILDAPMessage aMessage); + + /** + * Gets a list of all the LDAP attributes that should be requested + * from the LDAP server when a lookup is done. This avoids + * wasting server time, bandwidth, and client time processing + * unused attributes. + * + * Note that this is only required to be called by the + * nsILDAPAutoCompleteSession implementation when the + * nsILDAPAutoCompleteSession::formatter IDL attribute is set. . + * So if for some reason, the LDAP attributes to be returned by + * searches has to change (eg because the user changed a + * preference), the nsILDAPAutoCompleteSession::formatter IDL + * attribute should be re-set to the same object to force a new + * getAttributes() call. + * + * @param aCount number of attributes in the array + * @param aAttrs list of LDAP attributes to request + */ + void getAttributes(out unsigned long aCount, + [retval, array, size_is(aCount)] out string aAttrs); +}; diff --git a/xpfe/components/autocomplete/public/nsILDAPAutoCompleteSession.idl b/xpfe/components/autocomplete/public/nsILDAPAutoCompleteSession.idl index 5736ad607d7..9ad7b766dce 100644 --- a/xpfe/components/autocomplete/public/nsILDAPAutoCompleteSession.idl +++ b/xpfe/components/autocomplete/public/nsILDAPAutoCompleteSession.idl @@ -23,7 +23,10 @@ #include "nsIAutoCompleteSession.idl" -interface nsILDAPURL; // forward decl +// forward decls +// +interface nsILDAPURL; +interface nsILDAPAutoCompFormatter; /** * Extends nsIAutoCompleteSession to have various LDAP-specific parameters. @@ -50,32 +53,6 @@ interface nsILDAPAutoCompleteSession : nsIAutoCompleteSession { */ attribute wstring filterTemplate; - /** - * 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. - * - * If outputFormat is not set, or is set to the empty string, - * the component implementing this interface should set the outputFormat - * to a reasonable default. - * - * As an example, the "@mozilla.org/autocompleteSession;1?type=ldap" - * implementation currently happens to use a default of "[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 as an - * email address. See bug 81961. - * - * @exception NS_ERROR_NULL_POINTER getter: NULL pointer passed in - * @exception NS_ERROR_OUT_OF_MEMORY getter/setter: allocation error - * @exception NS_ERROR_ILLEGAL_VALUE setter: invalid template specified - * @exception NS_ERROR_UNEXPECTED setter: probably a bug - */ - attribute wstring outputFormat; - /** * At most this many nsIAutoCompleteItems will be returned. This * is useful for keeping bandwidth usage over slow-speed @@ -112,4 +89,10 @@ interface nsILDAPAutoCompleteSession : nsIAutoCompleteSession { * @exception NS_ERROR_NULL_POINTER NULL pointer passed to getter */ attribute nsILDAPURL serverURL; + + /** + * Callback used to format an individual LDAP message into an + * nsIAutoCompleteItem. + */ + attribute nsILDAPAutoCompFormatter formatter; }; diff --git a/xpfe/components/autocomplete/src/nsLDAPAutoCompleteSession.cpp b/xpfe/components/autocomplete/src/nsLDAPAutoCompleteSession.cpp index 06331df4b38..93cef49ac79 100644 --- a/xpfe/components/autocomplete/src/nsLDAPAutoCompleteSession.cpp +++ b/xpfe/components/autocomplete/src/nsLDAPAutoCompleteSession.cpp @@ -117,19 +117,8 @@ nsLDAPAutoCompleteSession::OnStartLookup(const PRUnichar *searchString, return NS_ERROR_FAILURE; } - // if mOutputFormat is unset, set it to our default, using the - // XPCOM setter so that the internal array will be built up - // - if (mOutputFormat.IsEmpty()) { - rv = SetOutputFormat(NS_LITERAL_STRING("[cn] <{mail}>").get()); - if (NS_FAILED(rv)) { - // if we got here, we've either hit a bug or run out of memory - // in either case, we're screwed. In a debug build, something - // will have already been logged. Time to bail out. - FinishAutoCompleteLookup(nsIAutoCompleteStatus::failed); - return NS_ERROR_FAILURE; - } - } + NS_ASSERTION(mFormatter, "nsLDAPAutoCompleteSession::OnStartLookup(): " + "formatter attribute has not been set"); // see if this is a narrow search that we could potentially do locally // @@ -461,8 +450,6 @@ nsLDAPAutoCompleteSession::OnLDAPBind(nsILDAPMessage *aMessage) nsresult nsLDAPAutoCompleteSession::OnLDAPSearchEntry(nsILDAPMessage *aMessage) { - nsresult rv; // temporary for return vals - PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, ("nsLDAPAutoCompleteSession::OnLDAPSearchEntry entered\n")); @@ -473,43 +460,22 @@ nsLDAPAutoCompleteSession::OnLDAPSearchEntry(nsILDAPMessage *aMessage) "nsLDAPAutoCompleteSession::OnLDAPSearchEntry(): " "mResultsArrayItems is uninitialized"); - nsAutoString value; - rv = ProcessOutputFormat(mOutputFormat, aMessage, &value, 0); - 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; - } + // Errors in this method 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. Also note + // that FinishAutoCompleteLookup is _NOT_ being called here, because + // this error may just have to do with this particular item. - // create an nsIAutoCompleteItem to hold the returned value + // generate an autocomplete item from this message by calling the + // formatter // - nsCOMPtr item = do_CreateInstance( - NS_AUTOCOMPLETEITEM_CONTRACTID, &rv); - + nsCOMPtr item; + nsresult rv = mFormatter->Format(aMessage, getter_AddRefs(item)); if (NS_FAILED(rv)) { - NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPSearchEntry couldn't" - " create " NS_AUTOCOMPLETEITEM_CONTRACTID "\n"); - - // don't call FinishAutoCompleteLookup(), as this could conceivably - // be an anomaly, and perhaps the next message will be ok. if this - // really was a problem, this search should eventually get - // reaped by a timeout (once that code gets implemented). - // - return NS_ERROR_FAILURE; - } - - // just use the first value for the email attribute; subsequent values - // are ignored. XXXdmose should do better than this; bug 76595. - // - rv = item->SetValue(value); - if (NS_FAILED(rv)) { - NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPSearchEntry(): " - "item->SetValue failed"); - + PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, + ("nsLDAPAutoCompleteSession::OnLDAPSearchEntry(): " + "mFormatter->Format() failed")); return NS_ERROR_FAILURE; } @@ -527,267 +493,6 @@ nsLDAPAutoCompleteSession::OnLDAPSearchEntry(nsILDAPMessage *aMessage) return NS_OK; } -// parse and process the outputFormat attribute. If aStringArray is -// non-NULL, return a list of the attributes from mOutputFormat in -// aStringArray. Otherwise, generate an autocomplete value from the -// information in aMessage and append it to aValue. Any errors -// (including failure to find a required attribute while building up aValue) -// return an NS_ERROR_* up the stack so that the caller doesn't try and -// generate an nsIAutoCompleteItem from this. -// -nsresult -nsLDAPAutoCompleteSession::ProcessOutputFormat( - nsAReadableString &aOutputFormat, nsILDAPMessage *aMessage, - nsAWritableString *aValue, nsCStringArray *aAttrs) -{ - nsresult rv; // temp for return values - - // get some iterators to parse aOutputFormat - // - nsReadingIterator iter, iterEnd; - aOutputFormat.BeginReading(iter); - aOutputFormat.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::ProcessOutputFormat(): " - "couldn't get console service"); - } - - PRBool attrRequired = PR_FALSE; // is this attr required or optional? - nsCAutoString attrName; // current attr to get - - // 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 = ParseAttrName(iter, iterEnd, attrRequired, consoleSvc, - attrName); - if ( NS_FAILED(rv) ) { - - // something unrecoverable happened; stop parsing and - // propagate the error up the stack - // - return rv; - } - - // if we're building an array - if ( aAttrs ) { - - // and it doesn't already contain this string - if (aAttrs->IndexOfIgnoreCase(attrName) == -1) { - - // add it - if (!aAttrs->AppendCString(attrName)) { - - // current AppendCString always returns PR_TRUE; - // if we hit this error, something has changed in - // that code - // - NS_ERROR( - "nsLDAPAutoCompleteSession::ProcessOutputFormat():" - " aAttrs->AppendCString(attrName) failed"); - return NS_ERROR_UNEXPECTED; - } - } - } else { - - // otherwise, append the first value of this attr to aValue - - rv = AppendFirstAttrValue(attrName, aMessage, attrRequired, - *aValue); - if ( NS_FAILED(rv) ) { - - // something unrecoverable happened; stop parsing and - // propagate the error up the stack - // - return NS_ERROR_FAILURE; - } - } - - attrName.Truncate(); // clear out for next pass - 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_ILLEGAL_VALUE; - } - - /*FALLTHROUGH*/ - - default: - - // if we're not just building an array of attribute names, append - // this character to the item we're generating. - // - if (!aAttrs) { - - // this character gets treated as a literal - // - (*aValue).Append(*iter); - } - } - - ++iter; // advance the iterator - } - - return NS_OK; -} - -nsresult -nsLDAPAutoCompleteSession::ParseAttrName( - nsReadingIterator &aIter, // iterators for mOutputString - nsReadingIterator &aIterEnd, - PRBool aAttrRequired, // required? or just optional? - nsCOMPtr &aConsoleSvc, // no need to reacquire this - nsAWritableCString &aAttrName) // attribute token -{ - // 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_ILLEGAL_VALUE; - - } else if ( (aAttrRequired && *aIter == PRUnichar('}')) || - (!aAttrRequired && *aIter == PRUnichar(']')) ) { - - // done with this attribute - // - break; - - } else { - - // this must be part of the attribute name - // - aAttrName.Append(char(*aIter)); - } - - ++aIter; - - } while (1); - - return NS_OK; -} - -nsresult -nsLDAPAutoCompleteSession::AppendFirstAttrValue( - nsAReadableCString &aAttrName, // attr to get - nsILDAPMessage *aMessage, // msg to get values from - PRBool aAttrRequired, // is this a required value? - nsAWritableString &aValue) -{ - // 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(PromiseFlatCString(aAttrName).get(), &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", - PromiseFlatCString(aAttrName).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) { @@ -1315,74 +1020,6 @@ nsLDAPAutoCompleteSession::SetFilterTemplate(const PRUnichar * aFilterTemplate) return NS_OK; } -// attribute wstring outputFormat; -NS_IMETHODIMP -nsLDAPAutoCompleteSession::GetOutputFormat(PRUnichar * *aOutputFormat) -{ - if (!aOutputFormat) { - return NS_ERROR_NULL_POINTER; - } - - *aOutputFormat = mOutputFormat.ToNewUnicode(); - if (!*aOutputFormat) { - return NS_ERROR_OUT_OF_MEMORY; - } - - return NS_OK; -} -NS_IMETHODIMP -nsLDAPAutoCompleteSession::SetOutputFormat(const PRUnichar * aOutputFormat) -{ - // set up an array of search attributes to be used in any calls to - // nsLDAPOperation::SearchExt(). This also allows any errors in - // aOutputFormat to be caught sooner, rather than later. - // - nsCStringArray searchAttrs; - nsresult rv = ProcessOutputFormat(nsDependentString(aOutputFormat), 0, 0, - &searchAttrs); - if (NS_FAILED(rv)) { - NS_WARNING("nsLDAPAutoCompleteSession::SetOutputFormat(): " - "ProcessOutputFormat() failed"); - return rv; - } - - // build up the raw XPCOM array that we'll be passing to - // SearchExt() - // - PRUint32 count = searchAttrs.Count(); // size of XPCOM array we'll need - PRUint32 rawSearchAttrsSize = 0; // grown as XPCOM array is built - char **rawSearchAttrs = - NS_STATIC_CAST(char **, nsMemory::Alloc(count * sizeof(char *))); - if (!rawSearchAttrs) { - NS_ERROR("nsLDAPAutoCompleteSession::SetOutputFormat: out of memory"); - return NS_ERROR_OUT_OF_MEMORY; - } - - // Loop through the string array, and build up the C-array. - // - while (rawSearchAttrsSize < count) { - if (!(rawSearchAttrs[rawSearchAttrsSize] = - (searchAttrs.CStringAt(rawSearchAttrsSize))->ToNewCString())) { - NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(rawSearchAttrsSize, - rawSearchAttrs); - NS_ERROR("nsLDAPAutoCompleteSession::SetOutputFormat: out of " - "memory"); - return NS_ERROR_OUT_OF_MEMORY; - } - rawSearchAttrsSize++; - } - - // ok, we've succeed in building up the new array. deallocate the - // old array as necessary, then assign in the new member vars. - // - NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mSearchAttrsSize, mSearchAttrs); - - mSearchAttrsSize = rawSearchAttrsSize; - mSearchAttrs = rawSearchAttrs; - mOutputFormat = aOutputFormat; - - return NS_OK; -} // attribute long maxHits; NS_IMETHODIMP @@ -1414,8 +1051,7 @@ nsLDAPAutoCompleteSession::GetServerURL(nsILDAPURL * *aServerURL) return NS_ERROR_NULL_POINTER; } - *aServerURL = mServerURL; - NS_IF_ADDREF(*aServerURL); + NS_IF_ADDREF(*aServerURL = mServerURL); return NS_OK; } @@ -1511,6 +1147,41 @@ nsLDAPAutoCompleteSession::IsMessageCurrent(nsILDAPMessage *aMessage, return NS_OK; } +// attribute nsILDAPAutoCompFormatter formatter +NS_IMETHODIMP +nsLDAPAutoCompleteSession::GetFormatter(nsILDAPAutoCompFormatter* *aFormatter) +{ + if (! aFormatter ) { + return NS_ERROR_NULL_POINTER; + } + + NS_IF_ADDREF(*aFormatter = mFormatter); + + return NS_OK; +} +NS_IMETHODIMP +nsLDAPAutoCompleteSession::SetFormatter(nsILDAPAutoCompFormatter* aFormatter) +{ + if (! aFormatter ) { + return NS_ERROR_NULL_POINTER; + } + + NS_ASSERTION(mState == UNBOUND || mState == BOUND, + "nsLDAPAutoCompleteSession::SetFormatter was called when " + "mState was set to something other than BOUND or UNBOUND"); + + mFormatter = aFormatter; + + // get and cache the attributes that will be used to do lookups + // + nsresult rv = mFormatter->GetAttributes(&mSearchAttrsSize, &mSearchAttrs); + if (NS_FAILED(rv)) { + NS_ERROR("nsLDAPAutoCompleteSession::SetFormatter(): " + " mFormatter->GetAttributes failed"); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} #endif - diff --git a/xpfe/components/autocomplete/src/nsLDAPAutoCompleteSession.h b/xpfe/components/autocomplete/src/nsLDAPAutoCompleteSession.h index f3d82a657b4..71864ec8985 100644 --- a/xpfe/components/autocomplete/src/nsLDAPAutoCompleteSession.h +++ b/xpfe/components/autocomplete/src/nsLDAPAutoCompleteSession.h @@ -26,6 +26,7 @@ #include "nsILDAPConnection.h" #include "nsILDAPMessageListener.h" #include "nsILDAPAutoCompleteSession.h" +#include "nsILDAPAutoCompFormatter.h" #include "nsILDAPURL.h" #include "nsString.h" #include "nsISupportsArray.h" @@ -60,13 +61,16 @@ class nsLDAPAutoCompleteSession : public nsILDAPMessageListener, nsCOMPtr mResultsArray; // cached, to avoid re-gets nsString mSearchString; // autocomplete this string nsString mFilterTemplate; // search filter template - nsString mOutputFormat; // how to format output nsCOMPtr mServerURL; // URL for the directory to search PRInt32 mMaxHits; // return at most this many entries PRUint32 mMinStringLength; // strings < this size are ignored char **mSearchAttrs; // outputFormat search attrs for SearchExt call PRUint32 mSearchAttrsSize; // size of above array + // used to format the ldap message into an nsIAutoCompleteItem + // + nsCOMPtr mFormatter; + // stopgap until nsLDAPService works nsresult InitConnection(); @@ -76,25 +80,6 @@ class nsLDAPAutoCompleteSession : public nsILDAPMessageListener, // add to the results set nsresult OnLDAPSearchEntry(nsILDAPMessage *aMessage); - // parse and process outputFormat - nsresult ProcessOutputFormat(nsAReadableString & aOutputFormat, - nsILDAPMessage *aMessage, - nsAWritableString *aValue, - nsCStringArray *aAttrs); - - // process a single attribute while parsing outputFormat - nsresult ParseAttrName(nsReadingIterator & aIter, - nsReadingIterator & aIterEnd, - PRBool aAttrRequired, - nsCOMPtr & aConsoleSvc, - nsAWritableCString & aAttrName); - - // append the first value associated with aAttrName in aMessage to aValue - nsresult AppendFirstAttrValue(nsAReadableCString &aAttrName, - nsILDAPMessage *aMessage, - PRBool aAttrRequired, - nsAWritableString &aValue); - // all done; call OnAutoComplete nsresult OnLDAPSearchResult(nsILDAPMessage *aMessage);