Back to index

lightning-sunbird  0.9+nobinonly
nsLDAPAutoCompleteSession.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * 
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 2001
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Dan Mosedale <dmose@netscape.com> (Original Author)
00025  *   Leif Hedstrom <leif@netscape.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 // Work around lack of conditional build logic in codewarrior's
00042 // build system.  The MOZ_LDAP_XPCOM preprocessor symbol is only 
00043 // defined on Mac because noone else needs this weirdness; thus 
00044 // the XP_MAC check first.  This conditional encloses the entire
00045 // file, so that in the case where the ldap option is turned off
00046 // in the mac build, a dummy (empty) object will be generated.
00047 //
00048 #if !defined(XP_MAC) || defined(MOZ_LDAP_XPCOM)
00049 
00050 #include "nsString.h"
00051 #include "nsLDAPAutoCompleteSession.h"
00052 #include "nsIComponentManager.h"
00053 #include "nsIServiceManager.h"
00054 #include "nsIProxyObjectManager.h"
00055 #include "nsILDAPURL.h"
00056 #include "nsILDAPService.h"
00057 #include "nsXPIDLString.h"
00058 #include "nsReadableUtils.h"
00059 #include "nspr.h"
00060 #include "nsLDAP.h"
00061 #include "nsIStringBundle.h"
00062 #include "nsCRT.h"
00063 #include "nsIObserverService.h"
00064 #include "nsNetUtil.h"
00065 #include "nsICategoryManager.h"
00066 #include "nsCategoryManagerUtils.h"
00067 #include "nsILDAPService.h"
00068 #include "nsILDAPMessage.h"
00069 
00070 #ifdef PR_LOGGING
00071 static PRLogModuleInfo *sLDAPAutoCompleteLogModule = 0;
00072 #endif
00073 
00074 
00075 // Because this object gets called via proxies, we need to use a THREADSAFE
00076 // ISUPPORTS; if bug 101252 gets fixed, we can go back to the non-threadsafe
00077 // version.
00078 //
00079 NS_IMPL_THREADSAFE_ISUPPORTS3(nsLDAPAutoCompleteSession, 
00080                               nsIAutoCompleteSession, nsILDAPMessageListener,
00081                               nsILDAPAutoCompleteSession)
00082 
00083 nsLDAPAutoCompleteSession::nsLDAPAutoCompleteSession() :
00084     mState(UNBOUND), 
00085     mFilterTemplate("(|(cn=%v1*%v2-*)(mail=%v1*%v2-*)(sn=%v1*%v2-*))"),
00086     mMaxHits(100), mMinStringLength(2), mCjkMinStringLength(0), 
00087     mSearchAttrs(0), mSearchAttrsSize(0), mVersion(nsILDAPConnection::VERSION3)
00088 {
00089 }
00090 
00091 nsLDAPAutoCompleteSession::~nsLDAPAutoCompleteSession()
00092 {
00093     if (mSearchAttrs) {
00094         NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(mSearchAttrsSize, mSearchAttrs);
00095     }
00096 }
00097 
00098 #define IS_CJK_CHAR_FOR_LDAP(u)  (0x2e80 <= (u) && (u) <= 0xd7ff)
00099 
00100 /* void onStartLookup (in wstring searchString, in nsIAutoCompleteResults previousSearchResult, in nsIAutoCompleteListener listener); */
00101 NS_IMETHODIMP 
00102 nsLDAPAutoCompleteSession::OnStartLookup(const PRUnichar *searchString, 
00103                                          nsIAutoCompleteResults *previousSearchResult,
00104                                          nsIAutoCompleteListener *listener)
00105 {
00106     nsresult rv; // hold return values from XPCOM calls
00107 
00108 #ifdef PR_LOGGING
00109     // initialize logging, if it hasn't been already
00110     //
00111     if (!sLDAPAutoCompleteLogModule) {
00112         sLDAPAutoCompleteLogModule = PR_NewLogModule("ldapautocomplete");
00113 
00114         NS_ABORT_IF_FALSE(sLDAPAutoCompleteLogModule, 
00115                           "failed to initialize ldapautocomplete log module");
00116     }
00117 #endif
00118 
00119     PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
00120            ("nsLDAPAutoCompleteSession::OnStartLookup entered\n"));
00121     
00122     if (!listener) {
00123         NS_ERROR("nsLDAPAutoCompleteSession::OnStartLookup(): NULL listener"
00124                  "passed in");
00125         return NS_ERROR_NULL_POINTER;
00126     } else {
00127         mListener = listener;   // save it for later callbacks
00128     }
00129 
00130     // ignore the empty string, strings with @ in them, and strings
00131     // that are too short
00132     //
00133     if (searchString[0] == 0 ||
00134         nsDependentString(searchString).FindChar(PRUnichar('@'), 0) != 
00135         kNotFound || 
00136         nsDependentString(searchString).FindChar(PRUnichar(','), 0) != 
00137         kNotFound || 
00138         ( !IS_CJK_CHAR_FOR_LDAP(searchString[0]) ?
00139           mMinStringLength && nsCRT::strlen(searchString) < mMinStringLength :
00140           mCjkMinStringLength && nsCRT::strlen(searchString) < 
00141           mCjkMinStringLength ) ) {
00142 
00143         FinishAutoCompleteLookup(nsIAutoCompleteStatus::ignored, 0, mState);
00144         return NS_OK;
00145     } else {
00146         mSearchString = searchString;        // save it for later use
00147     }
00148 
00149     // make sure this was called appropriately.
00150     //
00151     if (mState == SEARCHING || mState == BINDING) {
00152         NS_ERROR("nsLDAPAutoCompleteSession::OnStartLookup(): called while "
00153                  "search already in progress; no lookup started.");
00154         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems,
00155                                  NS_ERROR_FAILURE, mState);
00156         return NS_ERROR_FAILURE;
00157     }
00158 
00159     NS_ASSERTION(mFormatter, "nsLDAPAutoCompleteSession::OnStartLookup(): "
00160                  "formatter attribute has not been set");
00161 
00162     // see if this is a narrow search that we could potentially do locally
00163     //
00164     if (previousSearchResult) {
00165 
00166         // get the string representing previous search results
00167         //
00168         nsXPIDLString prevSearchStr;
00169 
00170         rv = previousSearchResult->GetSearchString(
00171             getter_Copies(prevSearchStr));
00172         if ( NS_FAILED(rv) ) {
00173             NS_ERROR("nsLDAPAutoCompleteSession::OnStartLookup(): couldn't "
00174                      "get search string from previousSearchResult");
00175             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, 
00176                                      NS_ERROR_FAILURE, mState);
00177             return NS_ERROR_FAILURE;
00178         }
00179 
00180         // does the string actually contain anything?
00181         //
00182         if ( prevSearchStr.get() && prevSearchStr.get()[0]) {
00183 
00184             PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
00185                    ("nsLDAPAutoCompleteSession::OnStartLookup(): starting "
00186                     "narrowing search\n"));
00187 
00188             // XXXdmose for performance, we should really do a local, 
00189             // synchronous search against the existing dataset instead of 
00190             // just kicking off a new LDAP search here.  When implementing
00191             // this, need to be sure that only previous results which did not
00192             // hit the size limit and were successfully completed are used.
00193             //
00194             mState = SEARCHING;
00195             return StartLDAPSearch();
00196         }
00197     }
00198 
00199     // init connection if necesary
00200     //
00201     switch (mState) {
00202     case UNBOUND:
00203 
00204         // initialize the connection.  
00205         //
00206         rv = InitConnection();
00207         if (NS_FAILED(rv)) {
00208 
00209             // InitConnection() will have already called 
00210             // FinishAutoCompleteLookup for us as necessary
00211             //
00212             return rv;
00213         }
00214 
00215         return NS_OK;
00216 
00217     case BOUND:
00218 
00219         PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
00220                ("nsLDAPAutoComplete::OnStartLookup(): subsequent search "
00221                 "starting"));
00222 
00223         // kick off an LDAP search
00224         mState = SEARCHING;
00225         return StartLDAPSearch();
00226 
00227     case INITIALIZING:
00228         // We don't need to do anything here (for now at least), because
00229         // we can't really abandon the initialization. If we allowed the
00230         // initialization to be aborted, we could potentially lock the
00231         // UI thread again, since the DNS service might be stalled.
00232         //
00233         return NS_OK;
00234 
00235     case BINDING:
00236     case SEARCHING:
00237         // we should never get here
00238         NS_ERROR("nsLDAPAutoCompleteSession::OnStartLookup(): unexpected "
00239                  "value of mStatus");
00240         return NS_ERROR_UNEXPECTED;
00241     }
00242     
00243     return NS_ERROR_UNEXPECTED;     /*NOTREACHED*/
00244 }
00245 
00246 /* void onStopLookup (); */
00247 NS_IMETHODIMP 
00248 nsLDAPAutoCompleteSession::OnStopLookup()
00249 {
00250 #ifdef PR_LOGGING
00251     // initialize logging, if it hasn't been already
00252     //
00253     if (!sLDAPAutoCompleteLogModule) {
00254         sLDAPAutoCompleteLogModule = PR_NewLogModule("ldapautocomplete");
00255 
00256         NS_ABORT_IF_FALSE(sLDAPAutoCompleteLogModule, 
00257                           "failed to initialize ldapautocomplete log module");
00258     }
00259 #endif
00260 
00261     PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
00262            ("nsLDAPAutoCompleteSession::OnStopLookup entered\n"));
00263 
00264     switch (mState) {
00265     case UNBOUND:
00266         // nothing to stop
00267         return NS_OK;
00268 
00269     case BOUND:
00270         // nothing to stop
00271         return NS_OK;
00272 
00273     case INITIALIZING:
00274         // We can't or shouldn't abor the initialization, because then the
00275         // DNS service can hang again...
00276         //
00277         return NS_OK;
00278 
00279     case BINDING:
00280     case SEARCHING:
00281         // Abandon the operation, if there is one
00282         //
00283         if (mOperation) {
00284             nsresult rv = mOperation->AbandonExt();
00285 
00286             if (NS_FAILED(rv)) {
00287                 // since there's nothing interesting that can or should be
00288                 // done if this abandon failed, warn about it and move on
00289                 //
00290                 NS_WARNING("nsLDAPAutoCompleteSession::OnStopLookup(): "
00291                            "error calling mOperation->AbandonExt()");
00292             }
00293 
00294             // force nsCOMPtr to release mOperation
00295             //
00296             mOperation = 0;
00297         }
00298 
00299         // Set the status properly, set to UNBOUND of we were binding, or
00300         // to BOUND if we were searching.
00301         //
00302         mState = (mState == BINDING ? UNBOUND : BOUND);
00303     }
00304 
00305     mResultsArray = 0;
00306     mResults = 0;
00307     mListener = 0;
00308 
00309     return NS_OK;
00310 }
00311 
00312 /* void onAutoComplete (in wstring searchString, in nsIAutoCompleteResults previousSearchResult, in nsIAutoCompleteListener listener); */
00313 NS_IMETHODIMP 
00314 nsLDAPAutoCompleteSession::OnAutoComplete(const PRUnichar *searchString, 
00315                                  nsIAutoCompleteResults *previousSearchResult, 
00316                                           nsIAutoCompleteListener *listener)
00317 {
00318     // OnStopLookup should have already been called, so there's nothing to
00319     // free here.  Additionally, as of this writing, noone is hanging around 
00320     // waiting for mListener->OnAutoComplete() to be called either, and if 
00321     // they were, it's unclear what we'd return, since we're not guaranteed 
00322     // to be in any particular state.  My suspicion is that this method 
00323     // (nsIAutoCompleteSession::OnAutoComplete) should probably be removed
00324     // from the interface.
00325 
00326     return NS_OK;
00327 }
00328 
00336 NS_IMETHODIMP 
00337 nsLDAPAutoCompleteSession::OnLDAPMessage(nsILDAPMessage *aMessage)
00338 {
00339     PRInt32 messageType;
00340 
00341     // just in case.
00342     // XXXdmose the semantics of NULL are currently undefined, but are likely
00343     // to be defined once we insert timeout handling code into the XPCOM SDK
00344     // At that time we should figure out if this still the right thing to do.
00345     // 
00346     if (!aMessage) {
00347         return NS_OK;
00348     }
00349 
00350     // figure out what sort of message was returned
00351     //
00352     nsresult rv = aMessage->GetType(&messageType);
00353     if (NS_FAILED(rv)) {
00354         NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPMessage(): unexpected "
00355                  "error in aMessage->GetType()");
00356        
00357         // don't call FinishAutoCompleteLookup(), as this could conceivably 
00358         // be an anomaly, and perhaps the next message will be ok.  if this
00359         // really was a problem, this search should eventually get
00360         // reaped by a timeout (once that code gets implemented).
00361         //
00362         return NS_ERROR_UNEXPECTED;
00363     }
00364 
00365     // if this message is not associated with the current operation, 
00366     // discard it, since it is probably from a previous (aborted) 
00367     // operation
00368     //
00369     PRBool isCurrent;
00370     rv = IsMessageCurrent(aMessage, &isCurrent);
00371     if (NS_FAILED(rv)) {
00372         // IsMessageCurrent will have logged any necessary errors
00373         return rv;
00374     }
00375     if ( ! isCurrent ) {
00376         PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG,
00377                ("nsLDAPAutoCompleteSession::OnLDAPMessage(): received message "
00378                 "from operation other than current one; discarded"));
00379         return NS_OK;
00380     }
00381 
00382     // XXXdmose - we may want a small state machine either here or
00383     // or in the nsLDAPConnection object, to make sure that things are 
00384     // happening in the right order and timing out appropriately.  this will
00385     // certainly depend on how timeouts are implemented, and how binding
00386     // gets is dealt with by nsILDAPService.  also need to deal with the case
00387     // where a bind callback happens after onStopLookup was called.
00388     // 
00389     switch (messageType) {
00390 
00391     case nsILDAPMessage::RES_BIND:
00392 
00393         // a bind has completed
00394         //
00395         if (mState != BINDING) {
00396             PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
00397                    ("nsLDAPAutoCompleteSession::OnLDAPMessage(): LDAP bind "
00398                     "entry returned after OnStopLookup() called; ignoring"));
00399 
00400             // XXXdmose when nsLDAPService integration happens, need to make 
00401             // sure that the possibility of having an already bound 
00402             // connection, due to a previously unaborted bind, doesn't cause 
00403             // any problems.
00404 
00405             return NS_OK;
00406         }
00407 
00408         return OnLDAPBind(aMessage);
00409 
00410     case nsILDAPMessage::RES_SEARCH_ENTRY:
00411         
00412         // ignore this if OnStopLookup was already called
00413         //
00414         if (mState != SEARCHING) {
00415             PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
00416                    ("nsLDAPAutoCompleteSession::OnLDAPMessage(): LDAP search "
00417                     "entry returned after OnStopLoookup() called; ignoring"));
00418             return NS_OK;
00419         }
00420 
00421         // a search entry has been returned
00422         //
00423         return OnLDAPSearchEntry(aMessage);
00424 
00425     case nsILDAPMessage::RES_SEARCH_RESULT:
00426 
00427         // ignore this if OnStopLookup was already called
00428         //
00429         if (mState != SEARCHING) {
00430             PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG,
00431                    ("nsLDAPAutoCompleteSession::OnLDAPMessage(): LDAP search "
00432                     "result returned after OnStopLookup called; ignoring"));
00433             return NS_OK;
00434         }
00435 
00436         // the search is finished; we're all done
00437         //  
00438         return OnLDAPSearchResult(aMessage);
00439 
00440     default:
00441         
00442         // Given the LDAP operations nsLDAPAutoCompleteSession uses, we should
00443         // never get here.  If we do get here in a release build, it's
00444         // probably a bug, but maybe it's the LDAP server doing something
00445         // weird.  Might as well try and continue anyway.  The session should
00446         // eventually get reaped by the timeout code, if necessary.
00447         //
00448         NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPMessage(): unexpected "
00449                  "LDAP message received");
00450         return NS_OK;
00451     }
00452 }
00453 
00454 // void onLDAPInit (in nsresult aStatus);
00455 //
00456 NS_IMETHODIMP
00457 nsLDAPAutoCompleteSession::OnLDAPInit(nsILDAPConnection *aConn, nsresult aStatus)
00458 {
00459     nsresult rv;        // temp for xpcom return values
00460     nsCOMPtr<nsILDAPMessageListener> selfProxy;
00461     nsXPIDLString passwd;   // passwd to use to connect to server
00462 
00463     // Check the status from the initialization of the LDAP connection
00464     //
00465     if (NS_FAILED(aStatus)) {
00466         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, aStatus,
00467                                  UNBOUND);
00468         return NS_ERROR_FAILURE;
00469     }
00470 
00471     // If mAuthPrompter is set, we're expected to use it to get a password.
00472     //
00473     if (mAuthPrompter) {
00474         nsUTF8String spec;
00475         PRBool status;
00476 
00477         // we're going to use the URL spec of the server as the "realm" for 
00478         // wallet to remember the password by / for.
00479         //
00480         rv = mServerURL->GetSpec(spec);
00481         if (NS_FAILED(rv)) {
00482             NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPInit(): GetSpec"
00483                      " failed\n");
00484             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems,
00485                                      rv, UNBOUND);
00486             return NS_ERROR_FAILURE;
00487         }
00488 
00489         // get the string bundle service
00490         //
00491         nsCOMPtr<nsIStringBundleService> 
00492             stringBundleSvc(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv)); 
00493         if (NS_FAILED(rv)) {
00494             NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPInit():"
00495                      " error getting string bundle service");
00496             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems,
00497                                      rv, UNBOUND);
00498             return rv;
00499         }
00500 
00501         // get the LDAP string bundle
00502         //
00503         nsCOMPtr<nsIStringBundle> ldapBundle;
00504         rv = stringBundleSvc->CreateBundle(
00505             "chrome://mozldap/locale/ldap.properties",
00506             getter_AddRefs(ldapBundle));
00507         if (NS_FAILED(rv)) {
00508             NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPInit():"
00509                      " error creating string bundle"
00510                      " chrome://mozldap/locale/ldap.properties");
00511             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems,
00512                                      rv, UNBOUND);
00513             return rv;
00514         } 
00515 
00516         // get the title for the authentication prompt
00517         //
00518         nsXPIDLString authPromptTitle;
00519         rv = ldapBundle->GetStringFromName(
00520             NS_LITERAL_STRING("authPromptTitle").get(), 
00521             getter_Copies(authPromptTitle));
00522         if (NS_FAILED(rv)) {
00523             NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPInit():"
00524                      "error getting 'authPromptTitle' string from bundle "
00525                      "chrome://mozldap/locale/ldap.properties");
00526             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems,
00527                                      rv, UNBOUND);
00528             return rv;
00529         }
00530 
00531         // get the host name for the auth prompt
00532         //
00533         nsCAutoString host;
00534         rv = mServerURL->GetAsciiHost(host);
00535         if (NS_FAILED(rv)) {
00536             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
00537                                      UNBOUND);
00538             return rv;
00539         }
00540 
00541         // hostTemp is only necessary to work around a code-generation 
00542         // bug in egcs 1.1.2 (the version of gcc that comes with Red Hat 6.2),
00543         // which is the default compiler for Mozilla on linux at the moment.
00544         //
00545         NS_ConvertASCIItoUCS2 hostTemp(host);
00546         const PRUnichar *hostArray[1] = { hostTemp.get() };
00547 
00548         // format the hostname into the authprompt text string
00549         //
00550         nsXPIDLString authPromptText;
00551         rv = ldapBundle->FormatStringFromName(
00552             NS_LITERAL_STRING("authPromptText").get(),
00553             hostArray, sizeof(hostArray) / sizeof(const PRUnichar *),
00554             getter_Copies(authPromptText));
00555         if (NS_FAILED(rv)) {
00556             NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPInit():"
00557                      "error getting 'authPromptText' string from bundle "
00558                      "chrome://mozldap/locale/ldap.properties");
00559             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems,
00560                                      rv, UNBOUND);
00561             return rv;
00562         }
00563 
00564         // get authentication password, prompting the user if necessary
00565         //
00566         rv = mAuthPrompter->PromptPassword(
00567             authPromptTitle.get(), authPromptText.get(),
00568             NS_ConvertUTF8toUCS2(spec).get(),
00569             nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY, getter_Copies(passwd),
00570             &status);
00571         if (NS_FAILED(rv) || status == PR_FALSE) {
00572             PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
00573                    ("nsLDAPAutoCompleteSession::OnLDAPInit(): PromptPassword"
00574                     " encountered an error or was cancelled by the user"));
00575             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems,
00576                                      NS_ERROR_FAILURE, UNBOUND);
00577             return NS_ERROR_FAILURE;
00578         }
00579     }
00580 
00581     // create and initialize an LDAP operation (to be used for the bind)
00582     //  
00583     mOperation = do_CreateInstance("@mozilla.org/network/ldap-operation;1", 
00584                                    &rv);
00585     if (NS_FAILED(rv)) {
00586         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
00587                                  UNBOUND);
00588         return NS_ERROR_FAILURE;
00589     }
00590 
00591     // get a proxy object so the callback happens on the main thread
00592     //
00593     rv = NS_GetProxyForObject(NS_UI_THREAD_EVENTQ,
00594                               NS_GET_IID(nsILDAPMessageListener), 
00595                               NS_STATIC_CAST(nsILDAPMessageListener *, this), 
00596                               PROXY_ASYNC | PROXY_ALWAYS, 
00597                               getter_AddRefs(selfProxy));
00598     if (NS_FAILED(rv)) {
00599         NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPInit(): couldn't "
00600                  "create proxy to this object for callback");
00601         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
00602                                  UNBOUND);
00603         return NS_ERROR_FAILURE;
00604     }
00605 
00606     // our OnLDAPMessage accepts all result callbacks
00607     //
00608     rv = mOperation->Init(mConnection, selfProxy, nsnull);
00609     if (NS_FAILED(rv)) {
00610         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
00611                                  UNBOUND);
00612         return NS_ERROR_UNEXPECTED; // this should never happen
00613     }
00614 
00615     // kick off a bind operation 
00616     // 
00617     PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
00618            ("nsLDAPAutoCompleteSession:OnLDAPInit(): initiating "
00619             "SimpleBind\n"));
00620     rv = mOperation->SimpleBind(NS_ConvertUCS2toUTF8(passwd)); 
00621     if (NS_FAILED(rv)) {
00622 
00623         switch (rv) {
00624 
00625         case NS_ERROR_LDAP_SERVER_DOWN:
00626             // XXXdmose try to rebind for this one?  wait for nsILDAPServer to 
00627             // see...
00628             //
00629         case NS_ERROR_LDAP_CONNECT_ERROR:
00630         case NS_ERROR_LDAP_ENCODING_ERROR:
00631         case NS_ERROR_OUT_OF_MEMORY:
00632             PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
00633                    ("nsLDAPAutoCompleteSession::OnLDAPInit(): mSimpleBind "
00634                     "failed, rv = 0x%lx", rv));
00635             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
00636                                      UNBOUND);
00637             return NS_OK;
00638 
00639         case NS_ERROR_UNEXPECTED:
00640         default:
00641             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
00642                                      UNBOUND);
00643             return NS_ERROR_UNEXPECTED;
00644         }
00645     }
00646 
00647     // Change our state to binding.
00648     //
00649     mState = BINDING;
00650 
00651     return NS_OK;
00652 }
00653 
00654 nsresult
00655 nsLDAPAutoCompleteSession::OnLDAPBind(nsILDAPMessage *aMessage)
00656 {
00657     PRInt32 errCode;
00658 
00659     mOperation = 0;  // done with bind op; make nsCOMPtr release it
00660 
00661     // get the status of the bind
00662     //
00663     nsresult rv = aMessage->GetErrorCode(&errCode);
00664     if (NS_FAILED(rv)) {
00665         
00666         NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPBind(): couldn't get "
00667                  "error code from aMessage");
00668 
00669         // reset to the default state, and pass along the LDAP error code
00670         // to the formatter
00671         //
00672         FinishAutoCompleteLookup(
00673             nsIAutoCompleteStatus::failureItems,
00674             NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, errCode), 
00675             UNBOUND);
00676         return NS_ERROR_FAILURE;
00677     }
00678 
00679     // check to be sure the bind succeeded
00680     //
00681     if (errCode != nsILDAPErrors::SUCCESS) {
00682 
00683         PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_WARNING, 
00684                 ("nsLDAPAutoCompleteSession::OnLDAPBind(): error binding to "
00685                 "LDAP server, errCode = 0x%x", errCode));
00686 
00687         // if the login failed, tell the wallet to forget this password
00688         //
00689         if ( errCode == nsILDAPErrors::INAPPROPRIATE_AUTH ||
00690              errCode == nsILDAPErrors::INVALID_CREDENTIALS ) {
00691 
00692             // make sure the wallet service has been created, and in doing so,
00693             // pass in a login-failed message to tell it to forget this passwd.
00694             //
00695             // apparently getting passwords stored in the wallet
00696             // doesn't require the service to be running, which is why
00697             // this might not exist yet.
00698             //
00699             rv = NS_CreateServicesFromCategory("passwordmanager", mServerURL,
00700                                                "login-failed");
00701             if (NS_FAILED(rv)) {
00702                 NS_ERROR("nsLDAPAutoCompleteSession::ForgetPassword(): error"
00703                          " creating password manager service");
00704                 // not much to do at this point, though conceivably we could 
00705                 // pop up a dialog telling the user to go manually delete
00706                 // this password in the password manager.
00707             }
00708 
00709             // XXXdmose We should probably pop up an error dialog telling
00710             // the user that the login failed here, rather than just bringing 
00711             // up the password dialog again, which is what calling OnLDAPInit()
00712             // does.  See bug 152997.
00713 
00714             PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
00715                    ("nsLDAPAutoCompleteSession::OnLDAPBind(): auth error;"
00716                     " calling OnLDAPInit() again"));
00717         
00718             return OnLDAPInit(nsnull, NS_OK);
00719         }
00720 
00721         // reset to the default state
00722         //
00723         mState = UNBOUND;
00724         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, 
00725                      NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_LDAP, errCode),
00726                                  UNBOUND);
00727         return NS_ERROR_FAILURE;
00728     }
00729 
00730     // ok, we're starting a search
00731     //
00732     mState = SEARCHING;
00733 
00734     PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
00735            ("nsLDAPAutoCompleteSession::OnLDAPBind(): initial search "
00736             "starting\n"));
00737 
00738     return StartLDAPSearch();
00739 }
00740 
00741 nsresult
00742 nsLDAPAutoCompleteSession::OnLDAPSearchEntry(nsILDAPMessage *aMessage)
00743 {
00744     PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
00745            ("nsLDAPAutoCompleteSession::OnLDAPSearchEntry entered\n"));
00746 
00747     // make sure this is only getting called after StartLDAPSearch has 
00748     // initialized the result set
00749     //
00750     NS_ASSERTION(mResultsArray,
00751                  "nsLDAPAutoCompleteSession::OnLDAPSearchEntry(): "
00752                  "mResultsArrayItems is uninitialized");
00753 
00754     // Errors in this method return an error (which ultimately gets
00755     // ignored, since this is being called through an async proxy).
00756     // But the important thing is that we're bailing out here rather
00757     // than trying to generate a bogus nsIAutoCompleteItem.  Also note
00758     // that FinishAutoCompleteLookup is _NOT_ being called here, because
00759     // this error may just have to do with this particular item.
00760 
00761     // generate an autocomplete item from this message by calling the
00762     // formatter
00763     //
00764     nsCOMPtr<nsIAutoCompleteItem> item;
00765     nsresult rv = mFormatter->Format(aMessage, getter_AddRefs(item));
00766     if (NS_FAILED(rv)) {
00767         PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG,
00768                ("nsLDAPAutoCompleteSession::OnLDAPSearchEntry(): "
00769                 "mFormatter->Format() failed"));
00770         return NS_ERROR_FAILURE;
00771     }
00772 
00773     rv = mResultsArray->AppendElement(item);
00774     if (NS_FAILED(rv)) {
00775         NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPSearchEntry(): "
00776                  "mItems->AppendElement() failed");
00777         return NS_ERROR_FAILURE;
00778     }
00779 
00780     // remember that something has been returned
00781     //
00782     mEntriesReturned++;
00783 
00784     return NS_OK;
00785 }
00786 
00787 nsresult
00788 nsLDAPAutoCompleteSession::OnLDAPSearchResult(nsILDAPMessage *aMessage)
00789 {
00790     nsresult rv;        // temp for return vals
00791 
00792     PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
00793            ("nsLDAPAutoCompleteSession::OnLDAPSearchResult entered\n"));
00794 
00795     // figure out if we succeeded or failed, and set the status 
00796     // and default index appropriately
00797     //
00798     AutoCompleteStatus status;
00799     PRInt32 lderrno;
00800 
00801     if (mEntriesReturned) {
00802 
00803         status = nsIAutoCompleteStatus::matchFound;
00804 
00805         // there's at least one match, so the default index should
00806         // point to the first thing here.  This ensures that if the local
00807         // addressbook autocomplete session only found foo@local.domain, 
00808         // this will be given preference
00809         //
00810         rv = mResults->SetDefaultItemIndex(0);
00811         if (NS_FAILED(rv)) {
00812             NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPSearchResult(): "
00813                      "mResults->SetDefaultItemIndex(0) failed");
00814             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
00815                                      BOUND);
00816         }
00817     } else {
00818         // note that we only look at the error code if there are no results for
00819         // this session; if we got results and then an error happened, this
00820         // is ignored, in part because it seems likely to be confusing to the
00821         // user, and in part because it is likely to be scrolled out of view 
00822         // anyway
00823         //
00824         aMessage->GetErrorCode(&lderrno);
00825         if (lderrno != nsILDAPErrors::SUCCESS) {
00826 
00827             PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
00828                    ("nsLDAPAutoCompleteSession::OnLDAPSearchResult(): "
00829                     "lderrno=%d\n", lderrno));
00830             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, 
00831                                      NS_ERROR_GENERATE_FAILURE(
00832                                          NS_ERROR_MODULE_LDAP, lderrno), 
00833                                      BOUND);
00834             return NS_OK;
00835         }
00836 
00837         // we could potentially keep track of non-fatal errors to the 
00838         // search, and if there has been more than 1, and there are no entries,
00839         // we could return |failed| instead of |noMatch|.  It's unclear to me
00840         // that this actually buys us anything though.
00841         //
00842         status = nsIAutoCompleteStatus::noMatch;
00843     }
00844 
00845     // call the mListener's OnAutoComplete and clean up
00846     //
00847     // XXXdmose should we really leave the connection BOUND here?
00848     FinishAutoCompleteLookup(status, NS_OK, BOUND);
00849 
00850     return NS_OK;
00851 }
00852 
00853 nsresult
00854 nsLDAPAutoCompleteSession::StartLDAPSearch()
00855 {
00856     nsresult rv; // temp for xpcom return values
00857     nsCOMPtr<nsILDAPMessageListener> selfProxy; // for callback
00858 
00859     PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
00860            ("nsLDAPAutoCompleteSession::StartLDAPSearch entered\n"));
00861 
00862     // create and initialize an LDAP operation (to be used for the search
00863     //  
00864     mOperation = 
00865         do_CreateInstance("@mozilla.org/network/ldap-operation;1", &rv);
00866 
00867     if (NS_FAILED(rv)) {
00868         NS_ERROR("nsLDAPAutoCompleteSession::StartLDAPSearch(): couldn't "
00869                  "create @mozilla.org/network/ldap-operation;1");
00870 
00871         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
00872                                  BOUND);
00873         return NS_ERROR_FAILURE;
00874     }
00875 
00876     // get a proxy object so the callback happens on the main thread
00877     //
00878     rv = NS_GetProxyForObject(NS_UI_THREAD_EVENTQ, 
00879                               NS_GET_IID(nsILDAPMessageListener), 
00880                               NS_STATIC_CAST(nsILDAPMessageListener *, this), 
00881                               PROXY_ASYNC | PROXY_ALWAYS, 
00882                               getter_AddRefs(selfProxy));
00883     if (NS_FAILED(rv)) {
00884         NS_ERROR("nsLDAPAutoCompleteSession::StartLDAPSearch(): couldn't "
00885                  "create proxy to this object for callback");
00886         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
00887                                  BOUND);
00888         return NS_ERROR_FAILURE;
00889     }
00890 
00891     // initialize the LDAP operation object
00892     //
00893     rv = mOperation->Init(mConnection, selfProxy, nsnull);
00894     if (NS_FAILED(rv)) {
00895         NS_ERROR("nsLDAPAutoCompleteSession::StartLDAPSearch(): couldn't "
00896                  "initialize LDAP operation");
00897         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
00898                                  BOUND);
00899         return NS_ERROR_UNEXPECTED;
00900     }
00901 
00902     // set the server and client controls on the operation
00903     if (mSearchServerControls) {
00904         rv = mOperation->SetServerControls(mSearchServerControls);
00905         if (NS_FAILED(rv)) {
00906             NS_ERROR("nsLDAPAutoCompleteSession::StartLDAPSearch(): couldn't "
00907                      "initialize LDAP search operation server controls");
00908             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
00909                                      BOUND);
00910             return NS_ERROR_UNEXPECTED;
00911         }
00912     }
00913     if (mSearchClientControls) {
00914         rv = mOperation->SetClientControls(mSearchClientControls);
00915         if (NS_FAILED(rv)) {
00916             NS_ERROR("nsLDAPAutoCompleteSession::StartLDAPSearch(): couldn't "
00917                      "initialize LDAP search operation client controls");
00918             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
00919                                      BOUND);
00920             return NS_ERROR_UNEXPECTED;
00921         }
00922     }
00923 
00924     // get the search filter associated with the directory server url; 
00925     // it will be ANDed with the rest of the search filter that we're using.
00926     //
00927     nsCAutoString urlFilter;
00928     rv = mServerURL->GetFilter(urlFilter);
00929     if ( NS_FAILED(rv) ){
00930         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv,
00931                                  BOUND);
00932         return NS_ERROR_UNEXPECTED;
00933     }
00934 
00935     // get the LDAP service, since createFilter is called through it.
00936     //
00937     nsCOMPtr<nsILDAPService> ldapSvc = do_GetService(
00938         "@mozilla.org/network/ldap-service;1", &rv);
00939     if (NS_FAILED(rv)) {
00940         NS_ERROR("nsLDAPAutoCompleteSession::StartLDAPSearch(): couldn't "
00941                  "get @mozilla.org/network/ldap-service;1");
00942         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv,
00943                                  BOUND);
00944         return NS_ERROR_FAILURE;
00945     }
00946 
00947     // if urlFilter is unset (or set to the default "objectclass=*"), there's
00948     // no need to AND in an empty search term, so leave prefix and suffix empty
00949     //
00950     nsCAutoString prefix, suffix;
00951     if (urlFilter.Length() && !urlFilter.Equals(NS_LITERAL_CSTRING("(objectclass=*)"))) {
00952 
00953         // if urlFilter isn't parenthesized, we need to add in parens so that
00954         // the filter works as a term to &
00955         //
00956         if (urlFilter[0] != '(') {
00957             prefix = NS_LITERAL_CSTRING("(&(") + urlFilter +
00958                 NS_LITERAL_CSTRING(")");
00959         } else {
00960             prefix = NS_LITERAL_CSTRING("(&") + urlFilter;
00961         }
00962         
00963         suffix = ')';
00964     }
00965 
00966     // generate an LDAP search filter from mFilterTemplate.  If it's unset,
00967     // use the default.
00968     //
00969 #define MAX_AUTOCOMPLETE_FILTER_SIZE 1024
00970     nsCAutoString searchFilter;
00971     rv = ldapSvc->CreateFilter(MAX_AUTOCOMPLETE_FILTER_SIZE,
00972                                mFilterTemplate,
00973                                prefix, suffix, EmptyCString(), 
00974                                NS_ConvertUCS2toUTF8(mSearchString),
00975                                searchFilter);
00976     if (NS_FAILED(rv)) {
00977         switch(rv) {
00978 
00979         case NS_ERROR_OUT_OF_MEMORY:
00980             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv,
00981                                      BOUND);
00982             return rv;
00983 
00984         case NS_ERROR_NOT_AVAILABLE:
00985             PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
00986                    ("nsLDAPAutoCompleteSession::StartLDAPSearch(): "
00987                     "createFilter generated filter longer than max filter "
00988                     "size of %d", MAX_AUTOCOMPLETE_FILTER_SIZE));
00989             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv,
00990                                      BOUND);
00991             return rv;
00992 
00993         case NS_ERROR_INVALID_ARG:
00994         case NS_ERROR_UNEXPECTED:
00995         default:
00996 
00997             // all this stuff indicates code bugs
00998             //
00999             NS_ERROR("nsLDAPAutoCompleteSession::StartLDAPSearch(): "
01000                      "createFilter returned unexpected value");
01001             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
01002                                      BOUND);
01003             return NS_ERROR_UNEXPECTED;
01004         }
01005 
01006     }
01007 
01008     // If the results array for this search hasn't already been created, do
01009     // so now.  Note that we don't return ::failureItems here, because if
01010     // there's no array, there's nowhere to put the items.
01011     //
01012     rv = CreateResultsArray();
01013     if (NS_FAILED(rv)) {
01014         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failed, rv, BOUND);
01015     }
01016 
01017     // nothing returned yet!
01018     //
01019     mEntriesReturned = 0;
01020     
01021     // get the base dn to search
01022     //
01023     nsCAutoString dn;
01024     rv = mServerURL->GetDn(dn);
01025     if ( NS_FAILED(rv) ){
01026         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv,
01027                                  BOUND);
01028         return NS_ERROR_UNEXPECTED;
01029     }
01030 
01031     // and the scope
01032     //
01033     PRInt32 scope;
01034     rv = mServerURL->GetScope(&scope);
01035     if ( NS_FAILED(rv) ){
01036         mState = BOUND;
01037         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
01038                                  BOUND);
01039         return NS_ERROR_UNEXPECTED;
01040     }
01041 
01042     // take the relevant controls on this object and set them
01043     // on the operation
01044     rv = mOperation->SetServerControls(mSearchServerControls.get());
01045     if ( NS_FAILED(rv) ){
01046         mState = BOUND;
01047         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
01048                                  BOUND);
01049         return NS_ERROR_UNEXPECTED;
01050     }
01051 
01052     rv = mOperation->SetClientControls(mSearchClientControls.get());
01053     if ( NS_FAILED(rv) ){
01054         mState = BOUND;
01055         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
01056                                  BOUND);
01057         return NS_ERROR_UNEXPECTED;
01058     }
01059 
01060     // time to kick off the search.
01061     //
01062     // XXXdmose what about timeouts? 
01063     //
01064     rv = mOperation->SearchExt(dn, scope, searchFilter, mSearchAttrsSize,
01065                                NS_CONST_CAST(const char **, mSearchAttrs),
01066                                0, mMaxHits);
01067     if (NS_FAILED(rv)) {
01068         switch(rv) {
01069 
01070         case NS_ERROR_LDAP_ENCODING_ERROR:
01071             PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
01072                    ("nsLDAPAutoCompleteSession::StartLDAPSearch(): SearchExt "
01073                     "returned NS_ERROR_LDAP_ENCODING_ERROR"));
01074             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
01075                                      BOUND);
01076             return NS_OK;
01077 
01078         case NS_ERROR_LDAP_FILTER_ERROR:
01079             PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
01080                    ("nsLDAPAutoCompleteSession::StartLDAPSearch(): SearchExt "
01081                     "returned NS_ERROR_LDAP_FILTER_ERROR"));
01082             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
01083                                      BOUND);
01084             return NS_OK;
01085 
01086         case NS_ERROR_LDAP_SERVER_DOWN:
01087             // XXXdmose discuss with leif how to handle this in general in the 
01088             // LDAP XPCOM SDK.  
01089 
01090             PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
01091                    ("nsLDAPAutoCompleteSession::StartLDAPSearch(): SearchExt "
01092                     "returned NS_ERROR_LDAP_SERVER_DOWN"));
01093             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv,
01094                                      UNBOUND);
01095             return NS_OK;
01096 
01097         case NS_ERROR_OUT_OF_MEMORY:
01098             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
01099                                      BOUND);
01100             return NS_ERROR_OUT_OF_MEMORY;
01101 
01102         case NS_ERROR_LDAP_NOT_SUPPORTED:
01103         case NS_ERROR_NOT_INITIALIZED:        
01104         case NS_ERROR_INVALID_ARG:
01105         default:
01106 
01107             // all this stuff indicates code bugs
01108             //
01109             NS_ERROR("nsLDAPAutoCompleteSession::StartLDAPSearch(): SearchExt "
01110                      "returned unexpected value");
01111             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
01112                                      BOUND);
01113             return NS_ERROR_UNEXPECTED;
01114         }
01115     }
01116 
01117     return NS_OK;
01118 }
01119 
01120 // XXXdmose - stopgap routine until nsLDAPService is working
01121 //
01122 nsresult
01123 nsLDAPAutoCompleteSession::InitConnection()
01124 {
01125     nsresult rv;        // temp for xpcom return values
01126     nsCOMPtr<nsILDAPMessageListener> selfProxy;
01127     
01128     // create an LDAP connection
01129     //
01130     mConnection = do_CreateInstance(
01131         "@mozilla.org/network/ldap-connection;1", &rv);
01132     if (NS_FAILED(rv)) {
01133         NS_ERROR("nsLDAPAutoCompleteSession::InitConnection(): could "
01134                  "not create @mozilla.org/network/ldap-connection;1");
01135         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
01136                                  UNBOUND);
01137         return NS_ERROR_FAILURE;
01138     }
01139 
01140     // have we been properly initialized?
01141     //
01142     if (!mServerURL) {
01143         NS_ERROR("nsLDAPAutoCompleteSession::InitConnection(): mServerURL "
01144                  "is NULL");
01145         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
01146                                  UNBOUND);
01147         return NS_ERROR_NOT_INITIALIZED;
01148     }
01149 
01150     // host to connect to
01151     //
01152     nsCAutoString host;
01153     rv = mServerURL->GetAsciiHost(host);
01154     if (NS_FAILED(rv)) {
01155         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
01156                                  UNBOUND);
01157         return NS_ERROR_FAILURE;
01158     }
01159 
01160     // on which port
01161     //
01162     PRInt32 port;
01163     rv = mServerURL->GetPort(&port);
01164     if (NS_FAILED(rv)) {
01165         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv,
01166                                  UNBOUND);
01167         return NS_ERROR_FAILURE;
01168     }
01169         
01170     // which options
01171     //
01172     PRUint32 options;
01173     rv = mServerURL->GetOptions(&options);
01174     if (NS_FAILED(rv)) {
01175         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv,
01176                                  UNBOUND);
01177         return NS_ERROR_FAILURE;
01178     }
01179         
01180     // get a proxy object so the callback happens on the main thread
01181     //
01182     rv = NS_GetProxyForObject(NS_UI_THREAD_EVENTQ,
01183                               NS_GET_IID(nsILDAPMessageListener), 
01184                               NS_STATIC_CAST(nsILDAPMessageListener *, this), 
01185                               PROXY_ASYNC | PROXY_ALWAYS, 
01186                               getter_AddRefs(selfProxy));
01187     if (NS_FAILED(rv)) {
01188         NS_ERROR("nsLDAPAutoCompleteSession::InitConnection(): couldn't "
01189                  "create proxy to this object for callback");
01190         FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
01191                                  UNBOUND);
01192         return NS_ERROR_FAILURE;
01193     }
01194 
01195     // Initialize the connection. This will cause an asynchronous DNS
01196     // lookup to occur, and we'll finish the binding of the connection
01197     // in the OnLDAPInit() listener function.
01198     //
01199     rv = mConnection->Init(host.get(), port,
01200                            (options & nsILDAPURL::OPT_SECURE) ? PR_TRUE 
01201                            : PR_FALSE, mLogin, selfProxy, nsnull, mVersion);
01202     if NS_FAILED(rv) {
01203         switch (rv) {
01204 
01205         case NS_ERROR_OUT_OF_MEMORY:
01206         case NS_ERROR_NOT_AVAILABLE:
01207         case NS_ERROR_FAILURE:
01208             PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
01209                    ("nsLDAPAutoCompleteSession::InitConnection(): mSimpleBind "
01210                     "failed, rv = 0x%lx", rv));
01211             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
01212                                      UNBOUND);
01213             return rv;
01214 
01215         case NS_ERROR_ILLEGAL_VALUE:
01216         default:
01217             FinishAutoCompleteLookup(nsIAutoCompleteStatus::failureItems, rv, 
01218                                      UNBOUND);
01219             return NS_ERROR_UNEXPECTED;
01220         }
01221     }
01222 
01223     // set our state
01224     //
01225     mState = INITIALIZING;
01226 
01227     return NS_OK;
01228 }
01229 
01230 nsresult
01231 nsLDAPAutoCompleteSession::CreateResultsArray(void)
01232 {
01233     nsresult rv;
01234 
01235     // create a result set
01236     //
01237     mResults = do_CreateInstance(NS_AUTOCOMPLETERESULTS_CONTRACTID, &rv);
01238 
01239     if (NS_FAILED(rv)) {
01240         NS_ERROR("nsLDAPAutoCompleteSession::StartLDAPSearch() couldn't"
01241                  " create " NS_AUTOCOMPLETERESULTS_CONTRACTID);
01242         return NS_ERROR_FAILURE;
01243     }
01244 
01245     // This seems to be necessary for things to work, though I'm not sure
01246     // why that's true.
01247     //
01248     rv = mResults->SetSearchString(mSearchString.get());
01249     if (NS_FAILED(rv)) {
01250         NS_ERROR("nsLDAPAutoCompleteSession::OnLDAPSearchResult(): couldn't "
01251                  "set search string in results object");
01252         return NS_ERROR_FAILURE;
01253     }
01254 
01255     // get a pointer to the array in question now, so that we don't have to
01256     // keep re-fetching it every time an entry callback happens
01257     //
01258     rv = mResults->GetItems(getter_AddRefs(mResultsArray));
01259     if (NS_FAILED(rv)) {
01260         NS_ERROR("nsLDAPAutoCompleteSession::StartLDAPSearch() couldn't "
01261                  "get results array.");
01262         return NS_ERROR_FAILURE;
01263     }
01264 
01265     return NS_OK;
01266 }
01267 
01268 void
01269 nsLDAPAutoCompleteSession::FinishAutoCompleteLookup(
01270     AutoCompleteStatus aACStatus, const nsresult aResult,
01271     enum SessionState aEndState)
01272 {
01273     nsCOMPtr<nsIAutoCompleteItem> errorItem; // pointer to item we may create
01274     nsresult rv; // temp for return value 
01275 
01276     // if there's a listener, inform the listener that the search is over
01277     //
01278     rv = NS_OK;
01279     if (mListener) {
01280         
01281         switch (aACStatus) {
01282 
01283         case nsIAutoCompleteStatus::matchFound:
01284             rv = mListener->OnAutoComplete(mResults, aACStatus);
01285             break;
01286 
01287         case nsIAutoCompleteStatus::failureItems:
01288             // if the results array hasn't already been created, make one
01289             // to return the error message.  If there's an error, fallback
01290             // to ::failed
01291             //
01292             if (!mResults) {
01293                 rv = CreateResultsArray();
01294                 if (NS_FAILED(rv)) {
01295                     NS_ERROR("nsLDAPAutoCompleteSession::"
01296                              "FinishAutoCompleteLookup():"
01297                              " CreateResultsArray() failed");
01298                     rv = mListener->OnAutoComplete(0, 
01299                                                 nsIAutoCompleteStatus::failed);
01300                     break;
01301                 }
01302             }
01303 
01304             // create the error item
01305             //
01306             rv = mFormatter->FormatException(mState, aResult,
01307                                              getter_AddRefs(errorItem));
01308             if (NS_SUCCEEDED(rv)) {
01309 
01310                 // try and append the error item; falling back to ::failed
01311                 // if there's a problem 
01312                 //
01313                 rv = mResultsArray->AppendElement(errorItem);
01314                 if (NS_FAILED(rv)) {
01315                     NS_ERROR("nsLDAPAutoCompleteSession::"
01316                              "FinishAutoCompleteLookup():"
01317                              " mItems->AppendElement() failed");
01318                     rv = mListener->OnAutoComplete(0, 
01319                                                 nsIAutoCompleteStatus::failed);
01320                     break;
01321                 } 
01322 
01323                 // we don't want the autocomplete widget trying to 
01324                 // automagically use the error item for anything.  If 
01325                 // something goes wrong here, continue on anyway.
01326                 //
01327                 (void)mResults->SetDefaultItemIndex(-1);
01328 
01329                 rv = mListener->OnAutoComplete(mResults, 
01330                                           nsIAutoCompleteStatus::failureItems);
01331                 break;
01332             } 
01333 
01334             // fallback to ::failed
01335             NS_ERROR("nsLDAPAutoCompleteSession::FinishAutoCompleteLookup(): "
01336                      "error calling FormatException()");
01337 
01338             rv = mListener->OnAutoComplete(0, nsIAutoCompleteStatus::failed);
01339             break;
01340         
01341         case nsIAutoCompleteStatus::failed:
01342         default:
01343             rv = mListener->OnAutoComplete(0, aACStatus);
01344             break;
01345         }
01346 
01347     } else {
01348         // if there's no listener, something's wrong 
01349         // 
01350         NS_ERROR("nsLDAPAutoCompleteSession::FinishAutoCompleteLookup(): "
01351                  "called with mListener unset!");
01352     }
01353 
01354     if (NS_FAILED(rv)) {
01355 
01356         // there's nothing we can actually do here other than warn
01357         //
01358         NS_WARNING("nsLDAPAutoCompleteSession::FinishAutoCompleteLookup(): "
01359                    "error calling mListener->OnAutoComplete()");
01360     }
01361 
01362     // set the state appropriately
01363     //
01364     mState = aEndState;
01365 
01366     // we're done with various things; cause nsCOMPtr to release them
01367     //
01368     mResultsArray = 0;
01369     mResults = 0;
01370     mListener = 0;
01371     mOperation = 0;
01372 
01373     // If we are unbound, drop the connection (if any)
01374     //
01375     if (mState == UNBOUND) {
01376         mConnection = 0;
01377     }
01378 }
01379 
01380 // methods for nsILDAPAutoCompleteSession
01381 
01382 // attribute AUTF8String searchFilter;
01383 NS_IMETHODIMP 
01384 nsLDAPAutoCompleteSession::GetFilterTemplate(nsACString & aFilterTemplate)
01385 {
01386     aFilterTemplate.Assign(mFilterTemplate);
01387 
01388     return NS_OK;
01389 }
01390 NS_IMETHODIMP 
01391 nsLDAPAutoCompleteSession::SetFilterTemplate(const nsACString & aFilterTemplate)
01392 {
01393     mFilterTemplate.Assign(aFilterTemplate);
01394 
01395     return NS_OK;
01396 }
01397 
01398 
01399 // attribute long maxHits;
01400 NS_IMETHODIMP 
01401 nsLDAPAutoCompleteSession::GetMaxHits(PRInt32 *aMaxHits)
01402 {
01403     if (!aMaxHits) {
01404         return NS_ERROR_NULL_POINTER;
01405     }
01406 
01407     *aMaxHits = mMaxHits;
01408     return NS_OK;
01409 }
01410 NS_IMETHODIMP 
01411 nsLDAPAutoCompleteSession::SetMaxHits(PRInt32 aMaxHits)
01412 {
01413     if ( aMaxHits < -1 || aMaxHits > 65535) {
01414         return NS_ERROR_ILLEGAL_VALUE;
01415     }
01416 
01417     mMaxHits = aMaxHits;
01418     return NS_OK;
01419 }
01420 
01421 // attribute nsILDAPURL serverURL; 
01422 NS_IMETHODIMP 
01423 nsLDAPAutoCompleteSession::GetServerURL(nsILDAPURL * *aServerURL)
01424 {
01425     if (! aServerURL ) {
01426         return NS_ERROR_NULL_POINTER;
01427     }
01428     
01429     NS_IF_ADDREF(*aServerURL = mServerURL);
01430 
01431     return NS_OK;
01432 }
01433 NS_IMETHODIMP 
01434 nsLDAPAutoCompleteSession::SetServerURL(nsILDAPURL * aServerURL)
01435 {
01436     if (! aServerURL ) {
01437         return NS_ERROR_NULL_POINTER;
01438     }
01439 
01440     mServerURL = aServerURL;
01441 
01442     // the following line will cause the next call to OnStartLookup to 
01443     // call InitConnection again.  This will reinitialize all the relevant
01444     // member variables and kick off an LDAP bind.  By virtue of the magic of 
01445     // nsCOMPtrs, doing this will cause all the nsISupports-based stuff to
01446     // be Released, which will eventually result in the old connection being
01447     // destroyed, and the destructor for that calls ldap_unbind()
01448     //
01449     mState = UNBOUND;
01450 
01451     return NS_OK;
01452 }
01453 
01454 // attribute unsigned long minStringLength
01455 NS_IMETHODIMP
01456 nsLDAPAutoCompleteSession::GetMinStringLength(PRUint32 *aMinStringLength)
01457 {
01458     if (!aMinStringLength) {
01459         return NS_ERROR_NULL_POINTER;
01460     }
01461 
01462     *aMinStringLength = mMinStringLength;
01463     return NS_OK;
01464 }
01465 NS_IMETHODIMP
01466 nsLDAPAutoCompleteSession::SetMinStringLength(PRUint32 aMinStringLength)
01467 {
01468     mMinStringLength = aMinStringLength;
01469 
01470     return NS_OK;
01471 }
01472 
01473 // attribute unsigned long cjkMinStringLength
01474 NS_IMETHODIMP
01475 nsLDAPAutoCompleteSession::GetCjkMinStringLength(PRUint32 *aCjkMinStringLength)
01476 {
01477     if (!aCjkMinStringLength) {
01478         return NS_ERROR_NULL_POINTER;
01479     }
01480 
01481     *aCjkMinStringLength = mCjkMinStringLength;
01482     return NS_OK;
01483 }
01484 NS_IMETHODIMP
01485 nsLDAPAutoCompleteSession::SetCjkMinStringLength(PRUint32 aCjkMinStringLength)
01486 {
01487     mCjkMinStringLength = aCjkMinStringLength;
01488 
01489     return NS_OK;
01490 }
01491 
01492 // check to see if the message returned is related to our current operation
01493 // if there is no current operation, it's not.  :-)
01494 //
01495 nsresult 
01496 nsLDAPAutoCompleteSession::IsMessageCurrent(nsILDAPMessage *aMessage, 
01497                                             PRBool *aIsCurrent)
01498 {
01499     // if there's no operation, this message must be stale (ie non-current)
01500     //
01501     if ( !mOperation ) {
01502         *aIsCurrent = PR_FALSE;
01503         return NS_OK;
01504     }
01505 
01506     // get the message id from the current operation
01507     //
01508     PRInt32 currentId;
01509     nsresult rv = mOperation->GetMessageID(&currentId);
01510     if (NS_FAILED(rv)) {
01511         PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
01512                ("nsLDAPAutoCompleteSession::IsMessageCurrent(): unexpected "
01513                 "error 0x%lx calling mOperation->GetMessageId()", rv));
01514         return NS_ERROR_UNEXPECTED;
01515     }
01516 
01517     // get the message operation from the message
01518     //
01519     nsCOMPtr<nsILDAPOperation> msgOp;
01520     rv = aMessage->GetOperation(getter_AddRefs(msgOp));
01521     if (NS_FAILED(rv)) {
01522         PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
01523                ("nsLDAPAutoCompleteSession::IsMessageCurrent(): unexpected "
01524                 "error 0x%lx calling aMessage->GetOperation()", rv));
01525         return NS_ERROR_UNEXPECTED;
01526     }
01527 
01528     // get the message operation id from the message operation
01529     //
01530     PRInt32 msgOpId;
01531     rv = msgOp->GetMessageID(&msgOpId);
01532     if (NS_FAILED(rv)) {
01533         PR_LOG(sLDAPAutoCompleteLogModule, PR_LOG_DEBUG, 
01534                ("nsLDAPAutoCompleteSession::IsMessageCurrent(): unexpected "
01535                 "error 0x%lx calling msgOp->GetMessageId()", rv));
01536         return NS_ERROR_UNEXPECTED;
01537     }
01538     
01539     *aIsCurrent = (msgOpId == currentId);
01540 
01541     return NS_OK;
01542 }
01543 
01544 // attribute nsILDAPAutoCompFormatter formatter
01545 NS_IMETHODIMP
01546 nsLDAPAutoCompleteSession::GetFormatter(nsILDAPAutoCompFormatter* *aFormatter)
01547 {
01548     if (! aFormatter ) {
01549         return NS_ERROR_NULL_POINTER;
01550     }
01551     
01552     NS_IF_ADDREF(*aFormatter = mFormatter);
01553 
01554     return NS_OK;
01555 }
01556 NS_IMETHODIMP 
01557 nsLDAPAutoCompleteSession::SetFormatter(nsILDAPAutoCompFormatter* aFormatter)
01558 {
01559     if (! aFormatter ) {
01560         return NS_ERROR_NULL_POINTER;
01561     }
01562 
01563     NS_ASSERTION(mState == UNBOUND || mState == BOUND, 
01564                  "nsLDAPAutoCompleteSession::SetFormatter was called when "
01565                  "mState was set to something other than BOUND or UNBOUND");
01566 
01567     mFormatter = aFormatter;
01568 
01569     // get and cache the attributes that will be used to do lookups
01570     //
01571     nsresult rv = mFormatter->GetAttributes(&mSearchAttrsSize, &mSearchAttrs);
01572     if (NS_FAILED(rv)) {
01573         NS_ERROR("nsLDAPAutoCompleteSession::SetFormatter(): "
01574                  " mFormatter->GetAttributes failed");
01575         return NS_ERROR_FAILURE;
01576     }
01577     
01578     return NS_OK;
01579 }
01580 
01581 NS_IMETHODIMP
01582 nsLDAPAutoCompleteSession::SetLogin(const nsACString & aLogin)
01583 {
01584     mLogin = aLogin;
01585     return NS_OK;
01586 }
01587 NS_IMETHODIMP
01588 nsLDAPAutoCompleteSession::GetLogin(nsACString & aLogin) 
01589 {
01590     aLogin = mLogin;
01591     return NS_OK;
01592 }
01593 
01594 NS_IMETHODIMP
01595 nsLDAPAutoCompleteSession::GetAuthPrompter(nsIAuthPrompt **aAuthPrompter)
01596 {
01597     NS_IF_ADDREF(*aAuthPrompter = mAuthPrompter);
01598     return NS_OK;
01599 }
01600 NS_IMETHODIMP
01601 nsLDAPAutoCompleteSession::SetAuthPrompter(nsIAuthPrompt *aAuthPrompter)
01602 {
01603     mAuthPrompter = aAuthPrompter;
01604     return NS_OK;
01605 }
01606 
01607 // attribute unsigned long version;
01608 NS_IMETHODIMP 
01609 nsLDAPAutoCompleteSession::GetVersion(PRUint32 *aVersion)
01610 {
01611     if (!aVersion) {
01612         return NS_ERROR_NULL_POINTER;
01613     }
01614 
01615     *aVersion = mVersion;
01616     return NS_OK;
01617 }
01618 NS_IMETHODIMP 
01619 nsLDAPAutoCompleteSession::SetVersion(PRUint32 aVersion)
01620 {
01621     if ( mVersion != nsILDAPConnection::VERSION2 && 
01622          mVersion != nsILDAPConnection::VERSION3) {
01623         return NS_ERROR_ILLEGAL_VALUE;
01624     }
01625 
01626     mVersion = aVersion;
01627     return NS_OK;
01628 }
01629 
01630 NS_IMETHODIMP
01631 nsLDAPAutoCompleteSession::GetSearchServerControls(nsIMutableArray **aControls)
01632 {
01633     NS_IF_ADDREF(*aControls = mSearchServerControls);
01634     return NS_OK;
01635 }
01636 
01637 NS_IMETHODIMP
01638 nsLDAPAutoCompleteSession::SetSearchServerControls(nsIMutableArray *aControls)
01639 {
01640     mSearchServerControls = aControls;
01641     return NS_OK;
01642 }
01643 
01644 NS_IMETHODIMP
01645 nsLDAPAutoCompleteSession::GetSearchClientControls(nsIMutableArray **aControls)
01646 {
01647     NS_IF_ADDREF(*aControls = mSearchClientControls);
01648     return NS_OK;
01649 }
01650 
01651 NS_IMETHODIMP 
01652 nsLDAPAutoCompleteSession::SetSearchClientControls(nsIMutableArray *aControls)
01653 {
01654     mSearchClientControls = aControls;
01655     return NS_OK;
01656 }
01657 
01658 #endif