modules/libjar/nsJARChannel.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial