diff options
author | Matt A. Tobin <email@mattatobin.com> | 2019-11-03 00:17:46 -0400 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2019-11-03 00:17:46 -0400 |
commit | 302bf1b523012e11b60425d6eee1221ebc2724eb (patch) | |
tree | b191a895f8716efcbe42f454f37597a545a6f421 /ldap/xpcom/src | |
parent | 21b3f6247403c06f85e1f45d219f87549862198f (diff) | |
download | UXP-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')
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 |