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