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/nsLDAPService.cpp | |
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/nsLDAPService.cpp')
-rw-r--r-- | ldap/xpcom/src/nsLDAPService.cpp | 985 |
1 files changed, 985 insertions, 0 deletions
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)); +} |