Back to index

lightning-sunbird  0.9+nobinonly
nsDownloader.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 2003
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Darin Fisher <darin@netscape.com>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsDownloader.h"
00039 #include "nsICachingChannel.h"
00040 #include "nsIInputStream.h"
00041 #include "nsDirectoryServiceUtils.h"
00042 #include "nsDirectoryServiceDefs.h"
00043 #include "nsNetUtil.h"
00044 
00045 // XXX this code is ripped from profile/src/nsProfile.cpp and is further
00046 //     duplicated in uriloader/exthandler.  this should probably be moved
00047 //     into xpcom or some other shared library.
00048 #include <stdlib.h>
00049 #define TABLE_SIZE 36
00050 static const char table[] =
00051     { 'a','b','c','d','e','f','g','h','i','j',
00052       'k','l','m','n','o','p','q','r','s','t',
00053       'u','v','w','x','y','z','0','1','2','3',
00054       '4','5','6','7','8','9' };
00055 static void
00056 MakeRandomString(char *buf, PRInt32 bufLen)
00057 {
00058     PRInt32 i;
00059     for (i=0;i<bufLen;i++) {
00060         *buf++ = table[rand()%TABLE_SIZE];
00061     }
00062     *buf = 0;
00063 }
00064 
00065 struct seederHack {
00066     seederHack() {
00067         // turn PR_Now() into microseconds since epoch and salt rand with that.
00068         double fpTime;
00069         LL_L2D(fpTime, PR_Now());
00070         srand((uint)(fpTime)); // use 1e-6, PR_Now() on Mac is seconds
00071     }
00072 };
00073 
00074 static seederHack hackhackhack;
00075 
00076 // XXX
00077 
00078 nsDownloader::~nsDownloader()
00079 {
00080     if (mLocation && mLocationIsTemp) {
00081         // release the sink first since it may still hold an open file
00082         // descriptor to mLocation.  this needs to happen before the
00083         // file can be removed otherwise the Remove call will fail.
00084         mSink = 0;
00085 
00086         nsresult rv = mLocation->Remove(PR_FALSE);
00087         if (NS_FAILED(rv))
00088             NS_ERROR("unable to remove temp file");
00089     }
00090 }
00091 
00092 NS_IMPL_ISUPPORTS3(nsDownloader,
00093                    nsIDownloader,
00094                    nsIStreamListener,
00095                    nsIRequestObserver)
00096 
00097 NS_IMETHODIMP
00098 nsDownloader::Init(nsIDownloadObserver *observer, nsIFile *location)
00099 {
00100     mObserver = observer;
00101     mLocation = location;
00102     return NS_OK;
00103 }
00104 
00105 NS_IMETHODIMP 
00106 nsDownloader::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
00107 {
00108     nsresult rv = NS_ERROR_FAILURE;
00109     if (!mLocation) {
00110         nsCOMPtr<nsICachingChannel> caching = do_QueryInterface(request, &rv);
00111         if (NS_SUCCEEDED(rv))
00112             rv = caching->SetCacheAsFile(PR_TRUE);
00113     }
00114     if (NS_FAILED(rv)) {
00115         // OK, we will need to stream the data to disk ourselves.  Make
00116         // sure mLocation exists.
00117         if (!mLocation) {
00118             rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(mLocation));
00119             if (NS_FAILED(rv)) return rv;
00120 
00121             char buf[13];
00122             MakeRandomString(buf, 8);
00123             memcpy(buf+8, ".tmp", 5);
00124             rv = mLocation->AppendNative(nsDependentCString(buf, 12));
00125             if (NS_FAILED(rv)) return rv;
00126 
00127             rv = mLocation->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
00128             if (NS_FAILED(rv)) return rv;
00129 
00130             mLocationIsTemp = PR_TRUE;
00131         }
00132         
00133         rv = NS_NewLocalFileOutputStream(getter_AddRefs(mSink), mLocation);
00134         if (NS_FAILED(rv)) return rv;
00135 
00136         // we could wrap this output stream with a buffered output stream,
00137         // but it shouldn't be necessary since we will be writing large
00138         // chunks given to us via OnDataAvailable.
00139     }
00140     return rv;
00141 }
00142 
00143 NS_IMETHODIMP 
00144 nsDownloader::OnStopRequest(nsIRequest  *request,
00145                             nsISupports *ctxt,
00146                             nsresult     status)
00147 {
00148     if (!mSink && NS_SUCCEEDED(status)) {
00149         nsCOMPtr<nsICachingChannel> caching = do_QueryInterface(request, &status);
00150         if (NS_SUCCEEDED(status)) {
00151             status = caching->GetCacheFile(getter_AddRefs(mLocation));
00152             if (NS_SUCCEEDED(status)) {
00153                 NS_ASSERTION(mLocation, "success without a cache file");
00154                 // ok, then we need to hold a reference to the cache token in
00155                 // order to ensure that the cache file remains valid until we
00156                 // get destroyed.
00157                 caching->GetCacheToken(getter_AddRefs(mCacheToken));
00158             }
00159         }
00160     }
00161 
00162     mObserver->OnDownloadComplete(this, request, ctxt, status, mLocation);
00163     mObserver = nsnull;
00164 
00165     return NS_OK;
00166 }
00167 
00168 NS_METHOD
00169 nsDownloader::ConsumeData(nsIInputStream* in,
00170                           void* closure,
00171                           const char* fromRawSegment,
00172                           PRUint32 toOffset,
00173                           PRUint32 count,
00174                           PRUint32 *writeCount)
00175 {
00176     nsDownloader *self = (nsDownloader *) closure;
00177     if (self->mSink)
00178         return self->mSink->Write(fromRawSegment, count, writeCount);
00179 
00180     *writeCount = count;
00181     return NS_OK;
00182 }
00183 
00184 NS_IMETHODIMP 
00185 nsDownloader::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, 
00186                               nsIInputStream *inStr, 
00187                               PRUint32 sourceOffset, PRUint32 count)
00188 {
00189     PRUint32 n;  
00190     return inStr->ReadSegments(ConsumeData, this, count, &n);
00191 }