summaryrefslogtreecommitdiffstats
path: root/ldap/xpcom/src/nsLDAPConnection.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/xpcom/src/nsLDAPConnection.cpp')
-rw-r--r--ldap/xpcom/src/nsLDAPConnection.cpp737
1 files changed, 737 insertions, 0 deletions
diff --git a/ldap/xpcom/src/nsLDAPConnection.cpp b/ldap/xpcom/src/nsLDAPConnection.cpp
new file mode 100644
index 000000000..834e803b4
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPConnection.cpp
@@ -0,0 +1,737 @@
+/* -*- 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 "nsIServiceManager.h"
+#include "nsStringGlue.h"
+#include "nsIComponentManager.h"
+#include "nsIDNSRecord.h"
+#include "nsLDAPConnection.h"
+#include "nsLDAPMessage.h"
+#include "nsThreadUtils.h"
+#include "nsIConsoleService.h"
+#include "nsIDNSService.h"
+#include "nsINetAddr.h"
+#include "nsIRequestObserver.h"
+#include "nsError.h"
+#include "nsLDAPOperation.h"
+#include "nsILDAPErrors.h"
+#include "nsIClassInfoImpl.h"
+#include "nsILDAPURL.h"
+#include "nsIObserverService.h"
+#include "mozilla/Services.h"
+#include "nsMemory.h"
+#include "nsLDAPUtils.h"
+#include "nsProxyRelease.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Attributes.h"
+
+using namespace mozilla;
+
+const char kDNSServiceContractId[] = "@mozilla.org/network/dns-service;1";
+
+// constructor
+//
+nsLDAPConnection::nsLDAPConnection()
+ : mConnectionHandle(nullptr),
+ mPendingOperationsMutex("nsLDAPConnection.mPendingOperationsMutex"),
+ mPendingOperations(10),
+ mSSL(false),
+ mVersion(nsILDAPConnection::VERSION3),
+ mDNSRequest(nullptr)
+{
+}
+
+// destructor
+//
+nsLDAPConnection::~nsLDAPConnection()
+{
+ nsCOMPtr<nsIObserverService> obsServ =
+ do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
+ if (obsServ)
+ obsServ->RemoveObserver(this, "profile-change-net-teardown");
+ Close();
+}
+
+NS_IMPL_ADDREF(nsLDAPConnection)
+NS_IMPL_RELEASE(nsLDAPConnection)
+NS_IMPL_CLASSINFO(nsLDAPConnection, NULL, nsIClassInfo::THREADSAFE,
+ NS_LDAPCONNECTION_CID)
+
+NS_INTERFACE_MAP_BEGIN(nsLDAPConnection)
+ NS_INTERFACE_MAP_ENTRY(nsILDAPConnection)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+ NS_INTERFACE_MAP_ENTRY(nsIDNSListener)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsILDAPConnection)
+ NS_IMPL_QUERY_CLASSINFO(nsLDAPConnection)
+NS_INTERFACE_MAP_END_THREADSAFE
+NS_IMPL_CI_INTERFACE_GETTER(nsLDAPConnection, nsILDAPConnection,
+ nsISupportsWeakReference, nsIDNSListener,
+ nsIObserver)
+
+NS_IMETHODIMP
+nsLDAPConnection::Init(nsILDAPURL *aUrl, const nsACString &aBindName,
+ nsILDAPMessageListener *aMessageListener,
+ nsISupports *aClosure, uint32_t aVersion)
+{
+ NS_ENSURE_ARG_POINTER(aUrl);
+ NS_ENSURE_ARG_POINTER(aMessageListener);
+
+ nsresult rv;
+ nsCOMPtr<nsIObserverService> obsServ =
+ do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // We have to abort all LDAP pending operation before shutdown.
+ obsServ->AddObserver(this, "profile-change-net-teardown", true);
+
+ // Save various items that we'll use later
+ mBindName.Assign(aBindName);
+ mClosure = aClosure;
+ mInitListener = aMessageListener;
+
+ // Make sure we haven't called Init earlier, i.e. there's a DNS
+ // request pending.
+ NS_ASSERTION(!mDNSRequest, "nsLDAPConnection::Init() "
+ "Connection was already initialized\n");
+
+ // Check and save the version number
+ if (aVersion != nsILDAPConnection::VERSION2 &&
+ aVersion != nsILDAPConnection::VERSION3) {
+ NS_ERROR("nsLDAPConnection::Init(): illegal version");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ mVersion = aVersion;
+
+ // Get the port number, SSL flag for use later, once the DNS server(s)
+ // has resolved the host part.
+ rv = aUrl->GetPort(&mPort);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t options;
+ rv = aUrl->GetOptions(&options);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mSSL = options & nsILDAPURL::OPT_SECURE;
+
+ nsCOMPtr<nsIThread> curThread = do_GetCurrentThread();
+ if (!curThread) {
+ NS_ERROR("nsLDAPConnection::Init(): couldn't get current thread");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Do the pre-resolve of the hostname, using the DNS service. This
+ // will also initialize the LDAP connection properly, once we have
+ // the IPs resolved for the hostname. This includes creating the
+ // new thread for this connection.
+ //
+ // XXX - What return codes can we expect from the DNS service?
+ //
+ nsCOMPtr<nsIDNSService>
+ pDNSService(do_GetService(kDNSServiceContractId, &rv));
+
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPConnection::Init(): couldn't create the DNS Service object");
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = aUrl->GetAsciiHost(mDNSHost);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // if the caller has passed in a space-delimited set of hosts, as the
+ // ldap c-sdk allows, strip off the trailing hosts for now.
+ // Soon, we'd like to make multiple hosts work, but now make
+ // at least the first one work.
+ LdapCompressWhitespace(mDNSHost);
+
+ int32_t spacePos = mDNSHost.FindChar(' ');
+ // trim off trailing host(s)
+ if (spacePos != kNotFound)
+ mDNSHost.SetLength(spacePos);
+
+ rv = pDNSService->AsyncResolve(mDNSHost, 0, this, curThread,
+ getter_AddRefs(mDNSRequest));
+
+ if (NS_FAILED(rv)) {
+ switch (rv) {
+ case NS_ERROR_OUT_OF_MEMORY:
+ case NS_ERROR_UNKNOWN_HOST:
+ case NS_ERROR_FAILURE:
+ case NS_ERROR_OFFLINE:
+ break;
+
+ default:
+ rv = NS_ERROR_UNEXPECTED;
+ }
+ mDNSHost.Truncate();
+ }
+ return rv;
+}
+
+// this might get exposed to clients, so we've broken it
+// out of the destructor.
+void
+nsLDAPConnection::Close()
+{
+ int rc;
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("unbinding\n"));
+
+ if (mConnectionHandle) {
+ // note that the ldap_unbind() call in the 5.0 version of the LDAP C SDK
+ // appears to be exactly identical to ldap_unbind_s(), so it may in fact
+ // still be synchronous
+ //
+ rc = ldap_unbind(mConnectionHandle);
+#ifdef PR_LOGGING
+ if (rc != LDAP_SUCCESS) {
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Warning,
+ ("nsLDAPConnection::Close(): %s\n",
+ ldap_err2string(rc)));
+ }
+#endif
+ mConnectionHandle = nullptr;
+ }
+
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("unbound\n"));
+
+ NS_ASSERTION(!mThread || NS_SUCCEEDED(mThread->Shutdown()),
+ "Failed to shutdown thread cleanly");
+
+ // Cancel the DNS lookup if needed, and also drop the reference to the
+ // Init listener (if still there).
+ //
+ if (mDNSRequest) {
+ mDNSRequest->Cancel(NS_ERROR_ABORT);
+ mDNSRequest = nullptr;
+ }
+ mInitListener = nullptr;
+
+}
+
+NS_IMETHODIMP
+nsLDAPConnection::Observe(nsISupports *aSubject, const char *aTopic,
+ const char16_t *aData)
+{
+ if (!strcmp(aTopic, "profile-change-net-teardown")) {
+ // Abort all ldap requests.
+ /* We cannot use enumerate function to abort operations because
+ * nsILDAPOperation::AbandonExt() is modifying list of operations
+ * and this leads to starvation.
+ * We have to do a copy of pending operations.
+ */
+ nsTArray<nsILDAPOperation*> pending_operations;
+ {
+ MutexAutoLock lock(mPendingOperationsMutex);
+ for (auto iter = mPendingOperations.Iter(); !iter.Done(); iter.Next()) {
+ pending_operations.AppendElement(iter.UserData());
+ }
+ }
+ for (uint32_t i = 0; i < pending_operations.Length(); i++) {
+ pending_operations[i]->AbandonExt();
+ }
+ Close();
+ } else {
+ NS_NOTREACHED("unexpected topic");
+ return NS_ERROR_UNEXPECTED;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPConnection::GetClosure(nsISupports **_retval)
+{
+ if (!_retval) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+ NS_IF_ADDREF(*_retval = mClosure);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPConnection::SetClosure(nsISupports *aClosure)
+{
+ mClosure = aClosure;
+ return NS_OK;
+}
+
+// who we're binding as
+//
+// readonly attribute AUTF8String bindName
+//
+NS_IMETHODIMP
+nsLDAPConnection::GetBindName(nsACString& _retval)
+{
+ _retval.Assign(mBindName);
+ return NS_OK;
+}
+
+// wrapper for ldap_get_lderrno
+// XXX should copy before returning
+//
+NS_IMETHODIMP
+nsLDAPConnection::GetLdErrno(nsACString& matched, nsACString& errString,
+ int32_t *_retval)
+{
+ char *match, *err;
+
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ *_retval = ldap_get_lderrno(mConnectionHandle, &match, &err);
+ matched.Assign(match);
+ errString.Assign(err);
+ return NS_OK;
+}
+
+// return the error string corresponding to GetLdErrno.
+//
+// XXX - deal with optional params
+// XXX - how does ldap_perror know to look at the global errno?
+//
+NS_IMETHODIMP
+nsLDAPConnection::GetErrorString(char16_t **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ // get the error string
+ //
+ char *rv = ldap_err2string(ldap_get_lderrno(mConnectionHandle, 0, 0));
+ if (!rv) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // make a copy using the XPCOM shared allocator
+ //
+ *_retval = ToNewUnicode(NS_ConvertUTF8toUTF16(rv));
+ if (!*_retval) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ return NS_OK;
+}
+
+/**
+ * Add an nsILDAPOperation to the list of operations pending on
+ * this connection. This is also mainly intended for use by the
+ * nsLDAPOperation code.
+ */
+nsresult
+nsLDAPConnection::AddPendingOperation(uint32_t aOperationID, nsILDAPOperation *aOperation)
+{
+ NS_ENSURE_ARG_POINTER(aOperation);
+
+ nsIRunnable* runnable = new nsLDAPConnectionRunnable(aOperationID, aOperation,
+ this);
+ {
+ MutexAutoLock lock(mPendingOperationsMutex);
+ mPendingOperations.Put((uint32_t)aOperationID, aOperation);
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("pending operation added; total pending operations now = %d\n",
+ mPendingOperations.Count()));
+ }
+
+ nsresult rv;
+ if (!mThread)
+ {
+ rv = NS_NewThread(getter_AddRefs(mThread), runnable);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else
+ {
+ rv = mThread->Dispatch(runnable, nsIEventTarget::DISPATCH_NORMAL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+/**
+ * Remove an nsILDAPOperation from the list of operations pending on this
+ * connection. Mainly intended for use by the nsLDAPOperation code.
+ *
+ * @param aOperation operation to add
+ * @exception NS_ERROR_INVALID_POINTER aOperation was NULL
+ * @exception NS_ERROR_OUT_OF_MEMORY out of memory
+ * @exception NS_ERROR_FAILURE could not delete the operation
+ *
+ * void removePendingOperation(in nsILDAPOperation aOperation);
+ */
+nsresult
+nsLDAPConnection::RemovePendingOperation(uint32_t aOperationID)
+{
+ NS_ENSURE_TRUE(aOperationID > 0, NS_ERROR_UNEXPECTED);
+
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("nsLDAPConnection::RemovePendingOperation(): operation removed\n"));
+
+ {
+ MutexAutoLock lock(mPendingOperationsMutex);
+ mPendingOperations.Remove(aOperationID);
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("nsLDAPConnection::RemovePendingOperation(): operation "
+ "removed; total pending operations now = %d\n",
+ mPendingOperations.Count()));
+ }
+
+ return NS_OK;
+}
+
+class nsOnLDAPMessageRunnable : public Runnable
+{
+public:
+ nsOnLDAPMessageRunnable(nsLDAPMessage *aMsg, bool aClear)
+ : m_msg(aMsg)
+ , m_clear(aClear)
+ {}
+ NS_DECL_NSIRUNNABLE
+private:
+ RefPtr<nsLDAPMessage> m_msg;
+ bool m_clear;
+};
+
+NS_IMETHODIMP nsOnLDAPMessageRunnable::Run()
+{
+ // get the message listener object.
+ nsLDAPOperation *nsoperation = static_cast<nsLDAPOperation *>(m_msg->mOperation.get());
+ nsCOMPtr<nsILDAPMessageListener> listener;
+ nsresult rv = nsoperation->GetMessageListener(getter_AddRefs(listener));
+
+ if (m_clear)
+ {
+ // try to break cycles
+ nsoperation->Clear();
+ }
+
+ if (!listener)
+ {
+ NS_ERROR("nsLDAPConnection::InvokeMessageCallback(): probable "
+ "memory corruption: GetMessageListener() returned nullptr");
+ return rv;
+ }
+
+ return listener->OnLDAPMessage(m_msg);
+}
+
+nsresult
+nsLDAPConnection::InvokeMessageCallback(LDAPMessage *aMsgHandle,
+ nsILDAPMessage *aMsg,
+ int32_t aOperation,
+ bool aRemoveOpFromConnQ)
+{
+#if defined(DEBUG)
+ // We only want this being logged for debug builds so as not to affect performance too much.
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, ("InvokeMessageCallback entered\n"));
+#endif
+
+ // Get the operation.
+ nsCOMPtr<nsILDAPOperation> operation;
+ {
+ MutexAutoLock lock(mPendingOperationsMutex);
+ mPendingOperations.Get((uint32_t)aOperation, getter_AddRefs(operation));
+ }
+
+ NS_ENSURE_TRUE(operation, NS_ERROR_NULL_POINTER);
+
+ nsLDAPMessage *msg = static_cast<nsLDAPMessage *>(aMsg);
+ msg->mOperation = operation;
+
+ // proxy the listener callback to the ui thread.
+ RefPtr<nsOnLDAPMessageRunnable> runnable =
+ new nsOnLDAPMessageRunnable(msg, aRemoveOpFromConnQ);
+ // invoke the callback
+ NS_DispatchToMainThread(runnable);
+
+ // if requested (ie the operation is done), remove the operation
+ // from the connection queue.
+ if (aRemoveOpFromConnQ)
+ {
+ MutexAutoLock lock(mPendingOperationsMutex);
+ mPendingOperations.Remove(aOperation);
+
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("pending operation removed; total pending operations now ="
+ " %d\n", mPendingOperations.Count()));
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPConnection::OnLookupComplete(nsICancelable *aRequest,
+ nsIDNSRecord *aRecord,
+ nsresult aStatus)
+{
+ nsresult rv = NS_OK;
+
+ if (aRecord) {
+ // Build mResolvedIP list
+ //
+ mResolvedIP.Truncate();
+
+ int32_t index = 0;
+ nsCString addrbuf;
+ nsCOMPtr<nsINetAddr> addr;
+
+ while (NS_SUCCEEDED(aRecord->GetScriptableNextAddr(0, getter_AddRefs(addr)))) {
+ // We can only use v4 addresses
+ //
+ uint16_t family = 0;
+ bool v4mapped = false;
+ addr->GetFamily(&family);
+ addr->GetIsV4Mapped(&v4mapped);
+ if (family == nsINetAddr::FAMILY_INET || v4mapped) {
+ // If there are more IPs in the list, we separate them with
+ // a space, as supported/used by the LDAP C-SDK.
+ //
+ if (index++)
+ mResolvedIP.Append(' ');
+
+ // Convert the IPv4 address to a string, and append it to our
+ // list of IPs. Strip leading '::FFFF:' (the IPv4-mapped-IPv6
+ // indicator) if present.
+ //
+ addr->GetAddress(addrbuf);
+ if (addrbuf[0] == ':' && addrbuf.Length() > 7)
+ mResolvedIP.Append(Substring(addrbuf, 7));
+ else
+ mResolvedIP.Append(addrbuf);
+ }
+ }
+ }
+
+ if (NS_FAILED(aStatus)) {
+ // The DNS service failed, lets pass something reasonable
+ // back to the listener.
+ //
+ switch (aStatus) {
+ case NS_ERROR_OUT_OF_MEMORY:
+ case NS_ERROR_UNKNOWN_HOST:
+ case NS_ERROR_FAILURE:
+ case NS_ERROR_OFFLINE:
+ rv = aStatus;
+ break;
+
+ default:
+ rv = NS_ERROR_UNEXPECTED;
+ break;
+ }
+ } else if (!mResolvedIP.Length()) {
+ // We have no host resolved, that is very bad, and should most
+ // likely have been caught earlier.
+ //
+ NS_ERROR("nsLDAPConnection::OnStopLookup(): the resolved IP "
+ "string is empty.\n");
+
+ rv = NS_ERROR_UNKNOWN_HOST;
+ } else {
+ // We've got the IP(s) for the hostname, now lets setup the
+ // LDAP connection using this information. Note that if the
+ // LDAP server returns a referral, the C-SDK will perform a
+ // new, synchronous DNS lookup, which might hang (but hopefully
+ // if we've come this far, DNS is working properly).
+ //
+ mConnectionHandle = ldap_init(mResolvedIP.get(),
+ mPort == -1 ?
+ (mSSL ? LDAPS_PORT : LDAP_PORT) : mPort);
+ // Check that we got a proper connection, and if so, setup the
+ // threading functions for this connection.
+ //
+ if ( !mConnectionHandle ) {
+ rv = NS_ERROR_FAILURE; // LDAP C SDK API gives no useful error
+ } else {
+#if defined(DEBUG_dmose) || defined(DEBUG_bienvenu)
+ const int lDebug = 0;
+ ldap_set_option(mConnectionHandle, LDAP_OPT_DEBUG_LEVEL, &lDebug);
+#endif
+
+ // the C SDK currently defaults to v2. if we're to use v3,
+ // tell it so.
+ //
+ int version;
+ switch (mVersion) {
+ case 2:
+ break;
+ case 3:
+ version = LDAP_VERSION3;
+ ldap_set_option(mConnectionHandle, LDAP_OPT_PROTOCOL_VERSION,
+ &version);
+ break;
+ default:
+ NS_ERROR("nsLDAPConnection::OnLookupComplete(): mVersion"
+ " invalid");
+ }
+
+ // This code sets up the current connection to use PSM for SSL
+ // functionality. Making this use libssldap instead for
+ // non-browser user shouldn't be hard.
+
+ extern nsresult nsLDAPInstallSSL(LDAP *ld, const char *aHostName);
+
+ if (mSSL) {
+ if (ldap_set_option(mConnectionHandle, LDAP_OPT_SSL,
+ LDAP_OPT_ON) != LDAP_SUCCESS ) {
+ NS_ERROR("nsLDAPConnection::OnStopLookup(): Error"
+ " configuring connection to use SSL");
+ rv = NS_ERROR_UNEXPECTED;
+ }
+
+ rv = nsLDAPInstallSSL(mConnectionHandle, mDNSHost.get());
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPConnection::OnStopLookup(): Error"
+ " installing secure LDAP routines for"
+ " connection");
+ }
+ }
+ }
+ }
+
+ // Drop the DNS request object, we no longer need it, and set the flag
+ // indicating that DNS has finished.
+ //
+ mDNSRequest = nullptr;
+ mDNSHost.Truncate();
+
+ // Call the listener, and then we can release our reference to it.
+ //
+ mInitListener->OnLDAPInit(this, rv);
+ mInitListener = nullptr;
+
+ return rv;
+}
+
+nsLDAPConnectionRunnable::nsLDAPConnectionRunnable(int32_t aOperationID,
+ nsILDAPOperation *aOperation,
+ nsLDAPConnection *aConnection)
+ : mOperationID(aOperationID), mConnection(aConnection)
+{
+}
+
+nsLDAPConnectionRunnable::~nsLDAPConnectionRunnable()
+{
+ if (mConnection) {
+ NS_ReleaseOnMainThread(mConnection.forget());
+ }
+}
+
+NS_IMPL_ISUPPORTS(nsLDAPConnectionRunnable, nsIRunnable)
+
+NS_IMETHODIMP nsLDAPConnectionRunnable::Run()
+{
+ if (!mOperationID) {
+ NS_ERROR("mOperationID is null");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ LDAPMessage *msgHandle;
+ bool operationFinished = true;
+ RefPtr<nsLDAPMessage> msg;
+
+ struct timeval timeout = { 0, 0 };
+
+ nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
+ int32_t returnCode = ldap_result(mConnection->mConnectionHandle, mOperationID, LDAP_MSG_ONE, &timeout, &msgHandle);
+ switch (returnCode)
+ {
+ // timeout
+ case 0:
+ // XXX do we need a timer?
+ return thread->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
+ case -1:
+ NS_ERROR("We don't know what went wrong with the ldap operation");
+ return NS_ERROR_FAILURE;
+
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ // XXX what should we do with LDAP_RES_SEARCH_EXTENDED
+ operationFinished = false;
+ MOZ_FALLTHROUGH;
+ default:
+ {
+ msg = new nsLDAPMessage;
+ if (!msg)
+ return NS_ERROR_NULL_POINTER;
+
+ // initialize the message, using a protected method not available
+ // through nsILDAPMessage (which is why we need the raw pointer)
+ nsresult rv = msg->Init(mConnection, msgHandle);
+
+ switch (rv)
+ {
+ case NS_OK:
+ {
+ int32_t errorCode;
+ msg->GetErrorCode(&errorCode);
+
+ // maybe a version error, e.g., using v3 on a v2 server.
+ // if we're using v3, try v2.
+ if (errorCode == LDAP_PROTOCOL_ERROR &&
+ mConnection->mVersion == nsILDAPConnection::VERSION3)
+ {
+ nsAutoCString password;
+ mConnection->mVersion = nsILDAPConnection::VERSION2;
+ ldap_set_option(mConnection->mConnectionHandle,
+ LDAP_OPT_PROTOCOL_VERSION, &mConnection->mVersion);
+
+ if (NS_SUCCEEDED(rv))
+ {
+ // We don't want to notify callers that we are done, so
+ // redispatch the runnable.
+ // XXX do we need a timer?
+ rv = thread->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return NS_OK;
+ }
+ }
+ // If we're midway through a SASL Bind, we need to continue
+ // without letting our caller know what we're up to!
+ //
+ if (errorCode == LDAP_SASL_BIND_IN_PROGRESS) {
+ struct berval *creds;
+ ldap_parse_sasl_bind_result(
+ mConnection->mConnectionHandle, msgHandle,
+ &creds, 0);
+
+ nsCOMPtr<nsILDAPOperation> operation;
+ {
+ MutexAutoLock lock(mConnection->mPendingOperationsMutex);
+ mConnection->mPendingOperations.Get((uint32_t)mOperationID, getter_AddRefs(operation));
+ }
+
+ NS_ENSURE_TRUE(operation, NS_ERROR_NULL_POINTER);
+
+ nsresult rv = operation->SaslStep(creds->bv_val, creds->bv_len);
+ if (NS_SUCCEEDED(rv))
+ return NS_OK;
+ }
+ break;
+ }
+ // Error code handling in here
+ default:
+ return NS_OK;
+ }
+
+ // invoke the callback on the nsILDAPOperation corresponding to
+ // this message
+ rv = mConnection->InvokeMessageCallback(msgHandle, msg, mOperationID,
+ operationFinished);
+ if (NS_FAILED(rv))
+ {
+ NS_ERROR("CheckLDAPOperationResult(): error invoking message"
+ " callback");
+ // punt and hope things work out better next time around
+ return NS_OK;
+ }
+
+ if (!operationFinished)
+ {
+ // XXX do we need a timer?
+ rv = thread->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ break;
+ }
+ }
+ return NS_OK;
+}