summaryrefslogtreecommitdiffstats
path: root/ldap/xpcom/src
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2019-11-03 00:17:46 -0400
committerMatt A. Tobin <email@mattatobin.com>2019-11-03 00:17:46 -0400
commit302bf1b523012e11b60425d6eee1221ebc2724eb (patch)
treeb191a895f8716efcbe42f454f37597a545a6f421 /ldap/xpcom/src
parent21b3f6247403c06f85e1f45d219f87549862198f (diff)
downloadUXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.gz
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.lz
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.xz
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.zip
Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1
Diffstat (limited to 'ldap/xpcom/src')
-rw-r--r--ldap/xpcom/src/ldapComponents.manifest5
-rw-r--r--ldap/xpcom/src/moz.build53
-rw-r--r--ldap/xpcom/src/nsLDAPBERElement.cpp117
-rw-r--r--ldap/xpcom/src/nsLDAPBERElement.h30
-rw-r--r--ldap/xpcom/src/nsLDAPBERValue.cpp106
-rw-r--r--ldap/xpcom/src/nsLDAPBERValue.h40
-rw-r--r--ldap/xpcom/src/nsLDAPConnection.cpp737
-rw-r--r--ldap/xpcom/src/nsLDAPConnection.h139
-rw-r--r--ldap/xpcom/src/nsLDAPControl.cpp122
-rw-r--r--ldap/xpcom/src/nsLDAPControl.h42
-rw-r--r--ldap/xpcom/src/nsLDAPInternal.h11
-rw-r--r--ldap/xpcom/src/nsLDAPMessage.cpp649
-rw-r--r--ldap/xpcom/src/nsLDAPMessage.h66
-rw-r--r--ldap/xpcom/src/nsLDAPModification.cpp158
-rw-r--r--ldap/xpcom/src/nsLDAPModification.h42
-rw-r--r--ldap/xpcom/src/nsLDAPOperation.cpp975
-rw-r--r--ldap/xpcom/src/nsLDAPOperation.h104
-rw-r--r--ldap/xpcom/src/nsLDAPProtocolHandler.js62
-rw-r--r--ldap/xpcom/src/nsLDAPProtocolModule.cpp147
-rw-r--r--ldap/xpcom/src/nsLDAPSecurityGlue.cpp342
-rw-r--r--ldap/xpcom/src/nsLDAPServer.cpp133
-rw-r--r--ldap/xpcom/src/nsLDAPServer.h39
-rw-r--r--ldap/xpcom/src/nsLDAPService.cpp985
-rw-r--r--ldap/xpcom/src/nsLDAPService.h116
-rw-r--r--ldap/xpcom/src/nsLDAPSyncQuery.cpp386
-rw-r--r--ldap/xpcom/src/nsLDAPSyncQuery.h58
-rw-r--r--ldap/xpcom/src/nsLDAPURL.cpp725
-rw-r--r--ldap/xpcom/src/nsLDAPURL.h61
-rw-r--r--ldap/xpcom/src/nsLDAPUtils.h147
29 files changed, 6597 insertions, 0 deletions
diff --git a/ldap/xpcom/src/ldapComponents.manifest b/ldap/xpcom/src/ldapComponents.manifest
new file mode 100644
index 000000000..482bbd55e
--- /dev/null
+++ b/ldap/xpcom/src/ldapComponents.manifest
@@ -0,0 +1,5 @@
+component {b3de9249-b0e5-4c12-8d91-c9a434fd80f5} nsLDAPProtocolHandler.js
+contract @mozilla.org/network/protocol;1?name=ldap {b3de9249-b0e5-4c12-8d91-c9a434fd80f5}
+
+component {c85a5ef2-9c56-445f-b029-76889f2dd29b} nsLDAPProtocolHandler.js
+contract @mozilla.org/network/protocol;1?name=ldaps {c85a5ef2-9c56-445f-b029-76889f2dd29b}
diff --git a/ldap/xpcom/src/moz.build b/ldap/xpcom/src/moz.build
new file mode 100644
index 000000000..06753e317
--- /dev/null
+++ b/ldap/xpcom/src/moz.build
@@ -0,0 +1,53 @@
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ 'nsLDAPBERElement.cpp',
+ 'nsLDAPBERValue.cpp',
+ 'nsLDAPConnection.cpp',
+ 'nsLDAPControl.cpp',
+ 'nsLDAPMessage.cpp',
+ 'nsLDAPModification.cpp',
+ 'nsLDAPOperation.cpp',
+ 'nsLDAPProtocolModule.cpp',
+ 'nsLDAPSecurityGlue.cpp',
+ 'nsLDAPServer.cpp',
+ 'nsLDAPService.cpp',
+ 'nsLDAPURL.cpp',
+]
+
+if CONFIG['MOZ_PREF_EXTENSIONS']:
+ SOURCES += ['nsLDAPSyncQuery.cpp']
+ DEFINES['MOZ_PREF_EXTENSIONS'] = True
+
+EXTRA_COMPONENTS += [
+ 'ldapComponents.manifest',
+ 'nsLDAPProtocolHandler.js',
+]
+
+USE_LIBS += [
+ 'ldapsdks',
+]
+
+if CONFIG['MOZ_INCOMPLETE_EXTERNAL_LINKAGE']:
+ XPCOMBinaryComponent('mozldap')
+ USE_LIBS += [
+ 'nspr',
+ 'xpcomglue_s',
+ 'xul',
+ ]
+# js needs to come after xul for now, because it is an archive and its content
+# is discarded when it comes first.
+ USE_LIBS += [
+ 'js',
+ ]
+else:
+ Library('mozldap')
+ FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '/ldap/c-sdk/include',
+]
+
diff --git a/ldap/xpcom/src/nsLDAPBERElement.cpp b/ldap/xpcom/src/nsLDAPBERElement.cpp
new file mode 100644
index 000000000..1ea96793e
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPBERElement.cpp
@@ -0,0 +1,117 @@
+/* -*- 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 "nsLDAPBERElement.h"
+#include "nsStringGlue.h"
+#include "nsCOMPtr.h"
+#include "nsLDAPBERValue.h"
+
+NS_IMPL_ISUPPORTS(nsLDAPBERElement, nsILDAPBERElement)
+
+nsLDAPBERElement::nsLDAPBERElement()
+ : mElement(0)
+{
+}
+
+nsLDAPBERElement::~nsLDAPBERElement()
+{
+ if (mElement) {
+ // anything inside here is not something that we own separately from
+ // this object, so free it
+ ber_free(mElement, 1);
+ }
+
+ return;
+}
+
+NS_IMETHODIMP
+nsLDAPBERElement::Init(nsILDAPBERValue *aValue)
+{
+ if (aValue) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ mElement = ber_alloc_t(LBER_USE_DER);
+ return mElement ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+/* void putString (in AUTF8String aString, in unsigned long aTag); */
+NS_IMETHODIMP
+nsLDAPBERElement::PutString(const nsACString & aString, uint32_t aTag,
+ uint32_t *aBytesWritten)
+{
+ // XXX if the string translation feature of the C SDK is ever used,
+ // this const_cast will break
+ int i = ber_put_ostring(mElement,
+ const_cast<char *>(PromiseFlatCString(aString).get()),
+ aString.Length(), aTag);
+
+ if (i < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aBytesWritten = i;
+ return NS_OK;
+}
+
+/* void startSet (); */
+NS_IMETHODIMP nsLDAPBERElement::StartSet(uint32_t aTag)
+{
+ int i = ber_start_set(mElement, aTag);
+
+ if (i < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+/* void putSet (); */
+NS_IMETHODIMP nsLDAPBERElement::PutSet(uint32_t *aBytesWritten)
+{
+ int i = ber_put_set(mElement);
+
+ if (i < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *aBytesWritten = i;
+ return NS_OK;
+}
+
+/* nsILDAPBERValue flatten (); */
+NS_IMETHODIMP nsLDAPBERElement::GetAsValue(nsILDAPBERValue **_retval)
+{
+ // create the value object
+ nsCOMPtr<nsILDAPBERValue> berValue = new nsLDAPBERValue();
+
+ if (!berValue) {
+ NS_ERROR("nsLDAPBERElement::GetAsValue(): out of memory"
+ " creating nsLDAPBERValue object");
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ struct berval *bv;
+ if ( ber_flatten(mElement, &bv) < 0 ) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsresult rv = berValue->Set(bv->bv_len,
+ reinterpret_cast<uint8_t *>(bv->bv_val));
+
+ // whether or not we've succeeded, we're done with the ldap c sdk struct
+ ber_bvfree(bv);
+
+ // as of this writing, this error can only be NS_ERROR_OUT_OF_MEMORY
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // return the raw interface pointer
+ NS_ADDREF(*_retval = berValue.get());
+
+ return NS_OK;
+}
diff --git a/ldap/xpcom/src/nsLDAPBERElement.h b/ldap/xpcom/src/nsLDAPBERElement.h
new file mode 100644
index 000000000..bdeb9c439
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPBERElement.h
@@ -0,0 +1,30 @@
+/* -*- 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 "lber.h"
+#include "nsILDAPBERElement.h"
+
+// 070af769-b7f5-40e7-81be-196155ead84c
+#define NS_LDAPBERELEMENT_CID \
+ { 0x070af769, 0xb7f5, 0x40e7, \
+ { 0x81, 0xbe, 0x19, 0x61, 0x55, 0xea, 0xd8, 0x4c }}
+
+class nsLDAPBERElement final : public nsILDAPBERElement
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSILDAPBERELEMENT
+
+ nsLDAPBERElement();
+
+private:
+ ~nsLDAPBERElement();
+
+ BerElement *mElement;
+
+protected:
+};
+
diff --git a/ldap/xpcom/src/nsLDAPBERValue.cpp b/ldap/xpcom/src/nsLDAPBERValue.cpp
new file mode 100644
index 000000000..d71073b7a
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPBERValue.cpp
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "nsLDAPBERValue.h"
+#include "nsMemory.h"
+#include "nsStringGlue.h"
+
+NS_IMPL_ISUPPORTS(nsLDAPBERValue, nsILDAPBERValue)
+
+nsLDAPBERValue::nsLDAPBERValue() : mValue(0), mSize(0)
+{
+}
+
+nsLDAPBERValue::~nsLDAPBERValue()
+{
+ if (mValue) {
+ free(mValue);
+ }
+}
+
+// void get (out unsigned long aCount,
+// [array, size_is (aCount), retval] out octet aRetVal); */
+NS_IMETHODIMP
+nsLDAPBERValue::Get(uint32_t *aCount, uint8_t **aRetVal)
+{
+ // if mSize = 0, return a count of a 0 and a null pointer
+
+ if (mSize) {
+ // get a buffer to hold a copy of the data
+ //
+ uint8_t *array = static_cast<uint8_t *>(moz_xmalloc(mSize));
+
+ if (!array) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // copy and return
+ //
+ memcpy(array, mValue, mSize);
+ *aRetVal = array;
+ } else {
+ *aRetVal = 0;
+ }
+
+ *aCount = mSize;
+ return NS_OK;
+}
+
+// void set(in unsigned long aCount,
+// [array, size_is(aCount)] in octet aValue);
+NS_IMETHODIMP
+nsLDAPBERValue::Set(uint32_t aCount, uint8_t *aValue)
+{
+ // get rid of any old value being held here
+ //
+ if (mValue) {
+ free(mValue);
+ }
+
+ // if this is a non-zero value, allocate a buffer and copy
+ //
+ if (aCount) {
+ // get a buffer to hold a copy of this data
+ //
+ mValue = static_cast<uint8_t *>(moz_xmalloc(aCount));
+ if (!mValue) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // copy the data and return
+ //
+ memcpy(mValue, aValue, aCount);
+ } else {
+ // otherwise just set it to null
+ //
+ mValue = 0;
+ }
+
+ mSize = aCount;
+ return NS_OK;
+}
+
+// void setFromUTF8(in AUTF8String aValue);
+//
+NS_IMETHODIMP
+nsLDAPBERValue::SetFromUTF8(const nsACString & aValue)
+{
+ // get rid of any old value being held here
+ //
+ if (mValue) {
+ free(mValue);
+ }
+
+ // copy the data and return
+ //
+ mSize = aValue.Length();
+ if (mSize) {
+ mValue = reinterpret_cast<uint8_t *>(ToNewCString(aValue));
+ } else {
+ mValue = 0;
+ }
+ return NS_OK;
+}
diff --git a/ldap/xpcom/src/nsLDAPBERValue.h b/ldap/xpcom/src/nsLDAPBERValue.h
new file mode 100644
index 000000000..d7f563f03
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPBERValue.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+#ifndef _nsLDAPBERValue_h_
+#define _nsLDAPBERValue_h_
+
+#include "ldap.h"
+#include "nsILDAPBERValue.h"
+
+// 7c9fa10e-1dd2-11b2-a097-ac379e6803b2
+//
+#define NS_LDAPBERVALUE_CID \
+{ 0x7c9fa10e, 0x1dd2, 0x11b2, \
+ {0xa0, 0x97, 0xac, 0x37, 0x9e, 0x68, 0x03, 0xb2 }}
+
+class nsLDAPBERValue : public nsILDAPBERValue
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSILDAPBERVALUE
+
+ nsLDAPBERValue();
+
+protected:
+ virtual ~nsLDAPBERValue();
+
+ /**
+ * nsLDAPControl needs to be able to grovel through this without an
+ * an extra copy
+ */
+ friend class nsLDAPControl;
+
+ uint8_t *mValue; // pointer to an array
+ uint32_t mSize; // size of the value, in bytes
+};
+
+#endif // _nsLDAPBERValue_h_
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;
+}
diff --git a/ldap/xpcom/src/nsLDAPConnection.h b/ldap/xpcom/src/nsLDAPConnection.h
new file mode 100644
index 000000000..08c88295c
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPConnection.h
@@ -0,0 +1,139 @@
+/* -*- 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/. */
+
+#ifndef _nsLDAPConnection_h_
+#define _nsLDAPConnection_h_
+
+#include "nsILDAPConnection.h"
+#include "ldap.h"
+#include "nsStringGlue.h"
+#include "nsIThread.h"
+#include "nsIRunnable.h"
+#include "nsCOMPtr.h"
+#include "nsILDAPMessageListener.h"
+#include "nsInterfaceHashtable.h"
+#include "nspr.h"
+#include "nsWeakReference.h"
+#include "nsWeakPtr.h"
+#include "nsIDNSListener.h"
+#include "nsICancelable.h"
+#include "nsIRequest.h"
+#include "nsCOMArray.h"
+#include "nsIObserver.h"
+#include "nsAutoPtr.h"
+#include "mozilla/Mutex.h"
+
+/**
+ * Casting nsILDAPConnection to nsISupports is ambiguous.
+ * This method handles that.
+ */
+inline nsISupports*
+ToSupports(nsILDAPConnection* p)
+{
+ return NS_ISUPPORTS_CAST(nsILDAPConnection*, p);
+}
+
+// 0d871e30-1dd2-11b2-8ea9-831778c78e93
+//
+#define NS_LDAPCONNECTION_CID \
+{ 0x0d871e30, 0x1dd2, 0x11b2, \
+ { 0x8e, 0xa9, 0x83, 0x17, 0x78, 0xc7, 0x8e, 0x93 }}
+
+class nsLDAPConnection : public nsILDAPConnection,
+ public nsSupportsWeakReference,
+ public nsIDNSListener,
+ public nsIObserver
+
+{
+ friend class nsLDAPOperation;
+ friend class nsLDAPMessage;
+ friend class nsLDAPConnectionRunnable;
+ typedef mozilla::Mutex Mutex;
+
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSILDAPCONNECTION
+ NS_DECL_NSIDNSLISTENER
+ NS_DECL_NSIOBSERVER
+
+ // constructor & destructor
+ //
+ nsLDAPConnection();
+
+ protected:
+ virtual ~nsLDAPConnection();
+ // invoke the callback associated with a given message, and possibly
+ // delete it from the connection queue
+ //
+ nsresult InvokeMessageCallback(LDAPMessage *aMsgHandle,
+ nsILDAPMessage *aMsg,
+ int32_t aOperation,
+ bool aRemoveOpFromConnQ);
+ /**
+ * Add an nsILDAPOperation to the list of operations pending on
+ * this connection. This is mainly intended for use by the
+ * nsLDAPOperation code. Used so that the thread waiting on messages
+ * for this connection has an operation to callback to.
+ *
+ * @param aOperation operation to add
+ * @exception NS_ERROR_ILLEGAL_VALUE aOperation was NULL
+ * @exception NS_ERROR_UNEXPECTED this operation's msgId was not
+ * unique to this connection
+ * @exception NS_ERROR_OUT_OF_MEMORY out of memory
+ */
+ nsresult AddPendingOperation(uint32_t aOperationID, nsILDAPOperation *aOperation);
+
+ /**
+ * 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
+ */
+ nsresult RemovePendingOperation(uint32_t aOperationID);
+
+ void Close(); // close the connection
+ LDAP *mConnectionHandle; // the LDAP C SDK's connection object
+ nsCString mBindName; // who to bind as
+ nsCOMPtr<nsIThread> mThread; // thread which marshals results
+
+ Mutex mPendingOperationsMutex;
+ nsInterfaceHashtable<nsUint32HashKey, nsILDAPOperation> mPendingOperations;
+
+ int32_t mPort; // The LDAP port we're binding to
+ bool mSSL; // the options
+ uint32_t mVersion; // LDAP protocol version
+
+ nsCString mResolvedIP; // Preresolved list of host IPs
+ nsCOMPtr<nsILDAPMessageListener> mInitListener; // Init callback
+ nsCOMPtr<nsICancelable> mDNSRequest; // The "active" DNS request
+ nsCString mDNSHost; // The hostname being resolved
+ nsCOMPtr<nsISupports> mClosure; // private parameter (anything caller desires)
+};
+
+class nsLDAPConnectionRunnable : public nsIRunnable
+{
+ friend class nsLDAPConnection;
+ friend class nsLDAPMessage;
+
+public:
+ nsLDAPConnectionRunnable(int32_t aOperationID,
+ nsILDAPOperation *aOperation,
+ nsLDAPConnection *aConnection);
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIRUNNABLE
+
+ int32_t mOperationID;
+ RefPtr<nsLDAPConnection> mConnection;
+
+private:
+ virtual ~nsLDAPConnectionRunnable();
+};
+
+#endif // _nsLDAPConnection_h_
diff --git a/ldap/xpcom/src/nsLDAPControl.cpp b/ldap/xpcom/src/nsLDAPControl.cpp
new file mode 100644
index 000000000..a01972835
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPControl.cpp
@@ -0,0 +1,122 @@
+/* -*- 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 "nsLDAPControl.h"
+#include "prmem.h"
+#include "plstr.h"
+#include "nsLDAPBERValue.h"
+
+NS_IMPL_ISUPPORTS(nsLDAPControl, nsILDAPControl)
+
+nsLDAPControl::nsLDAPControl()
+ : mIsCritical(false)
+{
+}
+
+nsLDAPControl::~nsLDAPControl()
+{
+}
+
+/* attribute ACString oid; */
+NS_IMETHODIMP nsLDAPControl::GetOid(nsACString & aOid)
+{
+ aOid.Assign(mOid);
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPControl::SetOid(const nsACString & aOid)
+{
+ mOid = aOid;
+ return NS_OK;
+}
+
+/* attribute nsILDAPBERValue value; */
+NS_IMETHODIMP
+nsLDAPControl::GetValue(nsILDAPBERValue * *aValue)
+{
+ NS_IF_ADDREF(*aValue = mValue);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPControl::SetValue(nsILDAPBERValue * aValue)
+{
+ mValue = aValue;
+ return NS_OK;
+}
+
+/* attribute boolean isCritical; */
+NS_IMETHODIMP
+nsLDAPControl::GetIsCritical(bool *aIsCritical)
+{
+ *aIsCritical = mIsCritical;
+ return NS_OK;
+}
+NS_IMETHODIMP
+nsLDAPControl::SetIsCritical(bool aIsCritical)
+{
+ mIsCritical = aIsCritical;
+ return NS_OK;
+}
+
+/**
+ * utility routine for use inside the LDAP XPCOM SDK
+ */
+nsresult
+nsLDAPControl::ToLDAPControl(LDAPControl **control)
+{
+ // because nsLDAPProtocolModule::Init calls prldap_install_routines we know
+ // that the C SDK will be using the NSPR allocator under the hood, so our
+ // callers will therefore be able to use ldap_control_free() and friends on
+ // this control.
+ LDAPControl *ctl = static_cast<LDAPControl *>(PR_Calloc(1, sizeof(LDAPControl)));
+ if (!ctl) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // need to ensure that this string is also alloced by PR_Alloc
+ ctl->ldctl_oid = PL_strdup(mOid.get());
+ if (!ctl->ldctl_oid) {
+ PR_Free(ctl);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ ctl->ldctl_iscritical = mIsCritical;
+
+ if (!mValue) {
+ // no data associated with this control
+ ctl->ldctl_value.bv_len = 0;
+ ctl->ldctl_value.bv_val = 0;
+ } else {
+
+ // just to make the code below a bit more readable
+ nsLDAPBERValue *nsBerVal =
+ static_cast<nsLDAPBERValue *>(static_cast<nsILDAPBERValue *>
+ (mValue.get()));
+ ctl->ldctl_value.bv_len = nsBerVal->mSize;
+
+ if (!nsBerVal->mSize) {
+ // a zero-length value is associated with this control
+ return NS_ERROR_NOT_IMPLEMENTED;
+ } else {
+
+ // same for the berval itself
+ ctl->ldctl_value.bv_len = nsBerVal->mSize;
+ ctl->ldctl_value.bv_val = static_cast<char *>
+ (PR_Malloc(nsBerVal->mSize));
+ if (!ctl->ldctl_value.bv_val) {
+ ldap_control_free(ctl);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ memcpy(ctl->ldctl_value.bv_val, nsBerVal->mValue,
+ ctl->ldctl_value.bv_len);
+ }
+ }
+
+ *control = ctl;
+
+ return NS_OK;
+}
diff --git a/ldap/xpcom/src/nsLDAPControl.h b/ldap/xpcom/src/nsLDAPControl.h
new file mode 100644
index 000000000..6baf269c7
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPControl.h
@@ -0,0 +1,42 @@
+/* -*- 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 "nsILDAPControl.h"
+#include "nsCOMPtr.h"
+#include "nsILDAPBERValue.h"
+#include "nsStringGlue.h"
+#include "ldap.h"
+
+// {5B608BBE-C0EA-4f74-B209-9CDCD79EC401}
+#define NS_LDAPCONTROL_CID \
+ { 0x5b608bbe, 0xc0ea, 0x4f74, \
+ { 0xb2, 0x9, 0x9c, 0xdc, 0xd7, 0x9e, 0xc4, 0x1 } }
+
+class nsLDAPControl final : public nsILDAPControl
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSILDAPCONTROL
+
+ nsLDAPControl();
+
+ /**
+ * return a pointer to C-SDK compatible LDAPControl structure. Note that
+ * this is allocated with NS_Alloc and must be freed with NS_Free, both by
+ * ldap_control_free() and friends.
+ *
+ * @exception null pointer return if allocation failed
+ */
+ nsresult ToLDAPControl(LDAPControl **aControl);
+
+private:
+ ~nsLDAPControl();
+
+protected:
+ nsCOMPtr<nsILDAPBERValue> mValue; // the value portion of this control
+ bool mIsCritical; // should server abort if control not understood?
+ nsCString mOid; // Object ID for this control
+};
diff --git a/ldap/xpcom/src/nsLDAPInternal.h b/ldap/xpcom/src/nsLDAPInternal.h
new file mode 100644
index 000000000..989f113b3
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPInternal.h
@@ -0,0 +1,11 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "prlog.h"
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo *gLDAPLogModule; // defn in nsLDAPProtocolModule.cpp
+#endif
diff --git a/ldap/xpcom/src/nsLDAPMessage.cpp b/ldap/xpcom/src/nsLDAPMessage.cpp
new file mode 100644
index 000000000..782fa047f
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPMessage.cpp
@@ -0,0 +1,649 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "nsLDAPMessage.h"
+#include "nspr.h"
+#include "nsDebug.h"
+#include "nsMemory.h"
+#include "nsLDAPConnection.h"
+#include "nsISupportsUtils.h"
+#include "nsLDAPBERValue.h"
+#include "nsILDAPErrors.h"
+#include "nsIClassInfoImpl.h"
+#include "nsLDAPUtils.h"
+#include "mozilla/Logging.h"
+
+NS_IMPL_CLASSINFO(nsLDAPMessage, NULL, nsIClassInfo::THREADSAFE,
+ NS_LDAPMESSAGE_CID)
+
+NS_IMPL_ADDREF(nsLDAPMessage)
+NS_IMPL_RELEASE(nsLDAPMessage)
+NS_INTERFACE_MAP_BEGIN(nsLDAPMessage)
+ NS_INTERFACE_MAP_ENTRY(nsILDAPMessage)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsILDAPMessage)
+ NS_IMPL_QUERY_CLASSINFO(nsLDAPMessage)
+NS_INTERFACE_MAP_END_THREADSAFE
+NS_IMPL_CI_INTERFACE_GETTER(nsLDAPMessage, nsILDAPMessage)
+
+
+// constructor
+//
+nsLDAPMessage::nsLDAPMessage()
+ : mMsgHandle(0),
+ mErrorCode(LDAP_SUCCESS),
+ mMatchedDn(0),
+ mErrorMessage(0),
+ mReferrals(0),
+ mServerControls(0)
+{
+}
+
+// destructor
+//
+nsLDAPMessage::~nsLDAPMessage(void)
+{
+ if (mMsgHandle) {
+ int rc = ldap_msgfree(mMsgHandle);
+
+// If you are having problems compiling the following code on a Solaris
+// machine with the Forte 6 Update 1 compilers, then you need to make
+// sure you have applied all the required patches. See:
+// http://www.mozilla.org/unix/solaris-build.html for more details.
+
+ switch(rc) {
+ case LDAP_RES_BIND:
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_RESULT:
+ case LDAP_RES_MODIFY:
+ case LDAP_RES_ADD:
+ case LDAP_RES_DELETE:
+ case LDAP_RES_MODRDN:
+ case LDAP_RES_COMPARE:
+ case LDAP_RES_SEARCH_REFERENCE:
+ case LDAP_RES_EXTENDED:
+ case LDAP_RES_ANY:
+ // success
+ break;
+
+ case LDAP_SUCCESS:
+ // timed out (dunno why LDAP_SUCCESS is used to indicate this)
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Warning,
+ ("nsLDAPMessage::~nsLDAPMessage: ldap_msgfree() "
+ "timed out\n"));
+ break;
+
+ default:
+ // other failure
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Warning,
+ ("nsLDAPMessage::~nsLDAPMessage: ldap_msgfree() "
+ "failed: %s\n", ldap_err2string(rc)));
+ break;
+ }
+ }
+
+ if (mMatchedDn) {
+ ldap_memfree(mMatchedDn);
+ }
+
+ if (mErrorMessage) {
+ ldap_memfree(mErrorMessage);
+ }
+
+ if (mReferrals) {
+ ldap_value_free(mReferrals);
+ }
+
+ if (mServerControls) {
+ ldap_controls_free(mServerControls);
+ }
+
+}
+
+/**
+ * Initializes a message.
+ *
+ * @param aConnection The nsLDAPConnection this message is on
+ * @param aMsgHandle The native LDAPMessage to be wrapped.
+ *
+ * @exception NS_ERROR_ILLEGAL_VALUE null pointer passed in
+ * @exception NS_ERROR_UNEXPECTED internal err; shouldn't happen
+ * @exception NS_ERROR_LDAP_DECODING_ERROR problem during BER decoding
+ * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory
+ */
+nsresult
+nsLDAPMessage::Init(nsILDAPConnection *aConnection, LDAPMessage *aMsgHandle)
+{
+ int parseResult;
+
+ if (!aConnection || !aMsgHandle) {
+ NS_WARNING("Null pointer passed in to nsLDAPMessage::Init()");
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ // initialize the appropriate member vars
+ //
+ mConnection = aConnection;
+ mMsgHandle = aMsgHandle;
+
+ // cache the connection handle. we're violating the XPCOM type-system
+ // here since we're a friend of the connection class and in the
+ // same module.
+ //
+ mConnectionHandle = static_cast<nsLDAPConnection *>(aConnection)->mConnectionHandle;
+
+ // do any useful message parsing
+ //
+ const int msgType = ldap_msgtype(mMsgHandle);
+ if ( msgType == -1) {
+ NS_ERROR("nsLDAPMessage::Init(): ldap_msgtype() failed");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ switch (msgType) {
+
+ case LDAP_RES_SEARCH_REFERENCE:
+ // XXX should do something here?
+ break;
+
+ case LDAP_RES_SEARCH_ENTRY:
+ // nothing to do here
+ break;
+
+ case LDAP_RES_EXTENDED:
+ // XXX should do something here?
+ break;
+
+ case LDAP_RES_BIND:
+ case LDAP_RES_SEARCH_RESULT:
+ case LDAP_RES_MODIFY:
+ case LDAP_RES_ADD:
+ case LDAP_RES_DELETE:
+ case LDAP_RES_MODRDN:
+ case LDAP_RES_COMPARE:
+ parseResult = ldap_parse_result(mConnectionHandle,
+ mMsgHandle, &mErrorCode, &mMatchedDn,
+ &mErrorMessage,&mReferrals,
+ &mServerControls, 0);
+ switch (parseResult) {
+ case LDAP_SUCCESS:
+ // we're good
+ break;
+
+ case LDAP_DECODING_ERROR:
+ NS_WARNING("nsLDAPMessage::Init(): ldap_parse_result() hit a "
+ "decoding error");
+ return NS_ERROR_LDAP_DECODING_ERROR;
+
+ case LDAP_NO_MEMORY:
+ NS_WARNING("nsLDAPMessage::Init(): ldap_parse_result() ran out "
+ "of memory");
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ case LDAP_PARAM_ERROR:
+ case LDAP_MORE_RESULTS_TO_RETURN:
+ case LDAP_NO_RESULTS_RETURNED:
+ default:
+ NS_ERROR("nsLDAPMessage::Init(): ldap_parse_result returned "
+ "unexpected return code");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ break;
+
+ default:
+ NS_ERROR("nsLDAPMessage::Init(): unexpected message type");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
+
+/**
+ * The result code of the (possibly partial) operation.
+ *
+ * @exception NS_ERROR_ILLEGAL_VALUE null pointer passed in
+ *
+ * readonly attribute long errorCode;
+ */
+NS_IMETHODIMP
+nsLDAPMessage::GetErrorCode(int32_t *aErrorCode)
+{
+ if (!aErrorCode) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ *aErrorCode = mErrorCode;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPMessage::GetType(int32_t *aType)
+{
+ if (!aType) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ *aType = ldap_msgtype(mMsgHandle);
+ if (*aType == -1) {
+ return NS_ERROR_UNEXPECTED;
+ };
+
+ return NS_OK;
+}
+
+// we don't get to use exceptions, so we'll fake it. this is an error
+// handler for IterateAttributes().
+//
+nsresult
+nsLDAPMessage::IterateAttrErrHandler(int32_t aLderrno, uint32_t *aAttrCount,
+ char** *aAttributes, BerElement *position)
+{
+
+ // if necessary, free the position holder used by
+ // ldap_{first,next}_attribute()
+ //
+ if (position) {
+ ldap_ber_free(position, 0);
+ }
+
+ // deallocate any entries in the array that have been allocated, then
+ // the array itself
+ //
+ if (*aAttributes) {
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(*aAttrCount, *aAttributes);
+ }
+
+ // possibly spit out a debugging message, then return an appropriate
+ // error code
+ //
+ switch (aLderrno) {
+
+ case LDAP_PARAM_ERROR:
+ NS_WARNING("nsLDAPMessage::IterateAttributes() failure; probable bug "
+ "or memory corruption encountered");
+ return NS_ERROR_UNEXPECTED;
+ break;
+
+ case LDAP_DECODING_ERROR:
+ NS_WARNING("nsLDAPMessage::IterateAttributes(): decoding error");
+ return NS_ERROR_LDAP_DECODING_ERROR;
+ break;
+
+ case LDAP_NO_MEMORY:
+ return NS_ERROR_OUT_OF_MEMORY;
+ break;
+
+ }
+
+ NS_WARNING("nsLDAPMessage::IterateAttributes(): LDAP C SDK returned "
+ "unexpected value; possible bug or memory corruption");
+ return NS_ERROR_UNEXPECTED;
+}
+
+
+// wrapper for ldap_first_attribute
+//
+NS_IMETHODIMP
+nsLDAPMessage::GetAttributes(uint32_t *aAttrCount, char** *aAttributes)
+{
+ return IterateAttributes(aAttrCount, aAttributes, true);
+}
+
+// if getP is true, we get the attributes by recursing once
+// (without getP set) in order to fill in *attrCount, then allocate
+// and fill in the *aAttributes.
+//
+// if getP is false, just fill in *attrCount and return
+//
+nsresult
+nsLDAPMessage::IterateAttributes(uint32_t *aAttrCount, char** *aAttributes,
+ bool getP)
+{
+ BerElement *position;
+ nsresult rv;
+
+ if (!aAttrCount || !aAttributes ) {
+ return NS_ERROR_INVALID_POINTER;
+ }
+
+ // if we've been called from GetAttributes, recurse once in order to
+ // count the elements in this message.
+ //
+ if (getP) {
+ *aAttributes = 0;
+ *aAttrCount = 0;
+
+ rv = IterateAttributes(aAttrCount, aAttributes, false);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // create an array of the appropriate size
+ //
+ *aAttributes = static_cast<char **>(moz_xmalloc(*aAttrCount *
+ sizeof(char *)));
+ if (!*aAttributes) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ // get the first attribute
+ //
+ char *attr = ldap_first_attribute(mConnectionHandle,
+ mMsgHandle,
+ &position);
+ if (!attr) {
+ return IterateAttrErrHandler(ldap_get_lderrno(mConnectionHandle, 0, 0),
+ aAttrCount, aAttributes, position);
+ }
+
+ // if we're getting attributes, try and fill in the first field
+ //
+ if (getP) {
+ (*aAttributes)[0] = NS_strdup(attr);
+ if (!(*aAttributes)[0]) {
+ ldap_memfree(attr);
+ free(*aAttributes);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // note that we start counting again, in order to keep our place in
+ // the array so that we can unwind gracefully and avoid leakage if
+ // we hit an error as we're filling in the array
+ //
+ *aAttrCount = 1;
+ } else {
+
+ // otherwise just update the count
+ //
+ *aAttrCount = 1;
+ }
+ ldap_memfree(attr);
+
+ while (1) {
+
+ // get the next attribute
+ //
+ attr = ldap_next_attribute(mConnectionHandle, mMsgHandle, position);
+
+ // check to see if there is an error, or if we're just done iterating
+ //
+ if (!attr) {
+
+ // bail out if there's an error
+ //
+ int32_t lderrno = ldap_get_lderrno(mConnectionHandle, 0, 0);
+ if (lderrno != LDAP_SUCCESS) {
+ return IterateAttrErrHandler(lderrno, aAttrCount, aAttributes,
+ position);
+ }
+
+ // otherwise, there are no more attributes; we're done with
+ // the while loop
+ //
+ break;
+
+ } else if (getP) {
+
+ // if ldap_next_attribute did return successfully, and
+ // we're supposed to fill in a value, do so.
+ //
+ (*aAttributes)[*aAttrCount] = NS_strdup(attr);
+ if (!(*aAttributes)[*aAttrCount]) {
+ ldap_memfree(attr);
+ return IterateAttrErrHandler(LDAP_NO_MEMORY, aAttrCount,
+ aAttributes, position);
+ }
+
+ }
+ ldap_memfree(attr);
+
+ // we're done using *aAttrCount as a c-style array index (ie starting
+ // at 0). update it to reflect the number of elements now in the array
+ //
+ *aAttrCount += 1;
+ }
+
+ // free the position pointer, if necessary
+ //
+ if (position) {
+ ldap_ber_free(position, 0);
+ }
+
+ return NS_OK;
+}
+
+// readonly attribute wstring dn;
+NS_IMETHODIMP nsLDAPMessage::GetDn(nsACString& aDn)
+{
+ char *rawDn = ldap_get_dn(mConnectionHandle, mMsgHandle);
+
+ if (!rawDn) {
+ int32_t lderrno = ldap_get_lderrno(mConnectionHandle, 0, 0);
+
+ switch (lderrno) {
+
+ case LDAP_DECODING_ERROR:
+ NS_WARNING("nsLDAPMessage::GetDn(): ldap decoding error");
+ return NS_ERROR_LDAP_DECODING_ERROR;
+
+ case LDAP_PARAM_ERROR:
+ default:
+ NS_ERROR("nsLDAPMessage::GetDn(): internal error");
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("nsLDAPMessage::GetDn(): dn = '%s'", rawDn));
+
+ aDn.Assign(rawDn);
+ ldap_memfree(rawDn);
+
+ return NS_OK;
+}
+
+// wrapper for ldap_get_values()
+//
+NS_IMETHODIMP
+nsLDAPMessage::GetValues(const char *aAttr, uint32_t *aCount,
+ char16_t ***aValues)
+{
+ char **values;
+
+#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,
+ ("nsLDAPMessage::GetValues(): called with aAttr = '%s'", aAttr));
+#endif
+
+ values = ldap_get_values(mConnectionHandle, mMsgHandle, aAttr);
+
+ // bail out if there was a problem
+ //
+ if (!values) {
+ int32_t lderrno = ldap_get_lderrno(mConnectionHandle, 0, 0);
+
+ if ( lderrno == LDAP_DECODING_ERROR ) {
+ // this may not be an error; it could just be that the
+ // caller has asked for an attribute that doesn't exist.
+ //
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Warning,
+ ("nsLDAPMessage::GetValues(): ldap_get_values returned "
+ "LDAP_DECODING_ERROR"));
+ return NS_ERROR_LDAP_DECODING_ERROR;
+
+ } else if ( lderrno == LDAP_PARAM_ERROR ) {
+ NS_ERROR("nsLDAPMessage::GetValues(): internal error: 1");
+ return NS_ERROR_UNEXPECTED;
+
+ } else {
+ NS_ERROR("nsLDAPMessage::GetValues(): internal error: 2");
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+
+ // count the values
+ //
+ uint32_t numVals = ldap_count_values(values);
+
+ // create an array of the appropriate size
+ //
+ *aValues = static_cast<char16_t **>(moz_xmalloc(numVals * sizeof(char16_t *)));
+ if (!*aValues) {
+ ldap_value_free(values);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // clone the array (except for the trailing NULL entry) using the
+ // shared allocator for XPCOM correctness
+ //
+ uint32_t i;
+ for ( i = 0 ; i < numVals ; i++ ) {
+ nsDependentCString sValue(values[i]);
+ if (IsUTF8(sValue))
+ (*aValues)[i] = ToNewUnicode(NS_ConvertUTF8toUTF16(sValue));
+ else
+ (*aValues)[i] = ToNewUnicode(NS_ConvertASCIItoUTF16(sValue));
+ if ( ! (*aValues)[i] ) {
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, aValues);
+ ldap_value_free(values);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ // now free our value array since we already cloned the values array
+ // to the 'aValues' results array.
+ ldap_value_free(values);
+
+ *aCount = numVals;
+ return NS_OK;
+}
+
+// wrapper for get_values_len
+//
+NS_IMETHODIMP
+nsLDAPMessage::GetBinaryValues(const char *aAttr, uint32_t *aCount,
+ nsILDAPBERValue ***aValues)
+{
+ struct berval **values;
+
+#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,
+ ("nsLDAPMessage::GetBinaryValues(): called with aAttr = '%s'",
+ aAttr));
+#endif
+
+ values = ldap_get_values_len(mConnectionHandle, mMsgHandle, aAttr);
+
+ // bail out if there was a problem
+ //
+ if (!values) {
+ int32_t lderrno = ldap_get_lderrno(mConnectionHandle, 0, 0);
+
+ if ( lderrno == LDAP_DECODING_ERROR ) {
+ // this may not be an error; it could just be that the
+ // caller has asked for an attribute that doesn't exist.
+ //
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Warning,
+ ("nsLDAPMessage::GetBinaryValues(): ldap_get_values "
+ "returned LDAP_DECODING_ERROR"));
+ return NS_ERROR_LDAP_DECODING_ERROR;
+
+ } else if ( lderrno == LDAP_PARAM_ERROR ) {
+ NS_ERROR("nsLDAPMessage::GetBinaryValues(): internal error: 1");
+ return NS_ERROR_UNEXPECTED;
+
+ } else {
+ NS_ERROR("nsLDAPMessage::GetBinaryValues(): internal error: 2");
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+
+ // count the values
+ //
+ uint32_t numVals = ldap_count_values_len(values);
+
+ // create the out array
+ //
+ *aValues =
+ static_cast<nsILDAPBERValue **>(moz_xmalloc(numVals * sizeof(nsILDAPBERValue)));
+ if (!aValues) {
+ ldap_value_free_len(values);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // clone the array (except for the trailing NULL entry) using the
+ // shared allocator for XPCOM correctness
+ //
+ uint32_t i;
+ nsresult rv;
+ for ( i = 0 ; i < numVals ; i++ ) {
+
+ // create an nsBERValue object
+ //
+ nsCOMPtr<nsILDAPBERValue> berValue = new nsLDAPBERValue();
+ if (!berValue) {
+ NS_ERROR("nsLDAPMessage::GetBinaryValues(): out of memory"
+ " creating nsLDAPBERValue object");
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, aValues);
+ ldap_value_free_len(values);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // copy the value from the struct into the nsBERValue
+ //
+ rv = berValue->Set(values[i]->bv_len,
+ reinterpret_cast<uint8_t *>(values[i]->bv_val));
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPMessage::GetBinaryValues(): error setting"
+ " nsBERValue");
+ ldap_value_free_len(values);
+ return rv == NS_ERROR_OUT_OF_MEMORY ? rv : NS_ERROR_UNEXPECTED;
+ }
+
+ // put the nsIBERValue object into the out array
+ //
+ NS_ADDREF( (*aValues)[i] = berValue.get() );
+ }
+
+ *aCount = numVals;
+ ldap_value_free_len(values);
+ return NS_OK;
+}
+
+// readonly attribute nsILDAPOperation operation;
+NS_IMETHODIMP nsLDAPMessage::GetOperation(nsILDAPOperation **_retval)
+{
+ if (!_retval) {
+ NS_ERROR("nsLDAPMessage::GetOperation: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ NS_IF_ADDREF(*_retval = mOperation);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPMessage::ToUnicode(char16_t* *aString)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsLDAPMessage::GetErrorMessage(nsACString & aErrorMessage)
+{
+ aErrorMessage.Assign(mErrorMessage);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPMessage::GetMatchedDn(nsACString & aMatchedDn)
+{
+ aMatchedDn.Assign(mMatchedDn);
+ return NS_OK;
+}
diff --git a/ldap/xpcom/src/nsLDAPMessage.h b/ldap/xpcom/src/nsLDAPMessage.h
new file mode 100644
index 000000000..d3c0b2d2e
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPMessage.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+#ifndef _nsLDAPMessage_h_
+#define _nsLDAPMessage_h_
+
+#include "ldap.h"
+#include "nsILDAPMessage.h"
+#include "nsILDAPOperation.h"
+#include "nsCOMPtr.h"
+
+// 76e061ad-a59f-43b6-b812-ee6e8e69423f
+//
+#define NS_LDAPMESSAGE_CID \
+{ 0x76e061ad, 0xa59f, 0x43b6, \
+ { 0xb8, 0x12, 0xee, 0x6e, 0x8e, 0x69, 0x42, 0x3f }}
+
+class nsLDAPMessage : public nsILDAPMessage
+{
+ friend class nsLDAPOperation;
+ friend class nsLDAPConnection;
+ friend class nsLDAPConnectionRunnable;
+ friend class nsOnLDAPMessageRunnable;
+
+ public:
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSILDAPMESSAGE
+
+ // constructor & destructor
+ //
+ nsLDAPMessage();
+
+ protected:
+ virtual ~nsLDAPMessage();
+
+ nsresult IterateAttrErrHandler(int32_t aLderrno, uint32_t *aAttrCount,
+ char** *aAttributes, BerElement *position);
+ nsresult IterateAttributes(uint32_t *aAttrCount, char** *aAttributes,
+ bool getP);
+ nsresult Init(nsILDAPConnection *aConnection,
+ LDAPMessage *aMsgHandle);
+ LDAPMessage *mMsgHandle; // the message we're wrapping
+ nsCOMPtr<nsILDAPOperation> mOperation; // operation this msg relates to
+
+ LDAP *mConnectionHandle; // cached connection this op is on
+
+ // since we're caching the connection handle (above), we need to
+ // hold an owning ref to the relevant nsLDAPConnection object as long
+ // as we're around
+ //
+ nsCOMPtr<nsILDAPConnection> mConnection;
+
+ // the next five member vars are returned by ldap_parse_result()
+ //
+ int mErrorCode;
+ char *mMatchedDn;
+ char *mErrorMessage;
+ char **mReferrals;
+ LDAPControl **mServerControls;
+};
+
+#endif // _nsLDAPMessage_h
diff --git a/ldap/xpcom/src/nsLDAPModification.cpp b/ldap/xpcom/src/nsLDAPModification.cpp
new file mode 100644
index 000000000..77253835a
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPModification.cpp
@@ -0,0 +1,158 @@
+/* -*- 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 "nsLDAPModification.h"
+#include "nsILDAPBERValue.h"
+#include "nsISimpleEnumerator.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS(nsLDAPModification, nsILDAPModification)
+
+// constructor
+//
+nsLDAPModification::nsLDAPModification()
+ : mValuesLock("nsLDAPModification.mValuesLock")
+{
+}
+
+// destructor
+//
+nsLDAPModification::~nsLDAPModification()
+{
+}
+
+nsresult
+nsLDAPModification::Init()
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPModification::GetOperation(int32_t *aOperation)
+{
+ NS_ENSURE_ARG_POINTER(aOperation);
+
+ *aOperation = mOperation;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLDAPModification::SetOperation(int32_t aOperation)
+{
+ mOperation = aOperation;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPModification::GetType(nsACString& aType)
+{
+ aType.Assign(mType);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPModification::SetType(const nsACString& aType)
+{
+ mType.Assign(aType);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPModification::GetValues(nsIArray** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ MutexAutoLock lock(mValuesLock);
+
+ if (!mValues)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ NS_ADDREF(*aResult = mValues);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPModification::SetValues(nsIArray* aValues)
+{
+ NS_ENSURE_ARG_POINTER(aValues);
+
+ MutexAutoLock lock(mValuesLock);
+ nsresult rv;
+
+ if (!mValues)
+ mValues = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ else
+ rv = mValues->Clear();
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = aValues->Enumerate(getter_AddRefs(enumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool hasMoreElements;
+ rv = enumerator->HasMoreElements(&hasMoreElements);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISupports> value;
+
+ while (hasMoreElements)
+ {
+ rv = enumerator->GetNext(getter_AddRefs(value));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mValues->AppendElement(value, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = enumerator->HasMoreElements(&hasMoreElements);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPModification::SetUpModification(int32_t aOperation,
+ const nsACString &aType,
+ nsIArray *aValues)
+{
+ // Set the values using our local function before entering lock
+ // to avoid deadlocks due to holding the same lock twice.
+ nsresult rv = SetValues(aValues);
+
+ MutexAutoLock lock(mValuesLock);
+
+ mOperation = aOperation;
+ mType.Assign(aType);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLDAPModification::SetUpModificationOneValue(int32_t aOperation,
+ const nsACString &aType,
+ nsILDAPBERValue *aValue)
+{
+ NS_ENSURE_ARG_POINTER(aValue);
+
+ MutexAutoLock lock(mValuesLock);
+
+ mOperation = aOperation;
+ mType.Assign(aType);
+
+ nsresult rv;
+
+ if (!mValues)
+ mValues = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ else
+ rv = mValues->Clear();
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return mValues->AppendElement(aValue, false);
+}
diff --git a/ldap/xpcom/src/nsLDAPModification.h b/ldap/xpcom/src/nsLDAPModification.h
new file mode 100644
index 000000000..e9c9c5599
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPModification.h
@@ -0,0 +1,42 @@
+/* -*- 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/. */
+
+#ifndef _nsLDAPModification_h_
+#define _nsLDAPModification_h_
+
+#include "nsILDAPModification.h"
+#include "nsIMutableArray.h"
+#include "nsStringGlue.h"
+#include "nsCOMPtr.h"
+#include "mozilla/Mutex.h"
+
+// 5b0f4d00-062e-11d6-a7f2-fc943c3c039c
+//
+#define NS_LDAPMODIFICATION_CID \
+{ 0x5b0f4d00, 0x062e, 0x11d6, \
+ { 0xa7, 0xf2, 0xfc, 0x94, 0x3c, 0x3c, 0x03, 0x9c }}
+
+class nsLDAPModification : public nsILDAPModification
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSILDAPMODIFICATION
+
+ // constructor & destructor
+ //
+ nsLDAPModification();
+
+ nsresult Init();
+
+private:
+ virtual ~nsLDAPModification();
+
+ int32_t mOperation;
+ nsCString mType;
+ nsCOMPtr<nsIMutableArray> mValues;
+ mozilla::Mutex mValuesLock;
+};
+
+#endif // _nsLDAPModification_h_
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;
+}
diff --git a/ldap/xpcom/src/nsLDAPOperation.h b/ldap/xpcom/src/nsLDAPOperation.h
new file mode 100644
index 000000000..7a74046d7
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPOperation.h
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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/. */
+
+#ifndef _nsLDAPOperation_h_
+#define _nsLDAPOperation_h_
+
+#include "ldap.h"
+#include "nsCOMPtr.h"
+#include "nsILDAPConnection.h"
+#include "nsILDAPOperation.h"
+#include "nsILDAPMessageListener.h"
+#include "nsStringGlue.h"
+#include "nsIMutableArray.h"
+#include "nsLDAPConnection.h"
+
+// 97a479d0-9a44-47c6-a17a-87f9b00294bb
+#define NS_LDAPOPERATION_CID \
+{ 0x97a479d0, 0x9a44, 0x47c6, \
+ { 0xa1, 0x7a, 0x87, 0xf9, 0xb0, 0x02, 0x94, 0xbb}}
+
+class nsLDAPOperation : public nsILDAPOperation
+{
+ public:
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSILDAPOPERATION
+
+ // constructor & destructor
+ //
+ nsLDAPOperation();
+
+ /**
+ * used to break cycles
+ */
+ void Clear();
+
+ private:
+ virtual ~nsLDAPOperation();
+
+ /**
+ * wrapper for ldap_add_ext()
+ *
+ * XXX should move to idl, once LDAPControls have an IDL representation
+ */
+ nsresult AddExt(const char *base, // base DN to add
+ nsIArray *mods, // Array of modifications
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls);
+
+ /**
+ * wrapper for ldap_delete_ext()
+ *
+ * XXX should move to idl, once LDAPControls have an IDL representation
+ */
+ nsresult DeleteExt(const char *base, // base DN to delete
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls);
+
+ /**
+ * wrapper for ldap_modify_ext()
+ *
+ * XXX should move to idl, once LDAPControls have an IDL representation
+ */
+ nsresult ModifyExt(const char *base, // base DN to modify
+ nsIArray *mods, // array of modifications
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls);
+
+ /**
+ * wrapper for ldap_rename()
+ *
+ * XXX should move to idl, once LDAPControls have an IDL representation
+ */
+ nsresult Rename(const char *base, // base DN to rename
+ const char *newRDn, // new RDN
+ const char *newParent, // DN of the new parent
+ bool deleteOldRDn, // remove old RDN in the entry?
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls);
+
+ /**
+ * Helper function to copy the values of an nsILDAPModification into an
+ * array of berval's.
+ */
+ static nsresult CopyValues(nsILDAPModification* aMod, berval*** aBValues);
+
+ nsCOMPtr<nsILDAPMessageListener> mMessageListener; // results go here
+ nsCOMPtr<nsISupports> mClosure; // private parameter (anything caller desires)
+ RefPtr<nsLDAPConnection> mConnection; // connection this op is on
+
+ LDAP *mConnectionHandle; // cache connection handle
+ nsCString mSavePassword;
+ nsCString mMechanism;
+ nsCOMPtr<nsIAuthModule> mAuthModule;
+ int32_t mMsgID; // opaque handle to outbound message for this op
+
+ nsCOMPtr<nsIMutableArray> mClientControls;
+ nsCOMPtr<nsIMutableArray> mServerControls;
+};
+
+#endif // _nsLDAPOperation_h
diff --git a/ldap/xpcom/src/nsLDAPProtocolHandler.js b/ldap/xpcom/src/nsLDAPProtocolHandler.js
new file mode 100644
index 000000000..71fe082ff
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPProtocolHandler.js
@@ -0,0 +1,62 @@
+/* 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/. */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const kNetworkProtocolCIDPrefix = "@mozilla.org/network/protocol;1?name=";
+const nsIProtocolHandler = Components.interfaces.nsIProtocolHandler;
+
+function makeProtocolHandler(aCID, aProtocol, aDefaultPort) {
+ return {
+ classID: Components.ID(aCID),
+ QueryInterface: XPCOMUtils.generateQI([nsIProtocolHandler]),
+
+ scheme: aProtocol,
+ defaultPort: aDefaultPort,
+ protocolFlags: nsIProtocolHandler.URI_NORELATIVE |
+ nsIProtocolHandler.URI_DANGEROUS_TO_LOAD |
+ nsIProtocolHandler.ALLOWS_PROXY,
+
+ newURI: function (aSpec, aOriginCharset, aBaseURI) {
+ var url = Components.classes["@mozilla.org/network/ldap-url;1"]
+ .createInstance(Components.interfaces.nsIURI);
+
+ if (url instanceof Components.interfaces.nsILDAPURL)
+ url.init(Components.interfaces.nsIStandardURL.URLTYPE_STANDARD,
+ aDefaultPort, aSpec, aOriginCharset, aBaseURI);
+
+ return url;
+ },
+
+ newChannel: function (aURI) {
+ return this.newChannel2(aURI, null);
+ },
+
+ newChannel2: function (aURI, aLoadInfo) {
+ if ("@mozilla.org/network/ldap-channel;1" in Components.classes) {
+ var channel = Components.classes["@mozilla.org/network/ldap-channel;1"]
+ .createInstance(Components.interfaces.nsIChannel);
+ channel.init(aURI);
+ return channel;
+ }
+
+ throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ allowPort: function (port, scheme) {
+ return port == aDefaultPort;
+ }
+ };
+}
+
+function nsLDAPProtocolHandler() {}
+
+nsLDAPProtocolHandler.prototype = makeProtocolHandler("{b3de9249-b0e5-4c12-8d91-c9a434fd80f5}", "ldap", 389);
+
+function nsLDAPSProtocolHandler() {}
+
+nsLDAPSProtocolHandler.prototype = makeProtocolHandler("{c85a5ef2-9c56-445f-b029-76889f2dd29b}", "ldaps", 636);
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([nsLDAPProtocolHandler,
+ nsLDAPSProtocolHandler]);
diff --git a/ldap/xpcom/src/nsLDAPProtocolModule.cpp b/ldap/xpcom/src/nsLDAPProtocolModule.cpp
new file mode 100644
index 000000000..6f72e7c43
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPProtocolModule.cpp
@@ -0,0 +1,147 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "nsIClassInfoImpl.h"
+#include "mozilla/ModuleUtils.h"
+
+#include "nsLDAPInternal.h"
+#include "nsLDAPURL.h"
+#include "nsLDAPConnection.h"
+#include "nsLDAPOperation.h"
+#include "nsLDAPMessage.h"
+#include "nsLDAPModification.h"
+#include "nsLDAPServer.h"
+#include "nsLDAPService.h"
+#include "nsLDAPBERValue.h"
+#include "nsLDAPBERElement.h"
+#include "nsLDAPControl.h"
+#ifdef MOZ_PREF_EXTENSIONS
+#include "nsLDAPSyncQuery.h"
+#endif
+#include "ldappr.h"
+#include "mozilla/Logging.h"
+
+// use the default constructor
+//
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPConnection)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPOperation)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPMessage)
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsLDAPModification, Init)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPServer)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPURL)
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsLDAPService, Init)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPBERValue)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPBERElement)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPControl)
+#ifdef MOZ_PREF_EXTENSIONS
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPSyncQuery)
+#endif
+
+NS_DEFINE_NAMED_CID(NS_LDAPCONNECTION_CID);
+NS_DEFINE_NAMED_CID(NS_LDAPOPERATION_CID);
+NS_DEFINE_NAMED_CID(NS_LDAPMESSAGE_CID);
+NS_DEFINE_NAMED_CID(NS_LDAPMODIFICATION_CID);
+NS_DEFINE_NAMED_CID(NS_LDAPSERVER_CID);
+NS_DEFINE_NAMED_CID(NS_LDAPSERVICE_CID);
+NS_DEFINE_NAMED_CID(NS_LDAPURL_CID);
+NS_DEFINE_NAMED_CID(NS_LDAPBERVALUE_CID);
+NS_DEFINE_NAMED_CID(NS_LDAPBERELEMENT_CID);
+#ifdef MOZ_PREF_EXTENSIONS
+NS_DEFINE_NAMED_CID(NS_LDAPSYNCQUERY_CID);
+#endif
+NS_DEFINE_NAMED_CID(NS_LDAPCONTROL_CID);
+
+// a table of the CIDs implemented by this module
+//
+
+const mozilla::Module::CIDEntry kLDAPProtocolCIDs[] = {
+ { &kNS_LDAPCONNECTION_CID, false, NULL, nsLDAPConnectionConstructor},
+ { &kNS_LDAPOPERATION_CID, false, NULL, nsLDAPOperationConstructor},
+ { &kNS_LDAPMESSAGE_CID, false, NULL, nsLDAPMessageConstructor},
+ { &kNS_LDAPMODIFICATION_CID, false, NULL, nsLDAPModificationConstructor},
+ { &kNS_LDAPSERVER_CID, false, NULL, nsLDAPServerConstructor},
+ { &kNS_LDAPSERVICE_CID, false, NULL, nsLDAPServiceConstructor},
+ { &kNS_LDAPURL_CID, false, NULL, nsLDAPURLConstructor},
+ { &kNS_LDAPBERVALUE_CID, false, NULL, nsLDAPBERValueConstructor},
+ { &kNS_LDAPBERELEMENT_CID, false, NULL, nsLDAPBERElementConstructor},
+#ifdef MOZ_PREF_EXTENSIONS
+ { &kNS_LDAPSYNCQUERY_CID, false, NULL, nsLDAPSyncQueryConstructor},
+#endif
+ { &kNS_LDAPCONTROL_CID, false, NULL, nsLDAPControlConstructor},
+ { NULL }
+};
+
+
+const mozilla::Module::ContractIDEntry kLDAPProtocolContracts[] = {
+ { "@mozilla.org/network/ldap-connection;1", &kNS_LDAPCONNECTION_CID},
+ { "@mozilla.org/network/ldap-operation;1", &kNS_LDAPOPERATION_CID},
+ { "@mozilla.org/network/ldap-message;1", &kNS_LDAPMESSAGE_CID},
+ { "@mozilla.org/network/ldap-modification;1", &kNS_LDAPMODIFICATION_CID},
+ { "@mozilla.org/network/ldap-server;1", &kNS_LDAPSERVER_CID},
+ { "@mozilla.org/network/ldap-service;1", &kNS_LDAPSERVICE_CID},
+ { "@mozilla.org/network/ldap-url;1", &kNS_LDAPURL_CID},
+ { "@mozilla.org/network/ldap-ber-value;1", &kNS_LDAPBERVALUE_CID},
+ { "@mozilla.org/network/ldap-ber-element;1", &kNS_LDAPBERELEMENT_CID},
+#ifdef MOZ_PREF_EXTENSIONS
+ { "@mozilla.org/ldapsyncquery;1", &kNS_LDAPSYNCQUERY_CID},
+#endif
+ { "@mozilla.org/network/ldap-control;1", &kNS_LDAPCONTROL_CID},
+ { NULL }
+};
+
+static nsresult
+nsLDAPInitialize()
+{
+#ifdef PR_LOGGING
+ gLDAPLogModule = PR_NewLogModule("ldap");
+ if (!gLDAPLogModule) {
+ PR_fprintf(PR_STDERR,
+ "nsLDAP_Initialize(): PR_NewLogModule() failed\n");
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+#endif
+
+ // use NSPR under the hood for all networking
+ //
+ int rv = prldap_install_routines( NULL, 1 /* shared */ );
+
+ if (rv != LDAP_SUCCESS) {
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Error,
+ ("nsLDAPInitialize(): pr_ldap_install_routines() failed: %s\n",
+ ldap_err2string(rv)));
+ return NS_ERROR_FAILURE;
+ }
+
+ // Never block for more than 10000 milliseconds (ie 10 seconds) doing any
+ // sort of I/O operation.
+ //
+ rv = prldap_set_session_option(0, 0, PRLDAP_OPT_IO_MAX_TIMEOUT,
+ 10000);
+ if (rv != LDAP_SUCCESS) {
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Error,
+ ("nsLDAPInitialize(): error setting PRLDAP_OPT_IO_MAX_TIMEOUT:"
+ " %s\n", ldap_err2string(rv)));
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+static const mozilla::Module kLDAPProtocolModule = {
+ mozilla::Module::kVersion,
+ kLDAPProtocolCIDs,
+ kLDAPProtocolContracts,
+ NULL,
+ NULL,
+ nsLDAPInitialize,
+ NULL
+};
+
+NSMODULE_DEFN(nsLDAPProtocolModule) = &kLDAPProtocolModule;
+
+#ifdef PR_LOGGING
+PRLogModuleInfo *gLDAPLogModule = 0;
+#endif
diff --git a/ldap/xpcom/src/nsLDAPSecurityGlue.cpp b/ldap/xpcom/src/nsLDAPSecurityGlue.cpp
new file mode 100644
index 000000000..c39723a72
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPSecurityGlue.cpp
@@ -0,0 +1,342 @@
+/* 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/. */
+
+// Only build this code if PSM is being built also
+//
+#include "nsLDAPInternal.h"
+#include "nsCOMPtr.h"
+#include "nsIServiceManager.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsNetCID.h"
+#include "nsISocketProvider.h"
+#include "nsISSLSocketControl.h"
+#include "nsString.h"
+#include "nsMemory.h"
+#include "plstr.h"
+#include "ldap.h"
+#include "ldappr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
+// LDAP per-session data structure.
+//
+typedef struct {
+ char *hostname;
+ LDAP_X_EXTIOF_CLOSE_CALLBACK *realClose;
+ LDAP_X_EXTIOF_CONNECT_CALLBACK *realConnect;
+ LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *realDisposeHandle;
+} nsLDAPSSLSessionClosure;
+
+// LDAP per-socket data structure.
+//
+typedef struct {
+ nsLDAPSSLSessionClosure *sessionClosure; /* session info */
+} nsLDAPSSLSocketClosure;
+
+// free the per-socket data structure as necessary
+//
+static void
+nsLDAPSSLFreeSocketClosure(nsLDAPSSLSocketClosure **aClosure)
+{
+ if (aClosure && *aClosure) {
+ free(*aClosure);
+ *aClosure = nullptr;
+ }
+}
+
+// Replacement close() function, which cleans up local stuff associated
+// with this socket, and then calls the real close function.
+//
+extern "C" int LDAP_CALLBACK
+nsLDAPSSLClose(int s, struct lextiof_socket_private *socketarg)
+{
+ PRLDAPSocketInfo socketInfo;
+ nsLDAPSSLSocketClosure *socketClosure;
+ nsLDAPSSLSessionClosure *sessionClosure;
+
+ // get the socketInfo associated with this socket
+ //
+ memset(&socketInfo, 0, sizeof(socketInfo));
+ socketInfo.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
+ if (prldap_get_socket_info(s, socketarg, &socketInfo) != LDAP_SUCCESS) {
+ NS_ERROR("nsLDAPSSLClose(): prldap_get_socket_info() failed\n");
+ return -1;
+ }
+
+ // save off the session closure data in an automatic, since we're going to
+ // need to call through it
+ //
+ socketClosure = reinterpret_cast<nsLDAPSSLSocketClosure *>
+ (socketInfo.soinfo_appdata);
+ sessionClosure = socketClosure->sessionClosure;
+
+ // free the socket closure data
+ //
+ nsLDAPSSLFreeSocketClosure(
+ reinterpret_cast<nsLDAPSSLSocketClosure **>
+ (&socketInfo.soinfo_appdata));
+
+ // call the real close function
+ //
+ return (*(sessionClosure->realClose))(s, socketarg);
+}
+
+// Replacement connection function. Calls the real connect function,
+//
+extern "C" int LDAP_CALLBACK
+nsLDAPSSLConnect(const char *hostlist, int defport, int timeout,
+ unsigned long options,
+ struct lextiof_session_private *sessionarg,
+ struct lextiof_socket_private **socketargp )
+{
+ PRLDAPSocketInfo socketInfo;
+ PRLDAPSessionInfo sessionInfo;
+ nsLDAPSSLSocketClosure *socketClosure = nullptr;
+ nsLDAPSSLSessionClosure *sessionClosure;
+ int intfd = -1;
+ nsCOMPtr <nsISupports> securityInfo;
+ nsCOMPtr <nsISocketProvider> tlsSocketProvider;
+ nsCOMPtr <nsISSLSocketControl> sslSocketControl;
+ nsresult rv;
+
+ // Ensure secure option is set. Also, clear secure bit in options
+ // the we pass to the standard connect() function (since it doesn't know
+ // how to handle the secure option).
+ //
+ NS_ASSERTION(options & LDAP_X_EXTIOF_OPT_SECURE,
+ "nsLDAPSSLConnect(): called for non-secure connection");
+ options &= ~LDAP_X_EXTIOF_OPT_SECURE;
+
+ // Retrieve session info. so we can store a pointer to our session info.
+ // in our socket info. later.
+ //
+ memset(&sessionInfo, 0, sizeof(sessionInfo));
+ sessionInfo.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
+ if (prldap_get_session_info(nullptr, sessionarg, &sessionInfo)
+ != LDAP_SUCCESS) {
+ NS_ERROR("nsLDAPSSLConnect(): unable to get session info");
+ return -1;
+ }
+ sessionClosure = reinterpret_cast<nsLDAPSSLSessionClosure *>
+ (sessionInfo.seinfo_appdata);
+
+ // Call the real connect() callback to make the TCP connection. If it
+ // succeeds, *socketargp is set.
+ //
+ intfd = (*(sessionClosure->realConnect))(hostlist, defport, timeout,
+ options, sessionarg, socketargp);
+ if ( intfd < 0 ) {
+ PR_LOG(gLDAPLogModule, PR_LOG_DEBUG,
+ ("nsLDAPSSLConnect(): standard connect() function returned %d",
+ intfd));
+ return intfd;
+ }
+
+ // Retrieve socket info from the newly created socket so that we
+ // have the PRFileDesc onto which we will be layering SSL.
+ //
+ memset(&socketInfo, 0, sizeof(socketInfo));
+ socketInfo.soinfo_size = PRLDAP_SOCKETINFO_SIZE;
+ if (prldap_get_socket_info(intfd, *socketargp, &socketInfo)
+ != LDAP_SUCCESS) {
+ NS_ERROR("nsLDAPSSLConnect(): unable to get socket info");
+ goto close_socket_and_exit_with_error;
+ }
+
+ // Allocate a structure to hold our socket-specific data.
+ //
+ socketClosure = static_cast<nsLDAPSSLSocketClosure *>(moz_xmalloc(
+ sizeof(nsLDAPSSLSocketClosure)));
+ if (!socketClosure) {
+ NS_WARNING("nsLDAPSSLConnect(): unable to allocate socket closure");
+ goto close_socket_and_exit_with_error;
+ }
+ memset(socketClosure, 0, sizeof(nsLDAPSSLSocketClosure));
+ socketClosure->sessionClosure = sessionClosure;
+
+ // Add the NSPR layer for SSL provided by PSM to this socket.
+ //
+ tlsSocketProvider = do_GetService(NS_STARTTLSSOCKETPROVIDER_CONTRACTID,
+ &rv);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPSSLConnect(): unable to get socket provider service");
+ goto close_socket_and_exit_with_error;
+ }
+ // XXXdmose: Note that hostlist can be a list of hosts (in the
+ // current XPCOM SDK code, it will always be a list of IP
+ // addresses). Because of this, we need to use
+ // sessionClosure->hostname which was passed in separately to tell
+ // AddToSocket what to match the name in the certificate against.
+ // What exactly happen will happen when this is used with some IP
+ // address in the list other than the first one is not entirely
+ // clear, and I suspect it may depend on the format of the name in
+ // the certificate. Need to investigate.
+ //
+ rv = tlsSocketProvider->AddToSocket(PR_AF_INET,
+ sessionClosure->hostname,
+ defport,
+ nullptr,
+ mozilla::NeckoOriginAttributes(),
+ 0,
+ socketInfo.soinfo_prfd,
+ getter_AddRefs(securityInfo));
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPSSLConnect(): unable to add SSL layer to socket");
+ goto close_socket_and_exit_with_error;
+ }
+
+ // If possible we want to avoid using SSLv2, as this can confuse
+ // some directory servers (notably the netscape 4.1 ds). The only
+ // way that PSM provides for us to do this is to use a socket that can
+ // be used for the STARTTLS protocol, because the STARTTLS protocol disallows
+ // the use of SSLv2.
+ // (Thanks to Brian Ryner for helping figure this out).
+ //
+ sslSocketControl = do_QueryInterface(securityInfo, &rv);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("nsLDAPSSLConnect(): unable to QI to nsISSLSocketControl");
+ } else {
+ rv = sslSocketControl->StartTLS();
+ if (NS_FAILED(rv)) {
+ NS_WARNING("nsLDAPSSLConnect(): StartTLS failed");
+ }
+ }
+
+ // Attach our closure to the socketInfo.
+ //
+ socketInfo.soinfo_appdata = reinterpret_cast<prldap_socket_private *>
+ (socketClosure);
+ if (prldap_set_socket_info(intfd, *socketargp, &socketInfo)
+ != LDAP_SUCCESS ) {
+ NS_ERROR("nsLDAPSSLConnect(): unable to set socket info");
+ }
+ return intfd; // success
+
+close_socket_and_exit_with_error:
+ if (socketInfo.soinfo_prfd) {
+ PR_Close(socketInfo.soinfo_prfd);
+ }
+ if (socketClosure) {
+ nsLDAPSSLFreeSocketClosure(&socketClosure);
+ }
+ if ( intfd >= 0 && *socketargp ) {
+ (*(sessionClosure->realClose))(intfd, *socketargp);
+ }
+ return -1;
+
+}
+
+// Free data associated with this session (LDAP *) as necessary.
+//
+static void
+nsLDAPSSLFreeSessionClosure(nsLDAPSSLSessionClosure **aSessionClosure)
+{
+ if (aSessionClosure && *aSessionClosure) {
+
+ // free the hostname
+ //
+ if ( (*aSessionClosure)->hostname ) {
+ PL_strfree((*aSessionClosure)->hostname);
+ (*aSessionClosure)->hostname = nullptr;
+ }
+
+ // free the structure itself
+ //
+ free(*aSessionClosure);
+ *aSessionClosure = nullptr;
+ }
+}
+
+// Replacement session handle disposal code. First cleans up our local
+// stuff, then calls the original session handle disposal function.
+//
+extern "C" void LDAP_CALLBACK
+nsLDAPSSLDisposeHandle(LDAP *ld, struct lextiof_session_private *sessionarg)
+{
+ PRLDAPSessionInfo sessionInfo;
+ nsLDAPSSLSessionClosure *sessionClosure;
+ LDAP_X_EXTIOF_DISPOSEHANDLE_CALLBACK *disposehdl_fn;
+
+ memset(&sessionInfo, 0, sizeof(sessionInfo));
+ sessionInfo.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
+ if (prldap_get_session_info(ld, nullptr, &sessionInfo) == LDAP_SUCCESS) {
+ sessionClosure = reinterpret_cast<nsLDAPSSLSessionClosure *>
+ (sessionInfo.seinfo_appdata);
+ disposehdl_fn = sessionClosure->realDisposeHandle;
+ nsLDAPSSLFreeSessionClosure(&sessionClosure);
+ (*disposehdl_fn)(ld, sessionarg);
+ }
+}
+
+// Installs appropriate routines and data for making this connection
+// handle SSL. The aHostName is ultimately passed to PSM and is used to
+// validate certificates.
+//
+nsresult
+nsLDAPInstallSSL( LDAP *ld, const char *aHostName)
+{
+ struct ldap_x_ext_io_fns iofns;
+ nsLDAPSSLSessionClosure *sessionClosure;
+ PRLDAPSessionInfo sessionInfo;
+
+ // Allocate our own session information.
+ //
+ sessionClosure = static_cast<nsLDAPSSLSessionClosure *>(moz_xmalloc(
+ sizeof(nsLDAPSSLSessionClosure)));
+ if (!sessionClosure) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ memset(sessionClosure, 0, sizeof(nsLDAPSSLSessionClosure));
+
+ // Override a few functions, saving a pointer to the original function
+ // in each case so we can call it from our SSL savvy functions.
+ //
+ memset(&iofns, 0, sizeof(iofns));
+ iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
+ if (ldap_get_option(ld, LDAP_X_OPT_EXTIO_FN_PTRS,
+ static_cast<void *>(&iofns)) != LDAP_SUCCESS) {
+ NS_ERROR("nsLDAPInstallSSL(): unexpected error getting"
+ " LDAP_X_OPT_EXTIO_FN_PTRS");
+ nsLDAPSSLFreeSessionClosure(&sessionClosure);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // Make a copy of the hostname to pass to AddToSocket later
+ //
+ sessionClosure->hostname = PL_strdup(aHostName);
+ if (!sessionClosure->hostname) {
+ NS_ERROR("nsLDAPInstallSSL(): PL_strdup failed\n");
+ nsLDAPSSLFreeSessionClosure(&sessionClosure);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // Override functions
+ //
+ sessionClosure->realClose = iofns.lextiof_close;
+ iofns.lextiof_close = nsLDAPSSLClose;
+ sessionClosure->realConnect = iofns.lextiof_connect;
+ iofns.lextiof_connect = nsLDAPSSLConnect;
+ sessionClosure->realDisposeHandle = iofns.lextiof_disposehandle;
+ iofns.lextiof_disposehandle = nsLDAPSSLDisposeHandle;
+
+ if (ldap_set_option(ld, LDAP_X_OPT_EXTIO_FN_PTRS,
+ static_cast<void *>(&iofns)) != LDAP_SUCCESS) {
+ NS_ERROR("nsLDAPInstallSSL(): error setting LDAP_X_OPT_EXTIO_FN_PTRS");
+ nsLDAPSSLFreeSessionClosure(&sessionClosure);
+ return NS_ERROR_FAILURE;
+ }
+
+ // Store session info. for later retrieval.
+ //
+ sessionInfo.seinfo_size = PRLDAP_SESSIONINFO_SIZE;
+ sessionInfo.seinfo_appdata = reinterpret_cast<prldap_session_private *>
+ (sessionClosure);
+ if (prldap_set_session_info(ld, nullptr, &sessionInfo) != LDAP_SUCCESS) {
+ NS_ERROR("nsLDAPInstallSSL(): error setting prldap session info");
+ free(sessionClosure);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
diff --git a/ldap/xpcom/src/nsLDAPServer.cpp b/ldap/xpcom/src/nsLDAPServer.cpp
new file mode 100644
index 000000000..34504c5c7
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPServer.cpp
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "nsLDAPServer.h"
+
+NS_IMPL_ISUPPORTS(nsLDAPServer, nsILDAPServer)
+
+nsLDAPServer::nsLDAPServer()
+ : mSizeLimit(0),
+ mProtocolVersion(nsILDAPConnection::VERSION3)
+{
+}
+
+nsLDAPServer::~nsLDAPServer()
+{
+}
+
+// attribute wstring key;
+NS_IMETHODIMP nsLDAPServer::GetKey(char16_t **_retval)
+{
+ if (!_retval) {
+ NS_ERROR("nsLDAPServer::GetKey: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ *_retval = ToNewUnicode(mKey);
+ if (!*_retval) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPServer::SetKey(const char16_t *aKey)
+{
+ mKey = aKey;
+ return NS_OK;
+}
+
+// attribute AUTF8String username;
+NS_IMETHODIMP nsLDAPServer::GetUsername(nsACString& _retval)
+{
+ _retval.Assign(mUsername);
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPServer::SetUsername(const nsACString& aUsername)
+{
+ mUsername.Assign(aUsername);
+ return NS_OK;
+}
+
+// attribute AUTF8String password;
+NS_IMETHODIMP nsLDAPServer::GetPassword(nsACString& _retval)
+{
+ _retval.Assign(mPassword);
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPServer::SetPassword(const nsACString& aPassword)
+{
+ mPassword.Assign(aPassword);
+ return NS_OK;
+}
+
+// attribute AUTF8String binddn;
+NS_IMETHODIMP nsLDAPServer::GetBinddn(nsACString& _retval)
+{
+ _retval.Assign(mBindDN);
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPServer::SetBinddn(const nsACString& aBindDN)
+{
+ mBindDN.Assign(aBindDN);
+ return NS_OK;
+}
+
+// attribute unsigned long sizelimit;
+NS_IMETHODIMP nsLDAPServer::GetSizelimit(uint32_t *_retval)
+{
+ if (!_retval) {
+ NS_ERROR("nsLDAPServer::GetSizelimit: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ *_retval = mSizeLimit;
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPServer::SetSizelimit(uint32_t aSizeLimit)
+{
+ mSizeLimit = aSizeLimit;
+ return NS_OK;
+}
+
+// attribute nsILDAPURL url;
+NS_IMETHODIMP nsLDAPServer::GetUrl(nsILDAPURL **_retval)
+{
+ if (!_retval) {
+ NS_ERROR("nsLDAPServer::GetUrl: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ NS_IF_ADDREF(*_retval = mURL);
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPServer::SetUrl(nsILDAPURL *aURL)
+{
+ mURL = aURL;
+ return NS_OK;
+}
+
+// attribute long protocolVersion
+NS_IMETHODIMP nsLDAPServer::GetProtocolVersion(uint32_t *_retval)
+{
+ if (!_retval) {
+ NS_ERROR("nsLDAPServer::GetProtocolVersion: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ *_retval = mProtocolVersion;
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPServer::SetProtocolVersion(uint32_t aVersion)
+{
+ if (aVersion != nsILDAPConnection::VERSION2 &&
+ aVersion != nsILDAPConnection::VERSION3) {
+ NS_ERROR("nsLDAPServer::SetProtocolVersion: invalid version");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ mProtocolVersion = aVersion;
+ return NS_OK;
+}
diff --git a/ldap/xpcom/src/nsLDAPServer.h b/ldap/xpcom/src/nsLDAPServer.h
new file mode 100644
index 000000000..e8ec5da35
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPServer.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "nsCOMPtr.h"
+#include "nsStringGlue.h"
+#include "nsILDAPServer.h"
+#include "nsILDAPURL.h"
+
+// 8bbbaa54-f316-4271-87c3-d52b5b1c1f5b
+#define NS_LDAPSERVER_CID \
+{ 0x8bbbaa54, 0xf316, 0x4271, \
+ { 0x87, 0xc3, 0xd5, 0x2b, 0x5b, 0x1c, 0x1f, 0x5b}}
+
+class nsLDAPServer : public nsILDAPServer
+{
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSILDAPSERVER
+
+ // Constructor & destructor
+ //
+ nsLDAPServer();
+
+ protected:
+ virtual ~nsLDAPServer();
+
+ nsString mKey; // Unique identifier for this server object
+ nsCString mUsername; // Username / UID
+ nsCString mPassword; // Password to bind with
+ nsCString mBindDN; // DN associated with the UID above
+ uint32_t mSizeLimit; // Limit the LDAP search to this # of entries
+ uint32_t mProtocolVersion; // What version of LDAP to use?
+ // This "links" to a LDAP URL object, which holds further information
+ // related to the LDAP server. Like Host, port, base-DN and scope.
+ nsCOMPtr<nsILDAPURL> mURL;
+};
diff --git a/ldap/xpcom/src/nsLDAPService.cpp b/ldap/xpcom/src/nsLDAPService.cpp
new file mode 100644
index 000000000..9445a5f33
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPService.cpp
@@ -0,0 +1,985 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "nsLDAPService.h"
+#include "nsLDAPConnection.h"
+#include "nsLDAPOperation.h"
+#include "nsIServiceManager.h"
+#include "nsIConsoleService.h"
+#include "nsILDAPURL.h"
+#include "nsMemory.h"
+#include "nsILDAPErrors.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "mozilla/Logging.h"
+
+using namespace mozilla;
+
+// Constants for CIDs used here.
+//
+static NS_DEFINE_CID(kLDAPConnectionCID, NS_LDAPCONNECTION_CID);
+static NS_DEFINE_CID(kLDAPOperationCID, NS_LDAPOPERATION_CID);
+
+// First we provide all the methods for the "local" class
+// nsLDAPServiceEntry.
+//
+
+// constructor
+//
+nsLDAPServiceEntry::nsLDAPServiceEntry()
+ : mLeases(0),
+ mTimestamp(0),
+ mDelete(false),
+ mRebinding(false)
+
+{
+}
+
+// Init function
+//
+bool nsLDAPServiceEntry::Init()
+{
+ return true;
+}
+
+// Set/Get the timestamp when this server was last used. We might have
+// to use an "interval" here instead, see Bug #76887.
+//
+PRTime nsLDAPServiceEntry::GetTimestamp()
+{
+ return mTimestamp;
+}
+void nsLDAPServiceEntry::SetTimestamp()
+{
+ mTimestamp = PR_Now();
+}
+
+
+// Increment, decrement and Get the leases. This code might go away
+// with bug #75954.
+//
+void nsLDAPServiceEntry::IncrementLeases()
+{
+ mLeases++;
+}
+bool nsLDAPServiceEntry::DecrementLeases()
+{
+ if (!mLeases) {
+ return false;
+ }
+ mLeases--;
+
+ return true;
+}
+uint32_t nsLDAPServiceEntry::GetLeases()
+{
+ return mLeases;
+}
+
+// Get/Set the nsLDAPServer object for this entry.
+//
+already_AddRefed<nsILDAPServer> nsLDAPServiceEntry::GetServer()
+{
+ nsCOMPtr<nsILDAPServer> server = mServer;
+ return server.forget();
+}
+bool nsLDAPServiceEntry::SetServer(nsILDAPServer *aServer)
+{
+ if (!aServer) {
+ return false;
+ }
+ mServer = aServer;
+
+ return true;
+}
+
+// Get/Set/Clear the nsLDAPConnection object for this entry.
+//
+already_AddRefed<nsILDAPConnection> nsLDAPServiceEntry::GetConnection()
+{
+ nsCOMPtr<nsILDAPConnection> conn = mConnection;
+ return conn.forget();
+}
+void nsLDAPServiceEntry::SetConnection(nsILDAPConnection *aConnection)
+{
+ mConnection = aConnection;
+}
+
+// Get/Set the nsLDAPMessage object for this entry (it's a "cache").
+//
+already_AddRefed<nsILDAPMessage> nsLDAPServiceEntry::GetMessage()
+{
+ nsCOMPtr<nsILDAPMessage> message = mMessage;
+ return message.forget();
+}
+void nsLDAPServiceEntry::SetMessage(nsILDAPMessage *aMessage)
+{
+ mMessage = aMessage;
+}
+
+// Push/Pop pending listeners/callback for this server entry. This is
+// implemented as a "stack" on top of the nsCOMArray, since we can
+// potentially have more than one listener waiting for the connection
+// to be available for consumption.
+//
+already_AddRefed<nsILDAPMessageListener> nsLDAPServiceEntry::PopListener()
+{
+ if (mListeners.IsEmpty()) {
+ return 0;
+ }
+
+ nsCOMPtr<nsILDAPMessageListener> listener = mListeners[0];
+ mListeners.RemoveObjectAt(0);
+ return listener.forget();
+}
+bool nsLDAPServiceEntry::PushListener(nsILDAPMessageListener *listener)
+{
+ return mListeners.AppendObject(listener);
+}
+
+// Mark this server to currently be rebinding. This is to avoid a
+// race condition where multiple consumers could potentially request
+// to reconnect the connection.
+//
+bool nsLDAPServiceEntry::IsRebinding()
+{
+ return mRebinding;
+}
+void nsLDAPServiceEntry::SetRebinding(bool aState)
+{
+ mRebinding = aState;
+}
+
+// Mark a service entry for deletion, this is "dead" code right now,
+// see bug #75966.
+//
+bool nsLDAPServiceEntry::DeleteEntry()
+{
+ mDelete = true;
+
+ return true;
+}
+// This is the end of the nsLDAPServiceEntry class
+
+
+// Here begins the implementation for nsLDAPService
+//
+NS_IMPL_ISUPPORTS(nsLDAPService,
+ nsILDAPService,
+ nsILDAPMessageListener)
+
+
+// constructor
+//
+nsLDAPService::nsLDAPService()
+ : mLock("nsLDAPService.mLock")
+{
+}
+
+// destructor
+//
+nsLDAPService::~nsLDAPService()
+{
+}
+
+// Initializer
+//
+nsresult nsLDAPService::Init()
+{
+ return NS_OK;
+}
+
+// void addServer (in nsILDAPServer aServer);
+NS_IMETHODIMP nsLDAPService::AddServer(nsILDAPServer *aServer)
+{
+ nsLDAPServiceEntry *entry;
+ nsString key;
+ nsresult rv;
+
+ if (!aServer) {
+ NS_ERROR("nsLDAPService::AddServer: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ // Set up the hash key for the server entry
+ //
+ rv = aServer->GetKey(getter_Copies(key));
+ if (NS_FAILED(rv)) {
+ // Only pass along errors we are aware of
+ if ((rv == NS_ERROR_OUT_OF_MEMORY) || (rv == NS_ERROR_NULL_POINTER))
+ return rv;
+ else
+ return NS_ERROR_FAILURE;
+ }
+
+ // Create the new service server entry, and add it into the hash table
+ //
+ entry = new nsLDAPServiceEntry;
+ if (!entry) {
+ NS_ERROR("nsLDAPService::AddServer: out of memory ");
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ if (!entry->Init()) {
+ delete entry;
+ NS_ERROR("nsLDAPService::AddServer: out of memory ");
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (!entry->SetServer(aServer)) {
+ delete entry;
+ return NS_ERROR_FAILURE;
+ }
+
+ // We increment the refcount here for the server entry, when
+ // we purge a server completely from the service (TBD), we
+ // need to decrement the counter as well.
+ //
+ {
+ MutexAutoLock lock(mLock);
+
+ if (mServers.Get(key)) {
+ // Collision detected, lets just throw away this service entry
+ // and keep the old one.
+ //
+ delete entry;
+ return NS_ERROR_FAILURE;
+ }
+ mServers.Put(key, entry);
+ }
+ NS_ADDREF(aServer);
+
+ return NS_OK;
+}
+
+// void deleteServer (in wstring aKey);
+NS_IMETHODIMP nsLDAPService::DeleteServer(const char16_t *aKey)
+{
+ nsLDAPServiceEntry *entry;
+ MutexAutoLock lock(mLock);
+
+ // We should probably rename the key for this entry now that it's
+ // "deleted", so that we can add in a new one with the same ID.
+ // This is bug #77669.
+ //
+ mServers.Get(nsDependentString(aKey), &entry);
+ if (entry) {
+ if (entry->GetLeases() > 0) {
+ return NS_ERROR_FAILURE;
+ }
+ entry->DeleteEntry();
+ } else {
+ // There is no Server entry for this key
+ //
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+// nsILDAPServer getServer (in wstring aKey);
+NS_IMETHODIMP nsLDAPService::GetServer(const char16_t *aKey,
+ nsILDAPServer **_retval)
+{
+ nsLDAPServiceEntry *entry;
+ MutexAutoLock lock(mLock);
+
+ if (!_retval) {
+ NS_ERROR("nsLDAPService::GetServer: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (!mServers.Get(nsDependentString(aKey), &entry)) {
+ *_retval = 0;
+ return NS_ERROR_FAILURE;
+ }
+ if (!(*_retval = entry->GetServer().take())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+//void requestConnection (in wstring aKey,
+// in nsILDAPMessageListener aMessageListener);
+NS_IMETHODIMP nsLDAPService::RequestConnection(const char16_t *aKey,
+ nsILDAPMessageListener *aListener)
+{
+ nsLDAPServiceEntry *entry;
+ nsCOMPtr<nsILDAPConnection> conn;
+ nsCOMPtr<nsILDAPMessage> message;
+ nsresult rv;
+
+ if (!aListener) {
+ NS_ERROR("nsLDAPService::RequestConection: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ // Try to find a possibly cached connection and LDAP message.
+ //
+ {
+ MutexAutoLock lock(mLock);
+
+ if (!mServers.Get(nsDependentString(aKey), &entry)) {
+ return NS_ERROR_FAILURE;
+ }
+ entry->SetTimestamp();
+
+ conn = entry->GetConnection();
+ message = entry->GetMessage();
+ }
+
+ if (conn) {
+ if (message) {
+ // We already have the connection, and the message, ready to
+ // be used. This might be confusing, since we actually call
+ // the listener before this function returns, see bug #75899.
+ //
+ aListener->OnLDAPMessage(message);
+ return NS_OK;
+ }
+ } else {
+ rv = EstablishConnection(entry, aListener);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ }
+
+ // We got a new connection, now push the listeners on our stack,
+ // until we get the LDAP message back.
+ //
+ {
+ MutexAutoLock lock(mLock);
+
+ if (!mServers.Get(nsDependentString(aKey), &entry) ||
+ !entry->PushListener(static_cast<nsILDAPMessageListener *>
+ (aListener))) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ return NS_OK;
+}
+
+// nsILDAPConnection getConnection (in wstring aKey);
+NS_IMETHODIMP nsLDAPService::GetConnection(const char16_t *aKey,
+ nsILDAPConnection **_retval)
+{
+ nsLDAPServiceEntry *entry;
+ MutexAutoLock lock(mLock);
+
+ if (!_retval) {
+ NS_ERROR("nsLDAPService::GetConnection: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ if (!mServers.Get(nsDependentString(aKey), &entry)) {
+ *_retval = 0;
+ return NS_ERROR_FAILURE;
+ }
+ entry->SetTimestamp();
+ entry->IncrementLeases();
+ if (!(*_retval = entry->GetConnection().take())){
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+// void releaseConnection (in wstring aKey);
+NS_IMETHODIMP nsLDAPService::ReleaseConnection(const char16_t *aKey)
+{
+ nsLDAPServiceEntry *entry;
+ MutexAutoLock lock(mLock);
+
+ if (!mServers.Get(nsDependentString(aKey), &entry)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (entry->GetLeases() > 0) {
+ entry->SetTimestamp();
+ entry->DecrementLeases();
+ } else {
+ // Releasing a non-leased connection is currently a No-Op.
+ //
+ }
+
+ return NS_OK;
+}
+
+// void reconnectConnection (in wstring aKey,
+// in nsILDAPMessageListener aMessageListener);
+NS_IMETHODIMP nsLDAPService::ReconnectConnection(const char16_t *aKey,
+ nsILDAPMessageListener *aListener)
+{
+ nsLDAPServiceEntry *entry;
+ nsresult rv;
+
+ if (!aListener) {
+ NS_ERROR("nsLDAPService::ReconnectConnection: null pointer ");
+ return NS_ERROR_NULL_POINTER;
+ }
+
+ {
+ MutexAutoLock lock(mLock);
+
+ if (!mServers.Get(nsDependentString(aKey), &entry)) {
+ return NS_ERROR_FAILURE;
+ }
+ entry->SetTimestamp();
+
+ if (entry->IsRebinding()) {
+ if (!entry->PushListener(aListener)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+ // Clear the old connection and message, which should get garbaged
+ // collected now. We mark this as being "rebinding" now, and it
+ // we be marked as finished either if there's an error condition,
+ // or if the OnLDAPMessage() method gets called (i.e. bind() done).
+ //
+ entry->SetMessage(0);
+ entry->SetConnection(0);
+
+ // Get a new connection
+ //
+ entry->SetRebinding(true);
+ }
+
+ rv = EstablishConnection(entry, aListener);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ {
+ MutexAutoLock lock(mLock);
+
+ if (!entry->PushListener(static_cast<nsILDAPMessageListener *>
+ (aListener))) {
+ entry->SetRebinding(false);
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ return NS_OK;
+}
+
+/**
+ * Messages received are passed back via this function.
+ *
+ * @arg aMessage The message that was returned, 0 if none was.
+ *
+ * void OnLDAPMessage (in nsILDAPMessage aMessage)
+ */
+NS_IMETHODIMP
+nsLDAPService::OnLDAPMessage(nsILDAPMessage *aMessage)
+{
+ nsCOMPtr<nsILDAPOperation> operation;
+ nsCOMPtr<nsILDAPConnection> connection;
+ int32_t messageType;
+
+ // XXXleif: NULL messages are supposedly allowed, but the semantics
+ // are not definted (yet?). This is something to look out for...
+ //
+
+
+ // figure out what sort of message was returned
+ //
+ nsresult rv = aMessage->GetType(&messageType);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPService::OnLDAPMessage(): unexpected error in "
+ "nsLDAPMessage::GetType()");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ switch (messageType) {
+ case LDAP_RES_BIND:
+ // a bind has completed
+ //
+ rv = aMessage->GetOperation(getter_AddRefs(operation));
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPService::OnLDAPMessage(): unexpected error in "
+ "nsLDAPMessage::GetOperation()");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ rv = operation->GetConnection(getter_AddRefs(connection));
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPService::OnLDAPMessage(): unexpected error in "
+ "nsLDAPOperation::GetConnection()");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // Now we have the connection, lets find the corresponding
+ // server entry in the Service.
+ //
+ {
+ nsCOMPtr<nsILDAPMessageListener> listener;
+ nsCOMPtr<nsILDAPMessage> message;
+ nsLDAPServiceEntry *entry;
+ MutexAutoLock lock(mLock);
+
+ if (!mConnections.Get(connection, &entry)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ message = entry->GetMessage();
+ if (message) {
+ // We already have a message, lets keep that one.
+ //
+ return NS_ERROR_FAILURE;
+ }
+
+ entry->SetRebinding(false);
+ entry->SetMessage(aMessage);
+
+ // Now process all the pending callbacks/listeners. We
+ // have to make sure to unlock before calling a listener,
+ // since it's likely to call back into us again.
+ //
+ while ((listener = entry->PopListener())) {
+ MutexAutoUnlock unlock(mLock);
+ listener->OnLDAPMessage(aMessage);
+ }
+ }
+ break;
+
+ default:
+ NS_WARNING("nsLDAPService::OnLDAPMessage(): unexpected LDAP message "
+ "received");
+
+ // get the console service so we can log a message
+ //
+ nsCOMPtr<nsIConsoleService> consoleSvc =
+ do_GetService("@mozilla.org/consoleservice;1", &rv);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPChannel::OnLDAPMessage() couldn't get console "
+ "service");
+ break;
+ }
+
+ // log the message
+ //
+ rv = consoleSvc->LogStringMessage(
+ NS_LITERAL_STRING("LDAP: WARNING: nsLDAPService::OnLDAPMessage(): Unexpected LDAP message received").get());
+ NS_ASSERTION(NS_SUCCEEDED(rv), "nsLDAPService::OnLDAPMessage(): "
+ "consoleSvc->LogStringMessage() failed");
+ break;
+ }
+
+ return NS_OK;
+}
+
+// void onLDAPInit (in nsILDAPConnection aConn, in nsresult aStatus); */
+//
+NS_IMETHODIMP
+nsLDAPService::OnLDAPInit(nsILDAPConnection *aConn, nsresult aStatus)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// Helper function to establish an LDAP connection properly.
+//
+nsresult
+nsLDAPService::EstablishConnection(nsLDAPServiceEntry *aEntry,
+ nsILDAPMessageListener *aListener)
+{
+ nsCOMPtr<nsILDAPOperation> operation;
+ nsCOMPtr<nsILDAPServer> server;
+ nsCOMPtr<nsILDAPURL> url;
+ nsCOMPtr<nsILDAPConnection> conn, conn2;
+ nsCOMPtr<nsILDAPMessage> message;
+ nsAutoCString binddn;
+ nsAutoCString password;
+ uint32_t protocolVersion;
+ nsresult rv;
+
+ server = aEntry->GetServer();
+ if (!server) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Get username, password, and protocol version from the server entry.
+ //
+ rv = server->GetBinddn(binddn);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+ rv = server->GetPassword(password);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+ rv = server->GetProtocolVersion(&protocolVersion);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Get the host and port out of the URL, which is in the server entry.
+ //
+ rv = server->GetUrl(getter_AddRefs(url));
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+ // Create a new connection for this server.
+ //
+ conn = do_CreateInstance(kLDAPConnectionCID, &rv);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPService::EstablishConnection(): could not create "
+ "@mozilla.org/network/ldap-connection;1");
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = conn->Init(url, binddn, this, nullptr, protocolVersion);
+ if (NS_FAILED(rv)) {
+ switch (rv) {
+ // Only pass along errors we are aware of
+ //
+ case NS_ERROR_OUT_OF_MEMORY:
+ case NS_ERROR_NOT_AVAILABLE:
+ case NS_ERROR_FAILURE:
+ return rv;
+
+ case NS_ERROR_ILLEGAL_VALUE:
+ default:
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+
+ // Try to detect a collision, i.e. someone established a connection
+ // just before we did. If so, we drop ours. This code is somewhat
+ // similar to what happens in RequestConnection(), i.e. we try to
+ // call the listener directly if possible, and if not, push it on
+ // the stack of pending requests.
+ //
+ {
+ MutexAutoLock lock(mLock);
+
+ conn2 = aEntry->GetConnection();
+ message = aEntry->GetMessage();
+ }
+
+ if (conn2) {
+ // Drop the new connection, we can't use it.
+ //
+ conn = nullptr;
+ if (message) {
+ aListener->OnLDAPMessage(message);
+ return NS_OK;
+ }
+
+ {
+ MutexAutoLock lock(mLock);
+
+ if (!aEntry->PushListener(static_cast<nsILDAPMessageListener *>
+ (aListener))) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ return NS_OK;
+ }
+
+ // We made the connection, lets store it to the server entry,
+ // and also update the reverse lookup tables (for finding the
+ // server entry related to a particular connection).
+ //
+ {
+ MutexAutoLock lock(mLock);
+
+ aEntry->SetConnection(conn);
+ mConnections.Put(conn, aEntry);
+ }
+
+ // Setup the bind() operation.
+ //
+ operation = do_CreateInstance(kLDAPOperationCID, &rv);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = operation->Init(conn, this, nullptr);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_UNEXPECTED; // this should never happen
+ }
+
+ // Start a bind operation
+ //
+ // Here we need to support the password, see bug #75990
+ //
+ rv = operation->SimpleBind(password);
+ if (NS_FAILED(rv)) {
+ // Only pass along errors we are aware of
+ if ((rv == NS_ERROR_LDAP_ENCODING_ERROR) ||
+ (rv == NS_ERROR_FAILURE) ||
+ (rv == NS_ERROR_OUT_OF_MEMORY))
+ return rv;
+ else
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
+
+/* AString createFilter (in unsigned long aMaxSize, in AString aPattern, in AString aPrefix, in AString aSuffix, in AString aAttr, in AString aValue); */
+NS_IMETHODIMP nsLDAPService::CreateFilter(uint32_t aMaxSize,
+ const nsACString & aPattern,
+ const nsACString & aPrefix,
+ const nsACString & aSuffix,
+ const nsACString & aAttr,
+ const nsACString & aValue,
+ nsACString & _retval)
+{
+ if (!aMaxSize) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // figure out how big of an array we're going to need for the tokens,
+ // including a trailing NULL, and allocate space for it.
+ //
+ const char *iter = aValue.BeginReading();
+ const char *iterEnd = aValue.EndReading();
+ uint32_t numTokens = CountTokens(iter, iterEnd);
+ char **valueWords;
+ valueWords = static_cast<char **>(moz_xmalloc((numTokens + 1) *
+ sizeof(char *)));
+ if (!valueWords) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // build the array of values
+ //
+ uint32_t curToken = 0;
+ while (iter != iterEnd && curToken < numTokens ) {
+ valueWords[curToken] = NextToken(&iter, &iterEnd);
+ if ( !valueWords[curToken] ) {
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(curToken, valueWords);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ curToken++;
+ }
+ valueWords[numTokens] = 0; // end of array signal to LDAP C SDK
+
+ // make buffer to be used for construction
+ //
+ char *buffer = static_cast<char *>(moz_xmalloc(aMaxSize * sizeof(char)));
+ if (!buffer) {
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(numTokens, valueWords);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // create the filter itself
+ //
+ nsresult rv;
+ int result = ldap_create_filter(buffer, aMaxSize,
+ const_cast<char *>(PromiseFlatCString(aPattern).get()),
+ const_cast<char *>(PromiseFlatCString(aPrefix).get()),
+ const_cast<char *>(PromiseFlatCString(aSuffix).get()),
+ const_cast<char *>(PromiseFlatCString(aAttr).get()),
+ const_cast<char *>(PromiseFlatCString(aValue).get()),
+ valueWords);
+ switch (result) {
+ case LDAP_SUCCESS:
+ rv = NS_OK;
+ break;
+
+ case LDAP_SIZELIMIT_EXCEEDED:
+ MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug,
+ ("nsLDAPService::CreateFilter(): "
+ "filter longer than max size of %d generated",
+ aMaxSize));
+ rv = NS_ERROR_NOT_AVAILABLE;
+ break;
+
+ case LDAP_PARAM_ERROR:
+ rv = NS_ERROR_INVALID_ARG;
+ break;
+
+ default:
+ NS_ERROR("nsLDAPService::CreateFilter(): ldap_create_filter() "
+ "returned unexpected error");
+ rv = NS_ERROR_UNEXPECTED;
+ break;
+ }
+
+ _retval.Assign(buffer);
+
+ // done with the array and the buffer
+ //
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(numTokens, valueWords);
+ free(buffer);
+
+ return rv;
+}
+
+// Parse a distinguished name (DN) and returns the relative DN,
+// base DN and the list of attributes that make up the relative DN.
+NS_IMETHODIMP nsLDAPService::ParseDn(const char *aDn,
+ nsACString &aRdn,
+ nsACString &aBaseDn,
+ uint32_t *aRdnCount,
+ char ***aRdnAttrs)
+{
+ NS_ENSURE_ARG_POINTER(aRdnCount);
+ NS_ENSURE_ARG_POINTER(aRdnAttrs);
+
+ // explode the DN
+ char **dnComponents = ldap_explode_dn(aDn, 0);
+ if (!dnComponents) {
+ NS_ERROR("nsLDAPService::ParseDn: parsing DN failed");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // count DN components
+ if (!*dnComponents || !*(dnComponents + 1)) {
+ NS_ERROR("nsLDAPService::ParseDn: DN has too few components");
+ ldap_value_free(dnComponents);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // get the base DN
+ nsAutoCString baseDn(nsDependentCString(*(dnComponents + 1)));
+ for (char **component = dnComponents + 2; *component; ++component) {
+ baseDn.AppendLiteral(",");
+ baseDn.Append(nsDependentCString(*component));
+ }
+
+ // explode the RDN
+ char **rdnComponents = ldap_explode_rdn(*dnComponents, 0);
+ if (!rdnComponents) {
+ NS_ERROR("nsLDAPService::ParseDn: parsing RDN failed");
+ ldap_value_free(dnComponents);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // count RDN attributes
+ uint32_t rdnCount = 0;
+ for (char **component = rdnComponents; *component; ++component)
+ ++rdnCount;
+ if (rdnCount < 1) {
+ NS_ERROR("nsLDAPService::ParseDn: RDN has too few components");
+ ldap_value_free(dnComponents);
+ ldap_value_free(rdnComponents);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // get the RDN attribute names
+ char **attrNameArray = static_cast<char **>(
+ moz_xmalloc(rdnCount * sizeof(char *)));
+ if (!attrNameArray) {
+ NS_ERROR("nsLDAPService::ParseDn: out of memory ");
+ ldap_value_free(dnComponents);
+ ldap_value_free(rdnComponents);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ uint32_t index = 0;
+ for (char **component = rdnComponents; *component; ++component) {
+ uint32_t len = 0;
+ char *p;
+ for (p = *component; *p != '\0' && *p != '='; ++p)
+ ++len;
+ if (*p != '=') {
+ NS_ERROR("nsLDAPService::parseDn: "
+ "could not find '=' in RDN component");
+ ldap_value_free(dnComponents);
+ ldap_value_free(rdnComponents);
+ return NS_ERROR_UNEXPECTED;
+ }
+ if (!(attrNameArray[index] = (char*)NS_Alloc(len + 1))) {
+ NS_ERROR("nsLDAPService::ParseDn: out of memory ");
+ ldap_value_free(dnComponents);
+ ldap_value_free(rdnComponents);
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(index, attrNameArray);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ memcpy(attrNameArray[index], *component, len);
+ *(attrNameArray[index] + len) = '\0';
+ ++index;
+ }
+
+ // perform assignments
+ aRdn.Assign(*dnComponents);
+ aBaseDn.Assign(baseDn);
+ *aRdnCount = rdnCount;
+ *aRdnAttrs = attrNameArray;
+
+ ldap_value_free(dnComponents);
+ ldap_value_free(rdnComponents);
+ return NS_OK;
+}
+
+// Count the number of space-separated tokens between aIter and aIterEnd
+//
+uint32_t
+nsLDAPService::CountTokens(const char *aIter,
+ const char *aIterEnd)
+{
+ uint32_t count(0);
+
+ // keep iterating through the string until we hit the end
+ //
+ while (aIter != aIterEnd) {
+
+ // move past any leading spaces
+ //
+ while (aIter != aIterEnd &&
+ ldap_utf8isspace(const_cast<char *>(aIter))){
+ ++aIter;
+ }
+
+ // move past all chars in this token
+ //
+ while (aIter != aIterEnd) {
+
+ if (ldap_utf8isspace(const_cast<char *>(aIter))) {
+ ++count; // token finished; increment the count
+ ++aIter; // move past the space
+ break;
+ }
+
+ ++aIter; // move to next char
+
+ // if we've hit the end of this token and the end of this
+ // iterator simultaneous, be sure to bump the count, since we're
+ // never gonna hit the IsAsciiSpace where it's normally done.
+ //
+ if (aIter == aIterEnd) {
+ ++count;
+ }
+
+ }
+ }
+
+ return count;
+}
+
+// return the next token in this iterator
+//
+char*
+nsLDAPService::NextToken(const char **aIter,
+ const char **aIterEnd)
+{
+ // move past any leading whitespace
+ //
+ while (*aIter != *aIterEnd &&
+ ldap_utf8isspace(const_cast<char *>(*aIter))) {
+ ++(*aIter);
+ }
+
+ const char *start = *aIter;
+
+ // copy the token into our local variable
+ //
+ while (*aIter != *aIterEnd &&
+ !ldap_utf8isspace(const_cast<char *>(*aIter))) {
+ ++(*aIter);
+ }
+
+ return ToNewCString(Substring(start, *aIter));
+}
diff --git a/ldap/xpcom/src/nsLDAPService.h b/ldap/xpcom/src/nsLDAPService.h
new file mode 100644
index 000000000..2b0f4ef4e
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPService.h
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * 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 "ldap.h"
+#include "nsStringGlue.h"
+#include "nsCOMArray.h"
+#include "nsDataHashtable.h"
+#include "nsILDAPService.h"
+#include "nsILDAPMessage.h"
+#include "nsILDAPMessageListener.h"
+#include "nsCOMPtr.h"
+#include "nsILDAPServer.h"
+#include "nsILDAPConnection.h"
+#include "nsILDAPMessage.h"
+#include "mozilla/Mutex.h"
+
+// 6a89ae33-7a90-430d-888c-0dede53a951a
+//
+#define NS_LDAPSERVICE_CID \
+{ \
+ 0x6a89ae33, 0x7a90, 0x430d, \
+ {0x88, 0x8c, 0x0d, 0xed, 0xe5, 0x3a, 0x95, 0x1a} \
+}
+
+// This is a little "helper" class, we use to store information
+// related to one Service entry (one LDAP server).
+//
+class nsLDAPServiceEntry
+{
+ public:
+ nsLDAPServiceEntry();
+ virtual ~nsLDAPServiceEntry() {};
+ bool Init();
+
+ inline uint32_t GetLeases();
+ inline void IncrementLeases();
+ inline bool DecrementLeases();
+
+ inline PRTime GetTimestamp();
+ inline void SetTimestamp();
+
+ inline already_AddRefed<nsILDAPServer> GetServer();
+ inline bool SetServer(nsILDAPServer *aServer);
+
+ inline already_AddRefed<nsILDAPConnection> GetConnection();
+ inline void SetConnection(nsILDAPConnection *aConnection);
+
+ inline already_AddRefed<nsILDAPMessage> GetMessage();
+ inline void SetMessage(nsILDAPMessage *aMessage);
+
+ inline already_AddRefed<nsILDAPMessageListener> PopListener();
+ inline bool PushListener(nsILDAPMessageListener *);
+
+ inline bool IsRebinding();
+ inline void SetRebinding(bool);
+
+ inline bool DeleteEntry();
+
+ protected:
+ uint32_t mLeases; // The number of leases currently granted
+ PRTime mTimestamp; // Last time this server was "used"
+ bool mDelete; // This entry is due for deletion
+ bool mRebinding; // Keep state if we are rebinding or not
+
+ nsCOMPtr<nsILDAPServer> mServer;
+ nsCOMPtr<nsILDAPConnection> mConnection;
+ nsCOMPtr<nsILDAPMessage> mMessage;
+
+ // Array holding all the pending callbacks (listeners) for this entry
+ nsCOMArray<nsILDAPMessageListener> mListeners;
+};
+
+// This is the interface we're implementing.
+//
+class nsLDAPService : public nsILDAPService, public nsILDAPMessageListener
+{
+ public:
+ // interface decls
+ //
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSILDAPSERVICE
+ NS_DECL_NSILDAPMESSAGELISTENER
+
+ // constructor and destructor
+ //
+ nsLDAPService();
+
+ nsresult Init();
+
+ protected:
+ virtual ~nsLDAPService();
+ nsresult EstablishConnection(nsLDAPServiceEntry *,
+ nsILDAPMessageListener *);
+
+ // kinda like strtok_r, but with iterators. for use by
+ // createFilter
+ //
+ char *NextToken(const char **aIter, const char **aIterEnd);
+
+ // count how many tokens are in this string; for use by
+ // createFilter; note that unlike with NextToken, these params
+ // are copies, not references.
+ //
+ uint32_t CountTokens(const char * aIter, const char * aIterEnd);
+
+
+ mozilla::Mutex mLock; // Lock mechanism
+
+ // Hash table holding server entries
+ nsDataHashtable<nsStringHashKey, nsLDAPServiceEntry*> mServers;
+ // Hash table holding "reverse" lookups from connection to server
+ nsDataHashtable<nsVoidPtrHashKey, nsLDAPServiceEntry*> mConnections;
+};
diff --git a/ldap/xpcom/src/nsLDAPSyncQuery.cpp b/ldap/xpcom/src/nsLDAPSyncQuery.cpp
new file mode 100644
index 000000000..e1f70ee7f
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPSyncQuery.cpp
@@ -0,0 +1,386 @@
+/* -*- 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 "nsLDAPSyncQuery.h"
+#include "nsIServiceManager.h"
+#include "nsILDAPErrors.h"
+#include "nsThreadUtils.h"
+#include "nsILDAPMessage.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsMemory.h"
+
+// nsISupports Implementation
+
+NS_IMPL_ISUPPORTS(nsLDAPSyncQuery, nsILDAPSyncQuery, nsILDAPMessageListener)
+
+// Constructor
+//
+nsLDAPSyncQuery::nsLDAPSyncQuery() :
+ mFinished(false), // This is a control variable for event loop
+ mProtocolVersion(nsILDAPConnection::VERSION3)
+{
+}
+
+// Destructor
+//
+nsLDAPSyncQuery::~nsLDAPSyncQuery()
+{
+}
+
+
+// Messages received are passed back via this function.
+// void OnLDAPMessage (in nsILDAPMessage aMessage)
+//
+NS_IMETHODIMP
+nsLDAPSyncQuery::OnLDAPMessage(nsILDAPMessage *aMessage)
+{
+ int32_t messageType;
+
+ // just in case.
+ //
+ if (!aMessage) {
+ return NS_OK;
+ }
+
+ // figure out what sort of message was returned
+ //
+ nsresult rv = aMessage->GetType(&messageType);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPSyncQuery::OnLDAPMessage(): unexpected "
+ "error in aMessage->GetType()");
+ FinishLDAPQuery();
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ switch (messageType) {
+
+ case nsILDAPMessage::RES_BIND:
+
+ // a bind has completed
+ //
+ return OnLDAPBind(aMessage);
+
+ case nsILDAPMessage::RES_SEARCH_ENTRY:
+
+ // a search entry has been returned
+ //
+ return OnLDAPSearchEntry(aMessage);
+
+ case nsILDAPMessage::RES_SEARCH_RESULT:
+
+ // the search is finished; we're all done
+ //
+ return OnLDAPSearchResult(aMessage);
+
+ default:
+
+ // Given the LDAP operations nsLDAPSyncQuery uses, we should
+ // never get here. If we do get here in a release build, it's
+ // probably a bug, but maybe it's the LDAP server doing something
+ // weird. Might as well try and continue anyway. The session should
+ // eventually get reaped by the timeout code, if necessary.
+ //
+ NS_ERROR("nsLDAPSyncQuery::OnLDAPMessage(): unexpected "
+ "LDAP message received");
+ return NS_OK;
+ }
+}
+
+// void onLDAPInit (in nsresult aStatus);
+//
+NS_IMETHODIMP
+nsLDAPSyncQuery::OnLDAPInit(nsILDAPConnection *aConn, nsresult aStatus)
+{
+ nsresult rv; // temp for xpcom return values
+ // create and initialize an LDAP operation (to be used for the bind)
+ //
+ mOperation = do_CreateInstance("@mozilla.org/network/ldap-operation;1",
+ &rv);
+ if (NS_FAILED(rv)) {
+ FinishLDAPQuery();
+ return NS_ERROR_FAILURE;
+ }
+
+ // our OnLDAPMessage accepts all result callbacks
+ //
+ rv = mOperation->Init(mConnection, this, nullptr);
+ if (NS_FAILED(rv)) {
+ FinishLDAPQuery();
+ return NS_ERROR_UNEXPECTED; // this should never happen
+ }
+
+ // kick off a bind operation
+ //
+ rv = mOperation->SimpleBind(EmptyCString());
+ if (NS_FAILED(rv)) {
+ FinishLDAPQuery();
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsLDAPSyncQuery::OnLDAPBind(nsILDAPMessage *aMessage)
+{
+
+ int32_t errCode;
+
+ mOperation = nullptr; // done with bind op; make nsCOMPtr release it
+
+ // get the status of the bind
+ //
+ nsresult rv = aMessage->GetErrorCode(&errCode);
+ if (NS_FAILED(rv)) {
+
+ NS_ERROR("nsLDAPSyncQuery::OnLDAPBind(): couldn't get "
+ "error code from aMessage");
+ FinishLDAPQuery();
+ return NS_ERROR_FAILURE;
+ }
+
+
+ // check to be sure the bind succeeded
+ //
+ if (errCode != nsILDAPErrors::SUCCESS) {
+ FinishLDAPQuery();
+ return NS_ERROR_FAILURE;
+ }
+
+ // ok, we're starting a search
+ //
+ return StartLDAPSearch();
+}
+
+nsresult
+nsLDAPSyncQuery::OnLDAPSearchEntry(nsILDAPMessage *aMessage)
+{
+ uint32_t attrCount;
+ char** attributes;
+ nsresult rv = aMessage->GetAttributes(&attrCount, &attributes);
+ if (NS_FAILED(rv))
+ {
+ NS_WARNING("nsLDAPSyncQuery:OnLDAPSearchEntry(): "
+ "aMessage->GetAttributes() failed");
+ FinishLDAPQuery();
+ return rv;
+ }
+
+ // Iterate through the attributes received in this message
+ for (uint32_t i = 0; i < attrCount; i++)
+ {
+ char16_t **vals;
+ uint32_t valueCount;
+
+ // Get the values of this attribute.
+ // XXX better failure handling
+ rv = aMessage->GetValues(attributes[i], &valueCount, &vals);
+ if (NS_FAILED(rv))
+ {
+ NS_WARNING("nsLDAPSyncQuery:OnLDAPSearchEntry(): "
+ "aMessage->GetValues() failed\n");
+ FinishLDAPQuery();
+ break;
+ }
+
+ // Store all values of this attribute in the mResults.
+ for (uint32_t j = 0; j < valueCount; j++) {
+ mResults.Append(char16_t('\n'));
+ mResults.AppendASCII(attributes[i]);
+ mResults.Append(char16_t('='));
+ mResults.Append(vals[j]);
+ }
+
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(valueCount, vals);
+ }
+ NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(attrCount, attributes);
+
+ return rv;
+}
+
+
+nsresult
+nsLDAPSyncQuery::OnLDAPSearchResult(nsILDAPMessage *aMessage)
+{
+ // We are done with the LDAP search.
+ // Release the control variable for the eventloop and other members
+ //
+ FinishLDAPQuery();
+ return NS_OK;
+}
+
+nsresult
+nsLDAPSyncQuery::StartLDAPSearch()
+{
+ nsresult rv;
+ // create and initialize an LDAP operation (to be used for the search
+ //
+ mOperation =
+ do_CreateInstance("@mozilla.org/network/ldap-operation;1", &rv);
+
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPSyncQuery::StartLDAPSearch(): couldn't "
+ "create @mozilla.org/network/ldap-operation;1");
+ FinishLDAPQuery();
+ return NS_ERROR_FAILURE;
+ }
+
+ // initialize the LDAP operation object
+ //
+ rv = mOperation->Init(mConnection, this, nullptr);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPSyncQuery::StartLDAPSearch(): couldn't "
+ "initialize LDAP operation");
+ FinishLDAPQuery();
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // get the search filter associated with the directory server url;
+ //
+ nsAutoCString urlFilter;
+ rv = mServerURL->GetFilter(urlFilter);
+ if (NS_FAILED(rv)) {
+ FinishLDAPQuery();
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // get the base dn to search
+ //
+ nsAutoCString dn;
+ rv = mServerURL->GetDn(dn);
+ if (NS_FAILED(rv)) {
+ FinishLDAPQuery();
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // and the scope
+ //
+ int32_t scope;
+ rv = mServerURL->GetScope(&scope);
+ if (NS_FAILED(rv)) {
+ FinishLDAPQuery();
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsAutoCString attributes;
+ rv = mServerURL->GetAttributes(attributes);
+ if (NS_FAILED(rv)) {
+ FinishLDAPQuery();
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // time to kick off the search.
+ rv = mOperation->SearchExt(dn, scope, urlFilter, attributes, 0, 0);
+
+ if (NS_FAILED(rv)) {
+ FinishLDAPQuery();
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+// void initConnection ();
+//
+nsresult nsLDAPSyncQuery::InitConnection()
+{
+ // Because mConnection->Init proxies back to the main thread, this
+ // better be the main thread.
+ NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_FAILURE);
+ nsresult rv; // temp for xpcom return values
+ // create an LDAP connection
+ //
+ mConnection = do_CreateInstance("@mozilla.org/network/ldap-connection;1",
+ &rv);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("nsLDAPSyncQuery::InitConnection(): could "
+ "not create @mozilla.org/network/ldap-connection;1");
+ FinishLDAPQuery();
+ return NS_ERROR_FAILURE;
+ }
+
+ // have we been properly initialized?
+ //
+ if (!mServerURL) {
+ NS_ERROR("nsLDAPSyncQuery::InitConnection(): mServerURL "
+ "is NULL");
+ FinishLDAPQuery();
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ rv = mConnection->Init(mServerURL, EmptyCString(), this,
+ nullptr, mProtocolVersion);
+ if (NS_FAILED(rv)) {
+ FinishLDAPQuery();
+ return NS_ERROR_UNEXPECTED; // this should never happen
+ }
+
+ return NS_OK;
+}
+
+void
+nsLDAPSyncQuery::FinishLDAPQuery()
+{
+ // We are done with the LDAP operation.
+ // Release the Control variable for the eventloop
+ //
+ mFinished = true;
+
+ // Release member variables
+ //
+ mConnection = nullptr;
+ mOperation = nullptr;
+ mServerURL = nullptr;
+
+}
+
+/* wstring getQueryResults (in nsILDAPURL aServerURL, in unsigned long aVersion); */
+NS_IMETHODIMP nsLDAPSyncQuery::GetQueryResults(nsILDAPURL *aServerURL,
+ uint32_t aProtocolVersion,
+ char16_t **_retval)
+{
+ nsresult rv;
+
+ if (!aServerURL) {
+ NS_ERROR("nsLDAPSyncQuery::GetQueryResults() called without LDAP URL");
+ return NS_ERROR_FAILURE;
+ }
+ mServerURL = aServerURL;
+ mProtocolVersion = aProtocolVersion;
+
+ nsCOMPtr<nsIThread> currentThread = do_GetCurrentThread();
+
+ // Start an LDAP query.
+ // InitConnection will bind to the ldap server and post a OnLDAPMessage
+ // event. This event will trigger a search and the whole operation will
+ // be carried out by chain of events
+ //
+ rv = InitConnection();
+ if (NS_FAILED(rv))
+ return rv;
+
+ // We want this LDAP query to be synchronous while the XPCOM LDAP is
+ // async in nature. So this eventQueue handling will wait for the
+ // LDAP operation to be finished.
+ // mFinished controls the state of the LDAP opertion.
+ // It will be released in any case (success/failure)
+
+
+ // Run the event loop,
+ // mFinished is a control variable
+ //
+ while (!mFinished)
+ NS_ENSURE_STATE(NS_ProcessNextEvent(currentThread));
+
+ // Return results
+ //
+ if (!mResults.IsEmpty()) {
+ *_retval = ToNewUnicode(mResults);
+ if (!_retval)
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ return rv;
+
+}
diff --git a/ldap/xpcom/src/nsLDAPSyncQuery.h b/ldap/xpcom/src/nsLDAPSyncQuery.h
new file mode 100644
index 000000000..78cfd43a4
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPSyncQuery.h
@@ -0,0 +1,58 @@
+/* -*- 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 "nsCOMPtr.h"
+#include "nsILDAPConnection.h"
+#include "nsILDAPOperation.h"
+#include "nsILDAPMessageListener.h"
+#include "nsILDAPURL.h"
+#include "nsStringGlue.h"
+#include "nsILDAPSyncQuery.h"
+
+// DDDEE14E-ED81-4182-9323-C2AB22FBA68E
+#define NS_LDAPSYNCQUERY_CID \
+{ 0xdddee14e, 0xed81, 0x4182, \
+ { 0x93, 0x23, 0xc2, 0xab, 0x22, 0xfb, 0xa6, 0x8e }}
+
+
+class nsLDAPSyncQuery : public nsILDAPSyncQuery,
+ public nsILDAPMessageListener
+
+{
+ public:
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSILDAPMESSAGELISTENER
+ NS_DECL_NSILDAPSYNCQUERY
+
+ nsLDAPSyncQuery();
+
+ protected:
+ virtual ~nsLDAPSyncQuery();
+
+ nsCOMPtr<nsILDAPConnection> mConnection; // connection used for search
+ nsCOMPtr<nsILDAPOperation> mOperation; // current ldap op
+ nsCOMPtr<nsILDAPURL> mServerURL; // LDAP URL
+ bool mFinished; // control variable for eventQ
+ nsString mResults; // values to return
+ uint32_t mProtocolVersion; // LDAP version to use
+
+ nsresult InitConnection();
+ // check that we bound ok and start then call StartLDAPSearch
+ nsresult OnLDAPBind(nsILDAPMessage *aMessage);
+
+ // add to the results set
+ nsresult OnLDAPSearchEntry(nsILDAPMessage *aMessage);
+
+
+ nsresult OnLDAPSearchResult(nsILDAPMessage *aMessage);
+
+ // kick off a search
+ nsresult StartLDAPSearch();
+
+ // Clean up after the LDAP Query is done.
+ void FinishLDAPQuery();
+};
+
diff --git a/ldap/xpcom/src/nsLDAPURL.cpp b/ldap/xpcom/src/nsLDAPURL.cpp
new file mode 100644
index 000000000..375743848
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPURL.cpp
@@ -0,0 +1,725 @@
+/* -*- 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 "nsLDAPURL.h"
+#include "netCore.h"
+#include "plstr.h"
+#include "nsCOMPtr.h"
+#include "nsNetCID.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIStandardURL.h"
+#include "nsMsgUtils.h"
+
+// The two schemes we support, LDAP and LDAPS
+//
+NS_NAMED_LITERAL_CSTRING(LDAP_SCHEME, "ldap");
+NS_NAMED_LITERAL_CSTRING(LDAP_SSL_SCHEME, "ldaps");
+
+NS_IMPL_ISUPPORTS(nsLDAPURL, nsILDAPURL, nsIURI)
+
+nsLDAPURL::nsLDAPURL()
+ : mScope(SCOPE_BASE),
+ mOptions(0)
+{
+}
+
+nsLDAPURL::~nsLDAPURL()
+{
+}
+
+nsresult
+nsLDAPURL::Init(uint32_t aUrlType, int32_t aDefaultPort,
+ const nsACString &aSpec, const char* aOriginCharset,
+ nsIURI *aBaseURI)
+{
+ if (!mBaseURL)
+ {
+ mBaseURL = do_CreateInstance(NS_STANDARDURL_CONTRACTID);
+ if (!mBaseURL)
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIStandardURL> standardURL(do_QueryInterface(mBaseURL, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = standardURL->Init(aUrlType, aDefaultPort, aSpec, aOriginCharset,
+ aBaseURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Now get the spec from the mBaseURL in case it was a relative one
+ nsCString spec;
+ rv = mBaseURL->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return SetSpec(spec);
+}
+
+void
+nsLDAPURL::GetPathInternal(nsCString &aPath)
+{
+ aPath.Assign('/');
+
+ if (!mDN.IsEmpty())
+ aPath.Append(mDN);
+
+ if (!mAttributes.IsEmpty())
+ aPath.Append('?');
+
+ // If mAttributes isn't empty, cut off the internally stored commas at start
+ // and end, and append to the path.
+ if (!mAttributes.IsEmpty())
+ aPath.Append(Substring(mAttributes, 1, mAttributes.Length() - 2));
+
+ if (mScope || !mFilter.IsEmpty())
+ {
+ aPath.Append((mAttributes.IsEmpty() ? "??" : "?"));
+ if (mScope)
+ {
+ if (mScope == SCOPE_ONELEVEL)
+ aPath.Append("one");
+ else if (mScope == SCOPE_SUBTREE)
+ aPath.Append("sub");
+ }
+ if (!mFilter.IsEmpty())
+ {
+ aPath.Append('?');
+ aPath.Append(mFilter);
+ }
+ }
+}
+
+nsresult
+nsLDAPURL::SetPathInternal(const nsCString &aPath)
+{
+ LDAPURLDesc *desc;
+
+ // This is from the LDAP C-SDK, which currently doesn't
+ // support everything from RFC 2255... :(
+ //
+ int err = ldap_url_parse(aPath.get(), &desc);
+ switch (err) {
+ case LDAP_SUCCESS: {
+ // The base URL can pick up the host & port details and deal with them
+ // better than we can
+ mDN = desc->lud_dn;
+ mScope = desc->lud_scope;
+ mFilter = desc->lud_filter;
+ mOptions = desc->lud_options;
+ nsresult rv = SetAttributeArray(desc->lud_attrs);
+ if (NS_FAILED(rv))
+ return rv;
+
+ ldap_free_urldesc(desc);
+ return NS_OK;
+ }
+
+ case LDAP_URL_ERR_NOTLDAP:
+ case LDAP_URL_ERR_NODN:
+ case LDAP_URL_ERR_BADSCOPE:
+ return NS_ERROR_MALFORMED_URI;
+
+ case LDAP_URL_ERR_MEM:
+ NS_ERROR("nsLDAPURL::SetSpec: out of memory ");
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ case LDAP_URL_ERR_PARAM:
+ return NS_ERROR_INVALID_POINTER;
+ }
+
+ // This shouldn't happen...
+ return NS_ERROR_UNEXPECTED;
+}
+
+// A string representation of the URI. Setting the spec
+// causes the new spec to be parsed, initializing the URI. Setting
+// the spec (or any of the accessors) causes also any currently
+// open streams on the URI's channel to be closed.
+
+NS_IMETHODIMP
+nsLDAPURL::GetSpec(nsACString &_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetSpec(_retval);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::SetSpec(const nsACString &aSpec)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ // Cache the original spec in case we don't like what we've been passed and
+ // need to reset ourselves.
+ nsCString originalSpec;
+ nsresult rv = mBaseURL->GetSpec(originalSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mBaseURL->SetSpec(aSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = SetPathInternal(PromiseFlatCString(aSpec));
+ if (NS_FAILED(rv))
+ mBaseURL->SetSpec(originalSpec);
+
+ return rv;
+}
+
+NS_IMETHODIMP nsLDAPURL::GetPrePath(nsACString &_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetPrePath(_retval);
+}
+
+NS_IMETHODIMP nsLDAPURL::GetScheme(nsACString &_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetScheme(_retval);
+}
+
+NS_IMETHODIMP nsLDAPURL::SetScheme(const nsACString &aScheme)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ if (aScheme.Equals(LDAP_SCHEME, nsCaseInsensitiveCStringComparator()))
+ mOptions &= !OPT_SECURE;
+ else if (aScheme.Equals(LDAP_SSL_SCHEME,
+ nsCaseInsensitiveCStringComparator()))
+ mOptions |= OPT_SECURE;
+ else
+ return NS_ERROR_MALFORMED_URI;
+
+ return mBaseURL->SetScheme(aScheme);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetUserPass(nsACString &_retval)
+{
+ _retval.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPURL::SetUserPass(const nsACString &aUserPass)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetUsername(nsACString &_retval)
+{
+ _retval.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPURL::SetUsername(const nsACString &aUsername)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetPassword(nsACString &_retval)
+{
+ _retval.Truncate();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPURL::SetPassword(const nsACString &aPassword)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetHostPort(nsACString &_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetHostPort(_retval);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::SetHostPort(const nsACString &aHostPort)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->SetHostPort(aHostPort);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::SetHostAndPort(const nsACString &aHostPort)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->SetHostAndPort(aHostPort);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetHost(nsACString &_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetHost(_retval);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::SetHost(const nsACString &aHost)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->SetHost(aHost);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetPort(int32_t *_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetPort(_retval);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::SetPort(int32_t aPort)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->SetPort(aPort);
+}
+
+NS_IMETHODIMP nsLDAPURL::GetPath(nsACString &_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetPath(_retval);
+}
+
+NS_IMETHODIMP nsLDAPURL::SetPath(const nsACString &aPath)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsresult rv = SetPathInternal(PromiseFlatCString(aPath));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return mBaseURL->SetPath(aPath);
+}
+
+NS_IMETHODIMP nsLDAPURL::GetAsciiSpec(nsACString &_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ // XXX handle extra items?
+ return mBaseURL->GetAsciiSpec(_retval);
+}
+
+NS_IMETHODIMP nsLDAPURL::GetAsciiHost(nsACString &_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetAsciiHost(_retval);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetAsciiHostPort(nsACString &_retval)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetAsciiHostPort(_retval);
+}
+
+NS_IMETHODIMP nsLDAPURL::GetOriginCharset(nsACString &result)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->GetOriginCharset(result);
+}
+
+// boolean equals (in nsIURI other)
+// (based on nsSimpleURI::Equals)
+NS_IMETHODIMP nsLDAPURL::Equals(nsIURI *other, bool *_retval)
+{
+ *_retval = false;
+ if (other)
+ {
+ nsresult rv;
+ nsCOMPtr<nsILDAPURL> otherURL(do_QueryInterface(other, &rv));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString thisSpec, otherSpec;
+ uint32_t otherOptions;
+
+ rv = GetSpec(thisSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = otherURL->GetSpec(otherSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = otherURL->GetOptions(&otherOptions);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (thisSpec == otherSpec && mOptions == otherOptions)
+ *_retval = true;
+ }
+ }
+ return NS_OK;
+}
+
+// boolean schemeIs(in const char * scheme);
+//
+NS_IMETHODIMP nsLDAPURL::SchemeIs(const char *aScheme, bool *aEquals)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ return mBaseURL->SchemeIs(aScheme, aEquals);
+}
+
+// nsIURI clone ();
+//
+nsresult
+nsLDAPURL::CloneInternal(RefHandlingEnum aRefHandlingMode,
+ const nsACString& newRef, nsIURI** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ nsLDAPURL *clone = new nsLDAPURL();
+
+ if (!clone)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ clone->mDN = mDN;
+ clone->mScope = mScope;
+ clone->mFilter = mFilter;
+ clone->mOptions = mOptions;
+ clone->mAttributes = mAttributes;
+
+ nsresult rv;
+ if (aRefHandlingMode == eHonorRef) {
+ rv = mBaseURL->Clone(getter_AddRefs(clone->mBaseURL));
+ } else if (aRefHandlingMode == eReplaceRef) {
+ rv = mBaseURL->CloneWithNewRef(newRef, getter_AddRefs(clone->mBaseURL));
+ } else {
+ rv = mBaseURL->CloneIgnoringRef(getter_AddRefs(clone->mBaseURL));
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ADDREF(*aResult = clone);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLDAPURL::Clone(nsIURI** result)
+{
+ return CloneInternal(eHonorRef, EmptyCString(), result);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::CloneIgnoringRef(nsIURI** result)
+{
+ return CloneInternal(eIgnoreRef, EmptyCString(), result);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::CloneWithNewRef(const nsACString& newRef, nsIURI** result)
+{
+ return CloneInternal(eReplaceRef, newRef, result);
+}
+
+// string resolve (in string relativePath);
+//
+NS_IMETHODIMP nsLDAPURL::Resolve(const nsACString &relativePath,
+ nsACString &_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// The following attributes come from nsILDAPURL
+
+// attribute AUTF8String dn;
+//
+NS_IMETHODIMP nsLDAPURL::GetDn(nsACString& _retval)
+{
+ _retval.Assign(mDN);
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPURL::SetDn(const nsACString& aDn)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ mDN.Assign(aDn);
+
+ // Now get the current path
+ nsCString newPath;
+ GetPathInternal(newPath);
+
+ // and update the base url
+ return mBaseURL->SetPath(newPath);
+}
+
+NS_IMETHODIMP nsLDAPURL::GetAttributes(nsACString &aAttributes)
+{
+ if (mAttributes.IsEmpty())
+ {
+ aAttributes.Truncate();
+ return NS_OK;
+ }
+
+ NS_ASSERTION(mAttributes[0] == ',' &&
+ mAttributes[mAttributes.Length() - 1] == ',',
+ "mAttributes does not begin and end with a comma");
+
+ // We store the string internally with comma before and after, so strip
+ // them off here.
+ aAttributes = Substring(mAttributes, 1, mAttributes.Length() - 2);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLDAPURL::SetAttributes(const nsACString &aAttributes)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ if (aAttributes.IsEmpty())
+ mAttributes.Truncate();
+ else
+ {
+ // We need to make sure we start off the string with a comma.
+ if (aAttributes[0] != ',')
+ mAttributes = ',';
+
+ mAttributes.Append(aAttributes);
+
+ // Also end with a comma if appropriate.
+ if (mAttributes[mAttributes.Length() - 1] != ',')
+ mAttributes.Append(',');
+ }
+
+ // Now get the current path
+ nsCString newPath;
+ GetPathInternal(newPath);
+
+ // and update the base url
+ return mBaseURL->SetPath(newPath);
+}
+
+nsresult nsLDAPURL::SetAttributeArray(char** aAttributes)
+{
+ mAttributes.Truncate();
+
+ while (aAttributes && *aAttributes)
+ {
+ // Always start with a comma as that's what we store internally.
+ mAttributes.Append(',');
+ mAttributes.Append(*aAttributes);
+ ++aAttributes;
+ }
+
+ // Add a comma on the end if we have something.
+ if (!mAttributes.IsEmpty())
+ mAttributes.Append(',');
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLDAPURL::AddAttribute(const nsACString &aAttribute)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ if (mAttributes.IsEmpty())
+ {
+ mAttributes = ',';
+ mAttributes.Append(aAttribute);
+ mAttributes.Append(',');
+ }
+ else
+ {
+ // Wrap the attribute in commas, so that we can do an exact match.
+ nsAutoCString findAttribute(",");
+ findAttribute.Append(aAttribute);
+ findAttribute.Append(',');
+
+ // Check to see if the attribute is already stored. If it is, then also
+ // check to see if it is the last attribute in the string, or if the next
+ // character is a comma, this means we won't match substrings.
+ int32_t pos = mAttributes.Find(findAttribute, CaseInsensitiveCompare);
+ if (pos != -1)
+ return NS_OK;
+
+ mAttributes.Append(Substring(findAttribute, 1));
+ }
+
+ // Now get the current path
+ nsCString newPath;
+ GetPathInternal(newPath);
+
+ // and update the base url
+ return mBaseURL->SetPath(newPath);
+}
+
+NS_IMETHODIMP nsLDAPURL::RemoveAttribute(const nsACString &aAttribute)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ if (mAttributes.IsEmpty())
+ return NS_OK;
+
+ nsAutoCString findAttribute(",");
+ findAttribute.Append(aAttribute);
+ findAttribute.Append(',');
+
+ if (mAttributes.Equals(findAttribute, nsCaseInsensitiveCStringComparator()))
+ mAttributes.Truncate();
+ else
+ {
+ int32_t pos = mAttributes.Find(findAttribute, CaseInsensitiveCompare);
+ if (pos == -1)
+ return NS_OK;
+
+ mAttributes.Cut(pos, findAttribute.Length() - 1);
+ }
+
+ // Now get the current path
+ nsCString newPath;
+ GetPathInternal(newPath);
+
+ // and update the base url
+ return mBaseURL->SetPath(newPath);
+}
+
+NS_IMETHODIMP nsLDAPURL::HasAttribute(const nsACString &aAttribute,
+ bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsAutoCString findAttribute(",");
+ findAttribute.Append(aAttribute);
+ findAttribute.Append(',');
+
+ *_retval = mAttributes.Find(findAttribute, CaseInsensitiveCompare) != -1;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLDAPURL::GetScope(int32_t *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = mScope;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLDAPURL::SetScope(int32_t aScope)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ // Only allow scopes supported by the C-SDK
+ if ((aScope != SCOPE_BASE) && (aScope != SCOPE_ONELEVEL) &&
+ (aScope != SCOPE_SUBTREE))
+ return NS_ERROR_MALFORMED_URI;
+
+ mScope = aScope;
+
+ // Now get the current path
+ nsCString newPath;
+ GetPathInternal(newPath);
+
+ // and update the base url
+ return mBaseURL->SetPath(newPath);
+}
+
+NS_IMETHODIMP nsLDAPURL::GetFilter(nsACString& _retval)
+{
+ _retval.Assign(mFilter);
+ return NS_OK;
+}
+NS_IMETHODIMP nsLDAPURL::SetFilter(const nsACString& aFilter)
+{
+ if (!mBaseURL)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ mFilter.Assign(aFilter);
+
+ if (mFilter.IsEmpty())
+ mFilter.AssignLiteral("(objectclass=*)");
+
+ // Now get the current path
+ nsCString newPath;
+ GetPathInternal(newPath);
+
+ // and update the base url
+ return mBaseURL->SetPath(newPath);
+}
+
+NS_IMETHODIMP nsLDAPURL::GetOptions(uint32_t *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = mOptions;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLDAPURL::SetOptions(uint32_t aOptions)
+{
+ // Secure is the only option supported at the moment
+ if ((mOptions & OPT_SECURE) == (aOptions & OPT_SECURE))
+ return NS_OK;
+
+ mOptions = aOptions;
+
+ if ((aOptions & OPT_SECURE) == OPT_SECURE)
+ return SetScheme(LDAP_SSL_SCHEME);
+
+ return SetScheme(LDAP_SCHEME);
+}
+
+NS_IMETHODIMP nsLDAPURL::SetRef(const nsACString &aRef)
+{
+ return mBaseURL->SetRef(aRef);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetRef(nsACString &result)
+{
+ return mBaseURL->GetRef(result);
+}
+
+NS_IMETHODIMP nsLDAPURL::EqualsExceptRef(nsIURI *other, bool *result)
+{
+ return mBaseURL->EqualsExceptRef(other, result);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetSpecIgnoringRef(nsACString &result)
+{
+ return mBaseURL->GetSpecIgnoringRef(result);
+}
+
+NS_IMETHODIMP
+nsLDAPURL::GetHasRef(bool *result)
+{
+ return mBaseURL->GetHasRef(result);
+}
diff --git a/ldap/xpcom/src/nsLDAPURL.h b/ldap/xpcom/src/nsLDAPURL.h
new file mode 100644
index 000000000..b80da957d
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPURL.h
@@ -0,0 +1,61 @@
+/* -*- 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 "ldap.h"
+#include "nsStringGlue.h"
+#include "nsILDAPURL.h"
+#include "nsCOMPtr.h"
+
+// cb7c67f8-0053-4072-89e9-501cbd1b35ab
+#define NS_LDAPURL_CID \
+{ 0xcb7c67f8, 0x0053, 0x4072, \
+ { 0x89, 0xe9, 0x50, 0x1c, 0xbd, 0x1b, 0x35, 0xab}}
+
+/**
+ * nsLDAPURL
+ *
+ * nsLDAPURL uses an nsStandardURL stored in mBaseURL as its main url formatter.
+ *
+ * This is done to ensure that the pre-path sections of the URI are correctly
+ * formatted and to re-use the functions for nsIURI as appropriate.
+ *
+ * Handling of the path sections of the URI are done within nsLDAPURL/parts of
+ * the LDAP c-sdk. nsLDAPURL holds the individual sections of the path of the
+ * URI locally (to allow convenient get/set), but always updates the mBaseURL
+ * when one changes to ensure that mBaseURL.spec and the local data are kept
+ * consistent.
+ */
+
+class nsLDAPURL : public nsILDAPURL
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIURI
+ NS_DECL_NSILDAPURL
+
+ nsLDAPURL();
+
+protected:
+ enum RefHandlingEnum {
+ eIgnoreRef,
+ eHonorRef,
+ eReplaceRef
+ };
+ virtual ~nsLDAPURL();
+
+ void GetPathInternal(nsCString &aPath);
+ nsresult SetPathInternal(const nsCString &aPath);
+ nsresult SetAttributeArray(char** aAttributes);
+ nsresult CloneInternal(RefHandlingEnum aRefHandlingMode,
+ const nsACString& newRef, nsIURI** aResult);
+
+ nsCString mDN; // Base Distinguished Name (Base DN)
+ int32_t mScope; // Search scope (base, one or sub)
+ nsCString mFilter; // LDAP search filter
+ uint32_t mOptions; // Options
+ nsCString mAttributes;
+ nsCOMPtr<nsIURI> mBaseURL;
+};
diff --git a/ldap/xpcom/src/nsLDAPUtils.h b/ldap/xpcom/src/nsLDAPUtils.h
new file mode 100644
index 000000000..87f5dacc1
--- /dev/null
+++ b/ldap/xpcom/src/nsLDAPUtils.h
@@ -0,0 +1,147 @@
+/* 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/. */
+
+/* This module contains helper functions and macros for converting directory
+ module to frozen linkage.
+ */
+#include "nsServiceManagerUtils.h"
+#include "nsStringGlue.h"
+#include <ctype.h>
+
+#ifdef MOZILLA_INTERNAL_API
+/* Internal API helper macros */
+
+#define LdapCompressWhitespace(str) \
+ (str).CompressWhitespace()
+
+#else // MOZILLA_INTERNAL_API
+/* Frozen linkage helper functions */
+
+/* This macro has been copied from msgcore.h */
+#define IS_SPACE(VAL) \
+ (((((PRIntn)(VAL)) & 0x7f) == ((PRIntn)(VAL))) && isspace((PRIntn)(VAL)))
+
+/* This function has been copied from nsMsgUtils.cpp */
+inline void LdapCompressWhitespace(nsCString& aString)
+{
+ // This code is frozen linkage specific
+ aString.Trim(" \f\n\r\t\v");
+
+ char *start, *end;
+ aString.BeginWriting(&start, &end);
+
+ for (char *cur = start; cur < end; ++cur) {
+ if (!IS_SPACE(*cur))
+ continue;
+
+ *cur = ' ';
+
+ if (!IS_SPACE(*(cur + 1)))
+ continue;
+
+ // Loop through the white space
+ char *wend = cur + 2;
+ while (IS_SPACE(*wend))
+ ++wend;
+
+ uint32_t wlen = wend - cur - 1;
+
+ // fix "end"
+ end -= wlen;
+
+ // move everything forwards a bit
+ for (char *m = cur + 1; m < end; ++m) {
+ *m = *(m + wlen);
+ }
+ }
+
+ // Set the new length.
+ aString.SetLength(end - start);
+}
+
+/*
+ * Function copied from nsReadableUtils.
+ * Migrating to frozen linkage is the only change done
+ */
+inline
+bool IsUTF8(const nsACString& aString)
+{
+ const char *done_reading = aString.EndReading();
+
+ int32_t state = 0;
+ bool overlong = false;
+ bool surrogate = false;
+ bool nonchar = false;
+ uint16_t olupper = 0; // overlong byte upper bound.
+ uint16_t slower = 0; // surrogate byte lower bound.
+
+ const char *ptr = aString.BeginReading();
+
+ while (ptr < done_reading) {
+ uint8_t c;
+
+ if (0 == state) {
+
+ c = *ptr++;
+
+ if ((c & 0x80) == 0x00)
+ continue;
+
+ if ( c <= 0xC1 ) // [80-BF] where not expected, [C0-C1] for overlong.
+ return false;
+ else if ((c & 0xE0) == 0xC0)
+ state = 1;
+ else if ((c & 0xF0) == 0xE0) {
+ state = 2;
+ if ( c == 0xE0 ) { // to exclude E0[80-9F][80-BF]
+ overlong = true;
+ olupper = 0x9F;
+ } else if ( c == 0xED ) { // ED[A0-BF][80-BF] : surrogate codepoint
+ surrogate = true;
+ slower = 0xA0;
+ } else if ( c == 0xEF ) // EF BF [BE-BF] : non-character
+ nonchar = true;
+ } else if ( c <= 0xF4 ) { // XXX replace /w UTF8traits::is4byte when it's updated to exclude [F5-F7].(bug 199090)
+ state = 3;
+ nonchar = true;
+ if ( c == 0xF0 ) { // to exclude F0[80-8F][80-BF]{2}
+ overlong = true;
+ olupper = 0x8F;
+ }
+ else if ( c == 0xF4 ) { // to exclude F4[90-BF][80-BF]
+ // actually not surrogates but codepoints beyond 0x10FFFF
+ surrogate = true;
+ slower = 0x90;
+ }
+ } else
+ return false; // Not UTF-8 string
+ }
+
+ while (ptr < done_reading && state) {
+ c = *ptr++;
+ --state;
+
+ // non-character : EF BF [BE-BF] or F[0-7] [89AB]F BF [BE-BF]
+ if ( nonchar && ( !state && c < 0xBE ||
+ state == 1 && c != 0xBF ||
+ state == 2 && 0x0F != (0x0F & c) ))
+ nonchar = false;
+
+ if ((c & 0xC0) != 0x80 || overlong && c <= olupper ||
+ surrogate && slower <= c || nonchar && !state )
+ return false; // Not UTF-8 string
+ overlong = surrogate = false;
+ }
+ }
+ return !state; // state != 0 at the end indicates an invalid UTF-8 seq.
+}
+
+#define kNotFound -1
+
+#define nsCaseInsensitiveCStringComparator() \
+ CaseInsensitiveCompare
+#define nsCaseInsensitiveStringComparator() \
+ CaseInsensitiveCompare
+
+#endif // MOZILLA_INTERNAL_API