Back to index

lightning-sunbird  0.9+nobinonly
nsLDAPChannel.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  *   Dan Mosedale <dmose@mozilla.org>
00025  *   Warren Harris <warren@netscape.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsLDAPInternal.h"
00042 #include "nsLDAPConnection.h"
00043 #include "nsLDAPChannel.h"
00044 #include "nsString.h"
00045 #include "nsReadableUtils.h"
00046 #include "nsMimeTypes.h"
00047 #include "nsIPipe.h"
00048 #include "nsXPIDLString.h"
00049 #include "nsILDAPURL.h"
00050 #include "nsIProxyObjectManager.h"
00051 #include "nsIServiceManager.h"
00052 #include "nsIConsoleService.h"
00053 #include "nsILDAPMessage.h"
00054 
00055 #if !INVOKE_LDAP_CALLBACKS_ON_MAIN_THREAD
00056 #include "nsNetUtil.h"
00057 #include "nsIEventQueueService.h"
00058 #endif
00059 
00060 static NS_DEFINE_IID(kILDAPMessageListenerIID, NS_ILDAPMESSAGELISTENER_IID);
00061 static NS_DEFINE_IID(kILoadGroupIID, NS_ILOADGROUP_IID);
00062 static NS_DEFINE_IID(kIProgressEventSink, NS_IPROGRESSEVENTSINK_IID);
00063 
00064 NS_IMPL_THREADSAFE_ISUPPORTS3(nsLDAPChannel, 
00065                               nsIChannel, 
00066                               nsIRequest,   
00067                               nsILDAPMessageListener)
00068 
00069 nsLDAPChannel::nsLDAPChannel()
00070 {
00071 }
00072 
00073 nsLDAPChannel::~nsLDAPChannel()
00074 {
00075 }
00076 
00077 // initialize the channel
00078 //
00079 nsresult
00080 nsLDAPChannel::Init(nsIURI *uri)
00081 {
00082     nsresult rv;
00083 
00084     mStatus = NS_OK;
00085     mURI = uri;
00086     mLoadFlags = LOAD_NORMAL;
00087     mReadPipeOffset = 0;
00088     mReadPipeClosed = PR_FALSE;
00089 
00090     // create an LDAP connection
00091     //
00092     mConnection = do_CreateInstance("@mozilla.org/network/ldap-connection;1", 
00093                                     &rv);
00094     if (NS_FAILED(rv)) {
00095         NS_ERROR("nsLDAPChannel::Init(): could not create "
00096                  "@mozilla.org/network/ldap-connection;1");
00097         return NS_ERROR_FAILURE;
00098     }
00099 
00100     // i think that in the general case, it will be worthwhile to leave the
00101     // callbacks for this channel be invoked on the LDAP connection thread.
00102     // however, for the moment, I want to leave the code that invokes it on
00103     // the main thread here (though turned off), because it provides a 
00104     // useful demonstration of why PLEvents' lack of priorities hurts us 
00105     // (in this case in ldap: searches that return a lot of results).
00106     //
00107 #if INVOKE_LDAP_CALLBACKS_ON_MAIN_THREAD
00108 
00109     // get the proxy object manager
00110     //
00111     nsCOMPtr<nsIProxyObjectManager> proxyObjMgr = 
00112         do_GetService("@mozilla.org/xpcomproxy;1", &rv);
00113     if (NS_FAILED(rv)) {
00114         NS_ERROR("nsLDAPChannel::Init(): could not create "
00115                  "proxy object manager");
00116         return NS_ERROR_FAILURE;
00117     }
00118 
00119     // and use it to get a proxy for this callback, saving it off in mCallback
00120     //
00121     rv = proxyObjMgr->GetProxyForObject(NS_UI_THREAD_EVENTQ,
00122                                         kILDAPMessageListenerIID,
00123                                         NS_STATIC_CAST(nsILDAPMessageListener *
00124                                                        , this),
00125                                         PROXY_ASYNC|PROXY_ALWAYS,
00126                                         getter_AddRefs(mCallback));
00127     if (NS_FAILED(rv)) {
00128         NS_ERROR("nsLDAPChannel::Init(): could not create proxy object");
00129         return NS_ERROR_FAILURE;
00130     }
00131 
00132 #else   
00133     mCallback = this;
00134 #endif
00135     
00136     return NS_OK;
00137 }
00138 
00139 // impl code cribbed from nsJARChannel.cpp
00140 //
00141 NS_METHOD
00142 nsLDAPChannel::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
00143 {
00144     nsresult rv;
00145 
00146     if (aOuter)
00147         return NS_ERROR_NO_AGGREGATION;
00148 
00149     nsLDAPChannel* ldapChannel = new nsLDAPChannel();
00150     if (!ldapChannel)
00151         return NS_ERROR_OUT_OF_MEMORY;
00152 
00153     NS_ADDREF(ldapChannel);
00154     rv = ldapChannel->QueryInterface(aIID, aResult);
00155     NS_RELEASE(ldapChannel);
00156     return rv;
00157 }
00158 
00159 // nsIRequest methods
00160 
00161 NS_IMETHODIMP
00162 nsLDAPChannel::GetName(nsACString &result)
00163 {
00164     if (mURI)
00165         return mURI->GetSpec(result);
00166     result.Truncate();
00167     return NS_OK;
00168 }
00169 
00170 NS_IMETHODIMP
00171 nsLDAPChannel::IsPending(PRBool *result)
00172 {
00173     NS_NOTYETIMPLEMENTED("nsLDAPChannel::IsPending");
00174     return NS_ERROR_NOT_IMPLEMENTED;
00175 }
00176 
00177 NS_IMETHODIMP
00178 nsLDAPChannel::GetStatus(nsresult *status)
00179 {
00180     return mStatus;
00181 }
00182 
00183 NS_IMETHODIMP
00184 nsLDAPChannel::Cancel(nsresult aStatus)
00185 {
00186     nsresult rv;
00187 
00188     // set the status
00189     //
00190     mStatus = aStatus;
00191 
00192     // if there is an operation running, abandon it and remove it from the 
00193     // queue
00194     //
00195     if (mCurrentOperation) {
00196 
00197         // if this fails in a non-debug build, there's not much we can do
00198         //
00199         rv = mCurrentOperation->AbandonExt();
00200         NS_ASSERTION(NS_SUCCEEDED(rv), "nsLDAPChannel::Cancel(): "
00201                      "mCurrentOperation->Abandon() failed\n");
00202         
00203         // make nsCOMPtr call Release()
00204         // 
00205         mCurrentOperation = 0;
00206 
00207     }
00208 
00209     // if the read pipe exists and hasn't already been closed, close it
00210     //
00211     if (mReadPipeOut != 0 && !mReadPipeClosed) {
00212         
00213         // XXX set mReadPipeClosed?
00214 
00215         // if this fails in a non-debug build, there's not much we can do
00216         //
00217         rv = mReadPipeOut->Close();
00218         NS_ASSERTION(NS_SUCCEEDED(rv), "nsLDAPChannel::Cancel(): "
00219                      "mReadPipeOut->Close() failed");
00220     }
00221 
00222     // remove self from loadgroup to stop the throbber
00223     //
00224     if (mLoadGroup) {
00225         rv = mLoadGroup->RemoveRequest(NS_STATIC_CAST(nsIRequest *, this), 
00226                                        mResponseContext, aStatus);
00227         if (NS_FAILED(rv)) 
00228             return rv;
00229     }
00230 
00231     // call listener's onstoprequest
00232     //
00233     if (mUnproxiedListener) {
00234         rv = mListener->OnStopRequest(this, mResponseContext, aStatus);
00235         if (NS_FAILED(rv)) 
00236             return rv;
00237     }
00238 
00239     // Drop notification callbacks to prevent cycles.
00240     mCallbacks = nsnull;
00241     mEventSink = nsnull;
00242 
00243     return NS_OK;
00244 }
00245 
00246 NS_IMETHODIMP
00247 nsLDAPChannel::Suspend(void)
00248 {
00249     NS_NOTYETIMPLEMENTED("nsLDAPChannel::Suspend");
00250     return NS_ERROR_NOT_IMPLEMENTED;
00251 }
00252 
00253 NS_IMETHODIMP
00254 nsLDAPChannel::Resume(void)
00255 {
00256     NS_NOTYETIMPLEMENTED("nsLDAPChannel::Resume");
00257     return NS_ERROR_NOT_IMPLEMENTED;
00258 }
00259 
00260 // nsIChannel methods
00261 //
00262 
00263 NS_IMETHODIMP
00264 nsLDAPChannel::SetOriginalURI(nsIURI *aOriginalURI)
00265 {
00266     mOriginalURI = aOriginalURI;
00267     return NS_OK;
00268 }
00269 
00270 NS_IMETHODIMP
00271 nsLDAPChannel::GetOriginalURI(nsIURI **aOriginalURI)
00272 {
00273     *aOriginalURI = mOriginalURI ? mOriginalURI : mURI;
00274     NS_IF_ADDREF(*aOriginalURI);
00275 
00276     return NS_OK;
00277 }
00278 
00279 // getter for URI attribute:
00280 //
00281 // Returns the URL to which the channel currently refers. If a redirect
00282 // or URI resolution occurs, this accessor returns the current location
00283 // to which the channel is referring.
00284 //
00285 NS_IMETHODIMP
00286 nsLDAPChannel::GetURI(nsIURI* *aURI)
00287 {
00288     *aURI = mURI;
00289     NS_IF_ADDREF(*aURI);
00290     return NS_OK;
00291 }
00292 
00293 // getter and setter for loadFlags attribute:
00294 //
00295 // The load attributes for the request. E.g. setting the load 
00296 // attributes with the LOAD_QUIET bit set causes the loading process to
00297 // not deliver status notifications to the program performing the load,
00298 // and to not contribute to keeping any nsILoadGroup it may be contained
00299 // in from firing its OnLoadComplete notification.
00300 //
00301 NS_IMETHODIMP
00302 nsLDAPChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
00303 {
00304     *aLoadFlags = mLoadFlags;
00305     return NS_OK;
00306 }
00307 
00308 NS_IMETHODIMP
00309 nsLDAPChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
00310 {
00311     mLoadFlags = aLoadFlags;
00312     return NS_OK;
00313 }
00314 
00315 // getter and setter for contentType attribute:
00316 //
00317 // The content MIME type of the channel if available. Note that the 
00318 // content type can often be wrongly specified (wrong file extension, wrong
00319 // MIME type, wrong document type stored on a server, etc.) and the caller
00320 // most likely wants to verify with the actual data. 
00321 //
00322 NS_IMETHODIMP
00323 nsLDAPChannel::GetContentType(nsACString &aContentType)
00324 {
00325     aContentType.AssignLiteral(TEXT_PLAIN);
00326     return NS_OK;
00327 }
00328 
00329 NS_IMETHODIMP
00330 nsLDAPChannel::SetContentType(const nsACString &aContentType)
00331 {
00332     NS_NOTYETIMPLEMENTED("nsLDAPChannel::SetContentType");
00333     return NS_ERROR_NOT_IMPLEMENTED;
00334 }
00335 
00336 NS_IMETHODIMP
00337 nsLDAPChannel::GetContentCharset(nsACString &aContentCharset)
00338 {
00339     aContentCharset.Truncate();
00340     return NS_OK;
00341 }
00342 
00343 NS_IMETHODIMP
00344 nsLDAPChannel::SetContentCharset(const nsACString &aContentCharset)
00345 {
00346     NS_NOTYETIMPLEMENTED("nsLDAPChannel::SetContentCharset");
00347     return NS_ERROR_NOT_IMPLEMENTED;
00348 }
00349 
00350 // getter and setter for contentLength attribute:
00351 //
00352 // Returns the length of the data associated with the channel if available.
00353 // If the length is unknown then -1 is returned.
00354 
00355 NS_IMETHODIMP
00356 nsLDAPChannel::GetContentLength(PRInt32 *)
00357 {
00358     NS_NOTYETIMPLEMENTED("nsLDAPChannel::GetContentLength");
00359     return NS_ERROR_NOT_IMPLEMENTED;
00360 }
00361 
00362 NS_IMETHODIMP
00363 nsLDAPChannel::SetContentLength(PRInt32)
00364 {
00365     NS_NOTYETIMPLEMENTED("nsLDAPChannel::SetContentLength");
00366     return NS_ERROR_NOT_IMPLEMENTED;
00367 }
00368 
00369 // getter and setter for the owner attribute:
00370 //
00371 // The owner corresponding to the entity that is responsible for this
00372 // channel. Used by security code to grant or deny privileges to
00373 // mobile code loaded from this channel.
00374 //
00375 // Note: This is a strong reference to the owner, so if the owner is also
00376 // holding a pointer to the channel, care must be taken to explicitly drop
00377 // its reference to the channel -- otherwise a leak will result.
00378 //
00379 NS_IMETHODIMP
00380 nsLDAPChannel::GetOwner(nsISupports* *aOwner)
00381 {
00382     *aOwner = mOwner;
00383     NS_IF_ADDREF(*aOwner);
00384 
00385     return NS_OK;
00386 }
00387 
00388 NS_IMETHODIMP
00389 nsLDAPChannel::SetOwner(nsISupports *aOwner)
00390 {
00391     mOwner = aOwner;
00392     return NS_OK;
00393 }
00394 
00395 // getter and setter for the loadGroup attribute:
00396 //
00397 // the load group of which the channel is a currently a member.
00398 //
00399 NS_IMETHODIMP
00400 nsLDAPChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
00401 {
00402     *aLoadGroup = mUnproxiedLoadGroup;
00403     NS_IF_ADDREF(*aLoadGroup);
00404   
00405     return NS_OK;
00406 }
00407 
00408 NS_IMETHODIMP
00409 nsLDAPChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
00410 {
00411     mUnproxiedLoadGroup = aLoadGroup;
00412 
00413     // in the case where the LDAP callbacks happen on the connection thread,
00414     // we'll need to call into the loadgroup from there
00415     //
00416 #if INVOKE_LDAP_CALLBACKS_ON_MAIN_THREAD
00417 
00418     mLoadGroup = mUnproxiedLoadGroup;
00419 
00420 #else
00421     nsresult rv;
00422 
00423     // get the proxy object manager
00424     //
00425     nsCOMPtr<nsIProxyObjectManager> proxyObjMgr = 
00426         do_GetService("@mozilla.org/xpcomproxy;1", &rv);
00427     if (NS_FAILED(rv)) {
00428         NS_ERROR("nsLDAPChannel::SetLoadGroup(): could not create "
00429                  "proxy object manager");
00430         return NS_ERROR_FAILURE;
00431     }
00432 
00433     // and use it to get and save a proxy for the load group
00434     //
00435     rv = proxyObjMgr->GetProxyForObject(NS_UI_THREAD_EVENTQ, kILoadGroupIID,
00436                                         mUnproxiedLoadGroup, 
00437                                         PROXY_SYNC|PROXY_ALWAYS,
00438                                         getter_AddRefs(mLoadGroup));
00439     if (NS_FAILED(rv)) {
00440         NS_ERROR("nsLDAPChannel::SetLoadGroup(): could not create proxy "
00441                  "event");
00442         return NS_ERROR_FAILURE;
00443     }
00444 #endif
00445 
00446     return NS_OK;
00447 }
00448 
00449 // getter and setter for the notificationCallbacks
00450 //
00451 // The capabilities callbacks of the channel. This is set by clients
00452 // who wish to provide a means to receive progress, status and 
00453 // protocol-specific notifications.
00454 //
00455 NS_IMETHODIMP
00456 nsLDAPChannel::GetNotificationCallbacks(nsIInterfaceRequestor* 
00457                                         *aNotificationCallbacks)
00458 {
00459     *aNotificationCallbacks = mCallbacks;
00460     NS_IF_ADDREF(*aNotificationCallbacks);
00461 
00462     return NS_OK;
00463 }
00464 
00465 NS_IMETHODIMP
00466 nsLDAPChannel::SetNotificationCallbacks(nsIInterfaceRequestor* 
00467                                         aNotificationCallbacks)
00468 {
00469     nsresult rv;
00470 
00471     // save off the callbacks
00472     //
00473     mCallbacks = aNotificationCallbacks;
00474     if (mCallbacks) {
00475 
00476         // get the (unproxied) event sink interface 
00477         //
00478         nsCOMPtr<nsIProgressEventSink> eventSink;
00479         rv = mCallbacks->GetInterface(NS_GET_IID(nsIProgressEventSink), 
00480                                       getter_AddRefs(eventSink));
00481         if (NS_FAILED(rv)) {
00482             NS_ERROR("nsLDAPChannel::SetNotificationCallbacks(): "
00483                      "mCallbacks->GetInterface failed");
00484             return NS_ERROR_FAILURE;
00485         }
00486 
00487 #if INVOKE_LDAP_CALLBACKS_ON_MAIN_THREAD
00488         mEventSink = eventSink;
00489 #else
00490         // get the proxy object manager
00491         //
00492         nsCOMPtr<nsIProxyObjectManager> proxyObjMgr = 
00493             do_GetService("@mozilla.org/xpcomproxy;1", &rv);
00494         if (NS_FAILED(rv)) {
00495             NS_ERROR("nsLDAPChannel::SetNotificationCallbacks(): could not "
00496                      "create proxy object manager");
00497             return NS_ERROR_FAILURE;
00498         }
00499 
00500         // and use it to get a proxy for this callback, saving it off 
00501         // in mEventSink
00502         //
00503         rv = proxyObjMgr->GetProxyForObject(NS_UI_THREAD_EVENTQ,
00504                                             kIProgressEventSink,
00505                                             eventSink,
00506                                             PROXY_ASYNC | PROXY_ALWAYS,
00507                                             getter_AddRefs(mEventSink));
00508         if (NS_FAILED(rv)) {
00509             NS_ERROR("nsLDAPChannel::SetNotificationCallbacks(): "
00510                      "couldn't get proxy for event sink");
00511             return NS_ERROR_FAILURE;
00512         }
00513 #endif
00514     }
00515 
00516     return NS_OK;
00517 }
00518 
00519 // getter for securityInfo attribute:
00520 //
00521 // Any security information about this channel.  This can be null.
00522 //
00523 NS_IMETHODIMP
00524 nsLDAPChannel::GetSecurityInfo(nsISupports* *aSecurityInfo)
00525 {
00526     NS_NOTYETIMPLEMENTED("nsLDAPChannel::GetSecurityInfo");
00527     return NS_ERROR_NOT_IMPLEMENTED;
00528 }
00529 
00530 // nsIChannel operations
00531 
00532 // Opens a blocking input stream to the URL's specified source.
00533 // @param startPosition - The offset from the start of the data
00534 //  from which to read.
00535 // @param readCount - The number of bytes to read. If -1, everything
00536 //  up to the end of the data is read. If greater than the end of 
00537 //  the data, the amount available is returned in the stream.
00538 //
00539 NS_IMETHODIMP
00540 nsLDAPChannel::Open(nsIInputStream* *result)
00541 {
00542     NS_NOTYETIMPLEMENTED("nsLDAPChannel::OpenInputStream");
00543     return NS_ERROR_NOT_IMPLEMENTED;
00544 }
00545 
00546 NS_IMETHODIMP
00547 nsLDAPChannel::AsyncOpen(nsIStreamListener* aListener,
00548                          nsISupports* aCtxt)
00549 {
00550     nsresult rv;
00551     nsCAutoString host;
00552     PRInt32 port;
00553     PRUint32 options;
00554 
00555     // slurp out relevant pieces of the URL
00556     //
00557     rv = mURI->GetAsciiHost(host);
00558     if (NS_FAILED(rv)) {
00559         NS_ERROR("nsLDAPChannel::AsyncRead(): mURI->GetAsciiHost failed\n");
00560         return NS_ERROR_FAILURE;
00561     }
00562 
00563     rv = mURI->GetPort(&port);
00564     if (NS_FAILED(rv)) {
00565         NS_ERROR("nsLDAPChannel::AsyncRead(): mURI->GetPort failed\n");
00566         return NS_ERROR_FAILURE;
00567     }
00568     if (port == -1)
00569         port = LDAP_PORT;
00570 
00571     // QI to nsILDAPURL so that we can call one of the methods on that iface
00572     //
00573     nsCOMPtr<nsILDAPURL> mLDAPURL = do_QueryInterface(mURI, &rv);
00574     if (NS_FAILED(rv)) {
00575         NS_ERROR("nsLDAPChannel::AsyncRead(): QI to nsILDAPURL failed\n");
00576         return NS_ERROR_FAILURE;
00577     }
00578 
00579     rv = mLDAPURL->GetOptions(&options);
00580     if (NS_FAILED(rv)) {
00581         NS_ERROR("nsLDAPChannel::AsyncRead(): mURI->GetOptions failed\n");
00582         return NS_ERROR_FAILURE;
00583     }
00584 
00585     rv = NS_CheckPortSafety(port, "ldap");
00586     if (NS_FAILED(rv))
00587         return rv;
00588 
00589     // save off the args
00590     //
00591     mResponseContext = aCtxt;
00592     mUnproxiedListener = aListener;
00593 
00594     // add ourselves to the appropriate loadgroup
00595     //
00596     if (mLoadGroup) {
00597         mLoadGroup->AddRequest(this, mResponseContext);
00598     }
00599 
00600     // we don't currently allow for a default host
00601     //
00602     if (host.IsEmpty())
00603         return NS_ERROR_MALFORMED_URI;
00604 
00605     // since the LDAP SDK does all the socket management, we don't have
00606     // an underlying transport channel to create an nsIInputStream to hand
00607     // back to the nsIStreamListener.  So we do it ourselves:
00608     //
00609     if (!mReadPipeIn) {
00610     
00611         // get a new pipe, propagating any error upwards
00612         //
00613         rv = NS_NewPipe(getter_AddRefs(mReadPipeIn), 
00614                         getter_AddRefs(mReadPipeOut), 
00615                         0, 0, PR_TRUE, PR_FALSE, 0);
00616         if (NS_FAILED(rv)) {
00617             NS_ERROR("nsLDAPChannel::AsyncRead(): unable to create new pipe");
00618             return NS_ERROR_FAILURE;
00619         }
00620     } 
00621 
00622     // get an AsyncStreamListener to proxy for mListener, if we're
00623     // compiled to have the LDAP callbacks happen on the LDAP connection=
00624     // thread.
00625     //
00626 #if INVOKE_LDAP_CALLBACKS_ON_MAIN_THREAD
00627     mListener = aListener;
00628 #else
00629     rv = NS_NewAsyncStreamListener(getter_AddRefs(mListener), 
00630                                    mUnproxiedListener, NS_UI_THREAD_EVENTQ);
00631     if (NS_FAILED(rv)) {
00632         NS_ERROR("nsLDAPChannel::AsyncRead(): unable to create new "
00633                  "AsyncStreamListener");
00634         return NS_ERROR_FAILURE;
00635     }
00636 
00637 #endif
00638 
00639     // we already know the content type, so we can fire this now
00640     //
00641     mUnproxiedListener->OnStartRequest(this, mResponseContext);
00642     if (NS_FAILED(rv)) {
00643         NS_ERROR("nsLDAPChannel::AsyncRead(): error firing OnStartRequest");
00644         return NS_ERROR_FAILURE;
00645     }
00646     
00647     // initialize it with the defaults
00648     // XXXdmose - need to deal with bind name
00649     // Need to deal with VERSION2 pref - don't know how to get it from here.
00650     rv = mConnection->Init(host.get(), port,
00651                            (options & nsILDAPURL::OPT_SECURE) ? PR_TRUE 
00652                            : PR_FALSE, EmptyCString(), this, nsnull, nsILDAPConnection::VERSION3);
00653     switch (rv) {
00654     case NS_OK:
00655         break;
00656 
00657     case NS_ERROR_OUT_OF_MEMORY:
00658     case NS_ERROR_NOT_AVAILABLE:
00659     case NS_ERROR_FAILURE:
00660         return rv;
00661 
00662     case NS_ERROR_ILLEGAL_VALUE:
00663     default:
00664         return NS_ERROR_UNEXPECTED;
00665     }
00666 
00667     // create and initialize an LDAP operation (to be used for the bind)
00668     //  
00669     mCurrentOperation = do_CreateInstance(
00670         "@mozilla.org/network/ldap-operation;1", &rv);
00671     if (NS_FAILED(rv)) {
00672         return NS_ERROR_FAILURE;
00673     }
00674 
00675     // our OnLDAPMessage accepts all result callbacks
00676     //
00677     rv = mCurrentOperation->Init(mConnection, mCallback, nsnull);
00678     if (NS_FAILED(rv))
00679         return NS_ERROR_UNEXPECTED; // this should never happen
00680 
00681     // kick off a bind operation 
00682     // 
00683     PR_LOG(gLDAPLogModule, PR_LOG_DEBUG, ("initiating SimpleBind\n"));
00684     rv = mCurrentOperation->SimpleBind(EmptyCString());
00685     if (NS_FAILED(rv)) {
00686 
00687         // XXXdmose better error handling / passthrough; deal with password
00688         //
00689         NS_WARNING("mCurrentOperation->SimpleBind failed.");
00690         return(rv);
00691     }
00692 
00693     return NS_OK;
00694 }
00695 
00703 NS_IMETHODIMP 
00704 nsLDAPChannel::OnLDAPMessage(nsILDAPMessage *aMessage)
00705 {
00706     PRInt32 messageType;
00707 
00708     // figure out what sort of message was returned
00709     //
00710     nsresult rv = aMessage->GetType(&messageType);
00711     if (NS_FAILED(rv)) {
00712         NS_ERROR("nsLDAPChannel::OnLDAPMessage(): unexpected error in "
00713                  "nsLDAPChannel::GetType()");
00714         return NS_ERROR_UNEXPECTED;
00715     }
00716 
00717     switch (messageType) {
00718 
00719     case LDAP_RES_BIND:
00720 
00721         // a bind has completed
00722         //
00723         return OnLDAPBind(aMessage);
00724         break;
00725 
00726     case LDAP_RES_SEARCH_ENTRY:
00727 
00728         // a search entry has been returned
00729         //
00730         return OnLDAPSearchEntry(aMessage);
00731         break;
00732 
00733     case LDAP_RES_SEARCH_RESULT:
00734 
00735         // the search is finished; we're all done
00736         //  
00737         return OnLDAPSearchResult(aMessage);
00738         break;
00739 
00740     default:
00741         NS_WARNING("nsLDAPChannel::OnLDAPMessage(): unexpected LDAP message "
00742                    "received");
00743 
00744         // get the console service so we can log a message
00745         //
00746         nsCOMPtr<nsIConsoleService> consoleSvc = 
00747             do_GetService("@mozilla.org/consoleservice;1", &rv);
00748         if (NS_FAILED(rv)) {
00749             NS_ERROR("nsLDAPChannel::OnLDAPMessage() couldn't get console "
00750                      "service");
00751             break;
00752         }
00753 
00754         // log the message
00755         //
00756         rv = consoleSvc->LogStringMessage(
00757             NS_LITERAL_STRING("LDAP: WARNING: nsLDAPChannel::OnLDAPMessage(): Unexpected LDAP message received").get());
00758         NS_ASSERTION(NS_SUCCEEDED(rv), "nsLDAPChannel::OnLDAPMessage(): "
00759                      "consoleSvc->LogStringMessage() failed");
00760         break;
00761     }
00762 
00763     return NS_OK;
00764 }
00765 
00766 nsresult
00767 nsLDAPChannel::OnLDAPBind(nsILDAPMessage *aMessage) 
00768 {
00769     nsCOMPtr<nsILDAPURL> url;
00770     nsCAutoString baseDn;
00771     nsCAutoString filter;
00772     PRInt32 scope;
00773     nsresult rv;
00774 
00775     // XXX should call ldap_parse_result() here
00776 
00777     mCurrentOperation = 0;  // done with bind op; make nsCOMPtr release it
00778 
00779     // create and initialize an LDAP operation (to be used for the search
00780     //  
00781     mCurrentOperation = do_CreateInstance(
00782         "@mozilla.org/network/ldap-operation;1", &rv);
00783     if (NS_FAILED(rv)) {
00784         NS_ERROR("nsLDAPChannel::OnLDAPBind(): couldn't create "
00785                  "@mozilla.org/network/ldap-operation;1");
00786         // XXX abort entire asyncread
00787         return NS_ERROR_FAILURE;
00788     }
00789 
00790     rv = mCurrentOperation->Init(mConnection, mCallback, nsnull);
00791     NS_ENSURE_SUCCESS(rv, rv);
00792 
00793     // QI() the URI to an nsILDAPURL so we get can the LDAP specific portions
00794     //
00795     url = do_QueryInterface(mURI, &rv);
00796     NS_ENSURE_SUCCESS(rv, rv);
00797 
00798     // get a base DN.  
00799     // XXXdmose - is it reasonable to barf on an empty dn?
00800     //
00801     rv = url->GetDn(baseDn);
00802     NS_ENSURE_SUCCESS(rv, rv);
00803     if (baseDn.IsEmpty()) {
00804         return NS_ERROR_MALFORMED_URI;
00805     }
00806 
00807     // get the scope
00808     //
00809     rv = url->GetScope(&scope);
00810     NS_ENSURE_SUCCESS(rv, rv);
00811 
00812     // and the filter 
00813     //
00814     rv = url->GetFilter(filter);
00815     NS_ENSURE_SUCCESS(rv, rv);
00816 
00817     // time to kick off the search.
00818     //
00819     // XXX what about timeouts? 
00820     // XXX failure is a reasonable thing; don't assert
00821     //
00822     PR_LOG(gLDAPLogModule, PR_LOG_DEBUG, 
00823            ("bind completed; starting search\n"));
00824     rv = mCurrentOperation->SearchExt(baseDn, scope, filter, 0, 0, 0,
00825                                       LDAP_NO_LIMIT);
00826     NS_ENSURE_SUCCESS(rv,rv);
00827     
00828     return NS_OK;
00829 }
00830 
00831 // void onLDAPInit (in nsresult aStatus); */
00832 //
00833 NS_IMETHODIMP
00834 nsLDAPChannel::OnLDAPInit(nsILDAPConnection *aConnection, nsresult aStatus)
00835 {
00836     return NS_ERROR_NOT_IMPLEMENTED;
00837 }
00838 
00839 // void OnLDAPSearchResult (in nsILDAPMessage aMessage);
00840 //
00841 nsresult
00842 nsLDAPChannel::OnLDAPSearchResult(nsILDAPMessage *aMessage)
00843 {
00844     PRInt32 errorCode;  // the LDAP error code
00845     nsresult rv;
00846 
00847     PR_LOG(gLDAPLogModule, PR_LOG_DEBUG, ("result returned\n"));
00848 
00849     // XXX should use GetErrorString here?
00850     //
00851     rv = aMessage->GetErrorCode(&errorCode);
00852     if ( NS_FAILED(rv) ) {
00853         NS_ERROR(ldap_err2string(errorCode));
00854         return NS_ERROR_FAILURE;
00855     }
00856 
00857     // we're done with the current operation.  cause nsCOMPtr to Release() it
00858     // so that if nsLDAPChannel::Cancel gets called, that doesn't try to call 
00859     // mCurrentOperation->Abandon().
00860     //
00861     mCurrentOperation = 0;
00862 
00863     // if the read pipe exists and hasn't already been closed, close it
00864     //
00865     if (mReadPipeOut != 0 && !mReadPipeClosed) {
00866 
00867         // if this fails in a non-debug build, there's not much we can do
00868         //
00869         rv = mReadPipeOut->Close();
00870         NS_ASSERTION(NS_SUCCEEDED(rv), "nsLDAPChannel::OnLDAPSearchResult(): "
00871                      "mReadPipeOut->Close() failed");
00872     }
00873 
00874     // remove self from loadgroup to stop the throbber
00875     //
00876     if (mLoadGroup) {
00877         rv = mLoadGroup->RemoveRequest(NS_STATIC_CAST(nsIRequest *, this), 
00878                                        mResponseContext, NS_OK);
00879         if (NS_FAILED(rv)) {
00880             NS_WARNING("nsLDAPChannel::OnSearchResult(): "
00881                        "mLoadGroup->RemoveChannel() failed");
00882             return rv;
00883         }
00884     }
00885 
00886     // call listener's onstoprequest
00887     //
00888     if (mListener) {
00889         rv = mListener->OnStopRequest(this, mResponseContext, NS_OK);
00890         if (NS_FAILED(rv)) {
00891             NS_WARNING("nsLDAPChannel::OnSearchResult(): "
00892                        "mListener->OnStopRequest failed\n");
00893             return rv;
00894         }
00895     }
00896 
00897     // Drop notification callbacks to prevent cycles.
00898     mCallbacks = nsnull;
00899     mEventSink = nsnull;
00900 
00901     return NS_OK;
00902 }
00903 
00904 // void OnLDAPSearchEntry (in nsILDAPMessage aMessage);
00905 //
00906 // XXXdmose most of this function should live in nsILDAPMessage::toString()
00907 //
00908 nsresult
00909 nsLDAPChannel::OnLDAPSearchEntry(nsILDAPMessage *aMessage)
00910 {
00911     nsresult rv;
00912     nsCAutoString dn;
00913     nsCString entry;
00914 
00915     PR_LOG(gLDAPLogModule, PR_LOG_DEBUG, ("entry returned!\n"));
00916 
00917     // get the DN
00918     // XXX better err handling
00919     //
00920     rv = aMessage->GetDn(dn);
00921     NS_ENSURE_SUCCESS(rv, rv);
00922 
00923     entry.SetCapacity(256);
00924     entry = NS_LITERAL_CSTRING("dn: ") + dn + NS_LITERAL_CSTRING("\n");
00925 
00926     char **attrs;
00927     PRUint32 attrCount;
00928 
00929     // get an array of all the attribute names
00930     // XXX better error-handling
00931     //
00932     rv = aMessage->GetAttributes(&attrCount, &attrs);
00933     NS_ENSURE_SUCCESS(rv, rv);
00934 
00935     // XXX is this an error?  should log in non-debug console too?
00936     //
00937     if (!attrCount) {
00938         NS_WARNING("Warning: entry received with no attributes");
00939     }
00940 
00941     // iterate through the attributes
00942     //
00943     for ( PRUint32 i=0 ; i < attrCount ; i++ ) {
00944 
00945         PRUnichar **vals;
00946         PRUint32 valueCount;
00947 
00948         // get the values of this attribute
00949         // XXX better failure handling
00950         //
00951         rv = aMessage->GetValues(attrs[i], &valueCount, &vals);
00952         if (NS_FAILED(rv)) {
00953             NS_WARNING("nsLDAPChannel:OnLDAPSearchEntry(): "
00954                        "aMessage->GetValues() failed\n");
00955             NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(attrCount, attrs);
00956             return rv;;
00957         }
00958 
00959         // print all values of this attribute
00960         //
00961         for ( PRUint32 j=0 ; j < valueCount; j++ ) {
00962             entry.Append(attrs[i]);
00963             entry.Append(": ");
00964             AppendUTF16toUTF8(vals[j], entry);
00965             entry.Append('\n');
00966         }
00967         NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(valueCount, vals);
00968 
00969     }
00970     NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(attrCount, attrs);
00971 
00972     // XXXdmose better error handling
00973     //
00974     if (NS_FAILED(rv)) {
00975         PR_LOG(gLDAPLogModule, PR_LOG_ERROR, 
00976                ("aMessage: error getting attribute\n"));
00977         return rv;
00978     }
00979 
00980     // separate this entry from the next
00981     //
00982     entry.Append('\n');
00983 
00984     // do the write
00985     // XXX better err handling
00986     //
00987     PRUint32 bytesWritten = 0;
00988     PRUint32 entryLength = entry.Length();
00989 
00990     rv = mReadPipeOut->Write(entry.get(), entryLength, &bytesWritten);
00991     NS_ENSURE_SUCCESS(rv, rv);
00992 
00993     // short writes shouldn't happen on blocking pipes!
00994     // XXX runtime error handling too
00995     //
00996     NS_ASSERTION(bytesWritten == entryLength, 
00997                  "nsLDAPChannel::OnLDAPSearchEntry(): "
00998                  "internal error: blocking pipe returned a short write");
00999 
01000     // XXXdmose deal more gracefully with an error here
01001     //
01002     rv = mListener->OnDataAvailable(this, mResponseContext, mReadPipeIn, 
01003                                     mReadPipeOffset, entryLength);
01004     NS_ENSURE_SUCCESS(rv, rv);
01005 
01006     mReadPipeOffset += entryLength;
01007 
01008     return NS_OK;
01009 }