Back to index

lightning-sunbird  0.9+nobinonly
nsURLFetcher.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or 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 #include "nsURLFetcher.h"
00040 
00041 #include "msgCore.h" // for pre-compiled headers
00042 #include "nsCOMPtr.h"
00043 #include <stdio.h>
00044 #include "nscore.h"
00045 #include "nsIFactory.h"
00046 #include "nsISupports.h"
00047 #include "comi18n.h"
00048 #include "prmem.h"
00049 #include "plstr.h"
00050 #include "nsIComponentManager.h"
00051 #include "nsString.h"
00052 #include "nsIIOService.h"
00053 #include "nsIChannel.h"
00054 #include "nsNetUtil.h"
00055 #include "nsMimeTypes.h"
00056 #include "nsIHttpChannel.h"
00057 #include "nsIWebProgress.h"
00058 #include "nsMsgAttachmentHandler.h"
00059 #include "nsMsgSend.h"
00060 #include "nsIStreamConverterService.h"
00061 #include "nsIMsgProgress.h"
00062 
00063 NS_IMPL_ISUPPORTS6(nsURLFetcher, nsIURLFetcher, nsIStreamListener, nsIURIContentListener, nsIInterfaceRequestor, nsIWebProgressListener, nsISupportsWeakReference)
00064 
00065 
00066 /* 
00067  * Inherited methods for nsMimeConverter
00068  */
00069 nsURLFetcher::nsURLFetcher()
00070 {
00071 #if defined(DEBUG_ducarroz)
00072   printf("CREATE nsURLFetcher: %x\n", this);
00073 #endif
00074 
00075   // Init member variables...
00076   mTotalWritten = 0;
00077   mBuffer = nsnull;
00078   mBufferSize = 0;
00079   mStillRunning = PR_TRUE;
00080   mCallback = nsnull;
00081   mOnStopRequestProcessed = PR_FALSE;
00082   mIsFile=PR_FALSE;
00083   nsURLFetcherStreamConsumer *consumer = new nsURLFetcherStreamConsumer(this);
00084   mConverter = do_QueryInterface(consumer);
00085 }
00086 
00087 nsURLFetcher::~nsURLFetcher()
00088 {
00089 #if defined(DEBUG_ducarroz)
00090   printf("DISPOSE nsURLFetcher: %x\n", this);
00091 #endif
00092   mStillRunning = PR_FALSE;
00093   
00094   PR_FREEIF(mBuffer);
00095   // Remove the DocShell as a listener of the old WebProgress...
00096   if (mLoadCookie) 
00097   {
00098     nsCOMPtr<nsIWebProgress> webProgress(do_QueryInterface(mLoadCookie));
00099 
00100     if (webProgress)
00101       webProgress->RemoveProgressListener(this);
00102   }
00103 }
00104 
00105 NS_IMETHODIMP nsURLFetcher::GetInterface(const nsIID & aIID, void * *aInstancePtr)
00106 {
00107    NS_ENSURE_ARG_POINTER(aInstancePtr);
00108    return QueryInterface(aIID, aInstancePtr);
00109 }
00110 
00111 // nsIURIContentListener support
00112 NS_IMETHODIMP 
00113 nsURLFetcher::OnStartURIOpen(nsIURI* aURI, PRBool* aAbortOpen)
00114 {
00115    return NS_OK;
00116 }
00117 
00118 NS_IMETHODIMP 
00119 nsURLFetcher::IsPreferred(const char * aContentType,
00120                                 char ** aDesiredContentType,
00121                                 PRBool * aCanHandleContent)
00122 
00123 {
00124   return CanHandleContent(aContentType, PR_TRUE, aDesiredContentType,
00125                           aCanHandleContent);
00126 }
00127 
00128 NS_IMETHODIMP 
00129 nsURLFetcher::CanHandleContent(const char * aContentType,
00130                                 PRBool aIsContentPreferred,
00131                                 char ** aDesiredContentType,
00132                                 PRBool * aCanHandleContent)
00133 
00134 {
00135     if (!mIsFile && nsCRT::strcasecmp(aContentType, MESSAGE_RFC822) == 0)
00136       *aDesiredContentType = nsCRT::strdup("text/html");
00137 
00138     // since we explicilty loaded the url, we always want to handle it!
00139     *aCanHandleContent = PR_TRUE;
00140   return NS_OK;
00141 } 
00142 
00143 NS_IMETHODIMP 
00144 nsURLFetcher::DoContent(const char * aContentType,
00145                       PRBool aIsContentPreferred,
00146                       nsIRequest *request,
00147                       nsIStreamListener ** aContentHandler,
00148                       PRBool * aAbortProcess)
00149 {
00150   nsresult rv = NS_OK;
00151 
00152   if (aAbortProcess)
00153     *aAbortProcess = PR_FALSE;
00154   QueryInterface(NS_GET_IID(nsIStreamListener), (void **) aContentHandler);
00155 
00156   /*
00157     Check the content-type to see if we need to insert a converter
00158   */
00159   if (nsCRT::strcasecmp(aContentType, UNKNOWN_CONTENT_TYPE) == 0 ||
00160         nsCRT::strcasecmp(aContentType, MULTIPART_MIXED_REPLACE) == 0 ||
00161         nsCRT::strcasecmp(aContentType, MULTIPART_MIXED) == 0 ||
00162         nsCRT::strcasecmp(aContentType, MULTIPART_BYTERANGES) == 0)
00163   {
00164     rv = InsertConverter(aContentType);
00165     if (NS_SUCCEEDED(rv))
00166       mConverterContentType.Adopt(nsCRT::strdup(aContentType));
00167   }
00168 
00169   return rv;
00170 }
00171 
00172 NS_IMETHODIMP 
00173 nsURLFetcher::GetParentContentListener(nsIURIContentListener** aParent)
00174 {
00175   *aParent = nsnull;
00176   return NS_OK;
00177 }
00178 
00179 NS_IMETHODIMP 
00180 nsURLFetcher::SetParentContentListener(nsIURIContentListener* aParent)
00181 {
00182   return NS_OK;
00183 }
00184 
00185 NS_IMETHODIMP 
00186 nsURLFetcher::GetLoadCookie(nsISupports ** aLoadCookie)
00187 {
00188   *aLoadCookie = mLoadCookie;
00189   NS_IF_ADDREF(*aLoadCookie);
00190   return NS_OK;
00191 }
00192 
00193 NS_IMETHODIMP 
00194 nsURLFetcher::SetLoadCookie(nsISupports * aLoadCookie)
00195 {
00196   // Remove the DocShell as a listener of the old WebProgress...
00197   if (mLoadCookie) 
00198   {
00199     nsCOMPtr<nsIWebProgress> webProgress(do_QueryInterface(mLoadCookie));
00200 
00201     if (webProgress)
00202       webProgress->RemoveProgressListener(this);
00203   }
00204 
00205   mLoadCookie = aLoadCookie;
00206 
00207   // Add the DocShell as a listener to the new WebProgress...
00208   if (mLoadCookie) 
00209   {
00210     nsCOMPtr<nsIWebProgress> webProgress(do_QueryInterface(mLoadCookie));
00211 
00212     if (webProgress) 
00213       webProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_ALL);
00214   }
00215   return NS_OK;
00216 
00217 }
00218 
00219 nsresult
00220 nsURLFetcher::StillRunning(PRBool *running)
00221 {
00222   *running = mStillRunning;
00223   return NS_OK;
00224 }
00225 
00226 
00227 // Methods for nsIStreamListener...
00228 nsresult
00229 nsURLFetcher::OnDataAvailable(nsIRequest *request, nsISupports * ctxt, nsIInputStream *aIStream, 
00230                               PRUint32 sourceOffset, PRUint32 aLength)
00231 {
00232   /* let our converter or consumer process the data */
00233   if (!mConverter)
00234     return NS_ERROR_FAILURE;
00235 
00236   return mConverter->OnDataAvailable(request, ctxt, aIStream, sourceOffset, aLength);
00237 }
00238 
00239 
00240 // Methods for nsIStreamObserver 
00241 nsresult
00242 nsURLFetcher::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
00243 {
00244   /* check if the user has canceld the operation */
00245   nsMsgAttachmentHandler *attachmentHdl = (nsMsgAttachmentHandler *)mTagData;
00246   if (attachmentHdl)
00247   {
00248     nsCOMPtr<nsIMsgSend> sendPtr;
00249     attachmentHdl->GetMimeDeliveryState(getter_AddRefs(sendPtr));
00250     if (sendPtr)
00251     {
00252       nsCOMPtr<nsIMsgProgress> progress;
00253       sendPtr->GetProgress(getter_AddRefs(progress));
00254       if (progress)
00255       {
00256         PRBool cancel = PR_FALSE;
00257         progress->GetProcessCanceledByUser(&cancel);
00258         if (cancel)
00259           return request->Cancel(NS_ERROR_ABORT);
00260       }
00261     }
00262     attachmentHdl->mRequest = request;
00263   }
00264 
00265   /* call our converter or consumer */
00266   if (mConverter)
00267     return mConverter->OnStartRequest(request, ctxt);
00268 
00269   return NS_OK;
00270 }
00271 
00272 NS_IMETHODIMP
00273 nsURLFetcher::OnStopRequest(nsIRequest *request, nsISupports * ctxt, nsresult aStatus)
00274 {
00275 #if defined(DEBUG_ducarroz)
00276   printf("nsURLFetcher::OnStopRequest()\n");
00277 #endif
00278 
00279   nsresult rv = NS_OK;
00280 
00281   // it's possible we could get in here from the channel calling us with an OnStopRequest and from our
00282   // onStatusChange method (in the case of an error). So we should protect against this to make sure we
00283   // don't process the on stop request twice...
00284 
00285   if (mOnStopRequestProcessed)
00286     return NS_OK;
00287   mOnStopRequestProcessed = PR_TRUE;
00288   
00289   /* first, call our converter or consumer */
00290   if (mConverter)
00291     rv = mConverter->OnStopRequest(request, ctxt, aStatus);
00292 
00293   nsMsgAttachmentHandler *attachmentHdl = (nsMsgAttachmentHandler *)mTagData;
00294   if (attachmentHdl)
00295     attachmentHdl->mRequest = nsnull;
00296 
00297   //
00298   // Now complete the stream!
00299   //
00300   mStillRunning = PR_FALSE;
00301 
00302   // time to close the output stream...
00303   if (mOutStream)
00304   {
00305     mOutStream->Close();
00306     mOutStream = nsnull;
00307   
00308     /* In case of multipart/x-mixed-replace, we need to truncate the file to the current part size */
00309     if (PL_strcasecmp(mConverterContentType, MULTIPART_MIXED_REPLACE) == 0)
00310   {
00311       PRInt64 fileSize;
00312       LL_I2L(fileSize, mTotalWritten);
00313       mLocalFile->SetFileSize(fileSize);
00314     }
00315   }
00316 
00317   // Now if there is a callback, we need to call it...
00318   if (mCallback)
00319     mCallback (aStatus, (const char *)mContentType, (const char *)mCharset, mTotalWritten, nsnull, mTagData);
00320 
00321   // Time to return...
00322   return NS_OK;
00323 }
00324 
00325 nsresult 
00326 nsURLFetcher::Initialize(nsILocalFile *localFile, 
00327                          nsIFileOutputStream *outputStream,
00328                          nsAttachSaveCompletionCallback cb, 
00329                          void *tagData)
00330 {
00331   if (!outputStream || !localFile)
00332     return NS_ERROR_INVALID_ARG;
00333 
00334   mOutStream = outputStream;
00335   mLocalFile = localFile;
00336   mCallback = cb;     //JFD: Please, no more callback, use a listener...
00337   mTagData = tagData; //JFD: TODO, WE SHOULD USE A NSCOMPTR to hold this stuff!!!
00338   return NS_OK;
00339 }
00340 
00341 nsresult
00342 nsURLFetcher::FireURLRequest(nsIURI *aURL, nsILocalFile *localFile, nsIFileOutputStream *outputStream, 
00343                              nsAttachSaveCompletionCallback cb, void *tagData)
00344 {
00345   nsresult rv;
00346 
00347   rv = Initialize(localFile, outputStream, cb, tagData);
00348   NS_ENSURE_SUCCESS(rv, rv);
00349 
00350   //check to see if aURL is a local file or not
00351   aURL->SchemeIs("file", &mIsFile);
00352   
00353   // we're about to fire a new url request so make sure the on stop request flag is cleared...
00354   mOnStopRequestProcessed = PR_FALSE;
00355 
00356   // let's try uri dispatching...
00357   nsCOMPtr<nsIURILoader> pURILoader (do_GetService(NS_URI_LOADER_CONTRACTID));
00358   NS_ENSURE_TRUE(pURILoader, NS_ERROR_FAILURE);
00359 
00360   nsCOMPtr<nsIChannel> channel;
00361   NS_ENSURE_SUCCESS(NS_NewChannel(getter_AddRefs(channel), aURL, nsnull, nsnull, this), NS_ERROR_FAILURE);
00362  
00363   return pURILoader->OpenURI(channel, PR_FALSE, this);
00364 }
00365 
00366 nsresult
00367 nsURLFetcher::InsertConverter(const char * aContentType)
00368 {
00369   nsresult rv;
00370 
00371   nsCOMPtr<nsIStreamConverterService> convServ(do_GetService("@mozilla.org/streamConverters;1", &rv));
00372   if (NS_SUCCEEDED(rv))
00373   {
00374     nsCOMPtr<nsIStreamListener> toListener(mConverter);
00375     nsCOMPtr<nsIStreamListener> fromListener;
00376 
00377     rv = convServ->AsyncConvertData(aContentType,
00378                                     "*/*",
00379                                     toListener,
00380                                     nsnull,
00381                                     getter_AddRefs(fromListener));
00382     if (NS_SUCCEEDED(rv))
00383       mConverter = fromListener;
00384   }
00385 
00386   return rv;
00387 }
00388 
00389 // web progress listener implementation
00390 
00391 NS_IMETHODIMP
00392 nsURLFetcher::OnProgressChange(nsIWebProgress *aProgress, nsIRequest *aRequest,
00393                              PRInt32 aCurSelfProgress, PRInt32 aMaxSelfProgress,
00394                              PRInt32 aCurTotalProgress, PRInt32 aMaxTotalProgress)
00395 {
00396   return NS_OK;
00397 }
00398 
00399 NS_IMETHODIMP
00400 nsURLFetcher::OnStateChange(nsIWebProgress *aProgress, nsIRequest *aRequest,
00401                           PRUint32 aStateFlags, nsresult aStatus)
00402 {
00403   // all we care about is the case where an error occurred (as in we were unable to locate the
00404   // the url....
00405 
00406   if (NS_FAILED(aStatus))
00407     OnStopRequest(aRequest, nsnull, aStatus);
00408 
00409   return NS_OK;
00410 }
00411 
00412 NS_IMETHODIMP
00413 nsURLFetcher::OnLocationChange(nsIWebProgress* aWebProgress,
00414                                nsIRequest* aRequest,
00415                                nsIURI *aURI)
00416 {
00417   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
00418   return NS_OK;
00419 }
00420 
00421 NS_IMETHODIMP 
00422 nsURLFetcher::OnStatusChange(nsIWebProgress* aWebProgress,
00423                              nsIRequest* aRequest,
00424                              nsresult aStatus,
00425                              const PRUnichar* aMessage)
00426 {
00427   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
00428   return NS_OK;
00429 }
00430 
00431 NS_IMETHODIMP 
00432 nsURLFetcher::OnSecurityChange(nsIWebProgress *aWebProgress, 
00433                                nsIRequest *aRequest, 
00434                                PRUint32 state)
00435 {
00436   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
00437   return NS_OK;
00438 }
00439 
00440 
00445 NS_IMPL_ISUPPORTS2(nsURLFetcherStreamConsumer, nsIStreamListener, nsIRequestObserver)
00446 
00447 nsURLFetcherStreamConsumer::nsURLFetcherStreamConsumer(nsURLFetcher* urlFetcher) :
00448   mURLFetcher(urlFetcher)
00449 {
00450 #if defined(DEBUG_ducarroz)
00451   printf("CREATE nsURLFetcherStreamConsumer: %x\n", this);
00452 #endif
00453 }
00454 
00455 nsURLFetcherStreamConsumer::~nsURLFetcherStreamConsumer()
00456 {
00457 #if defined(DEBUG_ducarroz)
00458   printf("DISPOSE nsURLFetcherStreamConsumer: %x\n", this);
00459 #endif
00460 }
00461 
00464 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
00465 NS_IMETHODIMP nsURLFetcherStreamConsumer::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
00466 {
00467   if (!mURLFetcher || !mURLFetcher->mOutStream)
00468     return NS_ERROR_FAILURE;
00469 
00470   /* In case of multipart/x-mixed-replace, we need to erase the output file content */
00471   if (PL_strcasecmp(mURLFetcher->mConverterContentType, MULTIPART_MIXED_REPLACE) == 0)
00472   {
00473     nsCOMPtr<nsISeekableStream> seekStream = do_QueryInterface(mURLFetcher->mOutStream);
00474     if (seekStream)
00475       seekStream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
00476     mURLFetcher->mTotalWritten = 0;
00477   }
00478 
00479   return NS_OK;
00480 }
00481 
00482 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
00483 NS_IMETHODIMP nsURLFetcherStreamConsumer::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
00484 {
00485   if (!mURLFetcher)
00486     return NS_ERROR_FAILURE;
00487 
00488   // Check the content type!
00489   nsCAutoString contentType;
00490   nsCAutoString charset;
00491 
00492   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
00493   if(!channel) return NS_ERROR_FAILURE;
00494 
00495   if (NS_SUCCEEDED(channel->GetContentType(contentType)) &&
00496       !contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE))
00497   {
00498     nsCAutoString uriSpec;
00499     nsCOMPtr <nsIURI> channelURI;
00500     channel->GetURI(getter_AddRefs(channelURI));
00501     channelURI->GetSpec(uriSpec);
00502     if (FindInReadable(NS_LITERAL_CSTRING("&realtype=message/rfc822"), uriSpec))
00503       mURLFetcher->mContentType = MESSAGE_RFC822;
00504     else
00505       mURLFetcher->mContentType = contentType;
00506   }
00507 
00508   if (NS_SUCCEEDED(channel->GetContentCharset(charset)) && !charset.IsEmpty())
00509   {
00510     mURLFetcher->mCharset = charset;
00511   }
00512 
00513   return NS_OK;
00514 }
00515 
00518 /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
00519 NS_IMETHODIMP nsURLFetcherStreamConsumer::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
00520 {
00521   PRUint32        readLen = count;
00522   PRUint32        wroteIt;
00523 
00524   if (!mURLFetcher)
00525     return NS_ERROR_FAILURE;
00526 
00527   if (!mURLFetcher->mOutStream)
00528     return NS_ERROR_INVALID_ARG;
00529 
00530   if (mURLFetcher->mBufferSize < count)
00531   {
00532     PR_FREEIF(mURLFetcher->mBuffer);
00533 
00534     if (count > 0x1000)
00535       mURLFetcher->mBufferSize = count;
00536     else
00537       mURLFetcher->mBufferSize = 0x1000;
00538 
00539     mURLFetcher->mBuffer = (char *)PR_Malloc(mURLFetcher->mBufferSize);
00540     if (!mURLFetcher->mBuffer)
00541       return NS_ERROR_OUT_OF_MEMORY; /* we couldn't allocate the object */
00542   }
00543 
00544   // read the data from the input stram...
00545   nsresult rv = inStr->Read(mURLFetcher->mBuffer, count, &readLen);
00546   if (NS_FAILED(rv))
00547     return rv;
00548 
00549   // write to the output file...
00550   mURLFetcher->mOutStream->Write(mURLFetcher->mBuffer, readLen, &wroteIt);
00551 
00552   if (wroteIt != readLen)
00553     return NS_ERROR_FAILURE;
00554   else
00555   {
00556     mURLFetcher->mTotalWritten += wroteIt;
00557     return NS_OK;
00558   }
00559 }