1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/modules/libjar/nsJARChannel.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1018 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim: set sw=4 ts=8 et tw=80 : */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsJAR.h" 1.11 +#include "nsJARChannel.h" 1.12 +#include "nsJARProtocolHandler.h" 1.13 +#include "nsMimeTypes.h" 1.14 +#include "nsNetUtil.h" 1.15 +#include "nsEscape.h" 1.16 +#include "nsIPrefService.h" 1.17 +#include "nsIPrefBranch.h" 1.18 +#include "nsIViewSourceChannel.h" 1.19 +#include "nsChannelProperties.h" 1.20 + 1.21 +#include "nsIScriptSecurityManager.h" 1.22 +#include "nsIPrincipal.h" 1.23 +#include "nsIFileURL.h" 1.24 + 1.25 +#include "mozilla/Preferences.h" 1.26 +#include "mozilla/net/RemoteOpenFileChild.h" 1.27 +#include "nsITabChild.h" 1.28 + 1.29 +using namespace mozilla; 1.30 +using namespace mozilla::net; 1.31 + 1.32 +static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID); 1.33 + 1.34 +// the entry for a directory will either be empty (in the case of the 1.35 +// top-level directory) or will end with a slash 1.36 +#define ENTRY_IS_DIRECTORY(_entry) \ 1.37 + ((_entry).IsEmpty() || '/' == (_entry).Last()) 1.38 + 1.39 +//----------------------------------------------------------------------------- 1.40 + 1.41 +// Ignore any LOG macro that we inherit from arbitrary headers. (We define our 1.42 +// own LOG macro below.) 1.43 +#ifdef LOG 1.44 +#undef LOG 1.45 +#endif 1.46 + 1.47 +#if defined(PR_LOGGING) 1.48 +// 1.49 +// set NSPR_LOG_MODULES=nsJarProtocol:5 1.50 +// 1.51 +static PRLogModuleInfo *gJarProtocolLog = nullptr; 1.52 +#endif 1.53 + 1.54 +// If you ever want to define PR_FORCE_LOGGING in this file, see bug 545995 1.55 +#define LOG(args) PR_LOG(gJarProtocolLog, PR_LOG_DEBUG, args) 1.56 +#define LOG_ENABLED() PR_LOG_TEST(gJarProtocolLog, 4) 1.57 + 1.58 +//----------------------------------------------------------------------------- 1.59 +// nsJARInputThunk 1.60 +// 1.61 +// this class allows us to do some extra work on the stream transport thread. 1.62 +//----------------------------------------------------------------------------- 1.63 + 1.64 +class nsJARInputThunk : public nsIInputStream 1.65 +{ 1.66 +public: 1.67 + NS_DECL_THREADSAFE_ISUPPORTS 1.68 + NS_DECL_NSIINPUTSTREAM 1.69 + 1.70 + nsJARInputThunk(nsIZipReader *zipReader, 1.71 + nsIURI* fullJarURI, 1.72 + const nsACString &jarEntry, 1.73 + bool usingJarCache) 1.74 + : mUsingJarCache(usingJarCache) 1.75 + , mJarReader(zipReader) 1.76 + , mJarEntry(jarEntry) 1.77 + , mContentLength(-1) 1.78 + { 1.79 + if (fullJarURI) { 1.80 +#ifdef DEBUG 1.81 + nsresult rv = 1.82 +#endif 1.83 + fullJarURI->GetAsciiSpec(mJarDirSpec); 1.84 + NS_ASSERTION(NS_SUCCEEDED(rv), "this shouldn't fail"); 1.85 + } 1.86 + } 1.87 + 1.88 + virtual ~nsJARInputThunk() 1.89 + { 1.90 + Close(); 1.91 + } 1.92 + 1.93 + int64_t GetContentLength() 1.94 + { 1.95 + return mContentLength; 1.96 + } 1.97 + 1.98 + nsresult Init(); 1.99 + 1.100 +private: 1.101 + 1.102 + bool mUsingJarCache; 1.103 + nsCOMPtr<nsIZipReader> mJarReader; 1.104 + nsCString mJarDirSpec; 1.105 + nsCOMPtr<nsIInputStream> mJarStream; 1.106 + nsCString mJarEntry; 1.107 + int64_t mContentLength; 1.108 +}; 1.109 + 1.110 +NS_IMPL_ISUPPORTS(nsJARInputThunk, nsIInputStream) 1.111 + 1.112 +nsresult 1.113 +nsJARInputThunk::Init() 1.114 +{ 1.115 + nsresult rv; 1.116 + if (ENTRY_IS_DIRECTORY(mJarEntry)) { 1.117 + // A directory stream also needs the Spec of the FullJarURI 1.118 + // because is included in the stream data itself. 1.119 + 1.120 + NS_ENSURE_STATE(!mJarDirSpec.IsEmpty()); 1.121 + 1.122 + rv = mJarReader->GetInputStreamWithSpec(mJarDirSpec, 1.123 + mJarEntry, 1.124 + getter_AddRefs(mJarStream)); 1.125 + } 1.126 + else { 1.127 + rv = mJarReader->GetInputStream(mJarEntry, 1.128 + getter_AddRefs(mJarStream)); 1.129 + } 1.130 + if (NS_FAILED(rv)) { 1.131 + // convert to the proper result if the entry wasn't found 1.132 + // so that error pages work 1.133 + if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) 1.134 + rv = NS_ERROR_FILE_NOT_FOUND; 1.135 + return rv; 1.136 + } 1.137 + 1.138 + // ask the JarStream for the content length 1.139 + uint64_t avail; 1.140 + rv = mJarStream->Available((uint64_t *) &avail); 1.141 + if (NS_FAILED(rv)) return rv; 1.142 + 1.143 + mContentLength = avail < INT64_MAX ? (int64_t) avail : -1; 1.144 + 1.145 + return NS_OK; 1.146 +} 1.147 + 1.148 +NS_IMETHODIMP 1.149 +nsJARInputThunk::Close() 1.150 +{ 1.151 + nsresult rv = NS_OK; 1.152 + 1.153 + if (mJarStream) 1.154 + rv = mJarStream->Close(); 1.155 + 1.156 + if (!mUsingJarCache && mJarReader) 1.157 + mJarReader->Close(); 1.158 + 1.159 + mJarReader = nullptr; 1.160 + 1.161 + return rv; 1.162 +} 1.163 + 1.164 +NS_IMETHODIMP 1.165 +nsJARInputThunk::Available(uint64_t *avail) 1.166 +{ 1.167 + return mJarStream->Available(avail); 1.168 +} 1.169 + 1.170 +NS_IMETHODIMP 1.171 +nsJARInputThunk::Read(char *buf, uint32_t count, uint32_t *countRead) 1.172 +{ 1.173 + return mJarStream->Read(buf, count, countRead); 1.174 +} 1.175 + 1.176 +NS_IMETHODIMP 1.177 +nsJARInputThunk::ReadSegments(nsWriteSegmentFun writer, void *closure, 1.178 + uint32_t count, uint32_t *countRead) 1.179 +{ 1.180 + // stream transport does only calls Read() 1.181 + return NS_ERROR_NOT_IMPLEMENTED; 1.182 +} 1.183 + 1.184 +NS_IMETHODIMP 1.185 +nsJARInputThunk::IsNonBlocking(bool *nonBlocking) 1.186 +{ 1.187 + *nonBlocking = false; 1.188 + return NS_OK; 1.189 +} 1.190 + 1.191 +//----------------------------------------------------------------------------- 1.192 +// nsJARChannel 1.193 +//----------------------------------------------------------------------------- 1.194 + 1.195 + 1.196 +nsJARChannel::nsJARChannel() 1.197 + : mOpened(false) 1.198 + , mAppURI(nullptr) 1.199 + , mContentLength(-1) 1.200 + , mLoadFlags(LOAD_NORMAL) 1.201 + , mStatus(NS_OK) 1.202 + , mIsPending(false) 1.203 + , mIsUnsafe(true) 1.204 + , mOpeningRemote(false) 1.205 +{ 1.206 +#if defined(PR_LOGGING) 1.207 + if (!gJarProtocolLog) 1.208 + gJarProtocolLog = PR_NewLogModule("nsJarProtocol"); 1.209 +#endif 1.210 + 1.211 + // hold an owning reference to the jar handler 1.212 + NS_ADDREF(gJarHandler); 1.213 +} 1.214 + 1.215 +nsJARChannel::~nsJARChannel() 1.216 +{ 1.217 + // release owning reference to the jar handler 1.218 + nsJARProtocolHandler *handler = gJarHandler; 1.219 + NS_RELEASE(handler); // nullptr parameter 1.220 +} 1.221 + 1.222 +NS_IMPL_ISUPPORTS_INHERITED(nsJARChannel, 1.223 + nsHashPropertyBag, 1.224 + nsIRequest, 1.225 + nsIChannel, 1.226 + nsIStreamListener, 1.227 + nsIRequestObserver, 1.228 + nsIDownloadObserver, 1.229 + nsIRemoteOpenFileListener, 1.230 + nsIJARChannel) 1.231 + 1.232 +nsresult 1.233 +nsJARChannel::Init(nsIURI *uri) 1.234 +{ 1.235 + nsresult rv; 1.236 + mJarURI = do_QueryInterface(uri, &rv); 1.237 + if (NS_FAILED(rv)) 1.238 + return rv; 1.239 + 1.240 + mOriginalURI = mJarURI; 1.241 + 1.242 + // Prevent loading jar:javascript URIs (see bug 290982). 1.243 + nsCOMPtr<nsIURI> innerURI; 1.244 + rv = mJarURI->GetJARFile(getter_AddRefs(innerURI)); 1.245 + if (NS_FAILED(rv)) 1.246 + return rv; 1.247 + bool isJS; 1.248 + rv = innerURI->SchemeIs("javascript", &isJS); 1.249 + if (NS_FAILED(rv)) 1.250 + return rv; 1.251 + if (isJS) { 1.252 + NS_WARNING("blocking jar:javascript:"); 1.253 + return NS_ERROR_INVALID_ARG; 1.254 + } 1.255 + 1.256 +#if defined(PR_LOGGING) 1.257 + mJarURI->GetSpec(mSpec); 1.258 +#endif 1.259 + return rv; 1.260 +} 1.261 + 1.262 +nsresult 1.263 +nsJARChannel::CreateJarInput(nsIZipReaderCache *jarCache, nsJARInputThunk **resultInput) 1.264 +{ 1.265 + MOZ_ASSERT(resultInput); 1.266 + 1.267 + // important to pass a clone of the file since the nsIFile impl is not 1.268 + // necessarily MT-safe 1.269 + nsCOMPtr<nsIFile> clonedFile; 1.270 + nsresult rv = mJarFile->Clone(getter_AddRefs(clonedFile)); 1.271 + if (NS_FAILED(rv)) 1.272 + return rv; 1.273 + 1.274 + nsCOMPtr<nsIZipReader> reader; 1.275 + if (jarCache) { 1.276 + if (mInnerJarEntry.IsEmpty()) 1.277 + rv = jarCache->GetZip(clonedFile, getter_AddRefs(reader)); 1.278 + else 1.279 + rv = jarCache->GetInnerZip(clonedFile, mInnerJarEntry, 1.280 + getter_AddRefs(reader)); 1.281 + } else { 1.282 + // create an uncached jar reader 1.283 + nsCOMPtr<nsIZipReader> outerReader = do_CreateInstance(kZipReaderCID, &rv); 1.284 + if (NS_FAILED(rv)) 1.285 + return rv; 1.286 + 1.287 + rv = outerReader->Open(clonedFile); 1.288 + if (NS_FAILED(rv)) 1.289 + return rv; 1.290 + 1.291 + if (mInnerJarEntry.IsEmpty()) 1.292 + reader = outerReader; 1.293 + else { 1.294 + reader = do_CreateInstance(kZipReaderCID, &rv); 1.295 + if (NS_FAILED(rv)) 1.296 + return rv; 1.297 + 1.298 + rv = reader->OpenInner(outerReader, mInnerJarEntry); 1.299 + } 1.300 + } 1.301 + if (NS_FAILED(rv)) 1.302 + return rv; 1.303 + 1.304 + nsRefPtr<nsJARInputThunk> input = new nsJARInputThunk(reader, 1.305 + mJarURI, 1.306 + mJarEntry, 1.307 + jarCache != nullptr 1.308 + ); 1.309 + rv = input->Init(); 1.310 + if (NS_FAILED(rv)) 1.311 + return rv; 1.312 + 1.313 + // Make GetContentLength meaningful 1.314 + mContentLength = input->GetContentLength(); 1.315 + 1.316 + input.forget(resultInput); 1.317 + return NS_OK; 1.318 +} 1.319 + 1.320 +nsresult 1.321 +nsJARChannel::LookupFile() 1.322 +{ 1.323 + LOG(("nsJARChannel::LookupFile [this=%x %s]\n", this, mSpec.get())); 1.324 + 1.325 + nsresult rv; 1.326 + nsCOMPtr<nsIURI> uri; 1.327 + 1.328 + rv = mJarURI->GetJARFile(getter_AddRefs(mJarBaseURI)); 1.329 + if (NS_FAILED(rv)) 1.330 + return rv; 1.331 + 1.332 + rv = mJarURI->GetJAREntry(mJarEntry); 1.333 + if (NS_FAILED(rv)) 1.334 + return rv; 1.335 + 1.336 + // The name of the JAR entry must not contain URL-escaped characters: 1.337 + // we're moving from URL domain to a filename domain here. nsStandardURL 1.338 + // does basic escaping by default, which breaks reading zipped files which 1.339 + // have e.g. spaces in their filenames. 1.340 + NS_UnescapeURL(mJarEntry); 1.341 + 1.342 + // try to get a nsIFile directly from the url, which will often succeed. 1.343 + { 1.344 + nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mJarBaseURI); 1.345 + if (fileURL) 1.346 + fileURL->GetFile(getter_AddRefs(mJarFile)); 1.347 + } 1.348 + // if we're in child process and have special "remoteopenfile:://" scheme, 1.349 + // create special nsIFile that gets file handle from parent when opened. 1.350 + if (!mJarFile && !gJarHandler->IsMainProcess()) { 1.351 + nsAutoCString scheme; 1.352 + rv = mJarBaseURI->GetScheme(scheme); 1.353 + if (NS_SUCCEEDED(rv) && scheme.EqualsLiteral("remoteopenfile")) { 1.354 + nsRefPtr<RemoteOpenFileChild> remoteFile = new RemoteOpenFileChild(); 1.355 + rv = remoteFile->Init(mJarBaseURI, mAppURI); 1.356 + NS_ENSURE_SUCCESS(rv, rv); 1.357 + mJarFile = remoteFile; 1.358 + 1.359 + nsIZipReaderCache *jarCache = gJarHandler->JarCache(); 1.360 + if (jarCache) { 1.361 + bool cached = false; 1.362 + rv = jarCache->IsCached(mJarFile, &cached); 1.363 + if (NS_SUCCEEDED(rv) && cached) { 1.364 + // zipcache already has file mmapped: don't open on parent, 1.365 + // just return and proceed to cache hit in CreateJarInput() 1.366 + return NS_OK; 1.367 + } 1.368 + } 1.369 + 1.370 + mOpeningRemote = true; 1.371 + 1.372 + if (gJarHandler->RemoteOpenFileInProgress(remoteFile, this)) { 1.373 + // JarHandler will trigger OnRemoteFileOpen() after the first 1.374 + // request for this file completes and we'll get a JAR cache 1.375 + // hit. 1.376 + return NS_OK; 1.377 + } 1.378 + 1.379 + // Open file on parent: OnRemoteFileOpenComplete called when done 1.380 + nsCOMPtr<nsITabChild> tabChild; 1.381 + NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, tabChild); 1.382 + rv = remoteFile->AsyncRemoteFileOpen(PR_RDONLY, this, tabChild.get()); 1.383 + NS_ENSURE_SUCCESS(rv, rv); 1.384 + } 1.385 + } 1.386 + // try to handle a nested jar 1.387 + if (!mJarFile) { 1.388 + nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(mJarBaseURI); 1.389 + if (jarURI) { 1.390 + nsCOMPtr<nsIFileURL> fileURL; 1.391 + nsCOMPtr<nsIURI> innerJarURI; 1.392 + rv = jarURI->GetJARFile(getter_AddRefs(innerJarURI)); 1.393 + if (NS_SUCCEEDED(rv)) 1.394 + fileURL = do_QueryInterface(innerJarURI); 1.395 + if (fileURL) { 1.396 + fileURL->GetFile(getter_AddRefs(mJarFile)); 1.397 + jarURI->GetJAREntry(mInnerJarEntry); 1.398 + } 1.399 + } 1.400 + } 1.401 + 1.402 + return rv; 1.403 +} 1.404 + 1.405 +nsresult 1.406 +nsJARChannel::OpenLocalFile() 1.407 +{ 1.408 + MOZ_ASSERT(mIsPending); 1.409 + 1.410 + // Local files are always considered safe. 1.411 + mIsUnsafe = false; 1.412 + 1.413 + nsRefPtr<nsJARInputThunk> input; 1.414 + nsresult rv = CreateJarInput(gJarHandler->JarCache(), 1.415 + getter_AddRefs(input)); 1.416 + if (NS_SUCCEEDED(rv)) { 1.417 + // Create input stream pump and call AsyncRead as a block. 1.418 + rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input); 1.419 + if (NS_SUCCEEDED(rv)) 1.420 + rv = mPump->AsyncRead(this, nullptr); 1.421 + } 1.422 + 1.423 + return rv; 1.424 +} 1.425 + 1.426 +void 1.427 +nsJARChannel::NotifyError(nsresult aError) 1.428 +{ 1.429 + MOZ_ASSERT(NS_FAILED(aError)); 1.430 + 1.431 + mStatus = aError; 1.432 + 1.433 + OnStartRequest(nullptr, nullptr); 1.434 + OnStopRequest(nullptr, nullptr, aError); 1.435 +} 1.436 + 1.437 +//----------------------------------------------------------------------------- 1.438 +// nsIRequest 1.439 +//----------------------------------------------------------------------------- 1.440 + 1.441 +NS_IMETHODIMP 1.442 +nsJARChannel::GetName(nsACString &result) 1.443 +{ 1.444 + return mJarURI->GetSpec(result); 1.445 +} 1.446 + 1.447 +NS_IMETHODIMP 1.448 +nsJARChannel::IsPending(bool *result) 1.449 +{ 1.450 + *result = mIsPending; 1.451 + return NS_OK; 1.452 +} 1.453 + 1.454 +NS_IMETHODIMP 1.455 +nsJARChannel::GetStatus(nsresult *status) 1.456 +{ 1.457 + if (mPump && NS_SUCCEEDED(mStatus)) 1.458 + mPump->GetStatus(status); 1.459 + else 1.460 + *status = mStatus; 1.461 + return NS_OK; 1.462 +} 1.463 + 1.464 +NS_IMETHODIMP 1.465 +nsJARChannel::Cancel(nsresult status) 1.466 +{ 1.467 + mStatus = status; 1.468 + if (mPump) 1.469 + return mPump->Cancel(status); 1.470 + 1.471 + NS_ASSERTION(!mIsPending, "need to implement cancel when downloading"); 1.472 + return NS_OK; 1.473 +} 1.474 + 1.475 +NS_IMETHODIMP 1.476 +nsJARChannel::Suspend() 1.477 +{ 1.478 + if (mPump) 1.479 + return mPump->Suspend(); 1.480 + 1.481 + NS_ASSERTION(!mIsPending, "need to implement suspend when downloading"); 1.482 + return NS_OK; 1.483 +} 1.484 + 1.485 +NS_IMETHODIMP 1.486 +nsJARChannel::Resume() 1.487 +{ 1.488 + if (mPump) 1.489 + return mPump->Resume(); 1.490 + 1.491 + NS_ASSERTION(!mIsPending, "need to implement resume when downloading"); 1.492 + return NS_OK; 1.493 +} 1.494 + 1.495 +NS_IMETHODIMP 1.496 +nsJARChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) 1.497 +{ 1.498 + *aLoadFlags = mLoadFlags; 1.499 + return NS_OK; 1.500 +} 1.501 + 1.502 +NS_IMETHODIMP 1.503 +nsJARChannel::SetLoadFlags(nsLoadFlags aLoadFlags) 1.504 +{ 1.505 + mLoadFlags = aLoadFlags; 1.506 + return NS_OK; 1.507 +} 1.508 + 1.509 +NS_IMETHODIMP 1.510 +nsJARChannel::GetLoadGroup(nsILoadGroup **aLoadGroup) 1.511 +{ 1.512 + NS_IF_ADDREF(*aLoadGroup = mLoadGroup); 1.513 + return NS_OK; 1.514 +} 1.515 + 1.516 +NS_IMETHODIMP 1.517 +nsJARChannel::SetLoadGroup(nsILoadGroup *aLoadGroup) 1.518 +{ 1.519 + mLoadGroup = aLoadGroup; 1.520 + return NS_OK; 1.521 +} 1.522 + 1.523 +//----------------------------------------------------------------------------- 1.524 +// nsIChannel 1.525 +//----------------------------------------------------------------------------- 1.526 + 1.527 +NS_IMETHODIMP 1.528 +nsJARChannel::GetOriginalURI(nsIURI **aURI) 1.529 +{ 1.530 + *aURI = mOriginalURI; 1.531 + NS_ADDREF(*aURI); 1.532 + return NS_OK; 1.533 +} 1.534 + 1.535 +NS_IMETHODIMP 1.536 +nsJARChannel::SetOriginalURI(nsIURI *aURI) 1.537 +{ 1.538 + NS_ENSURE_ARG_POINTER(aURI); 1.539 + mOriginalURI = aURI; 1.540 + return NS_OK; 1.541 +} 1.542 + 1.543 +NS_IMETHODIMP 1.544 +nsJARChannel::GetURI(nsIURI **aURI) 1.545 +{ 1.546 + if (mAppURI) { 1.547 + NS_IF_ADDREF(*aURI = mAppURI); 1.548 + } else { 1.549 + NS_IF_ADDREF(*aURI = mJarURI); 1.550 + } 1.551 + 1.552 + return NS_OK; 1.553 +} 1.554 + 1.555 +NS_IMETHODIMP 1.556 +nsJARChannel::GetOwner(nsISupports **aOwner) 1.557 +{ 1.558 + // JAR signatures are not processed to avoid main-thread network I/O (bug 726125) 1.559 + *aOwner = mOwner; 1.560 + NS_IF_ADDREF(*aOwner); 1.561 + return NS_OK; 1.562 +} 1.563 + 1.564 +NS_IMETHODIMP 1.565 +nsJARChannel::SetOwner(nsISupports *aOwner) 1.566 +{ 1.567 + mOwner = aOwner; 1.568 + return NS_OK; 1.569 +} 1.570 + 1.571 +NS_IMETHODIMP 1.572 +nsJARChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks) 1.573 +{ 1.574 + NS_IF_ADDREF(*aCallbacks = mCallbacks); 1.575 + return NS_OK; 1.576 +} 1.577 + 1.578 +NS_IMETHODIMP 1.579 +nsJARChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks) 1.580 +{ 1.581 + mCallbacks = aCallbacks; 1.582 + return NS_OK; 1.583 +} 1.584 + 1.585 +NS_IMETHODIMP 1.586 +nsJARChannel::GetSecurityInfo(nsISupports **aSecurityInfo) 1.587 +{ 1.588 + NS_PRECONDITION(aSecurityInfo, "Null out param"); 1.589 + NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo); 1.590 + return NS_OK; 1.591 +} 1.592 + 1.593 +NS_IMETHODIMP 1.594 +nsJARChannel::GetContentType(nsACString &result) 1.595 +{ 1.596 + // If the Jar file has not been open yet, 1.597 + // We return application/x-unknown-content-type 1.598 + if (!mOpened) { 1.599 + result.Assign(UNKNOWN_CONTENT_TYPE); 1.600 + return NS_OK; 1.601 + } 1.602 + 1.603 + if (mContentType.IsEmpty()) { 1.604 + 1.605 + // 1.606 + // generate content type and set it 1.607 + // 1.608 + const char *ext = nullptr, *fileName = mJarEntry.get(); 1.609 + int32_t len = mJarEntry.Length(); 1.610 + 1.611 + // check if we're displaying a directory 1.612 + // mJarEntry will be empty if we're trying to display 1.613 + // the topmost directory in a zip, e.g. jar:foo.zip!/ 1.614 + if (ENTRY_IS_DIRECTORY(mJarEntry)) { 1.615 + mContentType.AssignLiteral(APPLICATION_HTTP_INDEX_FORMAT); 1.616 + } 1.617 + else { 1.618 + // not a directory, take a guess by its extension 1.619 + for (int32_t i = len-1; i >= 0; i--) { 1.620 + if (fileName[i] == '.') { 1.621 + ext = &fileName[i + 1]; 1.622 + break; 1.623 + } 1.624 + } 1.625 + if (ext) { 1.626 + nsIMIMEService *mimeServ = gJarHandler->MimeService(); 1.627 + if (mimeServ) 1.628 + mimeServ->GetTypeFromExtension(nsDependentCString(ext), mContentType); 1.629 + } 1.630 + if (mContentType.IsEmpty()) 1.631 + mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE); 1.632 + } 1.633 + } 1.634 + result = mContentType; 1.635 + return NS_OK; 1.636 +} 1.637 + 1.638 +NS_IMETHODIMP 1.639 +nsJARChannel::SetContentType(const nsACString &aContentType) 1.640 +{ 1.641 + // If someone gives us a type hint we should just use that type instead of 1.642 + // doing our guessing. So we don't care when this is being called. 1.643 + 1.644 + // mContentCharset is unchanged if not parsed 1.645 + NS_ParseContentType(aContentType, mContentType, mContentCharset); 1.646 + return NS_OK; 1.647 +} 1.648 + 1.649 +NS_IMETHODIMP 1.650 +nsJARChannel::GetContentCharset(nsACString &aContentCharset) 1.651 +{ 1.652 + // If someone gives us a charset hint we should just use that charset. 1.653 + // So we don't care when this is being called. 1.654 + aContentCharset = mContentCharset; 1.655 + return NS_OK; 1.656 +} 1.657 + 1.658 +NS_IMETHODIMP 1.659 +nsJARChannel::SetContentCharset(const nsACString &aContentCharset) 1.660 +{ 1.661 + mContentCharset = aContentCharset; 1.662 + return NS_OK; 1.663 +} 1.664 + 1.665 +NS_IMETHODIMP 1.666 +nsJARChannel::GetContentDisposition(uint32_t *aContentDisposition) 1.667 +{ 1.668 + if (mContentDispositionHeader.IsEmpty()) 1.669 + return NS_ERROR_NOT_AVAILABLE; 1.670 + 1.671 + *aContentDisposition = mContentDisposition; 1.672 + return NS_OK; 1.673 +} 1.674 + 1.675 +NS_IMETHODIMP 1.676 +nsJARChannel::SetContentDisposition(uint32_t aContentDisposition) 1.677 +{ 1.678 + return NS_ERROR_NOT_AVAILABLE; 1.679 +} 1.680 + 1.681 +NS_IMETHODIMP 1.682 +nsJARChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename) 1.683 +{ 1.684 + return NS_ERROR_NOT_AVAILABLE; 1.685 +} 1.686 + 1.687 +NS_IMETHODIMP 1.688 +nsJARChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename) 1.689 +{ 1.690 + return NS_ERROR_NOT_AVAILABLE; 1.691 +} 1.692 + 1.693 +NS_IMETHODIMP 1.694 +nsJARChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader) 1.695 +{ 1.696 + if (mContentDispositionHeader.IsEmpty()) 1.697 + return NS_ERROR_NOT_AVAILABLE; 1.698 + 1.699 + aContentDispositionHeader = mContentDispositionHeader; 1.700 + return NS_OK; 1.701 +} 1.702 + 1.703 +NS_IMETHODIMP 1.704 +nsJARChannel::GetContentLength(int64_t *result) 1.705 +{ 1.706 + *result = mContentLength; 1.707 + return NS_OK; 1.708 +} 1.709 + 1.710 +NS_IMETHODIMP 1.711 +nsJARChannel::SetContentLength(int64_t aContentLength) 1.712 +{ 1.713 + // XXX does this really make any sense at all? 1.714 + mContentLength = aContentLength; 1.715 + return NS_OK; 1.716 +} 1.717 + 1.718 +NS_IMETHODIMP 1.719 +nsJARChannel::Open(nsIInputStream **stream) 1.720 +{ 1.721 + LOG(("nsJARChannel::Open [this=%x]\n", this)); 1.722 + 1.723 + NS_ENSURE_TRUE(!mOpened, NS_ERROR_IN_PROGRESS); 1.724 + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); 1.725 + 1.726 + mJarFile = nullptr; 1.727 + mIsUnsafe = true; 1.728 + 1.729 + nsresult rv = LookupFile(); 1.730 + if (NS_FAILED(rv)) 1.731 + return rv; 1.732 + 1.733 + // If mJarInput was not set by LookupFile, the JAR is a remote jar. 1.734 + if (!mJarFile) { 1.735 + NS_NOTREACHED("need sync downloader"); 1.736 + return NS_ERROR_NOT_IMPLEMENTED; 1.737 + } 1.738 + 1.739 + nsRefPtr<nsJARInputThunk> input; 1.740 + rv = CreateJarInput(gJarHandler->JarCache(), getter_AddRefs(input)); 1.741 + if (NS_FAILED(rv)) 1.742 + return rv; 1.743 + 1.744 + input.forget(stream); 1.745 + mOpened = true; 1.746 + // local files are always considered safe 1.747 + mIsUnsafe = false; 1.748 + return NS_OK; 1.749 +} 1.750 + 1.751 +NS_IMETHODIMP 1.752 +nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx) 1.753 +{ 1.754 + LOG(("nsJARChannel::AsyncOpen [this=%x]\n", this)); 1.755 + 1.756 + NS_ENSURE_ARG_POINTER(listener); 1.757 + NS_ENSURE_TRUE(!mOpened, NS_ERROR_IN_PROGRESS); 1.758 + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); 1.759 + 1.760 + mJarFile = nullptr; 1.761 + mIsUnsafe = true; 1.762 + 1.763 + // Initialize mProgressSink 1.764 + NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink); 1.765 + 1.766 + nsresult rv = LookupFile(); 1.767 + if (NS_FAILED(rv)) 1.768 + return rv; 1.769 + 1.770 + // These variables must only be set if we're going to trigger an 1.771 + // OnStartRequest, either from AsyncRead or OnDownloadComplete. 1.772 + // 1.773 + // That means: Do not add early return statements beyond this point! 1.774 + mListener = listener; 1.775 + mListenerContext = ctx; 1.776 + mIsPending = true; 1.777 + 1.778 + if (!mJarFile) { 1.779 + // Not a local file... 1.780 + // kick off an async download of the base URI... 1.781 + rv = NS_NewDownloader(getter_AddRefs(mDownloader), this); 1.782 + if (NS_SUCCEEDED(rv)) 1.783 + rv = NS_OpenURI(mDownloader, nullptr, mJarBaseURI, nullptr, 1.784 + mLoadGroup, mCallbacks, 1.785 + mLoadFlags & ~(LOAD_DOCUMENT_URI | LOAD_CALL_CONTENT_SNIFFERS)); 1.786 + } else if (mOpeningRemote) { 1.787 + // nothing to do: already asked parent to open file. 1.788 + } else { 1.789 + rv = OpenLocalFile(); 1.790 + } 1.791 + 1.792 + if (NS_FAILED(rv)) { 1.793 + mIsPending = false; 1.794 + mListenerContext = nullptr; 1.795 + mListener = nullptr; 1.796 + return rv; 1.797 + } 1.798 + 1.799 + 1.800 + if (mLoadGroup) 1.801 + mLoadGroup->AddRequest(this, nullptr); 1.802 + 1.803 + mOpened = true; 1.804 + return NS_OK; 1.805 +} 1.806 + 1.807 +//----------------------------------------------------------------------------- 1.808 +// nsIJARChannel 1.809 +//----------------------------------------------------------------------------- 1.810 +NS_IMETHODIMP 1.811 +nsJARChannel::GetIsUnsafe(bool *isUnsafe) 1.812 +{ 1.813 + *isUnsafe = mIsUnsafe; 1.814 + return NS_OK; 1.815 +} 1.816 + 1.817 +NS_IMETHODIMP 1.818 +nsJARChannel::SetAppURI(nsIURI *aURI) { 1.819 + NS_ENSURE_ARG_POINTER(aURI); 1.820 + 1.821 + nsAutoCString scheme; 1.822 + aURI->GetScheme(scheme); 1.823 + if (!scheme.EqualsLiteral("app")) { 1.824 + return NS_ERROR_INVALID_ARG; 1.825 + } 1.826 + 1.827 + mAppURI = aURI; 1.828 + return NS_OK; 1.829 +} 1.830 + 1.831 +//----------------------------------------------------------------------------- 1.832 +// nsIDownloadObserver 1.833 +//----------------------------------------------------------------------------- 1.834 + 1.835 +NS_IMETHODIMP 1.836 +nsJARChannel::OnDownloadComplete(nsIDownloader *downloader, 1.837 + nsIRequest *request, 1.838 + nsISupports *context, 1.839 + nsresult status, 1.840 + nsIFile *file) 1.841 +{ 1.842 + nsresult rv; 1.843 + 1.844 + nsCOMPtr<nsIChannel> channel(do_QueryInterface(request)); 1.845 + if (channel) { 1.846 + uint32_t loadFlags; 1.847 + channel->GetLoadFlags(&loadFlags); 1.848 + if (loadFlags & LOAD_REPLACE) { 1.849 + mLoadFlags |= LOAD_REPLACE; 1.850 + 1.851 + if (!mOriginalURI) { 1.852 + SetOriginalURI(mJarURI); 1.853 + } 1.854 + 1.855 + nsCOMPtr<nsIURI> innerURI; 1.856 + rv = channel->GetURI(getter_AddRefs(innerURI)); 1.857 + if (NS_SUCCEEDED(rv)) { 1.858 + nsCOMPtr<nsIJARURI> newURI; 1.859 + rv = mJarURI->CloneWithJARFile(innerURI, 1.860 + getter_AddRefs(newURI)); 1.861 + if (NS_SUCCEEDED(rv)) { 1.862 + mJarURI = newURI; 1.863 + } 1.864 + } 1.865 + if (NS_SUCCEEDED(status)) { 1.866 + status = rv; 1.867 + } 1.868 + } 1.869 + } 1.870 + 1.871 + if (NS_SUCCEEDED(status) && channel) { 1.872 + // Grab the security info from our base channel 1.873 + channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo)); 1.874 + 1.875 + nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel)); 1.876 + if (httpChannel) { 1.877 + // We only want to run scripts if the server really intended to 1.878 + // send us a JAR file. Check the server-supplied content type for 1.879 + // a JAR type. 1.880 + nsAutoCString header; 1.881 + httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Type"), 1.882 + header); 1.883 + nsAutoCString contentType; 1.884 + nsAutoCString charset; 1.885 + NS_ParseContentType(header, contentType, charset); 1.886 + nsAutoCString channelContentType; 1.887 + channel->GetContentType(channelContentType); 1.888 + mIsUnsafe = !(contentType.Equals(channelContentType) && 1.889 + (contentType.EqualsLiteral("application/java-archive") || 1.890 + contentType.EqualsLiteral("application/x-jar"))); 1.891 + } else { 1.892 + nsCOMPtr<nsIJARChannel> innerJARChannel(do_QueryInterface(channel)); 1.893 + if (innerJARChannel) { 1.894 + bool unsafe; 1.895 + innerJARChannel->GetIsUnsafe(&unsafe); 1.896 + mIsUnsafe = unsafe; 1.897 + } 1.898 + } 1.899 + 1.900 + channel->GetContentDispositionHeader(mContentDispositionHeader); 1.901 + mContentDisposition = NS_GetContentDispositionFromHeader(mContentDispositionHeader, this); 1.902 + } 1.903 + 1.904 + if (NS_SUCCEEDED(status) && mIsUnsafe && 1.905 + !Preferences::GetBool("network.jar.open-unsafe-types", false)) { 1.906 + status = NS_ERROR_UNSAFE_CONTENT_TYPE; 1.907 + } 1.908 + 1.909 + if (NS_SUCCEEDED(status)) { 1.910 + // Refuse to unpack view-source: jars even if open-unsafe-types is set. 1.911 + nsCOMPtr<nsIViewSourceChannel> viewSource = do_QueryInterface(channel); 1.912 + if (viewSource) { 1.913 + status = NS_ERROR_UNSAFE_CONTENT_TYPE; 1.914 + } 1.915 + } 1.916 + 1.917 + if (NS_SUCCEEDED(status)) { 1.918 + mJarFile = file; 1.919 + 1.920 + nsRefPtr<nsJARInputThunk> input; 1.921 + rv = CreateJarInput(nullptr, getter_AddRefs(input)); 1.922 + if (NS_SUCCEEDED(rv)) { 1.923 + // create input stream pump 1.924 + rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input); 1.925 + if (NS_SUCCEEDED(rv)) 1.926 + rv = mPump->AsyncRead(this, nullptr); 1.927 + } 1.928 + status = rv; 1.929 + } 1.930 + 1.931 + if (NS_FAILED(status)) { 1.932 + NotifyError(status); 1.933 + } 1.934 + 1.935 + return NS_OK; 1.936 +} 1.937 + 1.938 +//----------------------------------------------------------------------------- 1.939 +// nsIRemoteOpenFileListener 1.940 +//----------------------------------------------------------------------------- 1.941 +nsresult 1.942 +nsJARChannel::OnRemoteFileOpenComplete(nsresult aOpenStatus) 1.943 +{ 1.944 + nsresult rv = aOpenStatus; 1.945 + 1.946 + // NS_ERROR_ALREADY_OPENED here means we'll hit JAR cache in 1.947 + // OpenLocalFile(). 1.948 + if (NS_SUCCEEDED(rv) || rv == NS_ERROR_ALREADY_OPENED) { 1.949 + rv = OpenLocalFile(); 1.950 + } 1.951 + 1.952 + if (NS_FAILED(rv)) { 1.953 + NotifyError(rv); 1.954 + } 1.955 + 1.956 + return NS_OK; 1.957 +} 1.958 + 1.959 +//----------------------------------------------------------------------------- 1.960 +// nsIStreamListener 1.961 +//----------------------------------------------------------------------------- 1.962 + 1.963 +NS_IMETHODIMP 1.964 +nsJARChannel::OnStartRequest(nsIRequest *req, nsISupports *ctx) 1.965 +{ 1.966 + LOG(("nsJARChannel::OnStartRequest [this=%x %s]\n", this, mSpec.get())); 1.967 + 1.968 + return mListener->OnStartRequest(this, mListenerContext); 1.969 +} 1.970 + 1.971 +NS_IMETHODIMP 1.972 +nsJARChannel::OnStopRequest(nsIRequest *req, nsISupports *ctx, nsresult status) 1.973 +{ 1.974 + LOG(("nsJARChannel::OnStopRequest [this=%x %s status=%x]\n", 1.975 + this, mSpec.get(), status)); 1.976 + 1.977 + if (NS_SUCCEEDED(mStatus)) 1.978 + mStatus = status; 1.979 + 1.980 + if (mListener) { 1.981 + mListener->OnStopRequest(this, mListenerContext, status); 1.982 + mListener = 0; 1.983 + mListenerContext = 0; 1.984 + } 1.985 + 1.986 + if (mLoadGroup) 1.987 + mLoadGroup->RemoveRequest(this, nullptr, status); 1.988 + 1.989 + mPump = 0; 1.990 + mIsPending = false; 1.991 + mDownloader = 0; // this may delete the underlying jar file 1.992 + 1.993 + // Drop notification callbacks to prevent cycles. 1.994 + mCallbacks = 0; 1.995 + mProgressSink = 0; 1.996 + 1.997 + return NS_OK; 1.998 +} 1.999 + 1.1000 +NS_IMETHODIMP 1.1001 +nsJARChannel::OnDataAvailable(nsIRequest *req, nsISupports *ctx, 1.1002 + nsIInputStream *stream, 1.1003 + uint64_t offset, uint32_t count) 1.1004 +{ 1.1005 +#if defined(PR_LOGGING) 1.1006 + LOG(("nsJARChannel::OnDataAvailable [this=%x %s]\n", this, mSpec.get())); 1.1007 +#endif 1.1008 + 1.1009 + nsresult rv; 1.1010 + 1.1011 + rv = mListener->OnDataAvailable(this, mListenerContext, stream, offset, count); 1.1012 + 1.1013 + // simply report progress here instead of hooking ourselves up as a 1.1014 + // nsITransportEventSink implementation. 1.1015 + // XXX do the 64-bit stuff for real 1.1016 + if (mProgressSink && NS_SUCCEEDED(rv) && !(mLoadFlags & LOAD_BACKGROUND)) 1.1017 + mProgressSink->OnProgress(this, nullptr, offset + count, 1.1018 + uint64_t(mContentLength)); 1.1019 + 1.1020 + return rv; // let the pump cancel on failure 1.1021 +}