1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set sw=4 ts=8 et tw=80 : */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "nsJAR.h"
8 #include "nsJARChannel.h"
9 #include "nsJARProtocolHandler.h"
10 #include "nsMimeTypes.h"
11 #include "nsNetUtil.h"
12 #include "nsEscape.h"
13 #include "nsIPrefService.h"
14 #include "nsIPrefBranch.h"
15 #include "nsIViewSourceChannel.h"
16 #include "nsContentUtils.h"
17 #include "nsProxyRelease.h"
18 #include "nsContentSecurityManager.h"
19
20 #include "nsIScriptSecurityManager.h"
21 #include "nsIPrincipal.h"
22 #include "nsIFileURL.h"
23
24 #include "mozilla/IntegerPrintfMacros.h"
25 #include "mozilla/Preferences.h"
26 #include "nsITabChild.h"
27 #include "private/pprio.h"
28 #include "nsInputStreamPump.h"
29
30 using namespace mozilla;
31 using namespace mozilla::net;
32
33 static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
34
35 // the entry for a directory will either be empty (in the case of the
36 // top-level directory) or will end with a slash
37 #define ENTRY_IS_DIRECTORY(_entry) \
38 ((_entry).IsEmpty() || '/' == (_entry).Last())
39
40 //-----------------------------------------------------------------------------
41
42 // Ignore any LOG macro that we inherit from arbitrary headers. (We define our
43 // own LOG macro below.)
44 #ifdef LOG
45 #undef LOG
46 #endif
47
48 //
49 // set NSPR_LOG_MODULES=nsJarProtocol:5
50 //
51 static LazyLogModule gJarProtocolLog("nsJarProtocol");
52
53 #define LOG(args) MOZ_LOG(gJarProtocolLog, mozilla::LogLevel::Debug, args)
54 #define LOG_ENABLED() MOZ_LOG_TEST(gJarProtocolLog, mozilla::LogLevel::Debug)
55
56 //-----------------------------------------------------------------------------
57 // nsJARInputThunk
58 //
59 // this class allows us to do some extra work on the stream transport thread.
60 //-----------------------------------------------------------------------------
61
62 class nsJARInputThunk : public nsIInputStream {
63 public:
64 NS_DECL_THREADSAFE_ISUPPORTS
65 NS_DECL_NSIINPUTSTREAM
66
nsJARInputThunk(nsIZipReader * zipReader,nsIURI * fullJarURI,const nsACString & jarEntry,bool usingJarCache)67 nsJARInputThunk(nsIZipReader *zipReader, nsIURI *fullJarURI,
68 const nsACString &jarEntry, bool usingJarCache)
69 : mUsingJarCache(usingJarCache),
70 mJarReader(zipReader),
71 mJarEntry(jarEntry),
72 mContentLength(-1) {
73 if (fullJarURI) {
74 #ifdef DEBUG
75 nsresult rv =
76 #endif
77 fullJarURI->GetAsciiSpec(mJarDirSpec);
78 NS_ASSERTION(NS_SUCCEEDED(rv), "this shouldn't fail");
79 }
80 }
81
GetContentLength()82 int64_t GetContentLength() { return mContentLength; }
83
84 nsresult Init();
85
86 private:
~nsJARInputThunk()87 virtual ~nsJARInputThunk() { Close(); }
88
89 bool mUsingJarCache;
90 nsCOMPtr<nsIZipReader> mJarReader;
91 nsCString mJarDirSpec;
92 nsCOMPtr<nsIInputStream> mJarStream;
93 nsCString mJarEntry;
94 int64_t mContentLength;
95 };
96
NS_IMPL_ISUPPORTS(nsJARInputThunk,nsIInputStream)97 NS_IMPL_ISUPPORTS(nsJARInputThunk, nsIInputStream)
98
99 nsresult nsJARInputThunk::Init() {
100 nsresult rv;
101 if (ENTRY_IS_DIRECTORY(mJarEntry)) {
102 // A directory stream also needs the Spec of the FullJarURI
103 // because is included in the stream data itself.
104
105 NS_ENSURE_STATE(!mJarDirSpec.IsEmpty());
106
107 rv = mJarReader->GetInputStreamWithSpec(mJarDirSpec, mJarEntry,
108 getter_AddRefs(mJarStream));
109 } else {
110 rv = mJarReader->GetInputStream(mJarEntry, getter_AddRefs(mJarStream));
111 }
112 if (NS_FAILED(rv)) {
113 // convert to the proper result if the entry wasn't found
114 // so that error pages work
115 if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) rv = NS_ERROR_FILE_NOT_FOUND;
116 return rv;
117 }
118
119 // ask the JarStream for the content length
120 uint64_t avail;
121 rv = mJarStream->Available((uint64_t *)&avail);
122 if (NS_FAILED(rv)) return rv;
123
124 mContentLength = avail < INT64_MAX ? (int64_t)avail : -1;
125
126 return NS_OK;
127 }
128
129 NS_IMETHODIMP
Close()130 nsJARInputThunk::Close() {
131 nsresult rv = NS_OK;
132
133 if (mJarStream) rv = mJarStream->Close();
134
135 if (!mUsingJarCache && mJarReader) mJarReader->Close();
136
137 mJarReader = nullptr;
138
139 return rv;
140 }
141
142 NS_IMETHODIMP
Available(uint64_t * avail)143 nsJARInputThunk::Available(uint64_t *avail) {
144 return mJarStream->Available(avail);
145 }
146
147 NS_IMETHODIMP
Read(char * buf,uint32_t count,uint32_t * countRead)148 nsJARInputThunk::Read(char *buf, uint32_t count, uint32_t *countRead) {
149 return mJarStream->Read(buf, count, countRead);
150 }
151
152 NS_IMETHODIMP
ReadSegments(nsWriteSegmentFun writer,void * closure,uint32_t count,uint32_t * countRead)153 nsJARInputThunk::ReadSegments(nsWriteSegmentFun writer, void *closure,
154 uint32_t count, uint32_t *countRead) {
155 // stream transport does only calls Read()
156 return NS_ERROR_NOT_IMPLEMENTED;
157 }
158
159 NS_IMETHODIMP
IsNonBlocking(bool * nonBlocking)160 nsJARInputThunk::IsNonBlocking(bool *nonBlocking) {
161 *nonBlocking = false;
162 return NS_OK;
163 }
164
165 //-----------------------------------------------------------------------------
166 // nsJARChannel
167 //-----------------------------------------------------------------------------
168
nsJARChannel()169 nsJARChannel::nsJARChannel()
170 : mOpened(false),
171 mContentDisposition(0),
172 mContentLength(-1),
173 mLoadFlags(LOAD_NORMAL),
174 mStatus(NS_OK),
175 mIsPending(false),
176 mIsUnsafe(true),
177 mBlockRemoteFiles(false) {
178 mBlockRemoteFiles =
179 Preferences::GetBool("network.jar.block-remote-files", false);
180
181 // hold an owning reference to the jar handler
182 mJarHandler = gJarHandler;
183 }
184
~nsJARChannel()185 nsJARChannel::~nsJARChannel() {
186 NS_ReleaseOnMainThreadSystemGroup("nsJARChannel::mLoadInfo",
187 mLoadInfo.forget());
188 }
189
NS_IMPL_ISUPPORTS_INHERITED(nsJARChannel,nsHashPropertyBag,nsIRequest,nsIChannel,nsIStreamListener,nsIRequestObserver,nsIThreadRetargetableRequest,nsIThreadRetargetableStreamListener,nsIJARChannel)190 NS_IMPL_ISUPPORTS_INHERITED(nsJARChannel, nsHashPropertyBag, nsIRequest,
191 nsIChannel, nsIStreamListener, nsIRequestObserver,
192 nsIThreadRetargetableRequest,
193 nsIThreadRetargetableStreamListener, nsIJARChannel)
194
195 nsresult nsJARChannel::Init(nsIURI *uri) {
196 nsresult rv;
197 mJarURI = do_QueryInterface(uri, &rv);
198 if (NS_FAILED(rv)) return rv;
199
200 mOriginalURI = mJarURI;
201
202 // Prevent loading jar:javascript URIs (see bug 290982).
203 nsCOMPtr<nsIURI> innerURI;
204 rv = mJarURI->GetJARFile(getter_AddRefs(innerURI));
205 if (NS_FAILED(rv)) return rv;
206 bool isJS;
207 rv = innerURI->SchemeIs("javascript", &isJS);
208 if (NS_FAILED(rv)) return rv;
209 if (isJS) {
210 NS_WARNING("blocking jar:javascript:");
211 return NS_ERROR_INVALID_ARG;
212 }
213
214 mJarURI->GetSpec(mSpec);
215 return rv;
216 }
217
CreateJarInput(nsIZipReaderCache * jarCache,nsJARInputThunk ** resultInput)218 nsresult nsJARChannel::CreateJarInput(nsIZipReaderCache *jarCache,
219 nsJARInputThunk **resultInput) {
220 MOZ_ASSERT(resultInput);
221 MOZ_ASSERT(mJarFile || mTempMem);
222
223 // important to pass a clone of the file since the nsIFile impl is not
224 // necessarily MT-safe
225 nsCOMPtr<nsIFile> clonedFile;
226 nsresult rv = NS_OK;
227 if (mJarFile) {
228 rv = mJarFile->Clone(getter_AddRefs(clonedFile));
229 if (NS_FAILED(rv)) return rv;
230 }
231
232 nsCOMPtr<nsIZipReader> reader;
233 if (mPreCachedJarReader) {
234 reader = mPreCachedJarReader;
235 } else if (jarCache) {
236 MOZ_ASSERT(mJarFile);
237 if (mInnerJarEntry.IsEmpty())
238 rv = jarCache->GetZip(clonedFile, getter_AddRefs(reader));
239 else
240 rv = jarCache->GetInnerZip(clonedFile, mInnerJarEntry,
241 getter_AddRefs(reader));
242 } else {
243 // create an uncached jar reader
244 nsCOMPtr<nsIZipReader> outerReader = do_CreateInstance(kZipReaderCID, &rv);
245 if (NS_FAILED(rv)) return rv;
246
247 if (mJarFile) {
248 rv = outerReader->Open(clonedFile);
249 } else {
250 rv = outerReader->OpenMemory(mTempMem->Elements(), mTempMem->Length());
251 }
252 if (NS_FAILED(rv)) return rv;
253
254 if (mInnerJarEntry.IsEmpty())
255 reader = outerReader;
256 else {
257 reader = do_CreateInstance(kZipReaderCID, &rv);
258 if (NS_FAILED(rv)) return rv;
259
260 rv = reader->OpenInner(outerReader, mInnerJarEntry);
261 }
262 }
263 if (NS_FAILED(rv)) return rv;
264
265 RefPtr<nsJARInputThunk> input =
266 new nsJARInputThunk(reader, mJarURI, mJarEntry, jarCache != nullptr);
267 rv = input->Init();
268 if (NS_FAILED(rv)) return rv;
269
270 // Make GetContentLength meaningful
271 mContentLength = input->GetContentLength();
272
273 input.forget(resultInput);
274 return NS_OK;
275 }
276
LookupFile(bool aAllowAsync)277 nsresult nsJARChannel::LookupFile(bool aAllowAsync) {
278 LOG(("nsJARChannel::LookupFile [this=%p %s]\n", this, mSpec.get()));
279
280 if (mJarFile) return NS_OK;
281
282 nsresult rv;
283
284 rv = mJarURI->GetJARFile(getter_AddRefs(mJarBaseURI));
285 if (NS_FAILED(rv)) return rv;
286
287 rv = mJarURI->GetJAREntry(mJarEntry);
288 if (NS_FAILED(rv)) return rv;
289
290 // The name of the JAR entry must not contain URL-escaped characters:
291 // we're moving from URL domain to a filename domain here. nsStandardURL
292 // does basic escaping by default, which breaks reading zipped files which
293 // have e.g. spaces in their filenames.
294 NS_UnescapeURL(mJarEntry);
295
296 if (mJarFileOverride) {
297 mJarFile = mJarFileOverride;
298 LOG(("nsJARChannel::LookupFile [this=%p] Overriding mJarFile\n", this));
299 return NS_OK;
300 }
301
302 // try to get a nsIFile directly from the url, which will often succeed.
303 {
304 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mJarBaseURI);
305 if (fileURL) fileURL->GetFile(getter_AddRefs(mJarFile));
306 }
307
308 // try to handle a nested jar
309 if (!mJarFile) {
310 nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(mJarBaseURI);
311 if (jarURI) {
312 nsCOMPtr<nsIFileURL> fileURL;
313 nsCOMPtr<nsIURI> innerJarURI;
314 rv = jarURI->GetJARFile(getter_AddRefs(innerJarURI));
315 if (NS_SUCCEEDED(rv)) fileURL = do_QueryInterface(innerJarURI);
316 if (fileURL) {
317 fileURL->GetFile(getter_AddRefs(mJarFile));
318 jarURI->GetJAREntry(mInnerJarEntry);
319 }
320 }
321 }
322
323 return rv;
324 }
325
OpenLocalFile()326 nsresult nsJARChannel::OpenLocalFile() {
327 MOZ_ASSERT(mIsPending);
328
329 // Local files are always considered safe.
330 mIsUnsafe = false;
331
332 RefPtr<nsJARInputThunk> input;
333 nsresult rv = CreateJarInput(gJarHandler->JarCache(), getter_AddRefs(input));
334 if (NS_SUCCEEDED(rv)) {
335 // Create input stream pump and call AsyncRead as a block.
336 rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input.forget());
337 if (NS_SUCCEEDED(rv)) rv = mPump->AsyncRead(this, nullptr);
338 }
339
340 return rv;
341 }
342
NotifyError(nsresult aError)343 void nsJARChannel::NotifyError(nsresult aError) {
344 MOZ_ASSERT(NS_FAILED(aError));
345
346 mStatus = aError;
347
348 OnStartRequest(nullptr, nullptr);
349 OnStopRequest(nullptr, nullptr, aError);
350 }
351
FireOnProgress(uint64_t aProgress)352 void nsJARChannel::FireOnProgress(uint64_t aProgress) {
353 MOZ_ASSERT(NS_IsMainThread());
354 MOZ_ASSERT(mProgressSink);
355
356 mProgressSink->OnProgress(this, nullptr, aProgress, mContentLength);
357 }
358
359 //-----------------------------------------------------------------------------
360 // nsIRequest
361 //-----------------------------------------------------------------------------
362
363 NS_IMETHODIMP
GetName(nsACString & result)364 nsJARChannel::GetName(nsACString &result) { return mJarURI->GetSpec(result); }
365
366 NS_IMETHODIMP
IsPending(bool * result)367 nsJARChannel::IsPending(bool *result) {
368 *result = mIsPending;
369 return NS_OK;
370 }
371
372 NS_IMETHODIMP
GetStatus(nsresult * status)373 nsJARChannel::GetStatus(nsresult *status) {
374 if (mPump && NS_SUCCEEDED(mStatus))
375 mPump->GetStatus(status);
376 else
377 *status = mStatus;
378 return NS_OK;
379 }
380
381 NS_IMETHODIMP
Cancel(nsresult status)382 nsJARChannel::Cancel(nsresult status) {
383 mStatus = status;
384 if (mPump) return mPump->Cancel(status);
385
386 NS_ASSERTION(!mIsPending, "need to implement cancel when downloading");
387 return NS_OK;
388 }
389
390 NS_IMETHODIMP
Suspend()391 nsJARChannel::Suspend() {
392 if (mPump) return mPump->Suspend();
393
394 NS_ASSERTION(!mIsPending, "need to implement suspend when downloading");
395 return NS_OK;
396 }
397
398 NS_IMETHODIMP
Resume()399 nsJARChannel::Resume() {
400 if (mPump) return mPump->Resume();
401
402 NS_ASSERTION(!mIsPending, "need to implement resume when downloading");
403 return NS_OK;
404 }
405
406 NS_IMETHODIMP
GetLoadFlags(nsLoadFlags * aLoadFlags)407 nsJARChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) {
408 *aLoadFlags = mLoadFlags;
409 return NS_OK;
410 }
411
412 NS_IMETHODIMP
SetLoadFlags(nsLoadFlags aLoadFlags)413 nsJARChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
414 mLoadFlags = aLoadFlags;
415 return NS_OK;
416 }
417
418 NS_IMETHODIMP
GetIsDocument(bool * aIsDocument)419 nsJARChannel::GetIsDocument(bool *aIsDocument) {
420 return NS_GetIsDocumentChannel(this, aIsDocument);
421 }
422
423 NS_IMETHODIMP
GetLoadGroup(nsILoadGroup ** aLoadGroup)424 nsJARChannel::GetLoadGroup(nsILoadGroup **aLoadGroup) {
425 NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
426 return NS_OK;
427 }
428
429 NS_IMETHODIMP
SetLoadGroup(nsILoadGroup * aLoadGroup)430 nsJARChannel::SetLoadGroup(nsILoadGroup *aLoadGroup) {
431 mLoadGroup = aLoadGroup;
432 return NS_OK;
433 }
434
435 //-----------------------------------------------------------------------------
436 // nsIChannel
437 //-----------------------------------------------------------------------------
438
439 NS_IMETHODIMP
GetOriginalURI(nsIURI ** aURI)440 nsJARChannel::GetOriginalURI(nsIURI **aURI) {
441 *aURI = mOriginalURI;
442 NS_ADDREF(*aURI);
443 return NS_OK;
444 }
445
446 NS_IMETHODIMP
SetOriginalURI(nsIURI * aURI)447 nsJARChannel::SetOriginalURI(nsIURI *aURI) {
448 NS_ENSURE_ARG_POINTER(aURI);
449 mOriginalURI = aURI;
450 return NS_OK;
451 }
452
453 NS_IMETHODIMP
GetURI(nsIURI ** aURI)454 nsJARChannel::GetURI(nsIURI **aURI) {
455 NS_IF_ADDREF(*aURI = mJarURI);
456
457 return NS_OK;
458 }
459
460 NS_IMETHODIMP
GetOwner(nsISupports ** aOwner)461 nsJARChannel::GetOwner(nsISupports **aOwner) {
462 // JAR signatures are not processed to avoid main-thread network I/O (bug
463 // 726125)
464 *aOwner = mOwner;
465 NS_IF_ADDREF(*aOwner);
466 return NS_OK;
467 }
468
469 NS_IMETHODIMP
SetOwner(nsISupports * aOwner)470 nsJARChannel::SetOwner(nsISupports *aOwner) {
471 mOwner = aOwner;
472 return NS_OK;
473 }
474
475 NS_IMETHODIMP
GetLoadInfo(nsILoadInfo ** aLoadInfo)476 nsJARChannel::GetLoadInfo(nsILoadInfo **aLoadInfo) {
477 NS_IF_ADDREF(*aLoadInfo = mLoadInfo);
478 return NS_OK;
479 }
480
481 NS_IMETHODIMP
SetLoadInfo(nsILoadInfo * aLoadInfo)482 nsJARChannel::SetLoadInfo(nsILoadInfo *aLoadInfo) {
483 mLoadInfo = aLoadInfo;
484 return NS_OK;
485 }
486
487 NS_IMETHODIMP
GetNotificationCallbacks(nsIInterfaceRequestor ** aCallbacks)488 nsJARChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks) {
489 NS_IF_ADDREF(*aCallbacks = mCallbacks);
490 return NS_OK;
491 }
492
493 NS_IMETHODIMP
SetNotificationCallbacks(nsIInterfaceRequestor * aCallbacks)494 nsJARChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks) {
495 mCallbacks = aCallbacks;
496 return NS_OK;
497 }
498
499 NS_IMETHODIMP
GetSecurityInfo(nsISupports ** aSecurityInfo)500 nsJARChannel::GetSecurityInfo(nsISupports **aSecurityInfo) {
501 NS_PRECONDITION(aSecurityInfo, "Null out param");
502 NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
503 return NS_OK;
504 }
505
506 NS_IMETHODIMP
GetContentType(nsACString & result)507 nsJARChannel::GetContentType(nsACString &result) {
508 // If the Jar file has not been open yet,
509 // We return application/x-unknown-content-type
510 if (!mOpened) {
511 result.AssignLiteral(UNKNOWN_CONTENT_TYPE);
512 return NS_OK;
513 }
514
515 if (mContentType.IsEmpty()) {
516 //
517 // generate content type and set it
518 //
519 const char *ext = nullptr, *fileName = mJarEntry.get();
520 int32_t len = mJarEntry.Length();
521
522 // check if we're displaying a directory
523 // mJarEntry will be empty if we're trying to display
524 // the topmost directory in a zip, e.g. jar:foo.zip!/
525 if (ENTRY_IS_DIRECTORY(mJarEntry)) {
526 mContentType.AssignLiteral(APPLICATION_HTTP_INDEX_FORMAT);
527 } else {
528 // not a directory, take a guess by its extension
529 for (int32_t i = len - 1; i >= 0; i--) {
530 if (fileName[i] == '.') {
531 ext = &fileName[i + 1];
532 break;
533 }
534 }
535 if (ext) {
536 nsIMIMEService *mimeServ = gJarHandler->MimeService();
537 if (mimeServ)
538 mimeServ->GetTypeFromExtension(nsDependentCString(ext), mContentType);
539 }
540 if (mContentType.IsEmpty())
541 mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
542 }
543 }
544 result = mContentType;
545 return NS_OK;
546 }
547
548 NS_IMETHODIMP
SetContentType(const nsACString & aContentType)549 nsJARChannel::SetContentType(const nsACString &aContentType) {
550 // If someone gives us a type hint we should just use that type instead of
551 // doing our guessing. So we don't care when this is being called.
552
553 // mContentCharset is unchanged if not parsed
554 NS_ParseResponseContentType(aContentType, mContentType, mContentCharset);
555 return NS_OK;
556 }
557
558 NS_IMETHODIMP
GetContentCharset(nsACString & aContentCharset)559 nsJARChannel::GetContentCharset(nsACString &aContentCharset) {
560 // If someone gives us a charset hint we should just use that charset.
561 // So we don't care when this is being called.
562 aContentCharset = mContentCharset;
563 return NS_OK;
564 }
565
566 NS_IMETHODIMP
SetContentCharset(const nsACString & aContentCharset)567 nsJARChannel::SetContentCharset(const nsACString &aContentCharset) {
568 mContentCharset = aContentCharset;
569 return NS_OK;
570 }
571
572 NS_IMETHODIMP
GetContentDisposition(uint32_t * aContentDisposition)573 nsJARChannel::GetContentDisposition(uint32_t *aContentDisposition) {
574 if (mContentDispositionHeader.IsEmpty()) return NS_ERROR_NOT_AVAILABLE;
575
576 *aContentDisposition = mContentDisposition;
577 return NS_OK;
578 }
579
580 NS_IMETHODIMP
SetContentDisposition(uint32_t aContentDisposition)581 nsJARChannel::SetContentDisposition(uint32_t aContentDisposition) {
582 return NS_ERROR_NOT_AVAILABLE;
583 }
584
585 NS_IMETHODIMP
GetContentDispositionFilename(nsAString & aContentDispositionFilename)586 nsJARChannel::GetContentDispositionFilename(
587 nsAString &aContentDispositionFilename) {
588 return NS_ERROR_NOT_AVAILABLE;
589 }
590
591 NS_IMETHODIMP
SetContentDispositionFilename(const nsAString & aContentDispositionFilename)592 nsJARChannel::SetContentDispositionFilename(
593 const nsAString &aContentDispositionFilename) {
594 return NS_ERROR_NOT_AVAILABLE;
595 }
596
597 NS_IMETHODIMP
GetContentDispositionHeader(nsACString & aContentDispositionHeader)598 nsJARChannel::GetContentDispositionHeader(
599 nsACString &aContentDispositionHeader) {
600 if (mContentDispositionHeader.IsEmpty()) return NS_ERROR_NOT_AVAILABLE;
601
602 aContentDispositionHeader = mContentDispositionHeader;
603 return NS_OK;
604 }
605
606 NS_IMETHODIMP
GetContentLength(int64_t * result)607 nsJARChannel::GetContentLength(int64_t *result) {
608 *result = mContentLength;
609 return NS_OK;
610 }
611
612 NS_IMETHODIMP
SetContentLength(int64_t aContentLength)613 nsJARChannel::SetContentLength(int64_t aContentLength) {
614 // XXX does this really make any sense at all?
615 mContentLength = aContentLength;
616 return NS_OK;
617 }
618
619 NS_IMETHODIMP
Open(nsIInputStream ** stream)620 nsJARChannel::Open(nsIInputStream **stream) {
621 LOG(("nsJARChannel::Open [this=%p]\n", this));
622
623 NS_ENSURE_TRUE(!mOpened, NS_ERROR_IN_PROGRESS);
624 NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
625
626 mJarFile = nullptr;
627 mIsUnsafe = true;
628
629 nsresult rv = LookupFile(false);
630 if (NS_FAILED(rv)) return rv;
631
632 // If mJarInput was not set by LookupFile, the JAR is a remote jar.
633 if (!mJarFile) {
634 NS_NOTREACHED("need sync downloader");
635 return NS_ERROR_NOT_IMPLEMENTED;
636 }
637
638 RefPtr<nsJARInputThunk> input;
639 rv = CreateJarInput(gJarHandler->JarCache(), getter_AddRefs(input));
640 if (NS_FAILED(rv)) return rv;
641
642 input.forget(stream);
643 mOpened = true;
644 // local files are always considered safe
645 mIsUnsafe = false;
646 return NS_OK;
647 }
648
649 NS_IMETHODIMP
Open2(nsIInputStream ** aStream)650 nsJARChannel::Open2(nsIInputStream **aStream) {
651 nsCOMPtr<nsIStreamListener> listener;
652 nsresult rv =
653 nsContentSecurityManager::doContentSecurityCheck(this, listener);
654 NS_ENSURE_SUCCESS(rv, rv);
655 return Open(aStream);
656 }
657
658 NS_IMETHODIMP
AsyncOpen(nsIStreamListener * listener,nsISupports * ctx)659 nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx) {
660 MOZ_ASSERT(
661 !mLoadInfo || mLoadInfo->GetSecurityMode() == 0 ||
662 mLoadInfo->GetInitialSecurityCheckDone() ||
663 (mLoadInfo->GetSecurityMode() ==
664 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
665 nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
666 "security flags in loadInfo but asyncOpen2() not called");
667
668 LOG(("nsJARChannel::AsyncOpen [this=%p]\n", this));
669
670 NS_ENSURE_ARG_POINTER(listener);
671 NS_ENSURE_TRUE(!mOpened, NS_ERROR_IN_PROGRESS);
672 NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
673
674 mJarFile = nullptr;
675 mIsUnsafe = true;
676
677 // Initialize mProgressSink
678 NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink);
679
680 mListener = listener;
681 mListenerContext = ctx;
682 mIsPending = true;
683
684 nsresult rv = LookupFile(true);
685 if (NS_FAILED(rv)) {
686 mIsPending = false;
687 mListenerContext = nullptr;
688 mListener = nullptr;
689 mCallbacks = nullptr;
690 mProgressSink = nullptr;
691 return rv;
692 }
693
694 nsCOMPtr<nsIChannel> channel;
695
696 if (!mJarFile) {
697 // Not a local file...
698
699 // Check preferences to see if all remote jar support should be disabled
700 if (mBlockRemoteFiles) {
701 mIsUnsafe = true;
702 mIsPending = false;
703 mListenerContext = nullptr;
704 mListener = nullptr;
705 mCallbacks = nullptr;
706 mProgressSink = nullptr;
707 return NS_ERROR_UNSAFE_CONTENT_TYPE;
708 }
709
710 // kick off an async download of the base URI...
711 nsCOMPtr<nsIStreamListener> downloader = new MemoryDownloader(this);
712 uint32_t loadFlags =
713 mLoadFlags & ~(LOAD_DOCUMENT_URI | LOAD_CALL_CONTENT_SNIFFERS);
714 rv = NS_NewChannelInternal(getter_AddRefs(channel), mJarBaseURI, mLoadInfo,
715 nullptr, // PerformanceStorage
716 mLoadGroup, mCallbacks, loadFlags);
717 if (NS_FAILED(rv)) {
718 mIsPending = false;
719 mListenerContext = nullptr;
720 mListener = nullptr;
721 mCallbacks = nullptr;
722 mProgressSink = nullptr;
723 return rv;
724 }
725 if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
726 rv = channel->AsyncOpen2(downloader);
727 } else {
728 rv = channel->AsyncOpen(downloader, nullptr);
729 }
730 } else {
731 rv = OpenLocalFile();
732 }
733
734 if (NS_FAILED(rv)) {
735 mIsPending = false;
736 mListenerContext = nullptr;
737 mListener = nullptr;
738 mCallbacks = nullptr;
739 mProgressSink = nullptr;
740 return rv;
741 }
742
743 if (mLoadGroup) mLoadGroup->AddRequest(this, nullptr);
744
745 mOpened = true;
746
747 return NS_OK;
748 }
749
750 NS_IMETHODIMP
AsyncOpen2(nsIStreamListener * aListener)751 nsJARChannel::AsyncOpen2(nsIStreamListener *aListener) {
752 nsCOMPtr<nsIStreamListener> listener = aListener;
753 nsresult rv =
754 nsContentSecurityManager::doContentSecurityCheck(this, listener);
755 if (NS_FAILED(rv)) {
756 mIsPending = false;
757 mListenerContext = nullptr;
758 mListener = nullptr;
759 mCallbacks = nullptr;
760 mProgressSink = nullptr;
761 return rv;
762 }
763
764 return AsyncOpen(listener, nullptr);
765 }
766
767 //-----------------------------------------------------------------------------
768 // nsIJARChannel
769 //-----------------------------------------------------------------------------
770 NS_IMETHODIMP
GetIsUnsafe(bool * isUnsafe)771 nsJARChannel::GetIsUnsafe(bool *isUnsafe) {
772 *isUnsafe = mIsUnsafe;
773 return NS_OK;
774 }
775
776 NS_IMETHODIMP
GetJarFile(nsIFile ** aFile)777 nsJARChannel::GetJarFile(nsIFile **aFile) {
778 NS_IF_ADDREF(*aFile = mJarFile);
779 return NS_OK;
780 }
781
782 NS_IMETHODIMP
SetJarFile(nsIFile * aFile)783 nsJARChannel::SetJarFile(nsIFile *aFile) {
784 if (mOpened) {
785 return NS_ERROR_IN_PROGRESS;
786 }
787 mJarFileOverride = aFile;
788 return NS_OK;
789 }
790
791 NS_IMETHODIMP
EnsureCached(bool * aIsCached)792 nsJARChannel::EnsureCached(bool *aIsCached) {
793 nsresult rv;
794 *aIsCached = false;
795
796 if (mOpened) {
797 return NS_ERROR_ALREADY_OPENED;
798 }
799
800 if (mPreCachedJarReader) {
801 // We've already been called and found the JAR is cached
802 *aIsCached = true;
803 return NS_OK;
804 }
805
806 nsCOMPtr<nsIURI> innerFileURI;
807 rv = mJarURI->GetJARFile(getter_AddRefs(innerFileURI));
808 NS_ENSURE_SUCCESS(rv, rv);
809
810 nsCOMPtr<nsIFileURL> innerFileURL = do_QueryInterface(innerFileURI, &rv);
811 NS_ENSURE_SUCCESS(rv, rv);
812
813 nsCOMPtr<nsIFile> jarFile;
814 rv = innerFileURL->GetFile(getter_AddRefs(jarFile));
815 NS_ENSURE_SUCCESS(rv, rv);
816
817 nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
818 NS_ENSURE_SUCCESS(rv, rv);
819
820 nsCOMPtr<nsIProtocolHandler> handler;
821 rv = ioService->GetProtocolHandler("jar", getter_AddRefs(handler));
822 NS_ENSURE_SUCCESS(rv, rv);
823
824 nsCOMPtr<nsIJARProtocolHandler> jarHandler = do_QueryInterface(handler);
825 MOZ_ASSERT(jarHandler);
826
827 nsCOMPtr<nsIZipReaderCache> jarCache;
828 rv = jarHandler->GetJARCache(getter_AddRefs(jarCache));
829 NS_ENSURE_SUCCESS(rv, rv);
830
831 rv = jarCache->GetZipIfCached(jarFile, getter_AddRefs(mPreCachedJarReader));
832 if (rv == NS_ERROR_CACHE_KEY_NOT_FOUND) {
833 return NS_OK;
834 }
835 NS_ENSURE_SUCCESS(rv, rv);
836
837 *aIsCached = true;
838 return NS_OK;
839 }
840
841 NS_IMETHODIMP
GetZipEntry(nsIZipEntry ** aZipEntry)842 nsJARChannel::GetZipEntry(nsIZipEntry **aZipEntry) {
843 nsresult rv = LookupFile(false);
844 if (NS_FAILED(rv)) return rv;
845
846 if (!mJarFile) return NS_ERROR_NOT_AVAILABLE;
847
848 nsCOMPtr<nsIZipReader> reader;
849 rv = gJarHandler->JarCache()->GetZip(mJarFile, getter_AddRefs(reader));
850 if (NS_FAILED(rv)) return rv;
851
852 return reader->GetEntry(mJarEntry, aZipEntry);
853 }
854
855 //-----------------------------------------------------------------------------
856 // mozilla::net::MemoryDownloader::IObserver
857 //-----------------------------------------------------------------------------
858
OnDownloadComplete(MemoryDownloader * aDownloader,nsIRequest * request,nsISupports * context,nsresult status,MemoryDownloader::Data aData)859 void nsJARChannel::OnDownloadComplete(MemoryDownloader *aDownloader,
860 nsIRequest *request, nsISupports *context,
861 nsresult status,
862 MemoryDownloader::Data aData) {
863 nsresult rv;
864
865 nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
866 if (channel) {
867 uint32_t loadFlags;
868 channel->GetLoadFlags(&loadFlags);
869 if (loadFlags & LOAD_REPLACE) {
870 // Update our URI to reflect any redirects that happen during
871 // the HTTP request.
872 if (!mOriginalURI) {
873 SetOriginalURI(mJarURI);
874 }
875
876 nsCOMPtr<nsIURI> innerURI;
877 rv = channel->GetURI(getter_AddRefs(innerURI));
878 if (NS_SUCCEEDED(rv)) {
879 nsCOMPtr<nsIJARURI> newURI;
880 rv = mJarURI->CloneWithJARFile(innerURI, getter_AddRefs(newURI));
881 if (NS_SUCCEEDED(rv)) {
882 mJarURI = newURI;
883 }
884 }
885 if (NS_SUCCEEDED(status)) {
886 status = rv;
887 }
888 }
889 }
890
891 if (NS_SUCCEEDED(status) && channel) {
892 // In case the load info object has changed during a redirect,
893 // grab it from the target channel.
894 channel->GetLoadInfo(getter_AddRefs(mLoadInfo));
895 // Grab the security info from our base channel
896 channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
897
898 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
899 if (httpChannel) {
900 // We only want to run scripts if the server really intended to
901 // send us a JAR file. Check the server-supplied content type for
902 // a JAR type.
903 nsAutoCString header;
904 Unused << httpChannel->GetResponseHeader(
905 NS_LITERAL_CSTRING("Content-Type"), header);
906 nsAutoCString contentType;
907 nsAutoCString charset;
908 NS_ParseResponseContentType(header, contentType, charset);
909 nsAutoCString channelContentType;
910 channel->GetContentType(channelContentType);
911 mIsUnsafe = !(contentType.Equals(channelContentType) &&
912 (contentType.EqualsLiteral("application/java-archive") ||
913 contentType.EqualsLiteral("application/x-jar")));
914 } else {
915 nsCOMPtr<nsIJARChannel> innerJARChannel(do_QueryInterface(channel));
916 if (innerJARChannel) {
917 mIsUnsafe = innerJARChannel->GetIsUnsafe();
918 }
919 }
920
921 channel->GetContentDispositionHeader(mContentDispositionHeader);
922 mContentDisposition =
923 NS_GetContentDispositionFromHeader(mContentDispositionHeader, this);
924 }
925
926 // This is a defense-in-depth check for the preferences to see if all remote
927 // jar support should be disabled. This check may not be needed.
928 MOZ_RELEASE_ASSERT(!mBlockRemoteFiles);
929
930 if (NS_SUCCEEDED(status) && mIsUnsafe &&
931 !Preferences::GetBool("network.jar.open-unsafe-types", false)) {
932 status = NS_ERROR_UNSAFE_CONTENT_TYPE;
933 }
934
935 if (NS_SUCCEEDED(status)) {
936 // Refuse to unpack view-source: jars even if open-unsafe-types is set.
937 nsCOMPtr<nsIViewSourceChannel> viewSource = do_QueryInterface(channel);
938 if (viewSource) {
939 status = NS_ERROR_UNSAFE_CONTENT_TYPE;
940 }
941 }
942
943 if (NS_SUCCEEDED(status)) {
944 mTempMem = Move(aData);
945
946 RefPtr<nsJARInputThunk> input;
947 rv = CreateJarInput(nullptr, getter_AddRefs(input));
948 if (NS_SUCCEEDED(rv)) {
949 // create input stream pump
950 rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input.forget());
951 if (NS_SUCCEEDED(rv)) rv = mPump->AsyncRead(this, nullptr);
952 }
953 status = rv;
954 }
955
956 if (NS_FAILED(status)) {
957 NotifyError(status);
958 }
959 }
960
961 //-----------------------------------------------------------------------------
962 // nsIStreamListener
963 //-----------------------------------------------------------------------------
964
965 NS_IMETHODIMP
OnStartRequest(nsIRequest * req,nsISupports * ctx)966 nsJARChannel::OnStartRequest(nsIRequest *req, nsISupports *ctx) {
967 LOG(("nsJARChannel::OnStartRequest [this=%p %s]\n", this, mSpec.get()));
968
969 mRequest = req;
970 nsresult rv = mListener->OnStartRequest(this, mListenerContext);
971 mRequest = nullptr;
972
973 return rv;
974 }
975
976 NS_IMETHODIMP
OnStopRequest(nsIRequest * req,nsISupports * ctx,nsresult status)977 nsJARChannel::OnStopRequest(nsIRequest *req, nsISupports *ctx,
978 nsresult status) {
979 LOG(("nsJARChannel::OnStopRequest [this=%p %s status=%" PRIx32 "]\n", this,
980 mSpec.get(), static_cast<uint32_t>(status)));
981
982 if (NS_SUCCEEDED(mStatus)) mStatus = status;
983
984 if (mListener) {
985 mListener->OnStopRequest(this, mListenerContext, status);
986 mListener = nullptr;
987 mListenerContext = nullptr;
988 }
989
990 if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, status);
991
992 mPump = nullptr;
993 mIsPending = false;
994
995 // Drop notification callbacks to prevent cycles.
996 mCallbacks = nullptr;
997 mProgressSink = nullptr;
998
999 #if defined(XP_WIN) || defined(MOZ_WIDGET_COCOA)
1000 #else
1001 // To deallocate file descriptor by RemoteOpenFileChild destructor.
1002 mJarFile = nullptr;
1003 #endif
1004
1005 return NS_OK;
1006 }
1007
1008 NS_IMETHODIMP
OnDataAvailable(nsIRequest * req,nsISupports * ctx,nsIInputStream * stream,uint64_t offset,uint32_t count)1009 nsJARChannel::OnDataAvailable(nsIRequest *req, nsISupports *ctx,
1010 nsIInputStream *stream, uint64_t offset,
1011 uint32_t count) {
1012 LOG(("nsJARChannel::OnDataAvailable [this=%p %s]\n", this, mSpec.get()));
1013
1014 nsresult rv;
1015
1016 rv =
1017 mListener->OnDataAvailable(this, mListenerContext, stream, offset, count);
1018
1019 // simply report progress here instead of hooking ourselves up as a
1020 // nsITransportEventSink implementation.
1021 // XXX do the 64-bit stuff for real
1022 if (mProgressSink && NS_SUCCEEDED(rv)) {
1023 if (NS_IsMainThread()) {
1024 FireOnProgress(offset + count);
1025 } else {
1026 NS_DispatchToMainThread(NewRunnableMethod<uint64_t>(
1027 "nsJARChannel::FireOnProgress", this, &nsJARChannel::FireOnProgress,
1028 offset + count));
1029 }
1030 }
1031
1032 return rv; // let the pump cancel on failure
1033 }
1034
1035 NS_IMETHODIMP
RetargetDeliveryTo(nsIEventTarget * aEventTarget)1036 nsJARChannel::RetargetDeliveryTo(nsIEventTarget *aEventTarget) {
1037 MOZ_ASSERT(NS_IsMainThread());
1038
1039 nsCOMPtr<nsIThreadRetargetableRequest> request = do_QueryInterface(mRequest);
1040 if (!request) {
1041 return NS_ERROR_NO_INTERFACE;
1042 }
1043
1044 return request->RetargetDeliveryTo(aEventTarget);
1045 }
1046
1047 NS_IMETHODIMP
GetDeliveryTarget(nsIEventTarget ** aEventTarget)1048 nsJARChannel::GetDeliveryTarget(nsIEventTarget **aEventTarget) {
1049 MOZ_ASSERT(NS_IsMainThread());
1050
1051 nsCOMPtr<nsIThreadRetargetableRequest> request = do_QueryInterface(mRequest);
1052 if (!request) {
1053 return NS_ERROR_NO_INTERFACE;
1054 }
1055
1056 return request->GetDeliveryTarget(aEventTarget);
1057 }
1058
1059 NS_IMETHODIMP
CheckListenerChain()1060 nsJARChannel::CheckListenerChain() {
1061 MOZ_ASSERT(NS_IsMainThread());
1062
1063 nsCOMPtr<nsIThreadRetargetableStreamListener> listener =
1064 do_QueryInterface(mListener);
1065 if (!listener) {
1066 return NS_ERROR_NO_INTERFACE;
1067 }
1068
1069 return listener->CheckListenerChain();
1070 }
1071