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