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.org code.
15 : *
16 : * The Initial Developer of the Original Code is
17 : * Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2010
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either the GNU General Public License Version 2 or later (the "GPL"), or
25 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #include "nsBlobProtocolHandler.h"
38 : #include "nsSimpleURI.h"
39 : #include "nsDOMError.h"
40 : #include "nsCOMPtr.h"
41 : #include "nsClassHashtable.h"
42 : #include "nsNetUtil.h"
43 : #include "nsIURIWithPrincipal.h"
44 : #include "nsIPrincipal.h"
45 : #include "nsIDOMFile.h"
46 : #include "nsISerializable.h"
47 : #include "nsIClassInfo.h"
48 : #include "nsIObjectInputStream.h"
49 : #include "nsIObjectOutputStream.h"
50 : #include "nsIProgrammingLanguage.h"
51 :
52 : // -----------------------------------------------------------------------
53 : // Hash table
54 : struct FileDataInfo
55 4 : {
56 : nsCOMPtr<nsIDOMBlob> mFile;
57 : nsCOMPtr<nsIPrincipal> mPrincipal;
58 : };
59 :
60 : static nsClassHashtable<nsCStringHashKey, FileDataInfo>* gFileDataTable;
61 :
62 : void
63 2 : nsBlobProtocolHandler::AddFileDataEntry(nsACString& aUri,
64 : nsIDOMBlob* aFile,
65 : nsIPrincipal* aPrincipal)
66 : {
67 2 : if (!gFileDataTable) {
68 2 : gFileDataTable = new nsClassHashtable<nsCStringHashKey, FileDataInfo>;
69 2 : gFileDataTable->Init();
70 : }
71 :
72 2 : FileDataInfo* info = new FileDataInfo;
73 :
74 2 : info->mFile = aFile;
75 2 : info->mPrincipal = aPrincipal;
76 :
77 2 : gFileDataTable->Put(aUri, info);
78 2 : }
79 :
80 : void
81 2 : nsBlobProtocolHandler::RemoveFileDataEntry(nsACString& aUri)
82 : {
83 2 : if (gFileDataTable) {
84 2 : gFileDataTable->Remove(aUri);
85 2 : if (gFileDataTable->Count() == 0) {
86 2 : delete gFileDataTable;
87 2 : gFileDataTable = nsnull;
88 : }
89 : }
90 2 : }
91 :
92 : nsIPrincipal*
93 0 : nsBlobProtocolHandler::GetFileDataEntryPrincipal(nsACString& aUri)
94 : {
95 0 : if (!gFileDataTable) {
96 0 : return nsnull;
97 : }
98 :
99 : FileDataInfo* res;
100 0 : gFileDataTable->Get(aUri, &res);
101 0 : if (!res) {
102 0 : return nsnull;
103 : }
104 :
105 0 : return res->mPrincipal;
106 : }
107 :
108 : static FileDataInfo*
109 17 : GetFileDataInfo(const nsACString& aUri)
110 : {
111 17 : NS_ASSERTION(StringBeginsWith(aUri,
112 : NS_LITERAL_CSTRING(BLOBURI_SCHEME ":")),
113 : "Bad URI");
114 :
115 17 : if (!gFileDataTable) {
116 13 : return nsnull;
117 : }
118 :
119 : FileDataInfo* res;
120 4 : gFileDataTable->Get(aUri, &res);
121 4 : return res;
122 : }
123 :
124 : // -----------------------------------------------------------------------
125 : // Uri
126 :
127 : #define NS_BLOBURI_CID \
128 : { 0xf5475c51, 0x59a7, 0x4757, \
129 : { 0xb3, 0xd9, 0xe2, 0x11, 0xa9, 0x41, 0x08, 0x72 } }
130 :
131 : static NS_DEFINE_CID(kBLOBURICID, NS_BLOBURI_CID);
132 :
133 : class nsBlobURI : public nsSimpleURI,
134 : public nsIURIWithPrincipal
135 : {
136 : public:
137 15 : nsBlobURI(nsIPrincipal* aPrincipal) :
138 15 : nsSimpleURI(), mPrincipal(aPrincipal)
139 15 : {}
140 108 : virtual ~nsBlobURI() {}
141 :
142 : // For use only from deserialization
143 12 : nsBlobURI() : nsSimpleURI() {}
144 :
145 : NS_DECL_ISUPPORTS_INHERITED
146 : NS_DECL_NSIURIWITHPRINCIPAL
147 : NS_DECL_NSISERIALIZABLE
148 : NS_DECL_NSICLASSINFO
149 :
150 : // Override CloneInternal() and EqualsInternal()
151 : virtual nsresult CloneInternal(RefHandlingEnum aRefHandlingMode,
152 : nsIURI** aClone);
153 : virtual nsresult EqualsInternal(nsIURI* aOther,
154 : RefHandlingEnum aRefHandlingMode,
155 : bool* aResult);
156 :
157 : // Override StartClone to hand back a nsBlobURI
158 12 : virtual nsSimpleURI* StartClone(RefHandlingEnum /* unused */)
159 12 : { return new nsBlobURI(); }
160 :
161 : nsCOMPtr<nsIPrincipal> mPrincipal;
162 : };
163 :
164 : static NS_DEFINE_CID(kThisSimpleURIImplementationCID,
165 : NS_THIS_SIMPLEURI_IMPLEMENTATION_CID);
166 :
167 347 : NS_IMPL_ADDREF_INHERITED(nsBlobURI, nsSimpleURI)
168 347 : NS_IMPL_RELEASE_INHERITED(nsBlobURI, nsSimpleURI)
169 :
170 447 : NS_INTERFACE_MAP_BEGIN(nsBlobURI)
171 447 : NS_INTERFACE_MAP_ENTRY(nsIURIWithPrincipal)
172 445 : if (aIID.Equals(kBLOBURICID))
173 78 : foundInterface = static_cast<nsIURI*>(this);
174 367 : else if (aIID.Equals(kThisSimpleURIImplementationCID)) {
175 : // Need to return explicitly here, because if we just set foundInterface
176 : // to null the NS_INTERFACE_MAP_END_INHERITING will end up calling into
177 : // nsSimplURI::QueryInterface and finding something for this CID.
178 4 : *aInstancePtr = nsnull;
179 4 : return NS_NOINTERFACE;
180 : }
181 : else
182 363 : NS_INTERFACE_MAP_END_INHERITING(nsSimpleURI)
183 :
184 : // nsIURIWithPrincipal methods:
185 :
186 : NS_IMETHODIMP
187 2 : nsBlobURI::GetPrincipal(nsIPrincipal** aPrincipal)
188 : {
189 2 : NS_IF_ADDREF(*aPrincipal = mPrincipal);
190 :
191 2 : return NS_OK;
192 : }
193 :
194 : NS_IMETHODIMP
195 0 : nsBlobURI::GetPrincipalUri(nsIURI** aUri)
196 : {
197 0 : if (mPrincipal) {
198 0 : mPrincipal->GetURI(aUri);
199 : }
200 : else {
201 0 : *aUri = nsnull;
202 : }
203 :
204 0 : return NS_OK;
205 : }
206 :
207 : // nsISerializable methods:
208 :
209 : NS_IMETHODIMP
210 0 : nsBlobURI::Read(nsIObjectInputStream* aStream)
211 : {
212 0 : nsresult rv = nsSimpleURI::Read(aStream);
213 0 : NS_ENSURE_SUCCESS(rv, rv);
214 :
215 0 : return NS_ReadOptionalObject(aStream, true, getter_AddRefs(mPrincipal));
216 : }
217 :
218 : NS_IMETHODIMP
219 0 : nsBlobURI::Write(nsIObjectOutputStream* aStream)
220 : {
221 0 : nsresult rv = nsSimpleURI::Write(aStream);
222 0 : NS_ENSURE_SUCCESS(rv, rv);
223 :
224 : return NS_WriteOptionalCompoundObject(aStream, mPrincipal,
225 : NS_GET_IID(nsIPrincipal),
226 0 : true);
227 : }
228 :
229 : // nsIURI methods:
230 : nsresult
231 12 : nsBlobURI::CloneInternal(nsSimpleURI::RefHandlingEnum aRefHandlingMode,
232 : nsIURI** aClone)
233 : {
234 24 : nsCOMPtr<nsIURI> simpleClone;
235 : nsresult rv =
236 12 : nsSimpleURI::CloneInternal(aRefHandlingMode, getter_AddRefs(simpleClone));
237 12 : NS_ENSURE_SUCCESS(rv, rv);
238 :
239 : #ifdef DEBUG
240 24 : nsRefPtr<nsBlobURI> uriCheck;
241 12 : rv = simpleClone->QueryInterface(kBLOBURICID, getter_AddRefs(uriCheck));
242 12 : NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv) && uriCheck,
243 : "Unexpected!");
244 : #endif
245 :
246 12 : nsBlobURI* blobURI = static_cast<nsBlobURI*>(simpleClone.get());
247 :
248 12 : blobURI->mPrincipal = mPrincipal;
249 :
250 12 : simpleClone.forget(aClone);
251 12 : return NS_OK;
252 : }
253 :
254 : /* virtual */ nsresult
255 71 : nsBlobURI::EqualsInternal(nsIURI* aOther,
256 : nsSimpleURI::RefHandlingEnum aRefHandlingMode,
257 : bool* aResult)
258 : {
259 71 : if (!aOther) {
260 1 : *aResult = false;
261 1 : return NS_OK;
262 : }
263 :
264 140 : nsRefPtr<nsBlobURI> otherBlobUri;
265 70 : aOther->QueryInterface(kBLOBURICID, getter_AddRefs(otherBlobUri));
266 70 : if (!otherBlobUri) {
267 4 : *aResult = false;
268 4 : return NS_OK;
269 : }
270 :
271 : // Compare the member data that our base class knows about.
272 66 : if (!nsSimpleURI::EqualsInternal(otherBlobUri, aRefHandlingMode)) {
273 10 : *aResult = false;
274 10 : return NS_OK;
275 : }
276 :
277 : // Compare the piece of additional member data that we add to base class.
278 56 : if (mPrincipal && otherBlobUri->mPrincipal) {
279 : // Both of us have mPrincipals. Compare them.
280 0 : return mPrincipal->Equals(otherBlobUri->mPrincipal, aResult);
281 : }
282 : // else, at least one of us lacks a principal; only equal if *both* lack it.
283 56 : *aResult = (!mPrincipal && !otherBlobUri->mPrincipal);
284 56 : return NS_OK;
285 : }
286 :
287 : // nsIClassInfo methods:
288 : NS_IMETHODIMP
289 25 : nsBlobURI::GetInterfaces(PRUint32 *count, nsIID * **array)
290 : {
291 25 : *count = 0;
292 25 : *array = nsnull;
293 25 : return NS_OK;
294 : }
295 :
296 : NS_IMETHODIMP
297 25 : nsBlobURI::GetHelperForLanguage(PRUint32 language, nsISupports **_retval)
298 : {
299 25 : *_retval = nsnull;
300 25 : return NS_OK;
301 : }
302 :
303 : NS_IMETHODIMP
304 0 : nsBlobURI::GetContractID(char * *aContractID)
305 : {
306 : // Make sure to modify any subclasses as needed if this ever
307 : // changes.
308 0 : *aContractID = nsnull;
309 0 : return NS_OK;
310 : }
311 :
312 : NS_IMETHODIMP
313 0 : nsBlobURI::GetClassDescription(char * *aClassDescription)
314 : {
315 0 : *aClassDescription = nsnull;
316 0 : return NS_OK;
317 : }
318 :
319 : NS_IMETHODIMP
320 0 : nsBlobURI::GetClassID(nsCID * *aClassID)
321 : {
322 : // Make sure to modify any subclasses as needed if this ever
323 : // changes to not call the virtual GetClassIDNoAlloc.
324 0 : *aClassID = (nsCID*) nsMemory::Alloc(sizeof(nsCID));
325 0 : NS_ENSURE_TRUE(*aClassID, NS_ERROR_OUT_OF_MEMORY);
326 :
327 0 : return GetClassIDNoAlloc(*aClassID);
328 : }
329 :
330 : NS_IMETHODIMP
331 0 : nsBlobURI::GetImplementationLanguage(PRUint32 *aImplementationLanguage)
332 : {
333 0 : *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
334 0 : return NS_OK;
335 : }
336 :
337 : NS_IMETHODIMP
338 25 : nsBlobURI::GetFlags(PRUint32 *aFlags)
339 : {
340 25 : *aFlags = nsIClassInfo::MAIN_THREAD_ONLY;
341 25 : return NS_OK;
342 : }
343 :
344 : NS_IMETHODIMP
345 0 : nsBlobURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
346 : {
347 0 : *aClassIDNoAlloc = kBLOBURICID;
348 0 : return NS_OK;
349 : }
350 :
351 : // -----------------------------------------------------------------------
352 : // Protocol handler
353 :
354 148 : NS_IMPL_ISUPPORTS1(nsBlobProtocolHandler, nsIProtocolHandler)
355 :
356 : NS_IMETHODIMP
357 0 : nsBlobProtocolHandler::GetScheme(nsACString &result)
358 : {
359 0 : result.AssignLiteral(BLOBURI_SCHEME);
360 0 : return NS_OK;
361 : }
362 :
363 : NS_IMETHODIMP
364 0 : nsBlobProtocolHandler::GetDefaultPort(PRInt32 *result)
365 : {
366 0 : *result = -1;
367 0 : return NS_OK;
368 : }
369 :
370 : NS_IMETHODIMP
371 2 : nsBlobProtocolHandler::GetProtocolFlags(PRUint32 *result)
372 : {
373 : *result = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_SUBSUMERS |
374 2 : URI_IS_LOCAL_RESOURCE | URI_NON_PERSISTABLE;
375 2 : return NS_OK;
376 : }
377 :
378 : NS_IMETHODIMP
379 15 : nsBlobProtocolHandler::NewURI(const nsACString& aSpec,
380 : const char *aCharset,
381 : nsIURI *aBaseURI,
382 : nsIURI **aResult)
383 : {
384 15 : *aResult = nsnull;
385 : nsresult rv;
386 :
387 : FileDataInfo* info =
388 15 : GetFileDataInfo(aSpec);
389 :
390 : nsRefPtr<nsBlobURI> uri =
391 45 : new nsBlobURI(info ? info->mPrincipal.get() : nsnull);
392 :
393 15 : rv = uri->SetSpec(aSpec);
394 15 : NS_ENSURE_SUCCESS(rv, rv);
395 :
396 15 : NS_TryToSetImmutable(uri);
397 15 : uri.forget(aResult);
398 :
399 15 : return NS_OK;
400 : }
401 :
402 : NS_IMETHODIMP
403 2 : nsBlobProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
404 : {
405 2 : *result = nsnull;
406 :
407 4 : nsCString spec;
408 2 : uri->GetSpec(spec);
409 :
410 : FileDataInfo* info =
411 2 : GetFileDataInfo(spec);
412 :
413 2 : if (!info) {
414 0 : return NS_ERROR_DOM_BAD_URI;
415 : }
416 :
417 : #ifdef DEBUG
418 : {
419 4 : nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(uri);
420 4 : nsCOMPtr<nsIPrincipal> principal;
421 2 : uriPrinc->GetPrincipal(getter_AddRefs(principal));
422 2 : NS_ASSERTION(info->mPrincipal == principal, "Wrong principal!");
423 : }
424 : #endif
425 :
426 4 : nsCOMPtr<nsIInputStream> stream;
427 2 : nsresult rv = info->mFile->GetInternalStream(getter_AddRefs(stream));
428 2 : NS_ENSURE_SUCCESS(rv, rv);
429 :
430 4 : nsCOMPtr<nsIChannel> channel;
431 2 : rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
432 : uri,
433 2 : stream);
434 2 : NS_ENSURE_SUCCESS(rv, rv);
435 :
436 4 : nsCOMPtr<nsISupports> owner = do_QueryInterface(info->mPrincipal);
437 :
438 4 : nsAutoString type;
439 2 : rv = info->mFile->GetType(type);
440 2 : NS_ENSURE_SUCCESS(rv, rv);
441 :
442 2 : channel->SetOwner(owner);
443 2 : channel->SetOriginalURI(uri);
444 2 : channel->SetContentType(NS_ConvertUTF16toUTF8(type));
445 2 : channel.forget(result);
446 :
447 2 : return NS_OK;
448 : }
449 :
450 : NS_IMETHODIMP
451 0 : nsBlobProtocolHandler::AllowPort(PRInt32 port, const char *scheme,
452 : bool *_retval)
453 : {
454 : // don't override anything.
455 0 : *_retval = false;
456 0 : return NS_OK;
457 : }
|