/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsLDAPInternal.h" #include "nsLDAPOperation.h" #include "nsLDAPBERValue.h" #include "nsILDAPMessage.h" #include "nsILDAPModification.h" #include "nsIComponentManager.h" #include "nspr.h" #include "nsISimpleEnumerator.h" #include "nsLDAPControl.h" #include "nsILDAPErrors.h" #include "nsIClassInfoImpl.h" #include "nsIAuthModule.h" #include "nsArrayUtils.h" #include "nsMemory.h" #include "mozilla/Logging.h" // Helper function static nsresult TranslateLDAPErrorToNSError(const int ldapError) { switch (ldapError) { case LDAP_SUCCESS: return NS_OK; case LDAP_ENCODING_ERROR: return NS_ERROR_LDAP_ENCODING_ERROR; case LDAP_CONNECT_ERROR: return NS_ERROR_LDAP_CONNECT_ERROR; case LDAP_SERVER_DOWN: return NS_ERROR_LDAP_SERVER_DOWN; case LDAP_NO_MEMORY: return NS_ERROR_OUT_OF_MEMORY; case LDAP_NOT_SUPPORTED: return NS_ERROR_LDAP_NOT_SUPPORTED; case LDAP_PARAM_ERROR: return NS_ERROR_INVALID_ARG; case LDAP_FILTER_ERROR: return NS_ERROR_LDAP_FILTER_ERROR; default: MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Error, ("TranslateLDAPErrorToNSError: " "Do not know how to translate LDAP error: 0x%x", ldapError)); return NS_ERROR_UNEXPECTED; } } // constructor nsLDAPOperation::nsLDAPOperation() { } // destructor nsLDAPOperation::~nsLDAPOperation() { } NS_IMPL_CLASSINFO(nsLDAPOperation, NULL, nsIClassInfo::THREADSAFE, NS_LDAPOPERATION_CID) NS_IMPL_ADDREF(nsLDAPOperation) NS_IMPL_RELEASE(nsLDAPOperation) NS_INTERFACE_MAP_BEGIN(nsLDAPOperation) NS_INTERFACE_MAP_ENTRY(nsILDAPOperation) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsILDAPOperation) NS_IMPL_QUERY_CLASSINFO(nsLDAPOperation) NS_INTERFACE_MAP_END_THREADSAFE NS_IMPL_CI_INTERFACE_GETTER(nsLDAPOperation, nsILDAPOperation) /** * Initializes this operation. Must be called prior to use. * * @param aConnection connection this operation should use * @param aMessageListener where are the results are called back to. */ NS_IMETHODIMP nsLDAPOperation::Init(nsILDAPConnection *aConnection, nsILDAPMessageListener *aMessageListener, nsISupports *aClosure) { if (!aConnection) { return NS_ERROR_ILLEGAL_VALUE; } // so we know that the operation is not yet running (and therefore don't // try and call ldap_abandon_ext() on it) or remove it from the queue. // mMsgID = 0; // set the member vars // mConnection = static_cast(aConnection); mMessageListener = aMessageListener; mClosure = aClosure; // cache the connection handle // mConnectionHandle = mConnection->mConnectionHandle; return NS_OK; } NS_IMETHODIMP nsLDAPOperation::GetClosure(nsISupports **_retval) { if (!_retval) { return NS_ERROR_ILLEGAL_VALUE; } NS_IF_ADDREF(*_retval = mClosure); return NS_OK; } NS_IMETHODIMP nsLDAPOperation::SetClosure(nsISupports *aClosure) { mClosure = aClosure; return NS_OK; } NS_IMETHODIMP nsLDAPOperation::GetConnection(nsILDAPConnection* *aConnection) { if (!aConnection) { return NS_ERROR_ILLEGAL_VALUE; } *aConnection = mConnection; NS_IF_ADDREF(*aConnection); return NS_OK; } void nsLDAPOperation::Clear() { mMessageListener = nullptr; mClosure = nullptr; mConnection = nullptr; } NS_IMETHODIMP nsLDAPOperation::GetMessageListener(nsILDAPMessageListener **aMessageListener) { if (!aMessageListener) { return NS_ERROR_ILLEGAL_VALUE; } *aMessageListener = mMessageListener; NS_IF_ADDREF(*aMessageListener); return NS_OK; } NS_IMETHODIMP nsLDAPOperation::SaslBind(const nsACString &service, const nsACString &mechanism, nsIAuthModule *authModule) { nsresult rv; nsAutoCString bindName; struct berval creds; unsigned int credlen; mAuthModule = authModule; mMechanism.Assign(mechanism); rv = mConnection->GetBindName(bindName); NS_ENSURE_SUCCESS(rv, rv); creds.bv_val = NULL; mAuthModule->Init(PromiseFlatCString(service).get(), nsIAuthModule::REQ_DEFAULT, nullptr, NS_ConvertUTF8toUTF16(bindName).get(), nullptr); rv = mAuthModule->GetNextToken(nullptr, 0, (void **)&creds.bv_val, &credlen); if (NS_FAILED(rv) || !creds.bv_val) return rv; creds.bv_len = credlen; const int lderrno = ldap_sasl_bind(mConnectionHandle, bindName.get(), mMechanism.get(), &creds, NULL, NULL, &mMsgID); free(creds.bv_val); if (lderrno != LDAP_SUCCESS) return TranslateLDAPErrorToNSError(lderrno); // make sure the connection knows where to call back once the messages // for this operation start coming in rv = mConnection->AddPendingOperation(mMsgID, this); if (NS_FAILED(rv)) (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); return rv; } NS_IMETHODIMP nsLDAPOperation::SaslStep(const char *token, uint32_t tokenLen) { nsresult rv; nsAutoCString bindName; struct berval clientCreds; struct berval serverCreds; unsigned int credlen; rv = mConnection->RemovePendingOperation(mMsgID); NS_ENSURE_SUCCESS(rv, rv); serverCreds.bv_val = (char *) token; serverCreds.bv_len = tokenLen; rv = mConnection->GetBindName(bindName); NS_ENSURE_SUCCESS(rv, rv); rv = mAuthModule->GetNextToken(serverCreds.bv_val, serverCreds.bv_len, (void **) &clientCreds.bv_val, &credlen); NS_ENSURE_SUCCESS(rv, rv); clientCreds.bv_len = credlen; const int lderrno = ldap_sasl_bind(mConnectionHandle, bindName.get(), mMechanism.get(), &clientCreds, NULL, NULL, &mMsgID); free(clientCreds.bv_val); if (lderrno != LDAP_SUCCESS) return TranslateLDAPErrorToNSError(lderrno); // make sure the connection knows where to call back once the messages // for this operation start coming in rv = mConnection->AddPendingOperation(mMsgID, this); if (NS_FAILED(rv)) (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); return rv; } // wrapper for ldap_simple_bind() // NS_IMETHODIMP nsLDAPOperation::SimpleBind(const nsACString& passwd) { RefPtr connection = mConnection; // There is a possibilty that mConnection can be cleared by another // thread. Grabbing a local reference to mConnection may avoid this. // See https://bugzilla.mozilla.org/show_bug.cgi?id=557928#c1 nsresult rv; nsAutoCString bindName; int32_t originalMsgID = mMsgID; // Ugly hack alert: // the first time we get called with a passwd, remember it. // Then, if we get called again w/o a password, use the // saved one. Getting called again means we're trying to // fall back to VERSION2. // Since LDAP operations are thrown away when done, it won't stay // around in memory. if (!passwd.IsEmpty()) mSavePassword = passwd; NS_PRECONDITION(mMessageListener, "MessageListener not set"); rv = connection->GetBindName(bindName); if (NS_FAILED(rv)) return rv; MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("nsLDAPOperation::SimpleBind(): called; bindName = '%s'; ", bindName.get())); // this (nsLDAPOperation) may be released by RemovePendingOperation() // See https://bugzilla.mozilla.org/show_bug.cgi?id=1063829. RefPtr kungFuDeathGrip = this; // If this is a second try at binding, remove the operation from pending ops // because msg id has changed... if (originalMsgID) connection->RemovePendingOperation(originalMsgID); mMsgID = ldap_simple_bind(mConnectionHandle, bindName.get(), mSavePassword.get()); if (mMsgID == -1) { // XXX Should NS_ERROR_LDAP_SERVER_DOWN cause a rebind here? return TranslateLDAPErrorToNSError(ldap_get_lderrno(mConnectionHandle, 0, 0)); } // make sure the connection knows where to call back once the messages // for this operation start coming in rv = connection->AddPendingOperation(mMsgID, this); switch (rv) { case NS_OK: break; // note that the return value of ldap_abandon_ext() is ignored, as // there's nothing useful to do with it case NS_ERROR_OUT_OF_MEMORY: (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); return NS_ERROR_OUT_OF_MEMORY; break; case NS_ERROR_UNEXPECTED: case NS_ERROR_ILLEGAL_VALUE: default: (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); return NS_ERROR_UNEXPECTED; } return NS_OK; } /** * Given an nsIArray of nsILDAPControls, return the appropriate * zero-terminated array of LDAPControls ready to pass in to the C SDK. */ static nsresult convertControlArray(nsIArray *aXpcomArray, LDAPControl ***aArray) { // get the size of the original array uint32_t length; nsresult rv = aXpcomArray->GetLength(&length); NS_ENSURE_SUCCESS(rv, rv); // don't allocate an array if someone passed us in an empty one if (!length) { *aArray = 0; return NS_OK; } // allocate a local array of the form understood by the C-SDK; // +1 is to account for the final null terminator. PR_Calloc is // is used so that ldap_controls_free will work anywhere during the // iteration LDAPControl **controls = static_cast (PR_Calloc(length+1, sizeof(LDAPControl))); // prepare to enumerate the array nsCOMPtr enumerator; rv = aXpcomArray->Enumerate(getter_AddRefs(enumerator)); NS_ENSURE_SUCCESS(rv, rv); bool moreElements; rv = enumerator->HasMoreElements(&moreElements); NS_ENSURE_SUCCESS(rv, rv); uint32_t i = 0; while (moreElements) { // get the next array element nsCOMPtr isupports; rv = enumerator->GetNext(getter_AddRefs(isupports)); if (NS_FAILED(rv)) { ldap_controls_free(controls); return rv; } nsCOMPtr control = do_QueryInterface(isupports, &rv); if (NS_FAILED(rv)) { ldap_controls_free(controls); return NS_ERROR_INVALID_ARG; // bogus element in the array } nsLDAPControl *ctl = static_cast (static_cast (control.get())); // convert it to an LDAPControl structure placed in the new array rv = ctl->ToLDAPControl(&controls[i]); if (NS_FAILED(rv)) { ldap_controls_free(controls); return rv; } // on to the next element rv = enumerator->HasMoreElements(&moreElements); if (NS_FAILED(rv)) { ldap_controls_free(controls); return NS_ERROR_UNEXPECTED; } ++i; } *aArray = controls; return NS_OK; } NS_IMETHODIMP nsLDAPOperation::SearchExt(const nsACString& aBaseDn, int32_t aScope, const nsACString& aFilter, const nsACString &aAttributes, PRIntervalTime aTimeOut, int32_t aSizeLimit) { if (!mMessageListener) { NS_ERROR("nsLDAPOperation::SearchExt(): mMessageListener not set"); return NS_ERROR_NOT_INITIALIZED; } // XXX add control logging MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("nsLDAPOperation::SearchExt(): called with aBaseDn = '%s'; " "aFilter = '%s'; aAttributes = %s; aSizeLimit = %d", PromiseFlatCString(aBaseDn).get(), PromiseFlatCString(aFilter).get(), PromiseFlatCString(aAttributes).get(), aSizeLimit)); LDAPControl **serverctls = 0; nsresult rv; if (mServerControls) { rv = convertControlArray(mServerControls, &serverctls); if (NS_FAILED(rv)) { MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Error, ("nsLDAPOperation::SearchExt(): error converting server " "control array: %x", rv)); return rv; } } LDAPControl **clientctls = 0; if (mClientControls) { rv = convertControlArray(mClientControls, &clientctls); if (NS_FAILED(rv)) { MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Error, ("nsLDAPOperation::SearchExt(): error converting client " "control array: %x", rv)); ldap_controls_free(serverctls); return rv; } } // Convert our comma separated string to one that the C-SDK will like, i.e. // convert to a char array and add a last NULL element. nsTArray attrArray; ParseString(aAttributes, ',', attrArray); char **attrs = nullptr; uint32_t origLength = attrArray.Length(); if (origLength) { attrs = static_cast (NS_Alloc((origLength + 1) * sizeof(char *))); if (!attrs) return NS_ERROR_OUT_OF_MEMORY; for (uint32_t i = 0; i < origLength; ++i) attrs[i] = ToNewCString(attrArray[i]); attrs[origLength] = 0; } // XXX deal with timeout here int retVal = ldap_search_ext(mConnectionHandle, PromiseFlatCString(aBaseDn).get(), aScope, PromiseFlatCString(aFilter).get(), attrs, 0, serverctls, clientctls, 0, aSizeLimit, &mMsgID); // clean up ldap_controls_free(serverctls); ldap_controls_free(clientctls); // The last entry is null, so no need to free that. NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(origLength, attrs); rv = TranslateLDAPErrorToNSError(retVal); NS_ENSURE_SUCCESS(rv, rv); // make sure the connection knows where to call back once the messages // for this operation start coming in // rv = mConnection->AddPendingOperation(mMsgID, this); if (NS_FAILED(rv)) { switch (rv) { case NS_ERROR_OUT_OF_MEMORY: (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); return NS_ERROR_OUT_OF_MEMORY; default: (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); NS_ERROR("nsLDAPOperation::SearchExt(): unexpected error in " "mConnection->AddPendingOperation"); return NS_ERROR_UNEXPECTED; } } return NS_OK; } NS_IMETHODIMP nsLDAPOperation::GetMessageID(int32_t *aMsgID) { if (!aMsgID) { return NS_ERROR_ILLEGAL_VALUE; } *aMsgID = mMsgID; return NS_OK; } // as far as I can tell from reading the LDAP C SDK code, abandoning something // that has already been abandoned does not return an error // NS_IMETHODIMP nsLDAPOperation::AbandonExt() { nsresult rv; nsresult retStatus = NS_OK; if (!mMessageListener || mMsgID == 0) { NS_ERROR("nsLDAPOperation::AbandonExt(): mMessageListener or " "mMsgId not initialized"); return NS_ERROR_NOT_INITIALIZED; } // XXX handle controls here if (mServerControls || mClientControls) { return NS_ERROR_NOT_IMPLEMENTED; } rv = TranslateLDAPErrorToNSError(ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0)); NS_ENSURE_SUCCESS(rv, rv); // try to remove it from the pendingOperations queue, if it's there. // even if something goes wrong here, the abandon() has already succeeded // succeeded (and there's nothing else the caller can reasonably do), // so we only pay attention to this in debug builds. // // check mConnection in case we're getting bit by // http://bugzilla.mozilla.org/show_bug.cgi?id=239729, wherein we // theorize that ::Clearing the operation is nulling out the mConnection // from another thread. if (mConnection) { rv = mConnection->RemovePendingOperation(mMsgID); if (NS_FAILED(rv)) { // XXXdmose should we keep AbandonExt from happening on multiple // threads at the same time? that's when this condition is most // likely to occur. i _think_ the LDAP C SDK is ok with this; need // to verify. // NS_WARNING("nsLDAPOperation::AbandonExt: " "mConnection->RemovePendingOperation(this) failed."); } } return retStatus; } NS_IMETHODIMP nsLDAPOperation::GetClientControls(nsIMutableArray **aControls) { NS_IF_ADDREF(*aControls = mClientControls); return NS_OK; } NS_IMETHODIMP nsLDAPOperation::SetClientControls(nsIMutableArray *aControls) { mClientControls = aControls; return NS_OK; } NS_IMETHODIMP nsLDAPOperation::GetServerControls(nsIMutableArray **aControls) { NS_IF_ADDREF(*aControls = mServerControls); return NS_OK; } NS_IMETHODIMP nsLDAPOperation::SetServerControls(nsIMutableArray *aControls) { mServerControls = aControls; return NS_OK; } // wrappers for ldap_add_ext // nsresult nsLDAPOperation::AddExt(const char *base, nsIArray *mods, LDAPControl **serverctrls, LDAPControl **clientctrls) { if (!mMessageListener) { NS_ERROR("nsLDAPOperation::AddExt(): mMessageListener not set"); return NS_ERROR_NOT_INITIALIZED; } LDAPMod **attrs = 0; int retVal = LDAP_SUCCESS; uint32_t modCount = 0; nsresult rv = mods->GetLength(&modCount); NS_ENSURE_SUCCESS(rv, rv); if (mods && modCount) { attrs = static_cast(moz_xmalloc((modCount + 1) * sizeof(LDAPMod *))); if (!attrs) { NS_ERROR("nsLDAPOperation::AddExt: out of memory "); return NS_ERROR_OUT_OF_MEMORY; } nsAutoCString type; uint32_t index; for (index = 0; index < modCount && NS_SUCCEEDED(rv); ++index) { attrs[index] = new LDAPMod(); if (!attrs[index]) return NS_ERROR_OUT_OF_MEMORY; nsCOMPtr modif(do_QueryElementAt(mods, index, &rv)); if (NS_FAILED(rv)) break; #ifdef NS_DEBUG int32_t operation; NS_ASSERTION(NS_SUCCEEDED(modif->GetOperation(&operation)) && ((operation & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD), "AddExt can only add."); #endif attrs[index]->mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES; nsresult rv = modif->GetType(type); if (NS_FAILED(rv)) break; attrs[index]->mod_type = ToNewCString(type); rv = CopyValues(modif, &attrs[index]->mod_bvalues); if (NS_FAILED(rv)) break; } if (NS_SUCCEEDED(rv)) { attrs[modCount] = 0; retVal = ldap_add_ext(mConnectionHandle, base, attrs, serverctrls, clientctrls, &mMsgID); } else // reset the modCount so we correctly free the array. modCount = index; } for (uint32_t counter = 0; counter < modCount; ++counter) delete attrs[counter]; free(attrs); return NS_FAILED(rv) ? rv : TranslateLDAPErrorToNSError(retVal); } /** * wrapper for ldap_add_ext(): kicks off an async add request. * * @param aBaseDn Base DN to search * @param aModCount Number of modifications * @param aMods Array of modifications * * XXX doesn't currently handle LDAPControl params * * void addExt (in AUTF8String aBaseDn, in unsigned long aModCount, * [array, size_is (aModCount)] in nsILDAPModification aMods); */ NS_IMETHODIMP nsLDAPOperation::AddExt(const nsACString& aBaseDn, nsIArray *aMods) { MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("nsLDAPOperation::AddExt(): called with aBaseDn = '%s'", PromiseFlatCString(aBaseDn).get())); nsresult rv = AddExt(PromiseFlatCString(aBaseDn).get(), aMods, 0, 0); if (NS_FAILED(rv)) return rv; // make sure the connection knows where to call back once the messages // for this operation start coming in rv = mConnection->AddPendingOperation(mMsgID, this); if (NS_FAILED(rv)) { (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("nsLDAPOperation::AddExt(): abandoned due to rv %x", rv)); } return rv; } // wrappers for ldap_delete_ext // nsresult nsLDAPOperation::DeleteExt(const char *base, LDAPControl **serverctrls, LDAPControl **clientctrls) { if (!mMessageListener) { NS_ERROR("nsLDAPOperation::DeleteExt(): mMessageListener not set"); return NS_ERROR_NOT_INITIALIZED; } return TranslateLDAPErrorToNSError(ldap_delete_ext(mConnectionHandle, base, serverctrls, clientctrls, &mMsgID)); } /** * wrapper for ldap_delete_ext(): kicks off an async delete request. * * @param aBaseDn Base DN to delete * * XXX doesn't currently handle LDAPControl params * * void deleteExt(in AUTF8String aBaseDn); */ NS_IMETHODIMP nsLDAPOperation::DeleteExt(const nsACString& aBaseDn) { MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("nsLDAPOperation::DeleteExt(): called with aBaseDn = '%s'", PromiseFlatCString(aBaseDn).get())); nsresult rv = DeleteExt(PromiseFlatCString(aBaseDn).get(), 0, 0); if (NS_FAILED(rv)) return rv; // make sure the connection knows where to call back once the messages // for this operation start coming in rv = mConnection->AddPendingOperation(mMsgID, this); if (NS_FAILED(rv)) { (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("nsLDAPOperation::AddExt(): abandoned due to rv %x", rv)); } return rv; } // wrappers for ldap_modify_ext // nsresult nsLDAPOperation::ModifyExt(const char *base, nsIArray *mods, LDAPControl **serverctrls, LDAPControl **clientctrls) { if (!mMessageListener) { NS_ERROR("nsLDAPOperation::ModifyExt(): mMessageListener not set"); return NS_ERROR_NOT_INITIALIZED; } LDAPMod **attrs = 0; int retVal = 0; uint32_t modCount = 0; nsresult rv = mods->GetLength(&modCount); NS_ENSURE_SUCCESS(rv, rv); if (modCount && mods) { attrs = static_cast(moz_xmalloc((modCount + 1) * sizeof(LDAPMod *))); if (!attrs) { NS_ERROR("nsLDAPOperation::ModifyExt: out of memory "); return NS_ERROR_OUT_OF_MEMORY; } nsAutoCString type; uint32_t index; for (index = 0; index < modCount && NS_SUCCEEDED(rv); ++index) { attrs[index] = new LDAPMod(); if (!attrs[index]) return NS_ERROR_OUT_OF_MEMORY; nsCOMPtr modif(do_QueryElementAt(mods, index, &rv)); if (NS_FAILED(rv)) break; int32_t operation; nsresult rv = modif->GetOperation(&operation); if (NS_FAILED(rv)) break; attrs[index]->mod_op = operation | LDAP_MOD_BVALUES; rv = modif->GetType(type); if (NS_FAILED(rv)) break; attrs[index]->mod_type = ToNewCString(type); rv = CopyValues(modif, &attrs[index]->mod_bvalues); if (NS_FAILED(rv)) break; } if (NS_SUCCEEDED(rv)) { attrs[modCount] = 0; retVal = ldap_modify_ext(mConnectionHandle, base, attrs, serverctrls, clientctrls, &mMsgID); } else // reset the modCount so we correctly free the array. modCount = index; } for (uint32_t counter = 0; counter < modCount; ++counter) delete attrs[counter]; free(attrs); return NS_FAILED(rv) ? rv : TranslateLDAPErrorToNSError(retVal); } /** * wrapper for ldap_modify_ext(): kicks off an async modify request. * * @param aBaseDn Base DN to modify * @param aModCount Number of modifications * @param aMods Array of modifications * * XXX doesn't currently handle LDAPControl params * * void modifyExt (in AUTF8String aBaseDn, in unsigned long aModCount, * [array, size_is (aModCount)] in nsILDAPModification aMods); */ NS_IMETHODIMP nsLDAPOperation::ModifyExt(const nsACString& aBaseDn, nsIArray *aMods) { MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("nsLDAPOperation::ModifyExt(): called with aBaseDn = '%s'", PromiseFlatCString(aBaseDn).get())); nsresult rv = ModifyExt(PromiseFlatCString(aBaseDn).get(), aMods, 0, 0); if (NS_FAILED(rv)) return rv; // make sure the connection knows where to call back once the messages // for this operation start coming in rv = mConnection->AddPendingOperation(mMsgID, this); if (NS_FAILED(rv)) { (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("nsLDAPOperation::AddExt(): abandoned due to rv %x", rv)); } return rv; } // wrappers for ldap_rename // nsresult nsLDAPOperation::Rename(const char *base, const char *newRDn, const char *newParent, bool deleteOldRDn, LDAPControl **serverctrls, LDAPControl **clientctrls) { if (!mMessageListener) { NS_ERROR("nsLDAPOperation::Rename(): mMessageListener not set"); return NS_ERROR_NOT_INITIALIZED; } return TranslateLDAPErrorToNSError(ldap_rename(mConnectionHandle, base, newRDn, newParent, deleteOldRDn, serverctrls, clientctrls, &mMsgID)); } /** * wrapper for ldap_rename(): kicks off an async rename request. * * @param aBaseDn Base DN to rename * @param aNewRDn New relative DN * @param aNewParent DN of the new parent under which to move the * * XXX doesn't currently handle LDAPControl params * * void rename(in AUTF8String aBaseDn, in AUTF8String aNewRDn, * in AUTF8String aNewParent, in boolean aDeleteOldRDn); */ NS_IMETHODIMP nsLDAPOperation::Rename(const nsACString& aBaseDn, const nsACString& aNewRDn, const nsACString& aNewParent, bool aDeleteOldRDn) { MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("nsLDAPOperation::Rename(): called with aBaseDn = '%s'", PromiseFlatCString(aBaseDn).get())); nsresult rv = Rename(PromiseFlatCString(aBaseDn).get(), PromiseFlatCString(aNewRDn).get(), PromiseFlatCString(aNewParent).get(), aDeleteOldRDn, 0, 0); if (NS_FAILED(rv)) return rv; // make sure the connection knows where to call back once the messages // for this operation start coming in rv = mConnection->AddPendingOperation(mMsgID, this); if (NS_FAILED(rv)) { (void)ldap_abandon_ext(mConnectionHandle, mMsgID, 0, 0); MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("nsLDAPOperation::AddExt(): abandoned due to rv %x", rv)); } return rv; } // wrappers for ldap_search_ext // /* static */ nsresult nsLDAPOperation::CopyValues(nsILDAPModification* aMod, berval*** aBValues) { nsCOMPtr values; nsresult rv = aMod->GetValues(getter_AddRefs(values)); NS_ENSURE_SUCCESS(rv, rv); uint32_t valuesCount; rv = values->GetLength(&valuesCount); NS_ENSURE_SUCCESS(rv, rv); *aBValues = static_cast (moz_xmalloc((valuesCount + 1) * sizeof(berval *))); if (!*aBValues) return NS_ERROR_OUT_OF_MEMORY; uint32_t valueIndex; for (valueIndex = 0; valueIndex < valuesCount; ++valueIndex) { nsCOMPtr value(do_QueryElementAt(values, valueIndex, &rv)); berval* bval = new berval; if (NS_FAILED(rv) || !bval) { for (uint32_t counter = 0; counter < valueIndex && counter < valuesCount; ++counter) delete (*aBValues)[valueIndex]; free(*aBValues); delete bval; return NS_ERROR_OUT_OF_MEMORY; } value->Get((uint32_t*)&bval->bv_len, (uint8_t**)&bval->bv_val); (*aBValues)[valueIndex] = bval; } (*aBValues)[valuesCount] = 0; return NS_OK; }