Back to index

lightning-sunbird  0.9+nobinonly
nsLDAPService.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * 
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is the mozilla.org LDAP XPCOM SDK.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 2000
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Leif Hedstrom <leif@netscape.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsLDAPInternal.h"
00041 #include "nsLDAPService.h"
00042 #include "nsLDAPConnection.h"
00043 #include "nsLDAPOperation.h"
00044 #include "nsXPIDLString.h"
00045 #include "nsReadableUtils.h"
00046 #include "nsIServiceManager.h"
00047 #include "nsIConsoleService.h"
00048 #include "nsILDAPURL.h"
00049 #include "nsAutoLock.h"
00050 #include "nsCRT.h"
00051 
00052 // Constants for CIDs used here.
00053 //
00054 static NS_DEFINE_CID(kLDAPConnectionCID, NS_LDAPCONNECTION_CID);
00055 static NS_DEFINE_CID(kLDAPOperationCID, NS_LDAPOPERATION_CID);
00056 
00057 // First we provide all the methods for the "local" class
00058 // nsLDAPServiceEntry.
00059 //
00060 
00061 // constructor
00062 //
00063 nsLDAPServiceEntry::nsLDAPServiceEntry()
00064     : mLeases(0),
00065       mDelete(PR_FALSE),
00066       mRebinding(PR_FALSE)
00067 
00068 {
00069     mTimestamp = LL_Zero();
00070 }
00071 
00072 // destructor
00073 //
00074 nsLDAPServiceEntry::~nsLDAPServiceEntry()
00075 {
00076 }
00077 
00078 // Init function
00079 //
00080 PRBool nsLDAPServiceEntry::Init()
00081 {
00082     return PR_TRUE;
00083 }
00084 
00085 // Set/Get the timestamp when this server was last used. We might have
00086 // to use an "interval" here instead, see Bug #76887.
00087 //
00088 PRTime nsLDAPServiceEntry::GetTimestamp()
00089 {
00090     return mTimestamp;
00091 }
00092 void nsLDAPServiceEntry::SetTimestamp()
00093 {
00094     mTimestamp = PR_Now();
00095 }
00096 
00097 
00098 // Increment, decrement and Get the leases. This code might go away
00099 // with bug #75954.
00100 //
00101 void nsLDAPServiceEntry::IncrementLeases()
00102 {
00103     mLeases++;
00104 }
00105 PRBool nsLDAPServiceEntry::DecrementLeases()
00106 {
00107     if (!mLeases) {
00108         return PR_FALSE;
00109     }
00110     mLeases--;
00111 
00112     return PR_TRUE;
00113 }
00114 PRUint32 nsLDAPServiceEntry::GetLeases()
00115 {
00116     return mLeases;
00117 }
00118 
00119 // Get/Set the nsLDAPServer object for this entry.
00120 //
00121 already_AddRefed<nsILDAPServer> nsLDAPServiceEntry::GetServer()
00122 {
00123     nsILDAPServer *server;
00124 
00125     NS_IF_ADDREF(server = mServer);
00126     return server;
00127 }
00128 PRBool nsLDAPServiceEntry::SetServer(nsILDAPServer *aServer)
00129 {
00130     if (!aServer) {
00131         return PR_FALSE;
00132     }
00133     mServer = aServer;
00134 
00135     return PR_TRUE;
00136 }
00137 
00138 // Get/Set/Clear the nsLDAPConnection object for this entry.
00139 //
00140 already_AddRefed<nsILDAPConnection> nsLDAPServiceEntry::GetConnection()
00141 {
00142     nsILDAPConnection *conn;
00143 
00144     NS_IF_ADDREF(conn = mConnection);
00145     return conn;
00146 }
00147 void nsLDAPServiceEntry::SetConnection(nsILDAPConnection *aConnection)
00148 {
00149     mConnection = aConnection;
00150 }
00151 
00152 // Get/Set the nsLDAPMessage object for this entry (it's a "cache").
00153 //
00154 already_AddRefed<nsILDAPMessage> nsLDAPServiceEntry::GetMessage()
00155 {
00156     nsILDAPMessage *message;
00157 
00158     NS_IF_ADDREF(message = mMessage);
00159     return message;
00160 }
00161 void nsLDAPServiceEntry::SetMessage(nsILDAPMessage *aMessage)
00162 {
00163     mMessage = aMessage;
00164 }
00165 
00166 // Push/Pop pending listeners/callback for this server entry. This is
00167 // implemented as a "stack" on top of the nsVoidArrays, since we can
00168 // potentially have more than one listener waiting for the connection
00169 // to be available for consumption.
00170 //
00171 already_AddRefed<nsILDAPMessageListener> nsLDAPServiceEntry::PopListener()
00172 {
00173     nsILDAPMessageListener *listener;
00174     PRInt32 count = mListeners.Count();
00175     if (!count) {
00176         return 0;
00177     }
00178 
00179     listener = mListeners[0];
00180     NS_ADDREF(listener); // keep it alive
00181     mListeners.RemoveObjectAt(0);
00182 
00183     return listener;
00184 }
00185 PRBool nsLDAPServiceEntry::PushListener(nsILDAPMessageListener *listener)
00186 {
00187     PRBool ret;
00188     ret = mListeners.InsertObjectAt(listener, mListeners.Count());
00189 
00190     return ret;
00191 }
00192 
00193 // Mark this server to currently be rebinding. This is to avoid a
00194 // race condition where multiple consumers could potentially request
00195 // to reconnect the connection.
00196 //
00197 PRBool nsLDAPServiceEntry::IsRebinding()
00198 {
00199     return mRebinding;
00200 }
00201 void nsLDAPServiceEntry::SetRebinding(PRBool aState)
00202 {
00203     mRebinding = aState;
00204 }
00205 
00206 // Mark a service entry for deletion, this is "dead" code right now,
00207 // see bug #75966.
00208 //
00209 PRBool nsLDAPServiceEntry::DeleteEntry()
00210 {
00211     mDelete = PR_TRUE;
00212 
00213     return PR_TRUE;
00214 }
00215 // This is the end of the nsLDAPServiceEntry class
00216 
00217 
00218 // Here begins the implementation for nsLDAPService
00219 // 
00220 NS_IMPL_THREADSAFE_ISUPPORTS2(nsLDAPService,
00221                               nsILDAPService,
00222                               nsILDAPMessageListener)
00223 
00224 
00225 // constructor
00226 //
00227 nsLDAPService::nsLDAPService()
00228     : mLock(0),
00229       mServers(0),
00230       mConnections(0)
00231 {
00232 }
00233 
00234 // destructor
00235 //
00236 nsLDAPService::~nsLDAPService()
00237 {
00238     // Delete the hash table holding the entries
00239     if (mServers) {
00240         delete mServers;
00241     }
00242 
00243     // Delete the hash holding the "reverse" lookups from conn to server
00244     if (mConnections) {
00245         delete mConnections;
00246     }
00247 
00248     // Delete the lock object
00249     if (mLock) {
00250         PR_DestroyLock(mLock);
00251     }
00252 }
00253 
00254 // Initializer, create some internal hash tables etc.
00255 //
00256 nsresult nsLDAPService::Init()
00257 {
00258     if (!mLock) {
00259         mLock = PR_NewLock();
00260         if (!mLock) {
00261             NS_ERROR("nsLDAPService::Init: out of memory ");
00262             return NS_ERROR_OUT_OF_MEMORY;
00263         }
00264     }
00265 
00266     if (!mServers) {
00267         mServers = new nsHashtable(16, PR_FALSE);
00268         if (!mServers) {
00269             NS_ERROR("nsLDAPService::Init: out of memory ");
00270             return NS_ERROR_OUT_OF_MEMORY;
00271         }
00272     }
00273 
00274     if (!mConnections) {
00275         mConnections = new nsHashtable(16, PR_FALSE);
00276         if (!mConnections) {
00277             NS_ERROR("nsLDAPService::Init: out of memory ");
00278             return NS_ERROR_OUT_OF_MEMORY;
00279         }
00280     }
00281 
00282     return NS_OK;
00283 }
00284 
00285 // void addServer (in nsILDAPServer aServer);
00286 NS_IMETHODIMP nsLDAPService::AddServer(nsILDAPServer *aServer)
00287 {
00288     nsLDAPServiceEntry *entry;
00289     nsXPIDLString key;
00290     nsresult rv;
00291     
00292     if (!aServer) {
00293         NS_ERROR("nsLDAPService::AddServer: null pointer ");
00294         return NS_ERROR_NULL_POINTER;
00295     }
00296 
00297     // Set up the hash key for the server entry
00298     //
00299     rv = aServer->GetKey(getter_Copies(key));
00300     if (NS_FAILED(rv)) {
00301         switch (rv) {
00302         // Only pass along errors we are aware of
00303         //
00304         case NS_ERROR_OUT_OF_MEMORY:
00305         case NS_ERROR_NULL_POINTER:
00306             return rv;
00307 
00308         default:
00309             return NS_ERROR_FAILURE;
00310         }
00311     }
00312 
00313     // Create the new service server entry, and add it into the hash table
00314     //
00315     entry = new nsLDAPServiceEntry;
00316     if (!entry) {
00317         NS_ERROR("nsLDAPService::AddServer: out of memory ");
00318         return NS_ERROR_OUT_OF_MEMORY;
00319     }
00320     if (!entry->Init()) {
00321         delete entry;
00322         NS_ERROR("nsLDAPService::AddServer: out of memory ");
00323         return NS_ERROR_OUT_OF_MEMORY;
00324     }
00325 
00326     if (!entry->SetServer(aServer)) {
00327         delete entry;
00328         return NS_ERROR_FAILURE;
00329     }
00330 
00331     // We increment the refcount here for the server entry, when
00332     // we purge a server completely from the service (TBD), we
00333     // need to decrement the counter as well.
00334     //
00335     {
00336         nsStringKey hashKey(key);
00337         nsAutoLock lock(mLock);
00338 
00339         if (mServers->Exists(&hashKey)) {
00340             // Collision detected, lets just throw away this service entry
00341             // and keep the old one.
00342             //
00343             delete entry;
00344             return NS_ERROR_FAILURE;
00345         }
00346         mServers->Put(&hashKey, entry);
00347     }
00348     NS_ADDREF(aServer);
00349 
00350     return NS_OK;
00351 }
00352 
00353 // void deleteServer (in wstring aKey);
00354 NS_IMETHODIMP nsLDAPService::DeleteServer(const PRUnichar *aKey)
00355 {
00356     nsLDAPServiceEntry *entry;
00357     nsStringKey hashKey(aKey, -1, nsStringKey::NEVER_OWN);
00358     nsAutoLock lock(mLock);
00359         
00360     // We should probably rename the key for this entry now that it's
00361     // "deleted", so that we can add in a new one with the same ID.
00362     // This is bug #77669.
00363     //
00364     entry = NS_STATIC_CAST(nsLDAPServiceEntry *, mServers->Get(&hashKey));
00365     if (entry) {
00366         if (entry->GetLeases() > 0) {
00367             return NS_ERROR_FAILURE;
00368         }
00369         entry->DeleteEntry();
00370     } else {
00371         // There is no Server entry for this key
00372         //
00373         return NS_ERROR_FAILURE;
00374     }
00375 
00376     return NS_OK;
00377 }
00378 
00379 // nsILDAPServer getServer (in wstring aKey);
00380 NS_IMETHODIMP nsLDAPService::GetServer(const PRUnichar *aKey,
00381                                        nsILDAPServer **_retval)
00382 {
00383     nsLDAPServiceEntry *entry;
00384     nsStringKey hashKey(aKey, -1, nsStringKey::NEVER_OWN);
00385     nsAutoLock lock(mLock);
00386 
00387     if (!_retval) {
00388         NS_ERROR("nsLDAPService::GetServer: null pointer ");
00389         return NS_ERROR_NULL_POINTER;
00390     }
00391 
00392     entry = NS_STATIC_CAST(nsLDAPServiceEntry *, mServers->Get(&hashKey));
00393     if (!entry) {
00394         *_retval = 0;
00395         return NS_ERROR_FAILURE;
00396     }
00397     if (!(*_retval = entry->GetServer().get())) {
00398         return NS_ERROR_FAILURE;
00399     }
00400 
00401     return NS_OK;
00402 }
00403 
00404 //void requestConnection (in wstring aKey,
00405 //                        in nsILDAPMessageListener aMessageListener);
00406 NS_IMETHODIMP nsLDAPService::RequestConnection(const PRUnichar *aKey,
00407                                  nsILDAPMessageListener *aListener)
00408 {
00409     nsLDAPServiceEntry *entry;
00410     nsCOMPtr<nsILDAPConnection> conn;
00411     nsCOMPtr<nsILDAPMessage> message;
00412     nsresult rv;
00413     nsStringKey hashKey(aKey, -1, nsStringKey::NEVER_OWN);
00414 
00415     if (!aListener) {
00416         NS_ERROR("nsLDAPService::RequestConection: null pointer ");
00417         return NS_ERROR_NULL_POINTER;
00418     }
00419 
00420     // Try to find a possibly cached connection and LDAP message.
00421     //
00422     {
00423         nsAutoLock lock(mLock);
00424 
00425         entry = NS_STATIC_CAST(nsLDAPServiceEntry *, mServers->Get(&hashKey));
00426         if (!entry) {
00427             return NS_ERROR_FAILURE;
00428         }
00429         entry->SetTimestamp();
00430 
00431         conn = entry->GetConnection();
00432         message = entry->GetMessage();
00433     }
00434 
00435     if (conn) {
00436         if (message) {
00437             // We already have the connection, and the message, ready to
00438             // be used. This might be confusing, since we actually call
00439             // the listener before this function returns, see bug #75899.
00440             //
00441             aListener->OnLDAPMessage(message);
00442             return NS_OK;
00443         }
00444     } else {
00445         rv = EstablishConnection(entry, aListener);
00446         if (NS_FAILED(rv)) {
00447             return rv;
00448         }
00449 
00450     }
00451 
00452     // We got a new connection, now push the listeners on our stack,
00453     // until we get the LDAP message back.
00454     //
00455     {
00456         nsAutoLock lock(mLock);
00457             
00458         entry = NS_STATIC_CAST(nsLDAPServiceEntry *, mServers->Get(&hashKey));
00459         if (!entry ||
00460             !entry->PushListener(NS_STATIC_CAST(nsILDAPMessageListener *,
00461                                                 aListener))) {
00462             return NS_ERROR_FAILURE;
00463         }
00464     }
00465 
00466     return NS_OK;
00467 }
00468 
00469 // nsILDAPConnection getConnection (in wstring aKey);
00470 NS_IMETHODIMP nsLDAPService::GetConnection(const PRUnichar *aKey,
00471                                            nsILDAPConnection **_retval)
00472 {
00473     nsLDAPServiceEntry *entry;
00474     nsStringKey hashKey(aKey, -1, nsStringKey::NEVER_OWN);
00475     nsAutoLock lock(mLock);
00476 
00477     if (!_retval) {
00478         NS_ERROR("nsLDAPService::GetConnection: null pointer ");
00479         return NS_ERROR_NULL_POINTER;
00480     }
00481 
00482     entry = NS_STATIC_CAST(nsLDAPServiceEntry *, mServers->Get(&hashKey));
00483     if (!entry) {
00484         *_retval = 0;
00485         return NS_ERROR_FAILURE;
00486     }
00487     entry->SetTimestamp();
00488     entry->IncrementLeases();
00489     if (!(*_retval = entry->GetConnection().get())){
00490         return NS_ERROR_FAILURE;
00491     }
00492 
00493     return NS_OK;
00494 }
00495 
00496 // void releaseConnection (in wstring aKey);
00497 NS_IMETHODIMP nsLDAPService::ReleaseConnection(const PRUnichar *aKey)
00498 {
00499     nsLDAPServiceEntry *entry;
00500     nsStringKey hashKey(aKey, -1, nsStringKey::NEVER_OWN);
00501     nsAutoLock lock(mLock);
00502 
00503     entry = NS_STATIC_CAST(nsLDAPServiceEntry *, mServers->Get(&hashKey));
00504     if (!entry) {
00505         return NS_ERROR_FAILURE;
00506     }
00507 
00508     if (entry->GetLeases() > 0) {
00509         entry->SetTimestamp();
00510         entry->DecrementLeases();
00511     } else {
00512         // Releasing a non-leased connection is currently a No-Op.
00513         //
00514     }
00515 
00516     return NS_OK;
00517 }
00518 
00519 // void reconnectConnection (in wstring aKey,
00520 //                           in nsILDAPMessageListener aMessageListener);
00521 NS_IMETHODIMP nsLDAPService::ReconnectConnection(const PRUnichar *aKey,
00522                                  nsILDAPMessageListener *aListener)
00523 {
00524     nsLDAPServiceEntry *entry;
00525     nsresult rv;
00526     nsStringKey hashKey(aKey, -1, nsStringKey::NEVER_OWN);
00527 
00528     if (!aListener) {
00529         NS_ERROR("nsLDAPService::ReconnectConnection: null pointer ");
00530         return NS_ERROR_NULL_POINTER;
00531     }
00532 
00533     {
00534         nsAutoLock lock(mLock);
00535         
00536         entry = NS_STATIC_CAST(nsLDAPServiceEntry *, mServers->Get(&hashKey));
00537         if (!entry) {
00538             return NS_ERROR_FAILURE;
00539         }
00540         entry->SetTimestamp();
00541 
00542         if (entry->IsRebinding()) {
00543             if (!entry->PushListener(aListener)) {
00544                 return NS_ERROR_FAILURE;
00545             }
00546 
00547             return NS_OK;
00548         }
00549 
00550         // Clear the old connection and message, which should get garbaged
00551         // collected now. We mark this as being "rebinding" now, and it
00552         // we be marked as finished either if there's an error condition,
00553         // or if the OnLDAPMessage() method gets called (i.e. bind() done).
00554         //
00555         entry->SetMessage(0);
00556         entry->SetConnection(0);
00557 
00558         // Get a new connection
00559         //
00560         entry->SetRebinding(PR_TRUE);
00561     }
00562 
00563     rv = EstablishConnection(entry, aListener);
00564     if (NS_FAILED(rv)) {
00565         return rv;
00566     }
00567 
00568     {
00569         nsAutoLock lock(mLock);
00570         
00571         if (!entry->PushListener(NS_STATIC_CAST(nsILDAPMessageListener *,
00572                                                 aListener))) {
00573             entry->SetRebinding(PR_FALSE);
00574             return NS_ERROR_FAILURE;
00575         }
00576     }
00577 
00578     return NS_OK;
00579 }
00580 
00588 NS_IMETHODIMP 
00589 nsLDAPService::OnLDAPMessage(nsILDAPMessage *aMessage)
00590 {
00591     nsCOMPtr<nsILDAPOperation> operation;
00592     nsCOMPtr<nsILDAPConnection> connection;
00593     PRInt32 messageType;
00594 
00595     // XXXleif: NULL messages are supposedly allowed, but the semantics
00596     // are not definted (yet?). This is something to look out for...
00597     //
00598 
00599 
00600     // figure out what sort of message was returned
00601     //
00602     nsresult rv = aMessage->GetType(&messageType);
00603     if (NS_FAILED(rv)) {
00604         NS_ERROR("nsLDAPService::OnLDAPMessage(): unexpected error in "
00605                  "nsLDAPMessage::GetType()");
00606         return NS_ERROR_UNEXPECTED;
00607     }
00608 
00609     switch (messageType) {
00610     case LDAP_RES_BIND:
00611         // a bind has completed
00612         //
00613         rv = aMessage->GetOperation(getter_AddRefs(operation));
00614         if (NS_FAILED(rv)) {
00615             NS_ERROR("nsLDAPService::OnLDAPMessage(): unexpected error in "
00616                      "nsLDAPMessage::GetOperation()");
00617             return NS_ERROR_UNEXPECTED;
00618         }
00619 
00620         rv = operation->GetConnection(getter_AddRefs(connection));
00621         if (NS_FAILED(rv)) {
00622             NS_ERROR("nsLDAPService::OnLDAPMessage(): unexpected error in "
00623                      "nsLDAPOperation::GetConnection()");
00624             return NS_ERROR_UNEXPECTED;
00625         }
00626 
00627         // Now we have the connection, lets find the corresponding
00628         // server entry in the Service.
00629         //
00630         {
00631             nsCOMPtr<nsILDAPMessageListener> listener;
00632             nsCOMPtr<nsILDAPMessage> message;
00633             nsLDAPServiceEntry *entry;
00634             nsVoidKey connKey(NS_STATIC_CAST(nsILDAPConnection *,
00635                                              connection));
00636             nsAutoLock lock(mLock);
00637 
00638             entry = NS_STATIC_CAST(nsLDAPServiceEntry *,
00639                                    mConnections->Get(&connKey));
00640             if (!entry) {
00641                 return NS_ERROR_FAILURE;
00642             }
00643 
00644             message = entry->GetMessage();
00645             if (message) {
00646                 // We already have a message, lets keep that one.
00647                 //
00648                 return NS_ERROR_FAILURE;
00649             }
00650 
00651             entry->SetRebinding(PR_FALSE);
00652             entry->SetMessage(aMessage);
00653 
00654             // Now process all the pending callbacks/listeners. We
00655             // have to make sure to unlock before calling a listener,
00656             // since it's likely to call back into us again.
00657             //
00658             while (listener = entry->PopListener()) {
00659                 lock.unlock();
00660                 listener->OnLDAPMessage(aMessage);
00661                 lock.lock();
00662             }
00663         }
00664         break;
00665 
00666     default:
00667         NS_WARNING("nsLDAPService::OnLDAPMessage(): unexpected LDAP message "
00668                    "received");
00669 
00670         // get the console service so we can log a message
00671         //
00672         nsCOMPtr<nsIConsoleService> consoleSvc = 
00673             do_GetService("@mozilla.org/consoleservice;1", &rv);
00674         if (NS_FAILED(rv)) {
00675             NS_ERROR("nsLDAPChannel::OnLDAPMessage() couldn't get console "
00676                      "service");
00677             break;
00678         }
00679 
00680         // log the message
00681         //
00682         rv = consoleSvc->LogStringMessage(
00683             NS_LITERAL_STRING("LDAP: WARNING: nsLDAPService::OnLDAPMessage(): Unexpected LDAP message received").get());
00684         NS_ASSERTION(NS_SUCCEEDED(rv), "nsLDAPService::OnLDAPMessage(): "
00685                      "consoleSvc->LogStringMessage() failed");
00686         break;
00687     }
00688 
00689     return NS_OK;
00690 }
00691 
00692 // void onLDAPInit (in nsILDAPConnection aConn, in nsresult aStatus); */
00693 //
00694 NS_IMETHODIMP
00695 nsLDAPService::OnLDAPInit(nsILDAPConnection *aConn, nsresult aStatus)
00696 {
00697     return NS_ERROR_NOT_IMPLEMENTED;
00698 }
00699 
00700 // Helper function to establish an LDAP connection properly.
00701 //
00702 nsresult
00703 nsLDAPService::EstablishConnection(nsLDAPServiceEntry *aEntry,
00704                                    nsILDAPMessageListener *aListener)
00705 {
00706     nsCOMPtr<nsILDAPOperation> operation;
00707     nsCOMPtr<nsILDAPServer> server;
00708     nsCOMPtr<nsILDAPURL> url;
00709     nsCOMPtr<nsILDAPConnection> conn, conn2;
00710     nsCOMPtr<nsILDAPMessage> message;
00711     nsCAutoString host;
00712     nsCAutoString binddn;
00713     nsCAutoString password;
00714     PRInt32 port;
00715     PRUint32 options;
00716     PRUint32 protocolVersion;
00717     nsresult rv;
00718 
00719     server = aEntry->GetServer();
00720     if (!server) {
00721         return NS_ERROR_FAILURE;
00722     }
00723 
00724     // Get username, password, and protocol version from the server entry.
00725     //
00726     rv = server->GetBinddn(binddn);
00727     if (NS_FAILED(rv)) {
00728         return NS_ERROR_FAILURE;
00729     }
00730     rv = server->GetPassword(password);
00731     if (NS_FAILED(rv)) {
00732         return NS_ERROR_FAILURE;
00733     }
00734     rv = server->GetProtocolVersion(&protocolVersion);
00735     if (NS_FAILED(rv)) {
00736         return NS_ERROR_FAILURE;
00737     }
00738 
00739     // Get the host and port out of the URL, which is in the server entry.
00740     //
00741     rv = server->GetUrl(getter_AddRefs(url));
00742     if (NS_FAILED(rv)) {
00743         return NS_ERROR_FAILURE;
00744     }
00745     rv = url->GetAsciiHost(host);
00746     if (NS_FAILED(rv)) {
00747         return NS_ERROR_FAILURE;
00748     }
00749     rv = url->GetPort(&port);
00750     if (NS_FAILED(rv)) {
00751         return NS_ERROR_FAILURE;
00752     }
00753 
00754     rv = url->GetOptions(&options);
00755     if (NS_FAILED(rv)) {
00756       return NS_ERROR_FAILURE;
00757     }
00758     // Create a new connection for this server.
00759     //
00760     conn = do_CreateInstance(kLDAPConnectionCID, &rv);
00761     if (NS_FAILED(rv)) {
00762         NS_ERROR("nsLDAPService::EstablishConnection(): could not create "
00763                  "@mozilla.org/network/ldap-connection;1");
00764         return NS_ERROR_FAILURE;
00765     }
00766 
00767     // Here we need to provide the binddn, see bug #75990
00768     //
00769     rv = conn->Init(host.get(), port, 
00770                     (options & nsILDAPURL::OPT_SECURE) ? PR_TRUE : PR_FALSE, 
00771                     binddn, this, nsnull, protocolVersion);
00772     if (NS_FAILED(rv)) {
00773         switch (rv) {
00774         // Only pass along errors we are aware of
00775         //
00776         case NS_ERROR_OUT_OF_MEMORY:
00777         case NS_ERROR_NOT_AVAILABLE:
00778         case NS_ERROR_FAILURE:
00779             return rv;
00780 
00781         case NS_ERROR_ILLEGAL_VALUE:
00782         default:
00783             return NS_ERROR_UNEXPECTED;
00784         }
00785     }
00786 
00787     // Try to detect a collision, i.e. someone established a connection
00788     // just before we did. If so, we drop ours. This code is somewhat
00789     // similar to what happens in RequestConnection(), i.e. we try to
00790     // call the listener directly if possible, and if not, push it on
00791     // the stack of pending requests.
00792     //
00793     {
00794         nsAutoLock lock(mLock);
00795 
00796         conn2 = aEntry->GetConnection();
00797         message = aEntry->GetMessage();
00798     }
00799 
00800     if (conn2) {
00801         // Drop the new connection, we can't use it.
00802         //
00803         conn = 0;
00804         if (message) {
00805             aListener->OnLDAPMessage(message);
00806             return NS_OK;
00807         }
00808 
00809         {
00810             nsAutoLock lock(mLock);
00811 
00812             if (!aEntry->PushListener(NS_STATIC_CAST(nsILDAPMessageListener *,
00813                                                      aListener))) {
00814                 return NS_ERROR_FAILURE;
00815             }
00816         }
00817 
00818         return NS_OK;
00819     }
00820 
00821     // We made the connection, lets store it to the server entry,
00822     // and also update the reverse lookup tables (for finding the
00823     // server entry related to a particular connection).
00824     //
00825     {
00826         nsVoidKey connKey(NS_STATIC_CAST(nsILDAPConnection *, conn));
00827         nsAutoLock lock(mLock);
00828 
00829         aEntry->SetConnection(conn);
00830         mConnections->Put(&connKey, aEntry);
00831     }
00832 
00833     // Setup the bind() operation.
00834     //
00835     operation = do_CreateInstance(kLDAPOperationCID, &rv);
00836     if (NS_FAILED(rv)) {
00837         return NS_ERROR_FAILURE;
00838     }
00839 
00840     rv = operation->Init(conn, this, nsnull);
00841     if (NS_FAILED(rv)) {
00842         return NS_ERROR_UNEXPECTED; // this should never happen
00843     }
00844 
00845     // Start a bind operation 
00846     //
00847     // Here we need to support the password, see bug #75990
00848     // 
00849     rv = operation->SimpleBind(password);
00850     if (NS_FAILED(rv)) {
00851         switch (rv) {
00852         // Only pass along errors we are aware of
00853         //
00854         case NS_ERROR_LDAP_ENCODING_ERROR:
00855         case NS_ERROR_FAILURE:
00856         case NS_ERROR_OUT_OF_MEMORY:
00857             return rv;
00858 
00859         default:
00860             return NS_ERROR_UNEXPECTED;
00861         }
00862     }
00863 
00864     return NS_OK;
00865 }
00866 
00867 /* AString createFilter (in unsigned long aMaxSize, in AString aPattern, in AString aPrefix, in AString aSuffix, in AString aAttr, in AString aValue); */
00868 NS_IMETHODIMP nsLDAPService::CreateFilter(PRUint32 aMaxSize, 
00869                                           const nsACString & aPattern,
00870                                           const nsACString & aPrefix,
00871                                           const nsACString & aSuffix,
00872                                           const nsACString & aAttr,
00873                                           const nsACString & aValue,
00874                                           nsACString & _retval)
00875 {
00876     if (!aMaxSize) {
00877         return NS_ERROR_INVALID_ARG;
00878     }
00879 
00880     // prepare to tokenize |value| for %vM ... %vN
00881     //
00882     nsReadingIterator<char> iter, iterEnd; // setup the iterators
00883     aValue.BeginReading(iter);
00884     aValue.EndReading(iterEnd);
00885 
00886     // figure out how big of an array we're going to need for the tokens,
00887     // including a trailing NULL, and allocate space for it.
00888     //
00889     PRUint32 numTokens = CountTokens(iter, iterEnd); 
00890     char **valueWords;
00891     valueWords = NS_STATIC_CAST(char **, 
00892                                 nsMemory::Alloc((numTokens + 1) *
00893                                                 sizeof(char *)));
00894     if (!valueWords) {
00895         return NS_ERROR_OUT_OF_MEMORY;
00896     }
00897 
00898     // build the array of values
00899     //
00900     PRUint32 curToken = 0;
00901     while (iter != iterEnd && curToken < numTokens ) {
00902         valueWords[curToken] = NextToken(iter, iterEnd);
00903         if ( !valueWords[curToken] ) {
00904             NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(curToken, valueWords);
00905             return NS_ERROR_OUT_OF_MEMORY;
00906         }
00907         curToken++;
00908     }
00909     valueWords[numTokens] = 0;  // end of array signal to LDAP C SDK
00910 
00911     // make buffer to be used for construction 
00912     //
00913     char *buffer = NS_STATIC_CAST(char *, 
00914                                   nsMemory::Alloc(aMaxSize * sizeof(char)));
00915     if (!buffer) {
00916         NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(numTokens, valueWords);
00917         return NS_ERROR_OUT_OF_MEMORY;
00918     }
00919 
00920     // create the filter itself
00921     //
00922     nsresult rv;
00923     int result = ldap_create_filter(buffer, aMaxSize, 
00924                    NS_CONST_CAST(char *, PromiseFlatCString(aPattern).get()),
00925                    NS_CONST_CAST(char *, PromiseFlatCString(aPrefix).get()),
00926                    NS_CONST_CAST(char *, PromiseFlatCString(aSuffix).get()),
00927                    NS_CONST_CAST(char *, PromiseFlatCString(aAttr).get()),
00928                    NS_CONST_CAST(char *, PromiseFlatCString(aValue).get()),
00929                    valueWords);
00930     switch (result) {
00931     case LDAP_SUCCESS:
00932         rv = NS_OK;
00933         break;
00934 
00935     case LDAP_SIZELIMIT_EXCEEDED:
00936         PR_LOG(gLDAPLogModule, PR_LOG_DEBUG, 
00937                    ("nsLDAPService::CreateFilter(): "
00938                     "filter longer than max size of %d generated", 
00939                     aMaxSize));
00940         rv = NS_ERROR_NOT_AVAILABLE;
00941         break;
00942 
00943     case LDAP_PARAM_ERROR:
00944         rv = NS_ERROR_INVALID_ARG;
00945         break;
00946 
00947     default:
00948         NS_ERROR("nsLDAPService::CreateFilter(): ldap_create_filter() "
00949                  "returned unexpected error");
00950         rv = NS_ERROR_UNEXPECTED;
00951         break;
00952     }
00953 
00954     _retval.Assign(buffer);
00955 
00956     // done with the array and the buffer
00957     //
00958     NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(numTokens, valueWords);
00959     nsMemory::Free(buffer);
00960 
00961     return rv;
00962 }
00963 
00964 // Count the number of space-separated tokens between aIter and aIterEnd
00965 //
00966 PRUint32
00967 nsLDAPService::CountTokens(nsReadingIterator<char> aIter,
00968                            nsReadingIterator<char> aIterEnd)
00969 {
00970     PRUint32 count(0);
00971 
00972     // keep iterating through the string until we hit the end
00973     //
00974     while (aIter != aIterEnd) {
00975     
00976         // move past any leading spaces
00977         //
00978         while (aIter != aIterEnd &&
00979                ldap_utf8isspace(NS_CONST_CAST(char *, aIter.get()))){
00980             ++aIter;
00981         }
00982 
00983         // move past all chars in this token
00984         //
00985         while (aIter != aIterEnd) {
00986 
00987             if (ldap_utf8isspace(NS_CONST_CAST(char *, aIter.get()))) {
00988                 ++count;    // token finished; increment the count
00989                 ++aIter;    // move past the space
00990                 break;
00991             }
00992 
00993             ++aIter; // move to next char
00994 
00995             // if we've hit the end of this token and the end of this 
00996             // iterator simultaneous, be sure to bump the count, since we're
00997             // never gonna hit the IsAsciiSpace where it's normally done.
00998             //
00999             if (aIter == aIterEnd) {
01000                 ++count;
01001             }
01002 
01003         }
01004     }
01005 
01006     return count;
01007 }
01008 
01009 // return the next token in this iterator
01010 //
01011 char*
01012 nsLDAPService::NextToken(nsReadingIterator<char> & aIter,
01013                          nsReadingIterator<char> & aIterEnd)
01014 {
01015     // move past any leading whitespace
01016     //
01017     while (aIter != aIterEnd &&
01018            ldap_utf8isspace(NS_CONST_CAST(char *, aIter.get()))) {
01019         ++aIter;
01020     }
01021 
01022     nsACString::const_iterator start(aIter);
01023 
01024     // copy the token into our local variable
01025     //
01026     while (aIter != aIterEnd &&
01027            !ldap_utf8isspace(NS_CONST_CAST(char *, aIter.get()))) {
01028         ++aIter;
01029     }
01030 
01031     return ToNewCString(Substring(start, aIter));
01032 }