diff options
Diffstat (limited to 'ldap/xpcom/src/nsLDAPOperation.cpp')
-rw-r--r-- | ldap/xpcom/src/nsLDAPOperation.cpp | 975 |
1 files changed, 975 insertions, 0 deletions
diff --git a/ldap/xpcom/src/nsLDAPOperation.cpp b/ldap/xpcom/src/nsLDAPOperation.cpp new file mode 100644 index 000000000..72cf32089 --- /dev/null +++ b/ldap/xpcom/src/nsLDAPOperation.cpp @@ -0,0 +1,975 @@ +/* -*- 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<nsLDAPConnection*>(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<nsLDAPConnection> 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<nsLDAPOperation> 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<LDAPControl **> + (PR_Calloc(length+1, sizeof(LDAPControl))); + + // prepare to enumerate the array + nsCOMPtr<nsISimpleEnumerator> 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<nsISupports> isupports; + rv = enumerator->GetNext(getter_AddRefs(isupports)); + if (NS_FAILED(rv)) { + ldap_controls_free(controls); + return rv; + } + nsCOMPtr<nsILDAPControl> 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<nsLDAPControl *> + (static_cast<nsILDAPControl *> + (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<nsCString> attrArray; + ParseString(aAttributes, ',', attrArray); + char **attrs = nullptr; + uint32_t origLength = attrArray.Length(); + if (origLength) + { + attrs = static_cast<char **> (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<LDAPMod **>(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<nsILDAPModification> 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<LDAPMod **>(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<nsILDAPModification> 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<nsIArray> 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<berval **> + (moz_xmalloc((valuesCount + 1) * + sizeof(berval *))); + if (!*aBValues) + return NS_ERROR_OUT_OF_MEMORY; + + uint32_t valueIndex; + for (valueIndex = 0; valueIndex < valuesCount; ++valueIndex) { + nsCOMPtr<nsILDAPBERValue> 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; +} |