1 : /* ***** BEGIN LICENSE BLOCK *****
2 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 : *
4 : * The contents of this file are subject to the Mozilla Public License Version
5 : * 1.1 (the "License"); you may not use this file except in compliance with
6 : * the License. You may obtain a copy of the License at
7 : * http://www.mozilla.org/MPL/
8 : *
9 : * Software distributed under the License is distributed on an "AS IS" basis,
10 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 : * for the specific language governing rights and limitations under the
12 : * License.
13 : *
14 : * The Original Code is Mozilla.
15 : *
16 : * The Initial Developer of the Original Code is
17 : * Netscape Communications Corporation.
18 : * Portions created by the Initial Developer are Copyright (C) 2003
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Darin Fisher <darin@netscape.com>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsDownloader.h"
39 : #include "nsICachingChannel.h"
40 : #include "nsIInputStream.h"
41 : #include "nsDirectoryServiceUtils.h"
42 : #include "nsDirectoryServiceDefs.h"
43 : #include "nsNetUtil.h"
44 :
45 : // XXX this code is ripped from profile/src/nsProfile.cpp and is further
46 : // duplicated in uriloader/exthandler. this should probably be moved
47 : // into xpcom or some other shared library.
48 : #include <stdlib.h>
49 : #define TABLE_SIZE 36
50 : static const char table[] =
51 : { 'a','b','c','d','e','f','g','h','i','j',
52 : 'k','l','m','n','o','p','q','r','s','t',
53 : 'u','v','w','x','y','z','0','1','2','3',
54 : '4','5','6','7','8','9' };
55 : static void
56 1 : MakeRandomString(char *buf, PRInt32 bufLen)
57 : {
58 : // turn PR_Now() into milliseconds since epoch
59 : // and salt rand with that.
60 : double fpTime;
61 1 : LL_L2D(fpTime, PR_Now());
62 1 : srand((uint)(fpTime * 1e-6 + 0.5)); // use 1e-6, granularity of PR_Now() on the mac is seconds
63 :
64 : PRInt32 i;
65 9 : for (i=0;i<bufLen;i++) {
66 8 : *buf++ = table[rand()%TABLE_SIZE];
67 : }
68 1 : *buf = 0;
69 1 : }
70 : // XXX
71 :
72 6 : nsDownloader::~nsDownloader()
73 : {
74 2 : if (mLocation && mLocationIsTemp) {
75 : // release the sink first since it may still hold an open file
76 : // descriptor to mLocation. this needs to happen before the
77 : // file can be removed otherwise the Remove call will fail.
78 1 : if (mSink) {
79 0 : mSink->Close();
80 0 : mSink = nsnull;
81 : }
82 :
83 1 : nsresult rv = mLocation->Remove(false);
84 1 : if (NS_FAILED(rv))
85 0 : NS_ERROR("unable to remove temp file");
86 : }
87 8 : }
88 :
89 52 : NS_IMPL_ISUPPORTS3(nsDownloader,
90 : nsIDownloader,
91 : nsIStreamListener,
92 : nsIRequestObserver)
93 :
94 : NS_IMETHODIMP
95 2 : nsDownloader::Init(nsIDownloadObserver *observer, nsIFile *location)
96 : {
97 2 : mObserver = observer;
98 2 : mLocation = location;
99 2 : return NS_OK;
100 : }
101 :
102 : NS_IMETHODIMP
103 2 : nsDownloader::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
104 : {
105 2 : nsresult rv = NS_ERROR_FAILURE;
106 2 : if (!mLocation) {
107 2 : nsCOMPtr<nsICachingChannel> caching = do_QueryInterface(request, &rv);
108 1 : if (NS_SUCCEEDED(rv))
109 1 : rv = caching->SetCacheAsFile(true);
110 : }
111 2 : if (NS_FAILED(rv)) {
112 : // OK, we will need to stream the data to disk ourselves. Make
113 : // sure mLocation exists.
114 2 : if (!mLocation) {
115 1 : rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(mLocation));
116 1 : if (NS_FAILED(rv)) return rv;
117 :
118 : char buf[13];
119 1 : MakeRandomString(buf, 8);
120 1 : memcpy(buf+8, ".tmp", 5);
121 1 : rv = mLocation->AppendNative(nsDependentCString(buf, 12));
122 1 : if (NS_FAILED(rv)) return rv;
123 :
124 1 : rv = mLocation->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
125 1 : if (NS_FAILED(rv)) return rv;
126 :
127 1 : mLocationIsTemp = true;
128 : }
129 :
130 2 : rv = NS_NewLocalFileOutputStream(getter_AddRefs(mSink), mLocation);
131 2 : if (NS_FAILED(rv)) return rv;
132 :
133 : // we could wrap this output stream with a buffered output stream,
134 : // but it shouldn't be necessary since we will be writing large
135 : // chunks given to us via OnDataAvailable.
136 : }
137 2 : return rv;
138 : }
139 :
140 : NS_IMETHODIMP
141 2 : nsDownloader::OnStopRequest(nsIRequest *request,
142 : nsISupports *ctxt,
143 : nsresult status)
144 : {
145 2 : if (!mSink && NS_SUCCEEDED(status)) {
146 0 : nsCOMPtr<nsICachingChannel> caching = do_QueryInterface(request, &status);
147 0 : if (NS_SUCCEEDED(status)) {
148 0 : status = caching->GetCacheFile(getter_AddRefs(mLocation));
149 0 : if (NS_SUCCEEDED(status)) {
150 0 : NS_ASSERTION(mLocation, "success without a cache file");
151 : // ok, then we need to hold a reference to the cache token in
152 : // order to ensure that the cache file remains valid until we
153 : // get destroyed.
154 0 : caching->GetCacheToken(getter_AddRefs(mCacheToken));
155 : }
156 : }
157 : }
158 2 : else if (mSink) {
159 2 : mSink->Close();
160 2 : mSink = nsnull;
161 : }
162 :
163 2 : mObserver->OnDownloadComplete(this, request, ctxt, status, mLocation);
164 2 : mObserver = nsnull;
165 :
166 2 : return NS_OK;
167 : }
168 :
169 : NS_METHOD
170 1 : nsDownloader::ConsumeData(nsIInputStream* in,
171 : void* closure,
172 : const char* fromRawSegment,
173 : PRUint32 toOffset,
174 : PRUint32 count,
175 : PRUint32 *writeCount)
176 : {
177 1 : nsDownloader *self = (nsDownloader *) closure;
178 1 : if (self->mSink)
179 1 : return self->mSink->Write(fromRawSegment, count, writeCount);
180 :
181 0 : *writeCount = count;
182 0 : return NS_OK;
183 : }
184 :
185 : NS_IMETHODIMP
186 1 : nsDownloader::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
187 : nsIInputStream *inStr,
188 : PRUint32 sourceOffset, PRUint32 count)
189 : {
190 : PRUint32 n;
191 1 : return inStr->ReadSegments(ConsumeData, this, count, &n);
192 : }
|