Back to index

lightning-sunbird  0.9+nobinonly
nsAbLDAPDirectoryQuery.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Sun Microsystems, Inc.
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Seth Spitzer <sspitzer@netscape.com>
00024  *   Dan Mosedale <dmose@netscape.com>
00025  *   Paul Sandoz <paul.sandoz@sun.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 #include "nsAbLDAPDirectoryQuery.h"
00042 #include "nsAbBoolExprToLDAPFilter.h"
00043 #include "nsILDAPMessage.h"
00044 #include "nsILDAPErrors.h"
00045 #include "nsILDAPOperation.h"
00046 #include "nsIAbLDAPAttributeMap.h"
00047 #include "nsAbUtils.h"
00048 #include "nsLDAP.h"
00049 
00050 #include "nsIAuthPrompt.h"
00051 #include "nsIStringBundle.h"
00052 #include "nsXPIDLString.h"
00053 #include "nsAutoLock.h"
00054 #include "nsIProxyObjectManager.h"
00055 #include "prprf.h"
00056 #include "nsIWindowWatcher.h"
00057 #include "nsIDOMWindow.h"
00058 #include "nsICategoryManager.h"
00059 #include "nsCategoryManagerUtils.h"
00060 #include "nsAbLDAPDirectory.h"
00061 
00062 class nsAbQueryLDAPMessageListener : public nsILDAPMessageListener
00063 {
00064 public:
00065     NS_DECL_ISUPPORTS
00066     NS_DECL_NSILDAPMESSAGELISTENER
00067 
00068     nsAbQueryLDAPMessageListener (
00069         nsAbLDAPDirectoryQuery* directoryQuery,
00070         nsILDAPURL* url,
00071         nsILDAPConnection* connection,
00072         nsIAbDirectoryQueryArguments* queryArguments,
00073         nsIAbDirectoryQueryResultListener* queryListener,
00074         PRInt32 resultLimit = -1,
00075         PRInt32 timeOut = 0);
00076     virtual ~nsAbQueryLDAPMessageListener ();
00077 
00078 protected:
00079     nsresult OnLDAPMessageBind (nsILDAPMessage *aMessage);
00080     nsresult OnLDAPMessageSearchEntry (nsILDAPMessage *aMessage,
00081             nsIAbDirectoryQueryResult** result);
00082     nsresult OnLDAPMessageSearchResult (nsILDAPMessage *aMessage,
00083             nsIAbDirectoryQueryResult** result);
00084 
00085     nsresult QueryResultStatus (nsISupportsArray* properties,
00086             nsIAbDirectoryQueryResult** result, PRUint32 resultStatus);
00087 
00088 protected:
00089     friend class nsAbLDAPDirectoryQuery;
00090     nsresult Cancel ();
00091     nsresult Initiate ();
00092     nsresult DoSearch();
00093 
00094 
00095 protected:
00096     nsAbLDAPDirectoryQuery* mDirectoryQuery;
00097     PRInt32 mContextID;
00098     nsCOMPtr<nsILDAPURL> mUrl;
00099     nsILDAPConnection *mConnection;
00100     nsCOMPtr<nsIAbDirectoryQueryArguments> mQueryArguments;
00101     nsCOMPtr<nsIAbDirectoryQueryResultListener> mQueryListener;
00102     PRInt32 mResultLimit;
00103     PRInt32 mTimeOut;
00104 
00105     PRBool mBound;
00106     PRBool mFinished;
00107     PRBool mInitialized;
00108     PRBool mCanceled;
00109     PRBool mWaitingForPrevQueryToFinish;
00110 
00111     nsCOMPtr<nsILDAPOperation> mSearchOperation;
00112 
00113     PRLock* mLock;
00114 };
00115 
00116 
00117 NS_IMPL_THREADSAFE_ISUPPORTS1(nsAbQueryLDAPMessageListener, nsILDAPMessageListener)
00118 
00119 nsAbQueryLDAPMessageListener::nsAbQueryLDAPMessageListener (
00120         nsAbLDAPDirectoryQuery* directoryQuery,
00121         nsILDAPURL* url,
00122         nsILDAPConnection* connection,
00123         nsIAbDirectoryQueryArguments* queryArguments,
00124         nsIAbDirectoryQueryResultListener* queryListener,
00125         PRInt32 resultLimit,
00126         PRInt32 timeOut) :
00127     mDirectoryQuery (directoryQuery),
00128     mUrl (url),
00129     mConnection (connection),
00130     mQueryArguments (queryArguments),
00131     mQueryListener (queryListener),
00132     mResultLimit (resultLimit),
00133     mTimeOut (timeOut),
00134     mBound (PR_FALSE),
00135     mFinished (PR_FALSE),
00136     mInitialized(PR_FALSE),
00137     mCanceled (PR_FALSE),
00138     mWaitingForPrevQueryToFinish(PR_FALSE),
00139     mLock(0)
00140 
00141 {
00142 }
00143 
00144 nsAbQueryLDAPMessageListener::~nsAbQueryLDAPMessageListener ()
00145 {
00146     if (mLock)
00147         PR_DestroyLock (mLock);
00148 }
00149 
00150 nsresult nsAbQueryLDAPMessageListener::Initiate ()
00151 {
00152     if (mInitialized)
00153         return NS_OK;
00154                 
00155     mLock = PR_NewLock ();
00156     if(!mLock)
00157     {
00158         return NS_ERROR_OUT_OF_MEMORY;
00159     }
00160 
00161     mInitialized = PR_TRUE;
00162 
00163     return NS_OK;
00164 }
00165 
00166 nsresult nsAbQueryLDAPMessageListener::Cancel ()
00167 {
00168     nsresult rv;
00169 
00170     rv = Initiate();
00171     NS_ENSURE_SUCCESS(rv, rv);
00172 
00173     nsAutoLock lock(mLock);
00174 
00175     if (mFinished || mCanceled)
00176         return NS_OK;
00177 
00178     mCanceled = PR_TRUE;
00179     if (!mFinished)
00180       mWaitingForPrevQueryToFinish = PR_TRUE;
00181 
00182     return NS_OK;
00183 }
00184 
00185 NS_IMETHODIMP nsAbQueryLDAPMessageListener::OnLDAPMessage(nsILDAPMessage *aMessage)
00186 {
00187     nsresult rv;
00188 
00189     rv = Initiate();
00190     NS_ENSURE_SUCCESS(rv, rv);
00191 
00192     PRInt32 messageType;
00193     rv = aMessage->GetType(&messageType);
00194     NS_ENSURE_SUCCESS(rv, rv);
00195 
00196     PRBool cancelOperation = PR_FALSE;
00197 
00198     // Enter lock
00199     {
00200         nsAutoLock lock (mLock);
00201 
00202         if (mFinished)
00203             return NS_OK;
00204 
00205         if (messageType == nsILDAPMessage::RES_SEARCH_RESULT)
00206             mFinished = PR_TRUE;
00207         else if (mCanceled)
00208         {
00209             mFinished = PR_TRUE;
00210             cancelOperation = PR_TRUE;
00211         }
00212     }
00213     // Leave lock
00214 
00215     if (!mDirectoryQuery)
00216       return NS_ERROR_NULL_POINTER;
00217 
00218     nsCOMPtr<nsIAbDirectoryQueryResult> queryResult;
00219     if (!cancelOperation)
00220     {
00221         switch (messageType)
00222         {
00223         case nsILDAPMessage::RES_BIND:
00224             rv = OnLDAPMessageBind (aMessage);
00225             NS_ENSURE_SUCCESS(rv, rv);
00226             break;
00227         case nsILDAPMessage::RES_SEARCH_ENTRY:
00228             if (!mFinished && !mWaitingForPrevQueryToFinish)
00229             {
00230                 rv = OnLDAPMessageSearchEntry (aMessage, 
00231                                                getter_AddRefs (queryResult));
00232             }
00233             break;
00234         case nsILDAPMessage::RES_SEARCH_RESULT:
00235             mWaitingForPrevQueryToFinish = PR_FALSE;
00236             rv = OnLDAPMessageSearchResult (aMessage, getter_AddRefs (queryResult));
00237             NS_ENSURE_SUCCESS(rv, rv);
00238         default:
00239             break;
00240         }
00241     }
00242     else
00243     {
00244         if (mSearchOperation)
00245             rv = mSearchOperation->AbandonExt ();
00246 
00247         rv = QueryResultStatus (nsnull, getter_AddRefs (queryResult), nsIAbDirectoryQueryResult::queryResultStopped);
00248         // reset because we might re-use this listener...except don't do this
00249         // until the search is done, so we'll ignore results from a previous
00250         // search.
00251         if (messageType == nsILDAPMessage::RES_SEARCH_RESULT)
00252           mCanceled = mFinished = PR_FALSE;
00253 
00254     }
00255 
00256     if (queryResult && mQueryListener)
00257         rv = mQueryListener->OnQueryItem (queryResult);
00258 
00259     return rv;
00260 }
00261 
00262 NS_IMETHODIMP nsAbQueryLDAPMessageListener::OnLDAPInit(nsILDAPConnection *aConn, nsresult aStatus)
00263 {
00264     nsresult rv;
00265     nsXPIDLString passwd;
00266 
00267     // Make sure that the Init() worked properly
00268     NS_ENSURE_SUCCESS(aStatus, aStatus);
00269 
00270     if (!mDirectoryQuery)
00271       return NS_ERROR_NULL_POINTER;
00272 
00273     // If mLogin is set, we're expected to use it to get a password.
00274     //
00275     if (!mDirectoryQuery->mLogin.IsEmpty()) {
00276 // XXX hack until nsUTF8AutoString exists
00277 #define nsUTF8AutoString nsCAutoString
00278         nsUTF8AutoString spec;
00279         PRBool status;
00280 
00281         // we're going to use the URL spec of the server as the "realm" for 
00282         // wallet to remember the password by / for.
00283         // 
00284         rv = mDirectoryQuery->mDirectoryUrl->GetSpec(spec);
00285         if (NS_FAILED(rv)) {
00286             NS_ERROR("nsAbQueryLDAPMessageListener::OnLDAPInit(): GetSpec"
00287                      " failed\n");
00288             return NS_ERROR_FAILURE;
00289         }
00290 
00291         // get the string bundle service
00292         //
00293         nsCOMPtr<nsIStringBundleService> 
00294             stringBundleSvc(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv)); 
00295         if (NS_FAILED(rv)) {
00296             NS_ERROR("nsAbQueryLDAPMessageListener::OnLDAPInit():"
00297                      " error getting string bundle service");
00298             return rv;
00299         }
00300 
00301         // get the LDAP string bundle
00302         //
00303         nsCOMPtr<nsIStringBundle> ldapBundle;
00304         rv = stringBundleSvc->CreateBundle(
00305             "chrome://mozldap/locale/ldap.properties",
00306             getter_AddRefs(ldapBundle));
00307         if (NS_FAILED(rv)) {
00308             NS_ERROR("nsAbQueryLDAPMessageListener::OnLDAPInit():"
00309                      " error creating string bundle"
00310                      " chrome://mozldap/locale/ldap.properties");
00311             return rv;
00312         } 
00313 
00314         // get the title for the authentication prompt
00315         //
00316         nsXPIDLString authPromptTitle;
00317         rv = ldapBundle->GetStringFromName(
00318             NS_LITERAL_STRING("authPromptTitle").get(), 
00319             getter_Copies(authPromptTitle));
00320         if (NS_FAILED(rv)) {
00321             NS_ERROR("nsAbQueryLDAPMessageListener::OnLDAPInit():"
00322                      "error getting 'authPromptTitle' string from bundle "
00323                      "chrome://mozldap/locale/ldap.properties");
00324             return rv;
00325         }
00326 
00327         // get the host name for the auth prompt
00328         //
00329         nsCAutoString host;
00330         rv = mUrl->GetAsciiHost(host);
00331         if (NS_FAILED(rv)) {
00332             return NS_ERROR_FAILURE;
00333         }
00334 
00335         // hostTemp is only necessary to work around a code-generation 
00336         // bug in egcs 1.1.2 (the version of gcc that comes with Red Hat 6.2),
00337         // which is the default compiler for Mozilla on linux at the moment.
00338         //
00339         NS_ConvertASCIItoUCS2 hostTemp(host);
00340         const PRUnichar *hostArray[1] = { hostTemp.get() };
00341 
00342         // format the hostname into the authprompt text string
00343         //
00344         nsXPIDLString authPromptText;
00345         rv = ldapBundle->FormatStringFromName(
00346             NS_LITERAL_STRING("authPromptText").get(),
00347             hostArray, sizeof(hostArray) / sizeof(const PRUnichar *),
00348             getter_Copies(authPromptText));
00349         if (NS_FAILED(rv)) {
00350             NS_ERROR("nsAbQueryLDAPMessageListener::OnLDAPInit():"
00351                      "error getting 'authPromptText' string from bundle "
00352                      "chrome://mozldap/locale/ldap.properties");
00353             return rv;
00354         }
00355 
00356 
00357         // get the window watcher service, so we can get an auth prompter
00358         //
00359         nsCOMPtr<nsIWindowWatcher> windowWatcherSvc = 
00360             do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
00361         if (NS_FAILED(rv)) {
00362             NS_ERROR("nsAbQueryLDAPMessageListener::OnLDAPInit():"
00363                      " couldn't get window watcher service.");
00364             return rv;
00365         }
00366 
00367         // get the addressbook window, as it will be used to parent the auth
00368         // prompter dialog
00369         //
00370         nsCOMPtr<nsIDOMWindow> abDOMWindow;
00371         rv = windowWatcherSvc->GetWindowByName(
00372             NS_LITERAL_STRING("addressbookWindow").get(), nsnull,
00373             getter_AddRefs(abDOMWindow));
00374         if (NS_FAILED(rv)) {
00375             NS_ERROR("nsAbQueryLDAPMessageListener::OnLDAPInit():"
00376                      " error getting addressbook Window");
00377             return rv;
00378         }
00379 
00380         // get the auth prompter itself
00381         //
00382         nsCOMPtr<nsIAuthPrompt> authPrompter;
00383         rv = windowWatcherSvc->GetNewAuthPrompter(
00384             abDOMWindow, getter_AddRefs(authPrompter));
00385         if (NS_FAILED(rv)) {
00386             NS_ERROR("nsAbQueryLDAPMessageListener::OnLDAPInit():"
00387                      " error getting auth prompter");
00388             return rv;
00389         }
00390 
00391         // get authentication password, prompting the user if necessary
00392         //
00393         rv = authPrompter->PromptPassword(
00394             authPromptTitle.get(), authPromptText.get(),
00395             NS_ConvertUTF8toUCS2(spec).get(),
00396             nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY, getter_Copies(passwd),
00397             &status);
00398         if (NS_FAILED(rv) || !status) {
00399             return NS_ERROR_FAILURE;
00400         }
00401     }
00402 
00403     // Initiate the LDAP operation
00404     nsCOMPtr<nsILDAPOperation> ldapOperation =
00405         do_CreateInstance(NS_LDAPOPERATION_CONTRACTID, &rv);
00406     NS_ENSURE_SUCCESS(rv, rv);
00407 
00408     nsCOMPtr<nsILDAPMessageListener> proxyListener;
00409     rv = NS_GetProxyForObject(NS_UI_THREAD_EVENTQ,
00410                      NS_GET_IID(nsILDAPMessageListener),
00411                      NS_STATIC_CAST(nsILDAPMessageListener *, this),
00412                      PROXY_SYNC | PROXY_ALWAYS,
00413                      getter_AddRefs(proxyListener));
00414 
00415     rv = ldapOperation->Init(mConnection, proxyListener, nsnull);
00416     NS_ENSURE_SUCCESS(rv, rv);
00417 
00418     // Bind
00419     rv = ldapOperation->SimpleBind(NS_ConvertUCS2toUTF8(passwd));
00420     NS_ENSURE_SUCCESS(rv, rv);
00421 
00422     return rv;
00423 }
00424 
00425 nsresult nsAbQueryLDAPMessageListener::OnLDAPMessageBind (nsILDAPMessage *aMessage)
00426 {
00427     if (mBound)
00428         return NS_OK;
00429 
00430     // see whether the bind actually succeeded
00431     //
00432     PRInt32 errCode;
00433     nsresult rv = aMessage->GetErrorCode(&errCode);
00434     NS_ENSURE_SUCCESS(rv, rv);
00435 
00436     if (errCode != nsILDAPErrors::SUCCESS) {
00437 
00438         // if the login failed, tell the wallet to forget this password
00439         //
00440         if ( errCode == nsILDAPErrors::INAPPROPRIATE_AUTH ||
00441              errCode == nsILDAPErrors::INVALID_CREDENTIALS ) {
00442 
00443             // make sure the wallet service has been created, and in doing so,
00444             // pass in a login-failed message to tell it to forget this passwd.
00445             //
00446             // apparently getting passwords stored in the wallet
00447             // doesn't require the service to be running, which is why
00448             // this might not exist yet.
00449             //
00450             if (!mDirectoryQuery)
00451               return NS_ERROR_NULL_POINTER;
00452 
00453             rv = NS_CreateServicesFromCategory(
00454                 "passwordmanager", mDirectoryQuery->mDirectoryUrl,
00455                 "login-failed");
00456             if (NS_FAILED(rv)) {
00457                 NS_ERROR("nsLDAPAutoCompleteSession::ForgetPassword(): error"
00458                          " creating password manager service");
00459                 // not much to do at this point, though conceivably we could 
00460                 // pop up a dialog telling the user to go manually delete
00461                 // this password in the password manager.
00462             }
00463         } 
00464 
00465         // XXX this error should be propagated back to the UI somehow
00466         return NS_OK;
00467     }
00468 
00469     mBound = PR_TRUE;
00470     return DoSearch();
00471 }
00472 
00473  nsresult nsAbQueryLDAPMessageListener::DoSearch()
00474  {
00475    nsresult rv;
00476    mCanceled = mFinished = PR_FALSE;
00477 
00478     mSearchOperation = do_CreateInstance(NS_LDAPOPERATION_CONTRACTID, &rv);
00479     NS_ENSURE_SUCCESS(rv, rv);
00480 
00481     nsCOMPtr<nsIProxyObjectManager> proxyMgr = 
00482            do_GetService(NS_XPCOMPROXY_CONTRACTID, &rv);
00483          NS_ENSURE_SUCCESS(rv, rv);
00484     
00485     nsCOMPtr<nsILDAPMessageListener> proxyListener;
00486     rv = proxyMgr->GetProxyForObject( NS_UI_THREAD_EVENTQ, NS_GET_IID(nsILDAPMessageListener),
00487                                                                this, PROXY_SYNC | PROXY_ALWAYS, getter_AddRefs(proxyListener));
00488     NS_ENSURE_SUCCESS(rv, rv);
00489 
00490     rv = mSearchOperation->Init (mConnection, proxyListener, nsnull);
00491     NS_ENSURE_SUCCESS(rv, rv);
00492 
00493     nsCAutoString dn;
00494     rv = mUrl->GetDn (dn);
00495     NS_ENSURE_SUCCESS(rv, rv);
00496 
00497     PRInt32 scope;
00498     rv = mUrl->GetScope (&scope);
00499     NS_ENSURE_SUCCESS(rv, rv);
00500 
00501     nsCAutoString filter;
00502     rv = mUrl->GetFilter (filter);
00503     NS_ENSURE_SUCCESS(rv, rv);
00504 
00505     CharPtrArrayGuard attributes;
00506     rv = mUrl->GetAttributes (attributes.GetSizeAddr (), attributes.GetArrayAddr ());
00507     NS_ENSURE_SUCCESS(rv, rv);
00508 
00509     // I don't _think_ it's ever actually possible to get here without having
00510     // an nsAbLDAPDirectory object, but, just in case, I'll do a QI instead
00511     // of just static casts...
00512     nsCOMPtr<nsIAbLDAPDirectory> nsIAbDir = 
00513         do_QueryInterface(mDirectoryQuery, &rv);
00514     NS_ENSURE_SUCCESS(rv, rv);
00515     nsAbLDAPDirectory *dir = 
00516         NS_STATIC_CAST(nsAbLDAPDirectory *,
00517                        NS_STATIC_CAST(nsIAbLDAPDirectory *, nsIAbDir.get()));
00518 
00519     rv = mSearchOperation->SetServerControls(dir->mSearchServerControls.get());
00520     NS_ENSURE_SUCCESS(rv, rv);
00521 
00522     rv = mSearchOperation->SetClientControls(dir->mSearchClientControls.get());
00523     NS_ENSURE_SUCCESS(rv, rv);
00524 
00525     rv = mSearchOperation->SearchExt (dn, scope, filter,
00526             attributes.GetSize (), attributes.GetArray (),
00527             mTimeOut, mResultLimit);
00528     return rv;
00529 }
00530 
00531 nsresult nsAbQueryLDAPMessageListener::OnLDAPMessageSearchEntry (nsILDAPMessage *aMessage,
00532         nsIAbDirectoryQueryResult** result)
00533 {
00534     nsresult rv;
00535 
00536     if (!mDirectoryQuery)
00537       return NS_ERROR_NULL_POINTER;
00538     // the address book fields that we'll be asking for
00539     CharPtrArrayGuard properties;
00540     rv = mQueryArguments->GetReturnProperties (properties.GetSizeAddr(), properties.GetArrayAddr());
00541     NS_ENSURE_SUCCESS(rv, rv);
00542 
00543     // the map for translating between LDAP attrs <-> addrbook fields
00544     nsCOMPtr<nsISupports> iSupportsMap;
00545     rv = mQueryArguments->GetTypeSpecificArg(getter_AddRefs(iSupportsMap));
00546     NS_ENSURE_SUCCESS(rv, rv);
00547     nsCOMPtr<nsIAbLDAPAttributeMap> map = do_QueryInterface(iSupportsMap, &rv);
00548     NS_ENSURE_SUCCESS(rv, rv);
00549 
00550     // set up variables for handling the property values to be returned
00551     nsCOMPtr<nsISupportsArray> propertyValues;
00552     nsCOMPtr<nsIAbDirectoryQueryPropertyValue> propertyValue;
00553     rv = NS_NewISupportsArray(getter_AddRefs(propertyValues));
00554     NS_ENSURE_SUCCESS(rv, rv);
00555         
00556     if (!strcmp(properties[0], "card:nsIAbCard")) {
00557         // Meta property
00558         nsCAutoString dn;
00559         rv = aMessage->GetDn (dn);
00560         NS_ENSURE_SUCCESS(rv, rv);
00561 
00562         nsCOMPtr<nsIAbCard> card;
00563         rv = mDirectoryQuery->CreateCard (mUrl, dn.get(), getter_AddRefs (card));
00564         NS_ENSURE_SUCCESS(rv, rv);
00565 
00566         rv = map->SetCardPropertiesFromLDAPMessage(aMessage, card);
00567         NS_ENSURE_SUCCESS(rv, rv);
00568 
00569         propertyValue = new nsAbDirectoryQueryPropertyValue(properties[0], card);
00570         if (!propertyValue) 
00571             return NS_ERROR_OUT_OF_MEMORY;
00572 
00573         rv = propertyValues->AppendElement(propertyValue);
00574         NS_ENSURE_SUCCESS(rv, rv);
00575     } else {
00576 
00577         for (PRUint32 i = 0; i < properties.GetSize(); i++)
00578             {
00579                 // this is the precedence order list of attrs for this property
00580                 CharPtrArrayGuard attrs;
00581                 rv = map->GetAttributes(nsDependentCString(properties[i]),
00582                                         attrs.GetSizeAddr(),
00583                                         attrs.GetArrayAddr());
00584 
00585                 // if there are no attrs for this property, just move on
00586                 if (NS_FAILED(rv) || !strlen(attrs[0])) {
00587                     continue;
00588                 }
00589 
00590                 // iterate through list, until first property found
00591                 for (PRUint32 j=0; j < attrs.GetSize(); j++) {
00592 
00593                     // try and get the values for this ldap attribute
00594                     PRUnicharPtrArrayGuard vals;
00595                     rv = aMessage->GetValues(attrs[j], vals.GetSizeAddr(),
00596                                              vals.GetArrayAddr());
00597 
00598                     if (NS_SUCCEEDED(rv) && vals.GetSize()) {
00599                         propertyValue = new nsAbDirectoryQueryPropertyValue(
00600                             properties[i], vals[0]);
00601                         if (!propertyValue) {
00602                             return NS_ERROR_OUT_OF_MEMORY;
00603                         }
00604 
00605                         (void)propertyValues->AppendElement (propertyValue);
00606                         break;
00607                     }
00608                 }
00609             }
00610     }
00611 
00612     return QueryResultStatus (propertyValues, result, nsIAbDirectoryQueryResult::queryResultMatch);
00613 }
00614 nsresult nsAbQueryLDAPMessageListener::OnLDAPMessageSearchResult (nsILDAPMessage *aMessage,
00615         nsIAbDirectoryQueryResult** result)
00616 {
00617     nsresult rv;
00618     PRInt32 errorCode;
00619     rv = aMessage->GetErrorCode(&errorCode);
00620     NS_ENSURE_SUCCESS(rv, rv);
00621     
00622     if (errorCode == nsILDAPErrors::SUCCESS || errorCode == nsILDAPErrors::SIZELIMIT_EXCEEDED)
00623         rv = QueryResultStatus (nsnull, result,nsIAbDirectoryQueryResult::queryResultComplete);
00624     else
00625         rv = QueryResultStatus (nsnull, result, nsIAbDirectoryQueryResult::queryResultError);
00626 
00627     return rv;
00628 }
00629 nsresult nsAbQueryLDAPMessageListener::QueryResultStatus (nsISupportsArray* properties,
00630         nsIAbDirectoryQueryResult** result, PRUint32 resultStatus)
00631 {
00632     nsAbDirectoryQueryResult* _queryResult = new nsAbDirectoryQueryResult (
00633         mContextID,
00634         mQueryArguments,
00635         resultStatus,
00636         (resultStatus == nsIAbDirectoryQueryResult::queryResultMatch) ? properties : 0);
00637 
00638     if (!_queryResult)
00639         return NS_ERROR_OUT_OF_MEMORY;
00640 
00641     NS_IF_ADDREF(*result = _queryResult);
00642     return NS_OK;
00643 }
00644 
00645 
00646 
00647 
00648 
00649 NS_IMPL_THREADSAFE_ISUPPORTS1(nsAbLDAPDirectoryQuery, nsIAbDirectoryQuery)
00650 
00651 nsAbLDAPDirectoryQuery::nsAbLDAPDirectoryQuery() :
00652     mProtocolVersion (nsILDAPConnection::VERSION3),
00653     mInitialized(PR_FALSE),
00654     mLock (nsnull)
00655 {
00656 }
00657 
00658 nsAbLDAPDirectoryQuery::~nsAbLDAPDirectoryQuery()
00659 {
00660      nsAbQueryLDAPMessageListener *msgListener = 
00661         NS_STATIC_CAST(nsAbQueryLDAPMessageListener *, 
00662         NS_STATIC_CAST(nsILDAPMessageListener *, mListener.get()));
00663      if (msgListener)
00664      {
00665        msgListener->mDirectoryQuery = nsnull;
00666        msgListener->mQueryListener = nsnull;
00667      }
00668      if(mLock)
00669         PR_DestroyLock (mLock);
00670 }
00671 nsresult nsAbLDAPDirectoryQuery::Initiate ()
00672 {
00673     if (mInitialized)
00674         return NS_OK;
00675 
00676     mLock = PR_NewLock ();
00677     if(!mLock)
00678     {
00679         return NS_ERROR_OUT_OF_MEMORY;
00680     }
00681 
00682     mInitialized = PR_TRUE;
00683 
00684     return NS_OK;
00685 }
00686 
00687 NS_IMETHODIMP nsAbLDAPDirectoryQuery::DoQuery(nsIAbDirectoryQueryArguments* arguments,
00688         nsIAbDirectoryQueryResultListener* listener,
00689         PRInt32 resultLimit,
00690         PRInt32 timeOut,
00691         PRInt32* _retval)
00692 {
00693     nsresult rv;
00694     PRBool alreadyInitialized = mInitialized;
00695     rv = Initiate ();
00696     NS_ENSURE_SUCCESS(rv, rv);
00697 
00698     // Get the scope
00699     nsCAutoString scope;
00700     PRBool doSubDirectories;
00701     rv = arguments->GetQuerySubDirectories (&doSubDirectories);
00702     NS_ENSURE_SUCCESS(rv, rv);
00703     scope = (doSubDirectories) ? "sub" : "one";
00704 
00705     // Get the return attributes
00706     nsCAutoString returnAttributes;
00707     rv = getLdapReturnAttributes (arguments, returnAttributes);
00708     NS_ENSURE_SUCCESS(rv, rv);
00709 
00710 
00711     // Get the filter
00712     nsCOMPtr<nsISupports> supportsExpression;
00713     rv = arguments->GetExpression (getter_AddRefs (supportsExpression));
00714     NS_ENSURE_SUCCESS(rv, rv);
00715 
00716     nsCOMPtr<nsIAbBooleanExpression> expression (do_QueryInterface (supportsExpression, &rv));
00717     nsCAutoString filter;
00718 
00719     // figure out how we map attribute names to addressbook fields for this
00720     // query
00721     nsCOMPtr<nsISupports> iSupportsMap;
00722     rv = arguments->GetTypeSpecificArg(getter_AddRefs(iSupportsMap));
00723     NS_ENSURE_SUCCESS(rv, rv);
00724 
00725     nsCOMPtr<nsIAbLDAPAttributeMap> map = do_QueryInterface(iSupportsMap, &rv);
00726     NS_ENSURE_SUCCESS(rv, rv);
00727 
00728     rv = nsAbBoolExprToLDAPFilter::Convert (map, expression, filter);
00729     NS_ENSURE_SUCCESS(rv, rv);
00730 
00731     /*
00732      * Mozilla itself cannot arrive here with a blank filter
00733      * as the nsAbLDAPDirectory::StartSearch() disallows it.
00734      * But 3rd party LDAP query integration with Mozilla begins 
00735      * in this method.
00736      *
00737      * Default the filter string if blank, otherwise it gets
00738      * set to (objectclass=*) which returns everything. Set 
00739      * the default to (objectclass=inetorgperson) as this 
00740      * is the most appropriate default objectclass which is 
00741      * central to the makeup of the mozilla ldap address book 
00742      * entries.
00743     */
00744     if(filter.IsEmpty())
00745     {
00746         filter.AssignLiteral("(objectclass=inetorgperson)");
00747     }
00748 
00749     // Set up the search ldap url
00750     rv = GetLDAPURL (getter_AddRefs (mDirectoryUrl));
00751     NS_ENSURE_SUCCESS(rv, rv);
00752     
00753     nsCAutoString host;
00754     rv = mDirectoryUrl->GetAsciiHost(host);
00755     NS_ENSURE_SUCCESS(rv, rv);
00756 
00757     PRInt32 port;
00758     rv = mDirectoryUrl->GetPort(&port);
00759     NS_ENSURE_SUCCESS(rv, rv);
00760 
00761     nsCAutoString dn;
00762     rv = mDirectoryUrl->GetDn(dn);
00763     NS_ENSURE_SUCCESS(rv, rv);
00764 
00765     PRUint32 options;
00766     rv = mDirectoryUrl->GetOptions(&options);
00767     NS_ENSURE_SUCCESS(rv,rv);
00768 
00769     // get the directoryFilter from the directory url and merge it with the user's
00770     // search filter
00771     nsCAutoString urlFilter;
00772     rv = mDirectoryUrl->GetFilter(urlFilter);
00773 
00774     // if urlFilter is unset (or set to the default "objectclass=*"), there's
00775     // no need to AND in an empty search term, so leave prefix and suffix empty
00776 
00777     nsCAutoString searchFilter;
00778     if (urlFilter.Length() && !urlFilter.Equals(NS_LITERAL_CSTRING("(objectclass=*)"))) 
00779     {
00780 
00781       // if urlFilter isn't parenthesized, we need to add in parens so that
00782       // the filter works as a term to &
00783       //
00784       if (urlFilter[0] != '(') 
00785         searchFilter = NS_LITERAL_CSTRING("(&(") + urlFilter + NS_LITERAL_CSTRING(")");
00786       else
00787         searchFilter = NS_LITERAL_CSTRING("(&") + urlFilter;
00788 
00789       searchFilter += filter;
00790       searchFilter += ')';
00791     } 
00792     else
00793       searchFilter = filter;
00794 
00795     nsCString ldapSearchUrlString;
00796     char* _ldapSearchUrlString = 
00797         PR_smprintf ("ldap%s://%s:%d/%s?%s?%s?%s",
00798                      (options & nsILDAPURL::OPT_SECURE) ? "s" : "",
00799                      host.get (),
00800                      port,
00801                      dn.get (),
00802                      returnAttributes.get (),
00803                      scope.get (),
00804                      searchFilter.get ());
00805     if (!_ldapSearchUrlString)
00806         return NS_ERROR_OUT_OF_MEMORY;
00807     ldapSearchUrlString = _ldapSearchUrlString;
00808     PR_smprintf_free (_ldapSearchUrlString);
00809 
00810     nsCOMPtr<nsILDAPURL> url;
00811     url = do_CreateInstance(NS_LDAPURL_CONTRACTID, &rv);
00812     NS_ENSURE_SUCCESS(rv, rv);
00813 
00814     rv = url->SetSpec(ldapSearchUrlString);
00815     NS_ENSURE_SUCCESS(rv, rv);
00816 
00817     // Get the ldap connection
00818     nsCOMPtr<nsILDAPConnection> ldapConnection;
00819     rv = GetLDAPConnection (getter_AddRefs (ldapConnection));
00820     NS_ENSURE_SUCCESS(rv, rv);
00821 
00822     // too soon? Do we need a new listener?
00823     if (alreadyInitialized)
00824     {
00825       nsAbQueryLDAPMessageListener *msgListener = 
00826         NS_STATIC_CAST(nsAbQueryLDAPMessageListener *, 
00827         NS_STATIC_CAST(nsILDAPMessageListener *, mListener.get()));
00828       if (msgListener)
00829       {
00830         msgListener->mUrl = url;
00831         return msgListener->DoSearch();
00832       }
00833     }
00834 
00835 
00836     // Initiate LDAP message listener
00837     nsAbQueryLDAPMessageListener* _messageListener =
00838         new nsAbQueryLDAPMessageListener (
00839                 this,
00840                 url,
00841                 ldapConnection,
00842                 arguments,
00843                 listener,
00844                 resultLimit,
00845                 timeOut);
00846     if (_messageListener == NULL)
00847             return NS_ERROR_OUT_OF_MEMORY;
00848     mListener = _messageListener;
00849     *_retval = 1;
00850 
00851     // Now lets initialize the LDAP connection properly. We'll kick
00852     // off the bind operation in the callback function, |OnLDAPInit()|.
00853     rv = ldapConnection->Init(host.get(), port, options, mLogin,
00854                               mListener, nsnull, mProtocolVersion);
00855     NS_ENSURE_SUCCESS(rv, rv);
00856 
00857     return rv;
00858 }
00859 
00860 /* void stopQuery (in long contextID); */
00861 NS_IMETHODIMP nsAbLDAPDirectoryQuery::StopQuery(PRInt32 contextID)
00862 {
00863     nsresult rv;
00864 
00865     rv = Initiate ();
00866     NS_ENSURE_SUCCESS(rv, rv);
00867 
00868     if (!mListener)
00869             return NS_OK;
00870 
00871     nsAbQueryLDAPMessageListener *listener = 
00872       NS_STATIC_CAST(nsAbQueryLDAPMessageListener *, 
00873       NS_STATIC_CAST(nsILDAPMessageListener *, mListener.get()));
00874     if (listener)
00875       rv = listener->Cancel ();
00876     return rv;
00877 }
00878 
00879 
00880 nsresult nsAbLDAPDirectoryQuery::getLdapReturnAttributes (
00881     nsIAbDirectoryQueryArguments* arguments,
00882     nsCString& returnAttributes)
00883 {
00884     nsresult rv;
00885 
00886     CharPtrArrayGuard properties;
00887     rv = arguments->GetReturnProperties(properties.GetSizeAddr(),
00888                                         properties.GetArrayAddr());
00889     NS_ENSURE_SUCCESS(rv, rv);
00890 
00891     // figure out how we map attribute names to addressbook fields for this
00892     // query
00893     nsCOMPtr<nsISupports> iSupportsMap;
00894     rv = arguments->GetTypeSpecificArg(getter_AddRefs(iSupportsMap));
00895     NS_ENSURE_SUCCESS(rv, rv);
00896 
00897     nsCOMPtr<nsIAbLDAPAttributeMap> map = do_QueryInterface(iSupportsMap, &rv);
00898     NS_ENSURE_SUCCESS(rv, rv);
00899 
00900     if (!strcmp(properties[0], "card:nsIAbCard")) {
00901         // Meta property
00902         // require all attributes
00903         //
00904         rv = map->GetAllCardAttributes(returnAttributes);
00905         NS_ASSERTION(NS_SUCCEEDED(rv), "GetAllSupportedAttributes failed");
00906         return rv;
00907     }
00908 
00909     PRBool needComma = PR_FALSE;
00910     for (PRUint32 i = 0; i < properties.GetSize(); i++)
00911     {
00912         nsCAutoString attrs;
00913 
00914         // get all the attributes for this property
00915         rv = map->GetAttributeList(nsDependentCString(properties[i]), attrs);
00916 
00917         // if there weren't any attrs, just keep going
00918         if (NS_FAILED(rv) || attrs.IsEmpty()) {
00919             continue;
00920         }
00921 
00922         // add a comma, if necessary
00923         if (needComma) {
00924             returnAttributes.Append(PRUnichar (','));
00925         }
00926 
00927         returnAttributes.Append(attrs);
00928 
00929         // since we've added attrs, we definitely need a comma next time
00930         // we're here
00931         needComma = PR_TRUE;
00932     }
00933 
00934     return rv;
00935 }
00936