Back to index

lightning-sunbird  0.9+nobinonly
nsLDAPOperation.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 the mozilla.org LDAP XPCOM SDK.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 2000
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Dan Mosedale <dmose@mozilla.org>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsLDAPInternal.h"
00041 #include "nsLDAP.h"
00042 #include "nsLDAPOperation.h"
00043 #include "nsLDAPConnection.h"
00044 #include "nsILDAPMessage.h"
00045 #include "nsIComponentManager.h"
00046 #include "nsXPIDLString.h"
00047 #include "nspr.h"
00048 #include "nsISimpleEnumerator.h"
00049 #include "nsLDAPControl.h"
00050 
00051 // constructor
00052 nsLDAPOperation::nsLDAPOperation()
00053 {
00054 }
00055 
00056 // destructor
00057 nsLDAPOperation::~nsLDAPOperation()
00058 {
00059 }
00060 
00061 
00062 NS_IMPL_THREADSAFE_ADDREF(nsLDAPOperation)
00063 NS_IMPL_THREADSAFE_RELEASE(nsLDAPOperation)
00064 NS_INTERFACE_MAP_BEGIN(nsLDAPOperation)
00065   NS_INTERFACE_MAP_ENTRY(nsILDAPOperation)
00066   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsILDAPOperation)
00067   NS_IMPL_QUERY_CLASSINFO(nsLDAPOperation)
00068 NS_INTERFACE_MAP_END_THREADSAFE
00069 NS_IMPL_CI_INTERFACE_GETTER1(nsLDAPOperation, nsILDAPOperation)
00070 
00071 
00078 NS_IMETHODIMP
00079 nsLDAPOperation::Init(nsILDAPConnection *aConnection,
00080                       nsILDAPMessageListener *aMessageListener,
00081                       nsISupports *aClosure)
00082 {
00083     if (!aConnection) {
00084         return NS_ERROR_ILLEGAL_VALUE;
00085     }
00086 
00087     // so we know that the operation is not yet running (and therefore don't
00088     // try and call ldap_abandon_ext() on it) or remove it from the queue.
00089     //
00090     mMsgID = 0;
00091 
00092     // set the member vars
00093     //
00094     mConnection = aConnection;
00095     mMessageListener = aMessageListener;
00096     mClosure = aClosure;
00097 
00098     // cache the connection handle
00099     //
00100     mConnectionHandle = 
00101         NS_STATIC_CAST(nsLDAPConnection *, aConnection)->mConnectionHandle;
00102 
00103     return NS_OK;
00104 }
00105 
00106 NS_IMETHODIMP
00107 nsLDAPOperation::GetClosure(nsISupports **_retval)
00108 {
00109     if (!_retval) {
00110         return NS_ERROR_ILLEGAL_VALUE;
00111     }
00112     NS_IF_ADDREF(*_retval = mClosure);
00113     return NS_OK;
00114 }
00115 
00116 NS_IMETHODIMP
00117 nsLDAPOperation::SetClosure(nsISupports *aClosure)
00118 {
00119     mClosure = aClosure;
00120     return NS_OK;
00121 }
00122 
00123 NS_IMETHODIMP
00124 nsLDAPOperation::GetConnection(nsILDAPConnection* *aConnection)
00125 {
00126     if (!aConnection) {
00127         return NS_ERROR_ILLEGAL_VALUE;
00128     }
00129 
00130     *aConnection = mConnection;
00131     NS_IF_ADDREF(*aConnection);
00132 
00133     return NS_OK;
00134 }
00135 
00136 void
00137 nsLDAPOperation::Clear()
00138 {
00139   mMessageListener = nsnull;
00140   mClosure = nsnull;
00141   mConnection = nsnull;
00142 }
00143 
00144 NS_IMETHODIMP
00145 nsLDAPOperation::GetMessageListener(nsILDAPMessageListener **aMessageListener)
00146 {
00147     if (!aMessageListener) {
00148         return NS_ERROR_ILLEGAL_VALUE;
00149     }
00150 
00151     *aMessageListener = mMessageListener;
00152     NS_IF_ADDREF(*aMessageListener);
00153 
00154     return NS_OK;
00155 }
00156 
00157 // wrapper for ldap_simple_bind()
00158 //
00159 NS_IMETHODIMP
00160 nsLDAPOperation::SimpleBind(const nsACString& passwd)
00161 {
00162     nsresult rv;
00163     nsCAutoString bindName;
00164     PRBool originalMsgID = mMsgID;
00165     // Ugly hack alert:
00166     // the first time we get called with a passwd, remember it.
00167     // Then, if we get called again w/o a password, use the 
00168     // saved one. Getting called again means we're trying to
00169     // fall back to VERSION2.
00170     // Since LDAP operations are thrown away when done, it won't stay
00171     // around in memory.
00172     if (!passwd.IsEmpty())
00173       mSavePassword = passwd;
00174 
00175     NS_PRECONDITION(mMessageListener != 0, "MessageListener not set");
00176 
00177     rv = mConnection->GetBindName(bindName);
00178     if (NS_FAILED(rv))
00179         return rv;
00180 
00181     PR_LOG(gLDAPLogModule, PR_LOG_DEBUG, 
00182            ("nsLDAPOperation::SimpleBind(): called; bindName = '%s'; ",
00183             bindName.get()));
00184 
00185     // If this is a second try at binding, remove the operation from pending ops
00186     // because msg id has changed...
00187     if (originalMsgID)
00188       NS_STATIC_CAST(nsLDAPConnection *, 
00189                         NS_STATIC_CAST(nsILDAPConnection *, 
00190                         mConnection.get()))->RemovePendingOperation(this);
00191 
00192     mMsgID = ldap_simple_bind(mConnectionHandle, bindName.get(),
00193                               PromiseFlatCString(mSavePassword).get());
00194 
00195     if (mMsgID == -1) {
00196         const int lderrno = ldap_get_lderrno(mConnectionHandle, 0, 0);
00197         
00198         switch (lderrno) {
00199 
00200         case LDAP_ENCODING_ERROR:
00201             return NS_ERROR_LDAP_ENCODING_ERROR;
00202 
00203         case LDAP_CONNECT_ERROR:
00204             return NS_ERROR_LDAP_CONNECT_ERROR;
00205 
00206         case LDAP_SERVER_DOWN:
00207             // XXXdmose rebind here?
00208             return NS_ERROR_LDAP_SERVER_DOWN;
00209 
00210         case LDAP_NO_MEMORY:
00211             return NS_ERROR_OUT_OF_MEMORY;
00212 
00213         default:
00214             return NS_ERROR_UNEXPECTED;
00215         }
00216 
00217     } 
00218   
00219     // make sure the connection knows where to call back once the messages
00220     // for this operation start coming in
00221     rv = NS_STATIC_CAST(nsLDAPConnection *, 
00222                         NS_STATIC_CAST(nsILDAPConnection *, 
00223                         mConnection.get()))->AddPendingOperation(this);
00224     switch (rv) {
00225     case NS_OK:
00226         break;
00227 
00228         // note that the return value of ldap_abandon_ext() is ignored, as 
00229         // there's nothing useful to do with it
00230 
00231     case NS_ERROR_OUT_OF_MEMORY:
00232         (void *)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0);
00233         return NS_ERROR_OUT_OF_MEMORY;
00234         break;
00235 
00236     case NS_ERROR_UNEXPECTED:
00237     case NS_ERROR_ILLEGAL_VALUE:
00238     default:
00239         (void *)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0);
00240         return NS_ERROR_UNEXPECTED;
00241     }
00242 
00243     return NS_OK;
00244 }
00245 
00250 static nsresult
00251 convertControlArray(nsIArray *aXpcomArray, LDAPControl ***aArray)
00252 {
00253     // get the size of the original array
00254     PRUint32 length;
00255     nsresult rv  = aXpcomArray->GetLength(&length);
00256     NS_ENSURE_SUCCESS(rv, rv);
00257 
00258     // don't allocate an array if someone passed us in an empty one
00259     if (!length) {
00260         *aArray = 0;
00261         return NS_OK;
00262     }
00263 
00264     // allocate a local array of the form understood by the C-SDK;
00265     // +1 is to account for the final null terminator.  PR_Calloc is
00266     // is used so that ldap_controls_free will work anywhere during the
00267     // iteration
00268     LDAPControl **controls = 
00269         NS_STATIC_CAST(LDAPControl **,
00270                        PR_Calloc(length+1, sizeof(LDAPControl)));
00271 
00272     // prepare to enumerate the array
00273     nsCOMPtr<nsISimpleEnumerator> enumerator;
00274     rv = aXpcomArray->Enumerate(getter_AddRefs(enumerator));
00275     NS_ENSURE_SUCCESS(rv, rv);
00276 
00277     PRBool moreElements;
00278     rv = enumerator->HasMoreElements(&moreElements);
00279     NS_ENSURE_SUCCESS(rv, rv);
00280 
00281     PRUint32 i = 0;
00282     while (moreElements) {
00283 
00284         // get the next array element
00285         nsCOMPtr<nsISupports> isupports;
00286         rv = enumerator->GetNext(getter_AddRefs(isupports));
00287         if (NS_FAILED(rv)) {
00288             ldap_controls_free(controls);
00289             return rv;
00290         }
00291         nsCOMPtr<nsILDAPControl> control = do_QueryInterface(isupports, &rv);
00292         if (NS_FAILED(rv)) {
00293             ldap_controls_free(controls);
00294             return NS_ERROR_INVALID_ARG; // bogus element in the array
00295         }
00296         nsLDAPControl *ctl = NS_STATIC_CAST(nsLDAPControl *,
00297                                             NS_STATIC_CAST(nsILDAPControl *,
00298                                                            control.get()));
00299 
00300         // convert it to an LDAPControl structure placed in the new array
00301         rv = ctl->ToLDAPControl(&controls[i]);
00302         if (NS_FAILED(rv)) {
00303             ldap_controls_free(controls);
00304             return rv;
00305         }
00306 
00307         // on to the next element
00308         rv = enumerator->HasMoreElements(&moreElements);
00309         if (NS_FAILED(rv)) {
00310             ldap_controls_free(controls);
00311             return NS_ERROR_UNEXPECTED;
00312         }
00313         ++i;
00314     }
00315 
00316     *aArray = controls;
00317     return NS_OK;
00318 }
00319 
00320 NS_IMETHODIMP
00321 nsLDAPOperation::SearchExt(const nsACString& aBaseDn, PRInt32 aScope, 
00322                            const nsACString& aFilter, 
00323                            PRUint32 aAttrCount, const char **aAttributes,
00324                            PRIntervalTime aTimeOut, PRInt32 aSizeLimit) 
00325 {
00326     if (!mMessageListener) {
00327         NS_ERROR("nsLDAPOperation::SearchExt(): mMessageListener not set");
00328         return NS_ERROR_NOT_INITIALIZED;
00329     }
00330 
00331     // XXX add control logging
00332     PR_LOG(gLDAPLogModule, PR_LOG_DEBUG, 
00333            ("nsLDAPOperation::SearchExt(): called with aBaseDn = '%s'; "
00334             "aFilter = '%s', aAttrCounts = %u, aSizeLimit = %d", 
00335             PromiseFlatCString(aBaseDn).get(),
00336             PromiseFlatCString(aFilter).get(),
00337             aAttrCount, aSizeLimit));
00338 
00339     char **attrs = 0;
00340 
00341     // Convert our XPCOM style C-Array to one that the C-SDK will like, i.e.
00342     // add a last NULL element.
00343     //
00344     if (aAttrCount && aAttributes) {
00345         attrs = NS_STATIC_CAST(char **,
00346                     nsMemory::Alloc((aAttrCount + 1) * sizeof(char *)));
00347         if (!attrs) {
00348             NS_ERROR("nsLDAPOperation::SearchExt: out of memory ");
00349             return NS_ERROR_OUT_OF_MEMORY;
00350         }
00351         memcpy(attrs, aAttributes, aAttrCount * sizeof(char *));
00352         attrs[aAttrCount] = 0;
00353     }
00354 
00355     LDAPControl **serverctls = 0;
00356     nsresult rv;
00357     if (mServerControls) {
00358         rv = convertControlArray(mServerControls, &serverctls);
00359         if (NS_FAILED(rv)) {
00360             PR_LOG(gLDAPLogModule, PR_LOG_ERROR,
00361                    ("nsLDAPOperation::SearchExt(): error converting server "
00362                     "control array: %x", rv));
00363 
00364             if (attrs) {
00365                 nsMemory::Free(attrs);
00366             }
00367             return rv;
00368         }
00369     }
00370 
00371 
00372     LDAPControl **clientctls = 0;
00373     if (mClientControls) {
00374         rv = convertControlArray(mClientControls, &clientctls);
00375         if (NS_FAILED(rv)) {
00376             PR_LOG(gLDAPLogModule, PR_LOG_ERROR,
00377                    ("nsLDAPOperation::SearchExt(): error converting client "
00378                     "control array: %x", rv));
00379             if (attrs) {
00380                 nsMemory::Free(attrs);
00381             }
00382             ldap_controls_free(serverctls);
00383             return rv;
00384         }
00385     }
00386 
00387     // XXX deal with timeout here
00388     int retVal = ldap_search_ext(mConnectionHandle, 
00389                                  PromiseFlatCString(aBaseDn).get(),
00390                                  aScope, PromiseFlatCString(aFilter).get(), 
00391                                  attrs, 0, serverctls, clientctls, 0,
00392                                  aSizeLimit, &mMsgID);
00393 
00394     // clean up
00395     ldap_controls_free(serverctls);        
00396     ldap_controls_free(clientctls);
00397     if (attrs) {
00398         nsMemory::Free(attrs);
00399     }
00400 
00401     switch (retVal) {
00402 
00403     case LDAP_SUCCESS: 
00404         break;
00405 
00406     case LDAP_ENCODING_ERROR:
00407         return NS_ERROR_LDAP_ENCODING_ERROR;
00408 
00409     case LDAP_SERVER_DOWN:
00410         return NS_ERROR_LDAP_SERVER_DOWN;
00411 
00412     case LDAP_NO_MEMORY:
00413         return NS_ERROR_OUT_OF_MEMORY;
00414 
00415     case LDAP_NOT_SUPPORTED:
00416         return NS_ERROR_LDAP_NOT_SUPPORTED;
00417 
00418     case LDAP_PARAM_ERROR:
00419         return NS_ERROR_INVALID_ARG;
00420 
00421     case LDAP_FILTER_ERROR:
00422         return NS_ERROR_LDAP_FILTER_ERROR;
00423 
00424     default:
00425         NS_ERROR("nsLDAPOperation::SearchExt(): unexpected return value");
00426         return NS_ERROR_UNEXPECTED;
00427     }
00428 
00429     // make sure the connection knows where to call back once the messages
00430     // for this operation start coming in
00431     //
00432     rv = NS_STATIC_CAST(nsLDAPConnection *, NS_STATIC_CAST(
00433         nsILDAPConnection *, mConnection.get()))->AddPendingOperation(this);
00434     if (NS_FAILED(rv)) {
00435         switch (rv) {
00436         case NS_ERROR_OUT_OF_MEMORY: 
00437             (void *)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0);
00438             return NS_ERROR_OUT_OF_MEMORY;
00439 
00440         default: 
00441             (void *)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0);
00442             NS_ERROR("nsLDAPOperation::SearchExt(): unexpected error in "
00443                      "mConnection->AddPendingOperation");
00444             return NS_ERROR_UNEXPECTED;
00445         }
00446     }
00447 
00448     return NS_OK;
00449 }
00450 
00451 NS_IMETHODIMP
00452 nsLDAPOperation::GetMessageID(PRInt32 *aMsgID)
00453 {
00454     if (!aMsgID) {
00455         return NS_ERROR_ILLEGAL_VALUE;
00456     }
00457 
00458     *aMsgID = mMsgID;
00459    
00460     return NS_OK;
00461 }
00462 
00463 // as far as I can tell from reading the LDAP C SDK code, abandoning something
00464 // that has already been abandoned does not return an error
00465 //
00466 NS_IMETHODIMP
00467 nsLDAPOperation::AbandonExt()
00468 {
00469     nsresult rv;
00470     nsresult retStatus = NS_OK;
00471 
00472     if ( mMessageListener == 0 || mMsgID == 0 ) {
00473         NS_ERROR("nsLDAPOperation::AbandonExt(): mMessageListener or "
00474                  "mMsgId not initialized");
00475         return NS_ERROR_NOT_INITIALIZED;
00476     }
00477 
00478     // XXX handle controls here
00479     if (mServerControls || mClientControls) {
00480         return NS_ERROR_NOT_IMPLEMENTED;
00481     }
00482 
00483     rv = ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0);
00484     switch (rv) {
00485 
00486     case LDAP_SUCCESS:
00487         break;
00488 
00489     case LDAP_ENCODING_ERROR:
00490         return NS_ERROR_LDAP_ENCODING_ERROR;
00491     
00492     case LDAP_SERVER_DOWN:
00493         retStatus = NS_ERROR_LDAP_SERVER_DOWN;
00494         break;
00495 
00496     case LDAP_NO_MEMORY:
00497         return NS_ERROR_OUT_OF_MEMORY;
00498 
00499     case LDAP_PARAM_ERROR:
00500         return NS_ERROR_INVALID_ARG;
00501 
00502     default: 
00503         NS_ERROR("nsLDAPOperation::AbandonExt(): unexpected return value from "
00504                  "ldap_abandon_ext");
00505         return NS_ERROR_UNEXPECTED;
00506     }
00507 
00508     // try to remove it from the pendingOperations queue, if it's there.
00509     // even if something goes wrong here, the abandon() has already succeeded
00510     // succeeded (and there's nothing else the caller can reasonably do), 
00511     // so we only pay attention to this in debug builds.
00512     //
00513     // check mConnection in case we're getting bit by 
00514     // http://bugzilla.mozilla.org/show_bug.cgi?id=239729, wherein we 
00515     // theorize that ::Clearing the operation is nulling out the mConnection
00516     // from another thread.
00517     if (mConnection)
00518     {
00519       rv = NS_STATIC_CAST(nsLDAPConnection *, NS_STATIC_CAST(
00520           nsILDAPConnection *, mConnection.get()))->RemovePendingOperation(this);
00521 
00522       if (NS_FAILED(rv)) {
00523           // XXXdmose should we keep AbandonExt from happening on multiple 
00524           // threads at the same time?  that's when this condition is most 
00525           // likely to occur.  i _think_ the LDAP C SDK is ok with this; need 
00526           // to verify.
00527           //
00528           NS_WARNING("nsLDAPOperation::AbandonExt: "
00529                      "mConnection->RemovePendingOperation(this) failed.");
00530       }
00531     }
00532 
00533     return retStatus;
00534 }
00535 
00536 NS_IMETHODIMP
00537 nsLDAPOperation::GetClientControls(nsIMutableArray **aControls)
00538 {
00539     NS_IF_ADDREF(*aControls = mClientControls);
00540     return NS_OK;
00541 }
00542 
00543 NS_IMETHODIMP
00544 nsLDAPOperation::SetClientControls(nsIMutableArray *aControls)
00545 {
00546     mClientControls = aControls;
00547     return NS_OK;
00548 }
00549 
00550 NS_IMETHODIMP nsLDAPOperation::GetServerControls(nsIMutableArray **aControls)
00551 {
00552     NS_IF_ADDREF(*aControls = mServerControls);
00553     return NS_OK;
00554 }
00555 
00556 NS_IMETHODIMP nsLDAPOperation::SetServerControls(nsIMutableArray *aControls)
00557 {
00558     mServerControls = aControls;
00559     return NS_OK;
00560 }