From b6cf57be1e7f37dca588ed7c9c2d0c0d63b3f534 Mon Sep 17 00:00:00 2001 From: "dmose%netscape.com" Date: Sat, 5 May 2001 02:30:50 +0000 Subject: [PATCH] Initial implementation and supporting changes for nsLDAPService (bug 70422). Patch from Leif Hedstrom , r=dmose@netscape.com, sr=brendan@mozilla.org. git-svn-id: svn://10.0.0.236/trunk@94015 18797224-902f-48f8-a5cc-f745e15eee43 --- .../directory/xpcom/base/public/MANIFEST_IDL | 3 +- .../directory/xpcom/base/public/Makefile.in | 1 + .../directory/xpcom/base/public/makefile.win | 1 + .../xpcom/base/public/nsILDAPMessage.idl | 7 + .../xpcom/base/public/nsILDAPService.idl | 180 ++++ mozilla/directory/xpcom/base/src/Makefile.in | 3 +- mozilla/directory/xpcom/base/src/makefile.win | 2 +- .../xpcom/base/src/nsLDAPConnection.cpp | 8 +- .../xpcom/base/src/nsLDAPMessage.cpp | 13 +- .../xpcom/base/src/nsLDAPProtocolModule.cpp | 9 +- .../xpcom/base/src/nsLDAPService.cpp | 820 +++++++++++++++++- .../directory/xpcom/base/src/nsLDAPService.h | 91 +- mozilla/directory/xpcom/macbuild/mozldap.mcp | Bin 138773 -> 138773 bytes .../directory/xpcom/macbuild/mozldapIDL.mcp | Bin 81026 -> 81026 bytes 14 files changed, 1084 insertions(+), 54 deletions(-) create mode 100644 mozilla/directory/xpcom/base/public/nsILDAPService.idl diff --git a/mozilla/directory/xpcom/base/public/MANIFEST_IDL b/mozilla/directory/xpcom/base/public/MANIFEST_IDL index 0335b3d5c70..7dbc40aeb3b 100644 --- a/mozilla/directory/xpcom/base/public/MANIFEST_IDL +++ b/mozilla/directory/xpcom/base/public/MANIFEST_IDL @@ -4,4 +4,5 @@ nsILDAPMessage.idl nsILDAPURL.idl nsILDAPMessageListener.idl nsILDAPErrors.idl -nsILDAPServer.idl \ No newline at end of file +nsILDAPServer.idl +nsILDAPService.idl diff --git a/mozilla/directory/xpcom/base/public/Makefile.in b/mozilla/directory/xpcom/base/public/Makefile.in index 39fe226772f..66430410308 100644 --- a/mozilla/directory/xpcom/base/public/Makefile.in +++ b/mozilla/directory/xpcom/base/public/Makefile.in @@ -49,6 +49,7 @@ XPIDLSRCS = \ nsILDAPURL.idl \ nsILDAPErrors.idl \ nsILDAPServer.idl \ + nsILDAPService.idl \ $(NULL) ifdef MOZ_LDAP_XPCOM_EXPERIMENTAL diff --git a/mozilla/directory/xpcom/base/public/makefile.win b/mozilla/directory/xpcom/base/public/makefile.win index f4940402d98..ab1943194c4 100644 --- a/mozilla/directory/xpcom/base/public/makefile.win +++ b/mozilla/directory/xpcom/base/public/makefile.win @@ -32,6 +32,7 @@ XPIDLSRCS= .\nsILDAPConnection.idl \ .\nsILDAPMessageListener.idl \ .\nsILDAPErrors.idl \ .\nsILDAPServer.idl \ + .\nsILDAPService.idl \ !if defined(ENABLE_LDAP_EXPERIMENTAL) !endif $(NULL) diff --git a/mozilla/directory/xpcom/base/public/nsILDAPMessage.idl b/mozilla/directory/xpcom/base/public/nsILDAPMessage.idl index 01abc5108aa..46010cb64eb 100644 --- a/mozilla/directory/xpcom/base/public/nsILDAPMessage.idl +++ b/mozilla/directory/xpcom/base/public/nsILDAPMessage.idl @@ -76,6 +76,13 @@ interface nsILDAPMessage : nsISupports void getValues(in string attr, out unsigned long count, [retval, array, size_is(count)] out string values); + /** + * The operation this message originated from + * + * @exception NS_ERROR_NULL_POINTER NULL pointer to getter + */ + readonly attribute nsILDAPOperation operation; + /** * The result code (aka lderrno) for this message. * diff --git a/mozilla/directory/xpcom/base/public/nsILDAPService.idl b/mozilla/directory/xpcom/base/public/nsILDAPService.idl new file mode 100644 index 00000000000..c78eac0ba2f --- /dev/null +++ b/mozilla/directory/xpcom/base/public/nsILDAPService.idl @@ -0,0 +1,180 @@ +/* -*- 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 component. + * + * 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 + * + * 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 "nsISupports.idl" +interface nsILDAPServer; +interface nsILDAPConnection; +interface nsILDAPMessageListener; + +/** + * This interface provides an LDAP connection management service. + * It's used to cache already established LDAP connections, as well + * as reaping unused connections after a certain time period. This + * is done completely asynchronously, using callback functions. + */ + + +[scriptable, uuid(f73cc6c1-2301-484b-94a5-8885bca106d1)] +interface nsILDAPService : nsISupports { + + /** + * Add a (possibly) new LDAP server entry to the service. A + * server entry holds information about the host, port and + * other components of the LDAP URL, as well as information + * used for binding a connection to the LDAP server. + * + * An LDAP Server entry (nsILDAPServer) contains the URL, + * user credentials, and other information related to the actual + * server itself. It is used for connecting, binding, rebinding, + * setting timeouts and so forth. + * + * @param aServer an nsILDAPServer object + * + * @exception NS_ERROR_FAILURE the server has already been + * added to the service + * @exception NS_ERROR_NULL_POINTER NULL pointer + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + */ + void addServer(in nsILDAPServer aServer); + + /** + * Mark an LDAP server, in the Service, as a candidate for + * deletion. If there are still leases ("users") of this server, + * the operation fails. + * + * @param aKey unique key identifying the server entry + * + * @exception NS_ERROR_FAILURE either the server doesn't + * exist, or there are still + * leases oustanding + */ + void deleteServer(in wstring aKey); + + /** + * Get the nsILDAPServer object for the specified server entry + * in the service. + * + * @param aKey unique key identifying the server entry + * + * @exception NS_ERROR_FAILURE there is no server registered + * in the service with this key + * @exception NS_ERROR_NULL_POINTER NULL pointer + */ + nsILDAPServer getServer(in wstring aKey); + + /** + * Request a connection from the service, asynchronously. If there is + * one "cached" already, we will actually call the callback function + * before returning from this function. This might be considered a bug, + * but for now be aware of this (see Bugzilla bug #75989). + * + * Calling this method does not increment the leases on this connection, + * you'll have to use the getConnection() method to actually get the + * connection itself (presumably from the callback/listener object). + * The listener needs to implement nsILDAPMessageListener, providing + * the OnLDAPMessage() method. + * + * @param aKey unique key identifying the server entry + * @param aMessageListener the listener object, which we will call + * when the LDAP bind message is available + * + * @exception NS_ERROR_FAILURE there is no server registered + * in the service with this key, + * or we were unable to get a + * connection properly to the server + * @exception NS_ERROR_NOT_AVAILABLE couldn't create connection thread + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + * @exception NS_ERROR_UNEXPECTED unknown or unexpected error... + */ + void requestConnection(in wstring aKey, + in nsILDAPMessageListener aListener); + + /** + * This is the nsLDAPConnection object related to this server. + * This does increase the lease counter on the object, so you have + * to call the releaseConnection() method to return it. It is + * important that you do this in matching pairs, and that you do + * not keep any dangling references to an object around after you + * have called the releaseConnection() method. + * + * @param aKey unique key identifying the server entry + * + * @exception NS_ERROR_FAILURE there is no server registered + * in the service with this key + * @exception NS_ERROR_NULL_POINTER NULL pointer + */ + nsILDAPConnection getConnection(in wstring aKey); + + /** + * Release the lease on a (cached) LDAP connection, making it a + * potential candidate for disconnection. Note that this will not + * delete the actual LDAP server entry in the service, it's still + * registered and can be used in future calls to requestConnection(). + * + * This API might be deprecated in the future, once we figure out how + * to use weak references to support our special needs for reference + * counting. For the time being, it is vital that you call this function + * when you're done with a Connection, and that you do not keep any + * copies of the Connection object lingering around. + * + * @param aKey unique key identifying the server entry + * + * @exception NS_ERROR_FAILURE there is no server registered + * in the service with this key + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + */ + void releaseConnection(in wstring aKey); + + /** + * If we detect that a connection is broken (server disconnected us, + * or any other problem with the link), we need to try to reestablish + * the connection. This is very similar to requestConnection(), + * except you use this when detecting an error with a connection + * that is being cached. + * + * @param aKey unique key identifying the server entry + * @param aMessageListener the listener object, which we will call + * when the LDAP bind message is available + * + * @exception NS_ERROR_FAILURE there is no server registered + * in the service with this key, + * or we were unable to get a + * connection properly to the server + * @exception NS_ERROR_NOT_AVAILABLE couldn't create connection thread + * @exception NS_ERROR_OUT_OF_MEMORY ran out of memory + * @exception NS_ERROR_UNEXPECTED unknown or unexpected error... + */ + void reconnectConnection(in wstring aKey, + in nsILDAPMessageListener aListener); +}; diff --git a/mozilla/directory/xpcom/base/src/Makefile.in b/mozilla/directory/xpcom/base/src/Makefile.in index ac65e8c3007..a5fec6b9727 100644 --- a/mozilla/directory/xpcom/base/src/Makefile.in +++ b/mozilla/directory/xpcom/base/src/Makefile.in @@ -52,6 +52,7 @@ CPPSRCS = \ nsLDAPOperation.cpp \ nsLDAPURL.cpp \ nsLDAPServer.cpp \ + nsLDAPService.cpp \ $(NULL) ifdef MOZ_LDAP_XPCOM_EXPERIMENTAL @@ -60,7 +61,6 @@ DEFINES += -DMOZ_LDAP_XPCOM_EXPERIMENTAL CPPSRCS += \ nsLDAPProtocolHandler.cpp \ nsLDAPChannel.cpp \ - nsLDAPService.cpp \ $(NULL) REQUIRES += mimetype @@ -74,4 +74,3 @@ CSRCS = \ EXTRA_DSO_LDOPTS += $(MOZ_COMPONENT_LIBS) -lldap40 -llber40 include $(topsrcdir)/config/rules.mk - diff --git a/mozilla/directory/xpcom/base/src/makefile.win b/mozilla/directory/xpcom/base/src/makefile.win index 856af42f592..e8494e0a156 100644 --- a/mozilla/directory/xpcom/base/src/makefile.win +++ b/mozilla/directory/xpcom/base/src/makefile.win @@ -34,10 +34,10 @@ CPP_OBJS = .\$(OBJDIR)\nsLDAPURL.obj \ .\$(OBJDIR)\nsLDAPConnection.obj \ .\$(OBJDIR)\nsLDAPOperation.obj \ .\$(OBJDIR)\nsLDAPServer.obj \ + .\$(OBJDIR)\nsLDAPService.obj \ !if defined(ENABLE_LDAP_EXPERIMENTAL) .\$(OBJDIR)\nsLDAPProtocolHandler.obj \ .\$(OBJDIR)\nsLDAPChannel.obj \ - .\$(OBJDIR)\nsLDAPService.obj \ !endif $(NULL) diff --git a/mozilla/directory/xpcom/base/src/nsLDAPConnection.cpp b/mozilla/directory/xpcom/base/src/nsLDAPConnection.cpp index e54d417a233..aa876ee6318 100644 --- a/mozilla/directory/xpcom/base/src/nsLDAPConnection.cpp +++ b/mozilla/directory/xpcom/base/src/nsLDAPConnection.cpp @@ -49,6 +49,8 @@ extern "C" int nsLDAPThreadFuncsInit(LDAP *aLDAP); // constructor // nsLDAPConnection::nsLDAPConnection() + : mConnectionHandle(0), + mPendingOperations(0) { NS_INIT_ISUPPORTS(); } @@ -77,7 +79,6 @@ nsLDAPConnection::~nsLDAPConnection() if (mPendingOperations) { delete mPendingOperations; } - } NS_IMPL_THREADSAFE_ISUPPORTS2(nsLDAPConnection, nsILDAPConnection, @@ -573,6 +574,11 @@ nsLDAPConnection::InvokeMessageCallback(LDAPMessage *aMsgHandle, operation = getter_AddRefs(NS_STATIC_CAST(nsILDAPOperation *, data)); + // Make sure the mOperation member is set to this operation before + // we call the callback. + // + NS_STATIC_CAST(nsLDAPMessage *, aMsg)->mOperation = operation; + // get the message listener object (this may be a proxy for a // callback which should happen on another thread) // diff --git a/mozilla/directory/xpcom/base/src/nsLDAPMessage.cpp b/mozilla/directory/xpcom/base/src/nsLDAPMessage.cpp index b565793d1e1..3c81086a481 100644 --- a/mozilla/directory/xpcom/base/src/nsLDAPMessage.cpp +++ b/mozilla/directory/xpcom/base/src/nsLDAPMessage.cpp @@ -57,7 +57,6 @@ nsLDAPMessage::nsLDAPMessage() // nsLDAPMessage::~nsLDAPMessage(void) { - if (mMsgHandle) { int rc = ldap_msgfree(mMsgHandle); @@ -527,6 +526,18 @@ nsLDAPMessage::GetValues(const char *aAttr, PRUint32 *aCount, 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(PRUnichar* *aString) { diff --git a/mozilla/directory/xpcom/base/src/nsLDAPProtocolModule.cpp b/mozilla/directory/xpcom/base/src/nsLDAPProtocolModule.cpp index 992e2c1bfa6..8e51a9faaee 100644 --- a/mozilla/directory/xpcom/base/src/nsLDAPProtocolModule.cpp +++ b/mozilla/directory/xpcom/base/src/nsLDAPProtocolModule.cpp @@ -18,6 +18,7 @@ * Rights Reserved. * * Contributor(s): Dan Mosedale + * Leif Hedstrom * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the @@ -38,11 +39,11 @@ #include "nsLDAPOperation.h" #include "nsLDAPMessage.h" #include "nsLDAPServer.h" +#include "nsLDAPService.h" #ifdef MOZ_LDAP_XPCOM_EXPERIMENTAL #include "nsLDAPProtocolHandler.h" #include "nsLDAPChannel.h" -#include "nsLDAPService.h" #endif // use the default constructor @@ -52,10 +53,10 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPOperation); NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPMessage); NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPURL); NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPServer); +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsLDAPService, Init); #ifdef MOZ_LDAP_XPCOM_EXPERIMENTAL NS_GENERIC_FACTORY_CONSTRUCTOR(nsLDAPProtocolHandler); -NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsLDAPService, Init); #endif @@ -73,12 +74,12 @@ static nsModuleComponentInfo components[] = "@mozilla.org/network/ldap-message;1", nsLDAPMessageConstructor }, { "LDAP Server", NS_LDAPSERVER_CID, "@mozilla.org/network/ldap-server;1", nsLDAPServerConstructor }, + { "LDAP Service", NS_LDAPSERVICE_CID, + "@mozilla.org/network/ldap-service;1", nsLDAPServiceConstructor }, #ifdef MOZ_LDAP_XPCOM_EXPERIMENTAL { "LDAP Protocol Handler", NS_LDAPPROTOCOLHANDLER_CID, NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ldap", nsLDAPProtocolHandlerConstructor }, - { "LDAP Service", NS_LDAPSERVICE_CID, - "@mozilla.org/network/ldap-service;1", nsLDAPServiceConstructor }, #endif { "LDAP URL", NS_LDAPURL_CID, "@mozilla.org/network/ldap-url;1", nsLDAPURLConstructor } diff --git a/mozilla/directory/xpcom/base/src/nsLDAPService.cpp b/mozilla/directory/xpcom/base/src/nsLDAPService.cpp index acc7d2e3d7c..4caf207be8f 100644 --- a/mozilla/directory/xpcom/base/src/nsLDAPService.cpp +++ b/mozilla/directory/xpcom/base/src/nsLDAPService.cpp @@ -17,7 +17,7 @@ * Copyright (C) 2000 Netscape Communications Corporation. All * Rights Reserved. * - * Contributor(s): Dan Mosedale + * Contributor(s): Leif Hedstrom * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the @@ -34,10 +34,206 @@ #include "nsLDAPInternal.h" #include "nsLDAPService.h" +#include "nsLDAPConnection.h" +#include "nsLDAPOperation.h" +#include "nsXPIDLString.h" +#include "nsIServiceManager.h" +#include "nsIConsoleService.h" +#include "nsILDAPURL.h" +#include "nsAutoLock.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() +{ + nsresult rv; + + rv = NS_NewISupportsArray(getter_AddRefs(mListeners)); + if (NS_FAILED(rv)) { + return PR_FALSE; + } + + 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. +// +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. +// +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"). +// +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 avilable for consumption. +// +nsILDAPMessageListener *nsLDAPServiceEntry::PopListener() +{ + nsILDAPMessageListener *listener; + PRUint32 count; + + mListeners->Count(&count); + if (!count) { + return 0; + } + + listener = NS_STATIC_CAST(nsILDAPMessageListener *, + mListeners->ElementAt(0)); + mListeners->RemoveElementAt(0); + + return listener; +} +PRBool nsLDAPServiceEntry::PushListener(nsILDAPMessageListener *listener) +{ + PRBool ret; + PRUint32 count; + + mListeners->Count(&count); + ret = mListeners->InsertElementAt(listener, 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) { NS_INIT_ISUPPORTS(); } @@ -46,40 +242,610 @@ nsLDAPService::nsLDAPService() // 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); + } } -NS_IMPL_THREADSAFE_ISUPPORTS1(nsLDAPService,nsIRunnable); - -// initialize; should only be called by the generic constructor after -// creation. This is where the thread gets spun up. +// Initializer, create some internal hash tables etc. // -NS_IMETHODIMP -nsLDAPService::Init(void) +nsresult nsLDAPService::Init() { - nsresult rv; + if (!mLock) { + mLock = PR_NewLock(); + if (!mLock) { + NS_ERROR("nsLDAPService::Init: out of memory "); + return NS_ERROR_OUT_OF_MEMORY; + } + } - NS_ASSERTION(!mThread, "nsLDAPService already initialized!"); + if (!mServers) { + mServers = new nsHashtable(16, PR_FALSE); + if (!mServers) { + NS_ERROR("nsLDAPService::Init: out of memory "); + return NS_ERROR_OUT_OF_MEMORY; + } + } - rv = NS_NewThread(getter_AddRefs(mThread), - this, 0, PR_JOINABLE_THREAD); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -// for nsIRunnable. all the processing work happens here. -// -NS_IMETHODIMP -nsLDAPService::Run(void) -{ - PR_LOG(gLDAPLogModule, PR_LOG_DEBUG, ("nsLDAPService::Run() entered\n")); - - // XXX - should use mThreadRunning here - // - while (1) { - + 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())) { + 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 conn; + nsCOMPtr 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 = getter_AddRefs(entry->GetConnection()); + message = getter_AddRefs(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())){ + 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 operation; + nsCOMPtr 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 listener; + nsCOMPtr 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 = getter_AddRefs(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 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; +} + +// Helper function to establish an LDAP connection properly. +// +nsresult nsLDAPService::EstablishConnection(nsLDAPServiceEntry *aEntry, + nsILDAPMessageListener *aListener) +{ + nsCOMPtr operation; + nsCOMPtr server; + nsCOMPtr url; + nsCOMPtr conn, conn2; + nsCOMPtr message; + nsXPIDLCString host; + nsXPIDLString binddn; + nsXPIDLString password; + PRInt32 port; + nsresult rv; + + server = getter_AddRefs(aEntry->GetServer()); + if (!server) { + return NS_ERROR_FAILURE; + } + + // Get username and password from the server entry. + // + rv = server->GetBinddn(getter_Copies(binddn)); + if (NS_FAILED(rv)) { + return NS_ERROR_FAILURE; + } + rv = server->GetPassword(getter_Copies(password)); + 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->GetHost(getter_Copies(host)); + if (NS_FAILED(rv)) { + return NS_ERROR_FAILURE; + } + rv = url->GetPort(&port); + 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, port, 0); + 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 = getter_AddRefs(aEntry->GetConnection()); + message = getter_AddRefs(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); + 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(0); + 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; +} diff --git a/mozilla/directory/xpcom/base/src/nsLDAPService.h b/mozilla/directory/xpcom/base/src/nsLDAPService.h index 45028337d76..b31fd2152a7 100644 --- a/mozilla/directory/xpcom/base/src/nsLDAPService.h +++ b/mozilla/directory/xpcom/base/src/nsLDAPService.h @@ -17,7 +17,7 @@ * Copyright (C) 2000 Netscape Communications Corporation. All * Rights Reserved. * - * Contributor(s): Dan Mosedale + * Contributor(s): Leif Hedstrom * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the @@ -32,12 +32,19 @@ * GPL. */ -#ifndef nsLDAPService_h___ -#define nsLDAPService_h___ - -#include "nsIRunnable.h" +#include "nsLDAP.h" +#include "ldap.h" +#include "nsString.h" +#include "nsSupportsArray.h" +#include "nsHashtable.h" +#include "nsILDAPService.h" +#include "nsILDAPMessage.h" +#include "nsILDAPMessageListener.h" #include "nsCOMPtr.h" -#include "nsIThread.h" +#include "nsILDAPServer.h" +#include "nsILDAPConnection.h" +#include "nsILDAPMessage.h" + // 6a89ae33-7a90-430d-888c-0dede53a951a // @@ -47,28 +54,78 @@ {0x88, 0x8c, 0x0d, 0xed, 0xe5, 0x3a, 0x95, 0x1a} \ } -class nsLDAPService : public nsIRunnable +// 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(); + PRBool Init(); + + inline PRUint32 GetLeases(); + inline void IncrementLeases(); + inline PRBool DecrementLeases(); + + inline PRTime GetTimestamp(); + inline void SetTimestamp(); + + inline nsILDAPServer *GetServer(); + inline PRBool SetServer(nsILDAPServer *aServer); + + inline nsILDAPConnection *GetConnection(); + inline void SetConnection(nsILDAPConnection *aConnection); + + inline nsILDAPMessage *GetMessage(); + inline void SetMessage(nsILDAPMessage *aMessage); + + inline nsILDAPMessageListener *PopListener(); + inline PRBool PushListener(nsILDAPMessageListener *); + + inline PRBool IsRebinding(); + inline void SetRebinding(PRBool); + + inline PRBool DeleteEntry(); + + protected: + PRUint32 mLeases; // The number of leases currently granted + PRTime mTimestamp; // Last time this server was "used" + PRBool mDelete; // This entry is due for deletion + PRBool mRebinding; // Keep state if we are rebinding or not + + nsCOMPtr mServer; + nsCOMPtr mConnection; + nsCOMPtr mMessage; + + // Array holding all the pending callbacks (listeners) for this entry + nsCOMPtr mListeners; +}; + +// This is the interface we're implementing. +// +class nsLDAPService : public nsILDAPService, public nsILDAPMessageListener { public: // interface decls // NS_DECL_ISUPPORTS - NS_DECL_NSIRUNNABLE + NS_DECL_NSILDAPSERVICE + NS_DECL_NSILDAPMESSAGELISTENER // constructor and destructor // nsLDAPService(); virtual ~nsLDAPService(); - - // initialize; should only be called by the generic - // constructor after creation - // - NS_METHOD Init(void); + + nsresult Init(); protected: - nsCOMPtr mThread; - PRBool mThreadRunning; + nsresult EstablishConnection(nsLDAPServiceEntry *, + nsILDAPMessageListener *); + PRLock *mLock; // Lock mechanism + nsHashtable *mServers; // Hash table holding server entries + nsHashtable *mConnections; // Hash table holding "reverse" + // lookups from connection to server }; - -#endif // nsLDAPService_h___ diff --git a/mozilla/directory/xpcom/macbuild/mozldap.mcp b/mozilla/directory/xpcom/macbuild/mozldap.mcp index 93f574556b883ba53b2fe13dd24aa44616e85ed9..5b76c58adf2601b87b0ca462484a860528b173ff 100644 GIT binary patch delta 398 zcmbQbhhyp0?m(IFvpCrYGOFtWgA-5(pIyg3|s_`V2%>g_Q;m zP?aZC)N8Vil{N=V+vGOx;>|(UG90X+K-e&Se=4K$Jrv(d0<>s@T z&l$lYKe;U?zmt~O?8)<2OLE)2_~Q%=f~SE>PyqY(hcb*TOp};dCZ}6!dxGq6^Yn3L zV_-N0qJjLwQ2H#CJ_4nWf@q++V^IEaDE|bQKl!$0jUrHYAXGL8O8Z0UGZ0l3RvJz~ zRi02$FROr})U?#1)V$=>Vk_U&lA`?b)S~QSE9Y*%UHJYb6Z=dGOSS&exXAGkTnAjc}%UH6E0~iJi85pwCH(w0; p%{cu6E0ZW#=>Pw7Gr>aJU$8TYurXRp|I5o%&Sav1;s diff --git a/mozilla/directory/xpcom/macbuild/mozldapIDL.mcp b/mozilla/directory/xpcom/macbuild/mozldapIDL.mcp index 8e33065a3f15363e4ae5349ab661b81f591ed711..46ae93c25945682eaa53c2821ea5138740dd13eb 100644 GIT binary patch delta 379 zcmZqr$*nd>3NkA*Cq<#4C^fBkyLk3mpc;_<5fdGyp_Wc$0Xb&!LBY41Z}Xkv;ma%b z^l@|3%S=w4Y#6Gr*})@?akHiDLIqZkCt^3>xBbA(yvuohoB7 delta 346 zcmZqr$EjBrqckT6q&zLZC^t2)6sVhlB{MIjqBu1tr8pzM2*_swxdjSUYrrp+R&4mLwfn*82IVf!y# z#_cSkGPxx=`9QM}PGR6#wEdV7<9=qwQ`6hb7~L7Yrk^uo)Mq^fR3)>W$(*r)6#$jZ BWmEtF