1028 lines
28 KiB
C++
1028 lines
28 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is the mozilla.org LDAP XPCOM SDK.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 2000 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s): Leif Hedstrom <leif@netscape.com>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the
|
|
* terms of the GNU General Public License Version 2 or later (the
|
|
* "GPL"), in which case the provisions of the GPL are applicable
|
|
* instead of those above. If you wish to allow use of your
|
|
* version of this file only under the terms of the GPL and not to
|
|
* allow others to use your version of this file under the MPL,
|
|
* indicate your decision by deleting the provisions above and
|
|
* replace them with the notice and other provisions required by
|
|
* the GPL. If you do not delete the provisions above, a recipient
|
|
* may use your version of this file under either the MPL or the
|
|
* GPL.
|
|
*/
|
|
|
|
#include "nsLDAPInternal.h"
|
|
#include "nsLDAPService.h"
|
|
#include "nsLDAPConnection.h"
|
|
#include "nsLDAPOperation.h"
|
|
#include "nsXPIDLString.h"
|
|
#include "nsReadableUtils.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsIConsoleService.h"
|
|
#include "nsILDAPURL.h"
|
|
#include "nsAutoLock.h"
|
|
#include "nsCRT.h"
|
|
|
|
// 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),
|
|
mDelete(PR_FALSE),
|
|
mRebinding(PR_FALSE)
|
|
|
|
{
|
|
mTimestamp = LL_Zero();
|
|
}
|
|
|
|
// destructor
|
|
//
|
|
nsLDAPServiceEntry::~nsLDAPServiceEntry()
|
|
{
|
|
}
|
|
|
|
// Init function
|
|
//
|
|
PRBool nsLDAPServiceEntry::Init()
|
|
{
|
|
return PR_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++;
|
|
}
|
|
PRBool nsLDAPServiceEntry::DecrementLeases()
|
|
{
|
|
if (!mLeases) {
|
|
return PR_FALSE;
|
|
}
|
|
mLeases--;
|
|
|
|
return PR_TRUE;
|
|
}
|
|
PRUint32 nsLDAPServiceEntry::GetLeases()
|
|
{
|
|
return mLeases;
|
|
}
|
|
|
|
// Get/Set the nsLDAPServer object for this entry.
|
|
//
|
|
already_AddRefed<nsILDAPServer> nsLDAPServiceEntry::GetServer()
|
|
{
|
|
nsILDAPServer *server;
|
|
|
|
NS_IF_ADDREF(server = mServer);
|
|
return server;
|
|
}
|
|
PRBool nsLDAPServiceEntry::SetServer(nsILDAPServer *aServer)
|
|
{
|
|
if (!aServer) {
|
|
return PR_FALSE;
|
|
}
|
|
mServer = aServer;
|
|
|
|
return PR_TRUE;
|
|
}
|
|
|
|
// Get/Set/Clear the nsLDAPConnection object for this entry.
|
|
//
|
|
already_AddRefed<nsILDAPConnection> nsLDAPServiceEntry::GetConnection()
|
|
{
|
|
nsILDAPConnection *conn;
|
|
|
|
NS_IF_ADDREF(conn = mConnection);
|
|
return conn;
|
|
}
|
|
void nsLDAPServiceEntry::SetConnection(nsILDAPConnection *aConnection)
|
|
{
|
|
mConnection = aConnection;
|
|
}
|
|
|
|
// Get/Set the nsLDAPMessage object for this entry (it's a "cache").
|
|
//
|
|
already_AddRefed<nsILDAPMessage> nsLDAPServiceEntry::GetMessage()
|
|
{
|
|
nsILDAPMessage *message;
|
|
|
|
NS_IF_ADDREF(message = mMessage);
|
|
return message;
|
|
}
|
|
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 nsVoidArrays, since we can
|
|
// potentially have more than one listener waiting for the connection
|
|
// to be available for consumption.
|
|
//
|
|
already_AddRefed<nsILDAPMessageListener> nsLDAPServiceEntry::PopListener()
|
|
{
|
|
nsILDAPMessageListener *listener;
|
|
PRInt32 count = mListeners.Count();
|
|
if (!count) {
|
|
return 0;
|
|
}
|
|
|
|
listener = mListeners[0];
|
|
NS_ADDREF(listener); // keep it alive
|
|
mListeners.RemoveObjectAt(0);
|
|
|
|
return listener;
|
|
}
|
|
PRBool nsLDAPServiceEntry::PushListener(nsILDAPMessageListener *listener)
|
|
{
|
|
PRBool ret;
|
|
ret = mListeners.InsertObjectAt(listener, mListeners.Count());
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Mark this server to currently be rebinding. This is to avoid a
|
|
// race condition where multiple consumers could potentially request
|
|
// to reconnect the connection.
|
|
//
|
|
PRBool nsLDAPServiceEntry::IsRebinding()
|
|
{
|
|
return mRebinding;
|
|
}
|
|
void nsLDAPServiceEntry::SetRebinding(PRBool aState)
|
|
{
|
|
mRebinding = aState;
|
|
}
|
|
|
|
// Mark a service entry for deletion, this is "dead" code right now,
|
|
// see bug #75966.
|
|
//
|
|
PRBool nsLDAPServiceEntry::DeleteEntry()
|
|
{
|
|
mDelete = PR_TRUE;
|
|
|
|
return PR_TRUE;
|
|
}
|
|
// This is the end of the nsLDAPServiceEntry class
|
|
|
|
|
|
// Here begins the implementation for nsLDAPService
|
|
//
|
|
NS_IMPL_THREADSAFE_ISUPPORTS2(nsLDAPService,
|
|
nsILDAPService,
|
|
nsILDAPMessageListener)
|
|
|
|
|
|
// constructor
|
|
//
|
|
nsLDAPService::nsLDAPService()
|
|
: mLock(0),
|
|
mServers(0),
|
|
mConnections(0)
|
|
{
|
|
}
|
|
|
|
// destructor
|
|
//
|
|
nsLDAPService::~nsLDAPService()
|
|
{
|
|
// Delete the hash table holding the entries
|
|
if (mServers) {
|
|
delete mServers;
|
|
}
|
|
|
|
// Delete the hash holding the "reverse" lookups from conn to server
|
|
if (mConnections) {
|
|
delete mConnections;
|
|
}
|
|
|
|
// Delete the lock object
|
|
if (mLock) {
|
|
PR_DestroyLock(mLock);
|
|
}
|
|
}
|
|
|
|
// Initializer, create some internal hash tables etc.
|
|
//
|
|
nsresult nsLDAPService::Init()
|
|
{
|
|
if (!mLock) {
|
|
mLock = PR_NewLock();
|
|
if (!mLock) {
|
|
NS_ERROR("nsLDAPService::Init: out of memory ");
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (!mServers) {
|
|
mServers = new nsHashtable(16, PR_FALSE);
|
|
if (!mServers) {
|
|
NS_ERROR("nsLDAPService::Init: out of memory ");
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (!mConnections) {
|
|
mConnections = new nsHashtable(16, PR_FALSE);
|
|
if (!mConnections) {
|
|
NS_ERROR("nsLDAPService::Init: out of memory ");
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// void addServer (in nsILDAPServer aServer);
|
|
NS_IMETHODIMP nsLDAPService::AddServer(nsILDAPServer *aServer)
|
|
{
|
|
nsLDAPServiceEntry *entry;
|
|
nsXPIDLString 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)) {
|
|
switch (rv) {
|
|
// Only pass along errors we are aware of
|
|
//
|
|
case NS_ERROR_OUT_OF_MEMORY:
|
|
case NS_ERROR_NULL_POINTER:
|
|
return rv;
|
|
|
|
default:
|
|
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.
|
|
//
|
|
{
|
|
nsStringKey hashKey(key);
|
|
nsAutoLock lock(mLock);
|
|
|
|
if (mServers->Exists(&hashKey)) {
|
|
// Collision detected, lets just throw away this service entry
|
|
// and keep the old one.
|
|
//
|
|
delete entry;
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
mServers->Put(&hashKey, entry);
|
|
}
|
|
NS_ADDREF(aServer);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// void deleteServer (in wstring aKey);
|
|
NS_IMETHODIMP nsLDAPService::DeleteServer(const PRUnichar *aKey)
|
|
{
|
|
nsLDAPServiceEntry *entry;
|
|
nsStringKey hashKey(aKey, -1, nsStringKey::NEVER_OWN);
|
|
nsAutoLock 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.
|
|
//
|
|
entry = NS_STATIC_CAST(nsLDAPServiceEntry *, mServers->Get(&hashKey));
|
|
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 PRUnichar *aKey,
|
|
nsILDAPServer **_retval)
|
|
{
|
|
nsLDAPServiceEntry *entry;
|
|
nsStringKey hashKey(aKey, -1, nsStringKey::NEVER_OWN);
|
|
nsAutoLock lock(mLock);
|
|
|
|
if (!_retval) {
|
|
NS_ERROR("nsLDAPService::GetServer: null pointer ");
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
entry = NS_STATIC_CAST(nsLDAPServiceEntry *, mServers->Get(&hashKey));
|
|
if (!entry) {
|
|
*_retval = 0;
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
if (!(*_retval = entry->GetServer().get())) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
//void requestConnection (in wstring aKey,
|
|
// in nsILDAPMessageListener aMessageListener);
|
|
NS_IMETHODIMP nsLDAPService::RequestConnection(const PRUnichar *aKey,
|
|
nsILDAPMessageListener *aListener)
|
|
{
|
|
nsLDAPServiceEntry *entry;
|
|
nsCOMPtr<nsILDAPConnection> conn;
|
|
nsCOMPtr<nsILDAPMessage> message;
|
|
nsresult rv;
|
|
nsStringKey hashKey(aKey, -1, nsStringKey::NEVER_OWN);
|
|
|
|
if (!aListener) {
|
|
NS_ERROR("nsLDAPService::RequestConection: null pointer ");
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
// Try to find a possibly cached connection and LDAP message.
|
|
//
|
|
{
|
|
nsAutoLock lock(mLock);
|
|
|
|
entry = NS_STATIC_CAST(nsLDAPServiceEntry *, mServers->Get(&hashKey));
|
|
if (!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.
|
|
//
|
|
{
|
|
nsAutoLock lock(mLock);
|
|
|
|
entry = NS_STATIC_CAST(nsLDAPServiceEntry *, mServers->Get(&hashKey));
|
|
if (!entry ||
|
|
!entry->PushListener(NS_STATIC_CAST(nsILDAPMessageListener *,
|
|
aListener))) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsILDAPConnection getConnection (in wstring aKey);
|
|
NS_IMETHODIMP nsLDAPService::GetConnection(const PRUnichar *aKey,
|
|
nsILDAPConnection **_retval)
|
|
{
|
|
nsLDAPServiceEntry *entry;
|
|
nsStringKey hashKey(aKey, -1, nsStringKey::NEVER_OWN);
|
|
nsAutoLock lock(mLock);
|
|
|
|
if (!_retval) {
|
|
NS_ERROR("nsLDAPService::GetConnection: null pointer ");
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
entry = NS_STATIC_CAST(nsLDAPServiceEntry *, mServers->Get(&hashKey));
|
|
if (!entry) {
|
|
*_retval = 0;
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
entry->SetTimestamp();
|
|
entry->IncrementLeases();
|
|
if (!(*_retval = entry->GetConnection().get())){
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// void releaseConnection (in wstring aKey);
|
|
NS_IMETHODIMP nsLDAPService::ReleaseConnection(const PRUnichar *aKey)
|
|
{
|
|
nsLDAPServiceEntry *entry;
|
|
nsStringKey hashKey(aKey, -1, nsStringKey::NEVER_OWN);
|
|
nsAutoLock lock(mLock);
|
|
|
|
entry = NS_STATIC_CAST(nsLDAPServiceEntry *, mServers->Get(&hashKey));
|
|
if (!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 PRUnichar *aKey,
|
|
nsILDAPMessageListener *aListener)
|
|
{
|
|
nsLDAPServiceEntry *entry;
|
|
nsresult rv;
|
|
nsStringKey hashKey(aKey, -1, nsStringKey::NEVER_OWN);
|
|
|
|
if (!aListener) {
|
|
NS_ERROR("nsLDAPService::ReconnectConnection: null pointer ");
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
{
|
|
nsAutoLock lock(mLock);
|
|
|
|
entry = NS_STATIC_CAST(nsLDAPServiceEntry *, mServers->Get(&hashKey));
|
|
if (!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(PR_TRUE);
|
|
}
|
|
|
|
rv = EstablishConnection(entry, aListener);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
{
|
|
nsAutoLock lock(mLock);
|
|
|
|
if (!entry->PushListener(NS_STATIC_CAST(nsILDAPMessageListener *,
|
|
aListener))) {
|
|
entry->SetRebinding(PR_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;
|
|
PRInt32 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;
|
|
nsVoidKey connKey(NS_STATIC_CAST(nsILDAPConnection *,
|
|
connection));
|
|
nsAutoLock lock(mLock);
|
|
|
|
entry = NS_STATIC_CAST(nsLDAPServiceEntry *,
|
|
mConnections->Get(&connKey));
|
|
if (!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(PR_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()) {
|
|
lock.unlock();
|
|
listener->OnLDAPMessage(aMessage);
|
|
lock.lock();
|
|
}
|
|
}
|
|
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;
|
|
nsCAutoString host;
|
|
nsCAutoString binddn;
|
|
nsCAutoString password;
|
|
PRInt32 port;
|
|
PRUint32 options;
|
|
PRUint32 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;
|
|
}
|
|
rv = url->GetAsciiHost(host);
|
|
if (NS_FAILED(rv)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
rv = url->GetPort(&port);
|
|
if (NS_FAILED(rv)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
rv = url->GetOptions(&options);
|
|
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;
|
|
}
|
|
|
|
// Here we need to provide the binddn, see bug #75990
|
|
//
|
|
rv = conn->Init(host.get(), port,
|
|
(options & nsILDAPURL::OPT_SECURE) ? PR_TRUE : PR_FALSE,
|
|
binddn, this, nsnull, 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.
|
|
//
|
|
{
|
|
nsAutoLock lock(mLock);
|
|
|
|
conn2 = aEntry->GetConnection();
|
|
message = aEntry->GetMessage();
|
|
}
|
|
|
|
if (conn2) {
|
|
// Drop the new connection, we can't use it.
|
|
//
|
|
conn = 0;
|
|
if (message) {
|
|
aListener->OnLDAPMessage(message);
|
|
return NS_OK;
|
|
}
|
|
|
|
{
|
|
nsAutoLock lock(mLock);
|
|
|
|
if (!aEntry->PushListener(NS_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).
|
|
//
|
|
{
|
|
nsVoidKey connKey(NS_STATIC_CAST(nsILDAPConnection *, conn));
|
|
nsAutoLock lock(mLock);
|
|
|
|
aEntry->SetConnection(conn);
|
|
mConnections->Put(&connKey, aEntry);
|
|
}
|
|
|
|
// Setup the bind() operation.
|
|
//
|
|
operation = do_CreateInstance(kLDAPOperationCID, &rv);
|
|
if (NS_FAILED(rv)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
rv = operation->Init(conn, this, nsnull);
|
|
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)) {
|
|
switch (rv) {
|
|
// Only pass along errors we are aware of
|
|
//
|
|
case NS_ERROR_LDAP_ENCODING_ERROR:
|
|
case NS_ERROR_FAILURE:
|
|
case NS_ERROR_OUT_OF_MEMORY:
|
|
return rv;
|
|
|
|
default:
|
|
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(PRUint32 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;
|
|
}
|
|
|
|
// prepare to tokenize |value| for %vM ... %vN
|
|
//
|
|
nsReadingIterator<char> iter, iterEnd; // setup the iterators
|
|
aValue.BeginReading(iter);
|
|
aValue.EndReading(iterEnd);
|
|
|
|
// figure out how big of an array we're going to need for the tokens,
|
|
// including a trailing NULL, and allocate space for it.
|
|
//
|
|
PRUint32 numTokens = CountTokens(iter, iterEnd);
|
|
char **valueWords;
|
|
valueWords = NS_STATIC_CAST(char **,
|
|
nsMemory::Alloc((numTokens + 1) *
|
|
sizeof(char *)));
|
|
if (!valueWords) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
// build the array of values
|
|
//
|
|
PRUint32 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 = NS_STATIC_CAST(char *,
|
|
nsMemory::Alloc(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,
|
|
NS_CONST_CAST(char *, PromiseFlatCString(aPattern).get()),
|
|
NS_CONST_CAST(char *, PromiseFlatCString(aPrefix).get()),
|
|
NS_CONST_CAST(char *, PromiseFlatCString(aSuffix).get()),
|
|
NS_CONST_CAST(char *, PromiseFlatCString(aAttr).get()),
|
|
NS_CONST_CAST(char *, PromiseFlatCString(aValue).get()),
|
|
valueWords);
|
|
switch (result) {
|
|
case LDAP_SUCCESS:
|
|
rv = NS_OK;
|
|
break;
|
|
|
|
case LDAP_SIZELIMIT_EXCEEDED:
|
|
PR_LOG(gLDAPLogModule, PR_LOG_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);
|
|
nsMemory::Free(buffer);
|
|
|
|
return rv;
|
|
}
|
|
|
|
// Count the number of space-separated tokens between aIter and aIterEnd
|
|
//
|
|
PRUint32
|
|
nsLDAPService::CountTokens(nsReadingIterator<char> aIter,
|
|
nsReadingIterator<char> aIterEnd)
|
|
{
|
|
PRUint32 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(NS_CONST_CAST(char *, aIter.get()))){
|
|
++aIter;
|
|
}
|
|
|
|
// move past all chars in this token
|
|
//
|
|
while (aIter != aIterEnd) {
|
|
|
|
if (ldap_utf8isspace(NS_CONST_CAST(char *, aIter.get()))) {
|
|
++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(nsReadingIterator<char> & aIter,
|
|
nsReadingIterator<char> & aIterEnd)
|
|
{
|
|
// move past any leading whitespace
|
|
//
|
|
while (aIter != aIterEnd &&
|
|
ldap_utf8isspace(NS_CONST_CAST(char *, aIter.get()))) {
|
|
++aIter;
|
|
}
|
|
|
|
nsACString::const_iterator start(aIter);
|
|
|
|
// copy the token into our local variable
|
|
//
|
|
while (aIter != aIterEnd &&
|
|
!ldap_utf8isspace(NS_CONST_CAST(char *, aIter.get()))) {
|
|
++aIter;
|
|
}
|
|
|
|
return ToNewCString(Substring(start, aIter));
|
|
}
|