Back to index

lightning-sunbird  0.9+nobinonly
nsFTPChannel.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* vim:set ts=4 sts=4 sw=4 et cin: */
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 mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 
00040 #include "nsFTPChannel.h"
00041 #include "nsIStreamListener.h"
00042 #include "nsIServiceManager.h"
00043 #include "nsNetUtil.h"
00044 #include "nsMimeTypes.h"
00045 #include "nsIProxyObjectManager.h"
00046 #include "nsReadableUtils.h"
00047 #include "nsIPrefService.h"
00048 #include "nsIPrefBranch.h"
00049 #include "nsIStreamConverterService.h"
00050 #include "nsISocketTransport.h"
00051 #include "nsURLHelper.h"
00052 
00053 #if defined(PR_LOGGING)
00054 extern PRLogModuleInfo* gFTPLog;
00055 #endif /* PR_LOGGING */
00056 
00058 static inline PRUint32
00059 PRTimeToSeconds(PRTime t_usec)
00060 {
00061     PRTime usec_per_sec;
00062     PRUint32 t_sec;
00063     LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
00064     LL_DIV(t_usec, t_usec, usec_per_sec);
00065     LL_L2I(t_sec, t_usec);
00066     return t_sec;
00067 }
00068 
00069 #define NowInSeconds() PRTimeToSeconds(PR_Now())
00070 
00071 
00072 
00073 // There are two transport connections established for an 
00074 // ftp connection. One is used for the command channel , and
00075 // the other for the data channel. The command channel is the first
00076 // connection made and is used to negotiate the second, data, channel.
00077 // The data channel is driven by the command channel and is either
00078 // initiated by the server (PORT command) or by the client (PASV command).
00079 // Client initiation is the most common case and is attempted first.
00080 
00081 nsFTPChannel::nsFTPChannel()
00082     : mIsPending(0),
00083       mLoadFlags(LOAD_NORMAL),
00084       mSourceOffset(0),
00085       mAmount(0),
00086       mContentLength(-1),
00087       mFTPState(nsnull),
00088       mStatus(NS_OK),
00089       mCanceled(PR_FALSE),
00090       mStartPos(LL_MaxUint())
00091 {
00092 }
00093 
00094 nsFTPChannel::~nsFTPChannel()
00095 {
00096 #if defined(PR_LOGGING)
00097     nsCAutoString spec;
00098     mURL->GetAsciiSpec(spec);
00099     PR_LOG(gFTPLog, PR_LOG_ALWAYS, ("~nsFTPChannel() for %s", spec.get()));
00100 #endif
00101     NS_IF_RELEASE(mFTPState);
00102 }
00103 
00104 NS_IMPL_ADDREF_INHERITED(nsFTPChannel, nsHashPropertyBag)
00105 NS_IMPL_RELEASE_INHERITED(nsFTPChannel, nsHashPropertyBag)
00106 
00107 NS_INTERFACE_MAP_BEGIN(nsFTPChannel)
00108     NS_INTERFACE_MAP_ENTRY(nsIChannel)
00109     NS_INTERFACE_MAP_ENTRY(nsIFTPChannel)
00110     NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
00111     NS_INTERFACE_MAP_ENTRY(nsIUploadChannel)
00112     NS_INTERFACE_MAP_ENTRY(nsIRequest)
00113     NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) 
00114     NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
00115     NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
00116     NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
00117     NS_INTERFACE_MAP_ENTRY(nsICacheListener)
00118 NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag)
00119 
00120 nsresult
00121 nsFTPChannel::Init(nsIURI* uri, nsIProxyInfo* proxyInfo, nsICacheSession* session)
00122 {
00123     nsresult rv = nsHashPropertyBag::Init();
00124     if (NS_FAILED(rv))
00125         return rv;
00126 
00127     // setup channel state
00128     mURL = uri;
00129     mProxyInfo = proxyInfo;
00130 
00131     mIOService = do_GetIOService(&rv);
00132     if (NS_FAILED(rv)) return rv;
00133 
00134     mCacheSession = session;
00135     
00136     return NS_OK;
00137 }
00138 
00139 
00141 // nsIRequest methods:
00142 
00143 // The FTP channel doesn't maintain any connection state. Nor does it
00144 // interpret the protocol. The FTP connection thread is responsible for
00145 // these things and thus, the FTP channel simply calls through to the 
00146 // FTP connection thread using an xpcom proxy object to make the
00147 // cross thread call.
00148 
00149 NS_IMETHODIMP
00150 nsFTPChannel::GetName(nsACString &result)
00151 {
00152     return mURL->GetSpec(result);
00153 }
00154 
00155 NS_IMETHODIMP
00156 nsFTPChannel::IsPending(PRBool *result) {
00157     *result = mIsPending;
00158     return NS_OK;
00159 }
00160 
00161 NS_IMETHODIMP
00162 nsFTPChannel::GetStatus(nsresult *status)
00163 {
00164     *status = mStatus;
00165     return NS_OK;
00166 }
00167 
00168 NS_IMETHODIMP
00169 nsFTPChannel::Cancel(nsresult status) {
00170 
00171     PR_LOG(gFTPLog, 
00172            PR_LOG_DEBUG, 
00173            ("nsFTPChannel::Cancel() called [this=%x, status=%x, mCanceled=%d]\n", 
00174             this, status, mCanceled));
00175 
00176     NS_ASSERTION(NS_FAILED(status), "shouldn't cancel with a success code");
00177     
00178     if (mCanceled) 
00179         return NS_OK;
00180 
00181     mCanceled = PR_TRUE;
00182 
00183     mStatus = status;
00184 
00185     if (mFTPState) 
00186         (void)mFTPState->Cancel(status);
00187 
00188     return NS_OK;
00189 }
00190 
00191 NS_IMETHODIMP
00192 nsFTPChannel::Suspend(void) {
00193 
00194     PR_LOG(gFTPLog, PR_LOG_DEBUG, 
00195            ("nsFTPChannel::Suspend() called [this=%x]\n", this));
00196 
00197     if (mFTPState) {
00198         return mFTPState->Suspend();
00199     }
00200     return NS_OK;
00201 }
00202 
00203 NS_IMETHODIMP
00204 nsFTPChannel::Resume(void) {
00205 
00206     PR_LOG(gFTPLog, PR_LOG_DEBUG, 
00207            ("nsFTPChannel::Resume() called [this=%x]\n", this));
00208 
00209     if (mFTPState) {
00210         return mFTPState->Resume();
00211     }
00212     return NS_OK;
00213 }
00214 
00216 // nsIChannel methods:
00217 
00218 NS_IMETHODIMP
00219 nsFTPChannel::GetOriginalURI(nsIURI* *aURL)
00220 {
00221     *aURL = mOriginalURI ? mOriginalURI : mURL;
00222     NS_ADDREF(*aURL);
00223     return NS_OK;
00224 }
00225 
00226 NS_IMETHODIMP
00227 nsFTPChannel::SetOriginalURI(nsIURI* aURL)
00228 {
00229     mOriginalURI = aURL;
00230     return NS_OK;
00231 }
00232 
00233 NS_IMETHODIMP
00234 nsFTPChannel::GetURI(nsIURI* *aURL)
00235 {
00236     *aURL = mURL;
00237     NS_ADDREF(*aURL);
00238     return NS_OK;
00239 }
00240 
00241 NS_IMETHODIMP
00242 nsFTPChannel::Open(nsIInputStream **result)
00243 {
00244     return NS_ImplementChannelOpen(this, result);
00245 }
00246 
00247 nsresult
00248 nsFTPChannel::GenerateCacheKey(nsACString &cacheKey)
00249 {
00250     cacheKey.SetLength(0);
00251     
00252     nsCAutoString spec;
00253     mURL->GetAsciiSpec(spec);
00254 
00255     // Strip any trailing #ref from the URL before using it as the key
00256     const char *p = strchr(spec.get(), '#');
00257     if (p)
00258         cacheKey.Append(Substring(spec, 0, p - spec.get()));
00259     else
00260         cacheKey.Append(spec);
00261     return NS_OK;
00262 }
00263 
00264 NS_IMETHODIMP
00265 nsFTPChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
00266 {
00267     nsresult rv = AsyncOpenAt(listener, ctxt, mStartPos, mEntityID);
00268     // mEntityID no longer needed, clear it to avoid returning a wrong entity
00269     // id when someone asks us
00270     mEntityID.Truncate();
00271     return rv;
00272 }
00273 
00274 NS_IMETHODIMP
00275 nsFTPChannel::ResumeAt(PRUint64 aStartPos, const nsACString& aEntityID)
00276 {
00277     mEntityID = aEntityID;
00278     mStartPos = aStartPos;
00279     return NS_OK;
00280 }
00281 
00282 NS_IMETHODIMP
00283 nsFTPChannel::GetEntityID(nsACString& entityID)
00284 {
00285     if (mEntityID.IsEmpty())
00286       return NS_ERROR_NOT_RESUMABLE;
00287 
00288     entityID = mEntityID;
00289     return NS_OK;
00290 }
00291 
00292 nsresult
00293 nsFTPChannel::AsyncOpenAt(nsIStreamListener *listener, nsISupports *ctxt,
00294                           PRUint64 startPos, const nsACString& entityID)
00295 {
00296     PRInt32 port;
00297     nsresult rv = mURL->GetPort(&port);
00298     if (NS_FAILED(rv))
00299         return rv;
00300  
00301     rv = NS_CheckPortSafety(port, "ftp", mIOService);
00302     if (NS_FAILED(rv))
00303         return rv;
00304 
00305     PR_LOG(gFTPLog, PR_LOG_DEBUG, ("nsFTPChannel::AsyncOpen() called\n"));
00306 
00307     mListener = listener;
00308     mUserContext = ctxt;
00309 
00310     // Add this request to the load group
00311     if (mLoadGroup)
00312         mLoadGroup->AddRequest(this, nsnull);
00313     PRBool offline;
00314 
00315     // If we're starting from the beginning, then its OK to use the cache,
00316     // because the entire file must be there (the cache doesn't support
00317     // partial entries yet)
00318     // Note that ftp doesn't store metadata, so disable caching if there was
00319     // an entityID. Storing this metadata isn't worth it until we can
00320     // get partial data out of the cache anyway...
00321     if (mCacheSession && !mUploadStream && entityID.IsEmpty() &&
00322         (startPos==0 || startPos==PRUint32(-1))) {
00323         mIOService->GetOffline(&offline);
00324 
00325         // Set the desired cache access mode accordingly...
00326         nsCacheAccessMode accessRequested;
00327         if (offline) {
00328             // Since we are offline, we can only read from the cache.
00329             accessRequested = nsICache::ACCESS_READ;
00330         }
00331         else if (mLoadFlags & LOAD_BYPASS_CACHE)
00332             accessRequested = nsICache::ACCESS_WRITE; // replace cache entry
00333         else
00334             accessRequested = nsICache::ACCESS_READ_WRITE; // normal browsing
00335         
00336         nsCAutoString cacheKey;
00337         GenerateCacheKey(cacheKey);
00338 
00339         rv = mCacheSession->AsyncOpenCacheEntry(cacheKey,
00340                                                 accessRequested,
00341                                                 this);
00342         if (NS_SUCCEEDED(rv))
00343             return rv;
00344         
00345         // If we failed to use the cache, try without
00346         PR_LOG(gFTPLog, PR_LOG_DEBUG,
00347                ("Opening cache entry failed [rv=%x]", rv));
00348     }
00349     
00350     return SetupState(startPos, entityID);
00351     // XXX this function must not fail since we have already called AddRequest!
00352 }
00353 
00354 void
00355 nsFTPChannel::GetFTPEventSink(nsCOMPtr<nsIFTPEventSink> &aResult)
00356 {
00357     if (!mFTPEventSink) {
00358         nsCOMPtr<nsIFTPEventSink> ftpSink;
00359         GetCallback(ftpSink);
00360         if (ftpSink)
00361             NS_GetProxyForObject(NS_CURRENT_EVENTQ,
00362                                  NS_GET_IID(nsIFTPEventSink),
00363                                  ftpSink,
00364                                  PROXY_ASYNC | PROXY_ALWAYS,
00365                                  getter_AddRefs(mFTPEventSink));
00366     }
00367     aResult = mFTPEventSink;
00368 }
00369 
00370 void
00371 nsFTPChannel::InitProgressSink()
00372 {
00373     // Build a proxy for the progress event sink since we may need to call it
00374     // while we are deep inside some of our state logic, and we wouldn't want
00375     // to worry about some weird re-entrancy scenario.
00376     nsCOMPtr<nsIProgressEventSink> progressSink;
00377     NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, progressSink);
00378     if (progressSink)
00379         NS_GetProxyForObject(NS_CURRENT_EVENTQ,
00380                              NS_GET_IID(nsIProgressEventSink),
00381                              progressSink,
00382                              PROXY_ASYNC | PROXY_ALWAYS,
00383                              getter_AddRefs(mProgressSink));
00384 }
00385 
00386 nsresult 
00387 nsFTPChannel::SetupState(PRUint64 startPos, const nsACString& entityID)
00388 {
00389     if (!mFTPState) {
00390         NS_NEWXPCOM(mFTPState, nsFtpState);
00391         if (!mFTPState) return NS_ERROR_OUT_OF_MEMORY;
00392         NS_ADDREF(mFTPState);
00393     }
00394     nsresult rv = mFTPState->Init(this,
00395                                   mCacheEntry,
00396                                   mProxyInfo,
00397                                   startPos,
00398                                   entityID);
00399     if (NS_FAILED(rv)) return rv;
00400 
00401     (void) mFTPState->SetWriteStream(mUploadStream);
00402 
00403     rv = mFTPState->Connect();
00404     if (NS_FAILED(rv)) return rv;
00405 
00406     mIsPending = PR_TRUE;
00407     return NS_OK;
00408 }
00409 
00410 NS_IMETHODIMP
00411 nsFTPChannel::GetLoadFlags(PRUint32 *aLoadFlags)
00412 {
00413     *aLoadFlags = mLoadFlags;
00414     return NS_OK;
00415 }
00416 
00417 NS_IMETHODIMP
00418 nsFTPChannel::SetLoadFlags(PRUint32 aLoadFlags)
00419 {
00420     mLoadFlags = aLoadFlags;
00421     return NS_OK;
00422 }
00423 
00424 // FTP does not provide a file typing mechanism. We fallback to file
00425 // extension mapping.
00426 
00427 NS_IMETHODIMP
00428 nsFTPChannel::GetContentType(nsACString &aContentType)
00429 {
00430     if (mContentType.IsEmpty()) {
00431         aContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
00432     } else {
00433         aContentType = mContentType;
00434     }
00435 
00436     PR_LOG(gFTPLog, PR_LOG_DEBUG, ("nsFTPChannel::GetContentType() returned %s\n", PromiseFlatCString(aContentType).get()));
00437     return NS_OK;
00438 }
00439 
00440 NS_IMETHODIMP
00441 nsFTPChannel::SetContentType(const nsACString &aContentType)
00442 {
00443     PRBool dummy;
00444     net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy);
00445     return NS_OK;
00446 }
00447 
00448 NS_IMETHODIMP
00449 nsFTPChannel::GetContentCharset(nsACString &aContentCharset)
00450 {
00451     aContentCharset = mContentCharset;
00452     return NS_OK;
00453 }
00454 
00455 NS_IMETHODIMP
00456 nsFTPChannel::SetContentCharset(const nsACString &aContentCharset)
00457 {
00458     mContentCharset = aContentCharset;
00459     return NS_OK;
00460 }
00461 
00462 NS_IMETHODIMP
00463 nsFTPChannel::GetContentLength(PRInt32 *aContentLength)
00464 {
00465     *aContentLength = mContentLength;
00466     return NS_OK;
00467 }
00468 
00469 NS_IMETHODIMP
00470 nsFTPChannel::SetContentLength(PRInt32 aContentLength)
00471 {
00472     mContentLength = aContentLength;
00473     return NS_OK;
00474 }
00475 
00476 NS_IMETHODIMP
00477 nsFTPChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
00478 {
00479     *aLoadGroup = mLoadGroup;
00480     NS_IF_ADDREF(*aLoadGroup);
00481     return NS_OK;
00482 }
00483 
00484 NS_IMETHODIMP
00485 nsFTPChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
00486 {
00487     mLoadGroup = aLoadGroup;
00488     mProgressSink = nsnull;
00489     mFTPEventSink = nsnull;
00490     return NS_OK;
00491 }
00492 
00493 NS_IMETHODIMP
00494 nsFTPChannel::GetOwner(nsISupports* *aOwner)
00495 {
00496     *aOwner = mOwner.get();
00497     NS_IF_ADDREF(*aOwner);
00498     return NS_OK;
00499 }
00500 
00501 NS_IMETHODIMP
00502 nsFTPChannel::SetOwner(nsISupports* aOwner)
00503 {
00504     mOwner = aOwner;
00505     return NS_OK;
00506 }
00507 
00508 NS_IMETHODIMP
00509 nsFTPChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
00510 {
00511     NS_IF_ADDREF(*aNotificationCallbacks = mCallbacks);
00512     return NS_OK;
00513 }
00514 
00515 NS_IMETHODIMP
00516 nsFTPChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
00517 {
00518     mCallbacks = aNotificationCallbacks;
00519     mProgressSink = nsnull;
00520     mFTPEventSink = nsnull;
00521     return NS_OK;
00522 }
00523 
00524 NS_IMETHODIMP 
00525 nsFTPChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
00526 {
00527     *aSecurityInfo = nsnull;
00528     return NS_OK;
00529 }
00530 
00531 // nsIInterfaceRequestor method
00532 NS_IMETHODIMP
00533 nsFTPChannel::GetInterface(const nsIID &aIID, void **aResult)
00534 {
00535     // capture the progress event sink stuff. pass the rest through.
00536     if (aIID.Equals(NS_GET_IID(nsIProgressEventSink))) {
00537         *aResult = NS_STATIC_CAST(nsIProgressEventSink*, this);
00538         NS_ADDREF(this);
00539         return NS_OK;
00540     }
00541     NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, aIID, aResult);
00542     return aResult ? NS_OK : NS_ERROR_NO_INTERFACE;
00543 }
00544 
00545 // nsIProgressEventSink methods
00546 NS_IMETHODIMP
00547 nsFTPChannel::OnStatus(nsIRequest *request, nsISupports *aContext,
00548                        nsresult aStatus, const PRUnichar* aStatusArg)
00549 {
00550     if (!mProgressSink)
00551         InitProgressSink();
00552 
00553     if (aStatus == NS_NET_STATUS_CONNECTED_TO)
00554     {
00555         // The state machine needs to know that the data connection
00556         // was successfully started so that it can issue data commands
00557         // securely.
00558         if (mFTPState)
00559             mFTPState->DataConnectionEstablished();
00560         else
00561             NS_ERROR("ftp state is null.");
00562     }
00563 
00564     if (!mProgressSink || (mLoadFlags & LOAD_BACKGROUND) || !mIsPending || NS_FAILED(mStatus))
00565         return NS_OK;
00566 
00567     nsCAutoString host;
00568     mURL->GetHost(host);
00569     return mProgressSink->OnStatus(this, mUserContext, aStatus,
00570                                 NS_ConvertUTF8toUTF16(host).get());
00571 }
00572 
00573 NS_IMETHODIMP
00574 nsFTPChannel::OnProgress(nsIRequest *request, nsISupports* aContext,
00575                          PRUint64 aProgress, PRUint64 aProgressMax)
00576 {
00577     if (!mProgressSink)
00578         InitProgressSink();
00579 
00580     if (!mProgressSink || (mLoadFlags & LOAD_BACKGROUND) || !mIsPending)
00581         return NS_OK;
00582 
00583     return mProgressSink->OnProgress(this, mUserContext, 
00584                                   aProgress, aProgressMax);
00585 }
00586 
00587 
00588 // nsIRequestObserver methods.
00589 NS_IMETHODIMP
00590 nsFTPChannel::OnStopRequest(nsIRequest *request, nsISupports* aContext,
00591                             nsresult aStatus)
00592 {
00593     PR_LOG(gFTPLog, 
00594            PR_LOG_DEBUG, 
00595            ("nsFTPChannel::OnStopRequest() called [this=%x, request=%x, aStatus=%x]\n", 
00596             this, request, aStatus));
00597 
00598     nsresult rv = NS_OK;
00599     
00600     if (NS_SUCCEEDED(mStatus))
00601         mStatus = aStatus;
00602 
00603     if (mListener) {
00604         (void) mListener->OnStopRequest(this, mUserContext, mStatus);
00605     }
00606     if (mLoadGroup) {
00607         (void) mLoadGroup->RemoveRequest(this, nsnull, mStatus);
00608     }
00609     
00610     if (mCacheEntry) {
00611         if (NS_SUCCEEDED(mStatus)) {
00612             (void) mCacheEntry->SetExpirationTime( NowInSeconds() + 900 ); // valid for 15 minutes.
00613             (void) mCacheEntry->MarkValid();
00614        }
00615         else {
00616             (void) mCacheEntry->Doom();
00617         }
00618         mCacheEntry->Close();
00619         mCacheEntry = 0;
00620     }
00621 
00622     if (mUploadStream)
00623         mUploadStream->Close();
00624 
00625     if (mFTPState) {
00626         mFTPState->DataConnectionComplete();
00627         NS_RELEASE(mFTPState);
00628     }
00629     mIsPending = PR_FALSE;
00630 
00631     // Drop notification callbacks to prevent cycles.
00632     mCallbacks = nsnull;
00633     mProgressSink = nsnull;
00634     mFTPEventSink = nsnull;
00635 
00636     return rv;
00637 }
00638 
00639 NS_IMETHODIMP
00640 nsFTPChannel::OnStartRequest(nsIRequest *request, nsISupports *aContext) 
00641 {
00642     PR_LOG(gFTPLog, 
00643            PR_LOG_DEBUG, 
00644            ("nsFTPChannel::OnStartRequest() called [this=%x, request=%x]\n", 
00645             this, request));
00646    
00647     if (NS_SUCCEEDED(mStatus))
00648         request->GetStatus(&mStatus);
00649     
00650     nsCOMPtr<nsIResumableChannel> resumable = do_QueryInterface(request);
00651     if (resumable) {
00652         resumable->GetEntityID(mEntityID);
00653     }
00654     
00655     nsresult rv = NS_OK;
00656     if (mListener) {
00657         if (mContentType.IsEmpty()) {
00658             // Time to sniff!
00659             nsCOMPtr<nsIStreamConverterService> serv =
00660                 do_GetService("@mozilla.org/streamConverters;1", &rv);
00661             if (NS_SUCCEEDED(rv)) {
00662                 nsCOMPtr<nsIStreamListener> converter;
00663                 rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE,
00664                                             "*/*",
00665                                             mListener,
00666                                             mUserContext,
00667                                             getter_AddRefs(converter));
00668                 if (NS_SUCCEEDED(rv)) {
00669                     mListener = converter;
00670                 }
00671             }
00672         }
00673         
00674         rv = mListener->OnStartRequest(this, mUserContext);
00675         if (NS_FAILED(rv)) return rv;
00676     }
00677     return rv;
00678 }
00679 
00680 
00681 // nsIStreamListener method
00682 NS_IMETHODIMP
00683 nsFTPChannel::OnDataAvailable(nsIRequest *request, nsISupports* aContext,
00684                                nsIInputStream *aInputStream, PRUint32 aSourceOffset,
00685                                PRUint32 aLength) {
00686     return mListener->OnDataAvailable(this, mUserContext, aInputStream, aSourceOffset, aLength);
00687 }
00688 
00689 //-----------------------------------------------------------------------------
00690 // nsFTPChannel::nsICacheListener
00691 //-----------------------------------------------------------------------------
00692 
00693 NS_IMETHODIMP
00694 nsFTPChannel::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry,
00695                                      nsCacheAccessMode access,
00696                                      nsresult status)
00697 {
00698     nsresult rv;
00699     
00700     if (mCanceled) {
00701         NS_ASSERTION(NS_FAILED(mStatus), "Must be canceled with a failure status code");
00702         OnStartRequest(NS_STATIC_CAST(nsIRequest*, this), nsnull);
00703         OnStopRequest(NS_STATIC_CAST(nsIRequest*, this), nsnull,  mStatus);
00704         return mStatus;
00705     }
00706 
00707     if (NS_SUCCEEDED(status)) {
00708         mCacheEntry = entry;
00709     }
00710     
00711     rv = SetupState(PRUint32(-1), EmptyCString());
00712 
00713     if (NS_FAILED(rv)) {
00714         Cancel(rv);
00715     }
00716     return NS_OK;
00717 }
00718 
00719 NS_IMETHODIMP
00720 nsFTPChannel::SetUploadStream(nsIInputStream *stream, const nsACString &contentType, PRInt32 contentLength)
00721 {
00722     mUploadStream = stream;
00723     return NS_OK;
00724 }
00725 
00726 NS_IMETHODIMP
00727 nsFTPChannel::GetUploadStream(nsIInputStream **stream)
00728 {
00729     NS_ENSURE_ARG_POINTER(stream);
00730     *stream = mUploadStream;
00731     NS_IF_ADDREF(*stream);
00732     return NS_OK;
00733 }
00734