/modules/libjar/nsJARChannel.cpp

http://github.com/zpao/v8monkey · C++ · 965 lines · 688 code · 155 blank · 122 comment · 108 complexity · f1fda8c952d7ca20b235fab9334e962c MD5 · raw file

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. *
  3. * ***** BEGIN LICENSE BLOCK *****
  4. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  5. *
  6. * The contents of this file are subject to the Mozilla Public License Version
  7. * 1.1 (the "License"); you may not use this file except in compliance with
  8. * the License. You may obtain a copy of the License at
  9. * http://www.mozilla.org/MPL/
  10. *
  11. * Software distributed under the License is distributed on an "AS IS" basis,
  12. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. * for the specific language governing rights and limitations under the
  14. * License.
  15. *
  16. * The Original Code is mozilla.org Code.
  17. *
  18. * The Initial Developer of the Original Code is
  19. * Netscape Communications Corporation.
  20. * Portions created by the Initial Developer are Copyright (C) 1998
  21. * the Initial Developer. All Rights Reserved.
  22. *
  23. * Contributor(s):
  24. * Jeff Walden <jwalden+code@mit.edu>
  25. *
  26. * Alternatively, the contents of this file may be used under the terms of
  27. * either the GNU General Public License Version 2 or later (the "GPL"), or
  28. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29. * in which case the provisions of the GPL or the LGPL are applicable instead
  30. * of those above. If you wish to allow use of your version of this file only
  31. * under the terms of either the GPL or the LGPL, and not to allow others to
  32. * use your version of this file under the terms of the MPL, indicate your
  33. * decision by deleting the provisions above and replace them with the notice
  34. * and other provisions required by the GPL or the LGPL. If you do not delete
  35. * the provisions above, a recipient may use your version of this file under
  36. * the terms of any one of the MPL, the GPL or the LGPL.
  37. *
  38. * ***** END LICENSE BLOCK ***** */
  39. #include "nsJAR.h"
  40. #include "nsJARChannel.h"
  41. #include "nsJARProtocolHandler.h"
  42. #include "nsMimeTypes.h"
  43. #include "nsNetUtil.h"
  44. #include "nsEscape.h"
  45. #include "nsIPrefService.h"
  46. #include "nsIPrefBranch.h"
  47. #include "nsIViewSourceChannel.h"
  48. #include "nsChannelProperties.h"
  49. #include "nsIScriptSecurityManager.h"
  50. #include "nsIPrincipal.h"
  51. #include "nsIFileURL.h"
  52. #include "mozilla/Preferences.h"
  53. using namespace mozilla;
  54. static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
  55. // the entry for a directory will either be empty (in the case of the
  56. // top-level directory) or will end with a slash
  57. #define ENTRY_IS_DIRECTORY(_entry) \
  58. ((_entry).IsEmpty() || '/' == (_entry).Last())
  59. //-----------------------------------------------------------------------------
  60. #if defined(PR_LOGGING)
  61. //
  62. // set NSPR_LOG_MODULES=nsJarProtocol:5
  63. //
  64. static PRLogModuleInfo *gJarProtocolLog = nsnull;
  65. #endif
  66. #define LOG(args) PR_LOG(gJarProtocolLog, PR_LOG_DEBUG, args)
  67. #define LOG_ENABLED() PR_LOG_TEST(gJarProtocolLog, 4)
  68. //-----------------------------------------------------------------------------
  69. // nsJARInputThunk
  70. //
  71. // this class allows us to do some extra work on the stream transport thread.
  72. //-----------------------------------------------------------------------------
  73. class nsJARInputThunk : public nsIInputStream
  74. {
  75. public:
  76. NS_DECL_ISUPPORTS
  77. NS_DECL_NSIINPUTSTREAM
  78. nsJARInputThunk(nsIZipReader *zipReader,
  79. nsIURI* fullJarURI,
  80. const nsACString &jarEntry,
  81. nsIZipReaderCache *jarCache)
  82. : mJarCache(jarCache)
  83. , mJarReader(zipReader)
  84. , mJarEntry(jarEntry)
  85. , mContentLength(-1)
  86. {
  87. if (fullJarURI) {
  88. #ifdef DEBUG
  89. nsresult rv =
  90. #endif
  91. fullJarURI->GetAsciiSpec(mJarDirSpec);
  92. NS_ASSERTION(NS_SUCCEEDED(rv), "this shouldn't fail");
  93. }
  94. }
  95. virtual ~nsJARInputThunk()
  96. {
  97. if (!mJarCache && mJarReader)
  98. mJarReader->Close();
  99. }
  100. void GetJarReader(nsIZipReader **result)
  101. {
  102. NS_IF_ADDREF(*result = mJarReader);
  103. }
  104. PRInt32 GetContentLength()
  105. {
  106. return mContentLength;
  107. }
  108. nsresult EnsureJarStream();
  109. private:
  110. nsCOMPtr<nsIZipReaderCache> mJarCache;
  111. nsCOMPtr<nsIZipReader> mJarReader;
  112. nsCString mJarDirSpec;
  113. nsCOMPtr<nsIInputStream> mJarStream;
  114. nsCString mJarEntry;
  115. PRInt32 mContentLength;
  116. };
  117. NS_IMPL_THREADSAFE_ISUPPORTS1(nsJARInputThunk, nsIInputStream)
  118. nsresult
  119. nsJARInputThunk::EnsureJarStream()
  120. {
  121. if (mJarStream)
  122. return NS_OK;
  123. nsresult rv;
  124. if (ENTRY_IS_DIRECTORY(mJarEntry)) {
  125. // A directory stream also needs the Spec of the FullJarURI
  126. // because is included in the stream data itself.
  127. NS_ENSURE_STATE(!mJarDirSpec.IsEmpty());
  128. rv = mJarReader->GetInputStreamWithSpec(mJarDirSpec,
  129. mJarEntry,
  130. getter_AddRefs(mJarStream));
  131. }
  132. else {
  133. rv = mJarReader->GetInputStream(mJarEntry,
  134. getter_AddRefs(mJarStream));
  135. }
  136. if (NS_FAILED(rv)) {
  137. // convert to the proper result if the entry wasn't found
  138. // so that error pages work
  139. if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
  140. rv = NS_ERROR_FILE_NOT_FOUND;
  141. return rv;
  142. }
  143. // ask the JarStream for the content length
  144. rv = mJarStream->Available((PRUint32 *) &mContentLength);
  145. if (NS_FAILED(rv)) return rv;
  146. return NS_OK;
  147. }
  148. NS_IMETHODIMP
  149. nsJARInputThunk::Close()
  150. {
  151. if (mJarStream)
  152. return mJarStream->Close();
  153. return NS_OK;
  154. }
  155. NS_IMETHODIMP
  156. nsJARInputThunk::Available(PRUint32 *avail)
  157. {
  158. nsresult rv = EnsureJarStream();
  159. if (NS_FAILED(rv)) return rv;
  160. return mJarStream->Available(avail);
  161. }
  162. NS_IMETHODIMP
  163. nsJARInputThunk::Read(char *buf, PRUint32 count, PRUint32 *countRead)
  164. {
  165. nsresult rv = EnsureJarStream();
  166. if (NS_FAILED(rv)) return rv;
  167. return mJarStream->Read(buf, count, countRead);
  168. }
  169. NS_IMETHODIMP
  170. nsJARInputThunk::ReadSegments(nsWriteSegmentFun writer, void *closure,
  171. PRUint32 count, PRUint32 *countRead)
  172. {
  173. // stream transport does only calls Read()
  174. return NS_ERROR_NOT_IMPLEMENTED;
  175. }
  176. NS_IMETHODIMP
  177. nsJARInputThunk::IsNonBlocking(bool *nonBlocking)
  178. {
  179. *nonBlocking = false;
  180. return NS_OK;
  181. }
  182. //-----------------------------------------------------------------------------
  183. // nsJARChannel
  184. //-----------------------------------------------------------------------------
  185. nsJARChannel::nsJARChannel()
  186. : mContentLength(-1)
  187. , mLoadFlags(LOAD_NORMAL)
  188. , mStatus(NS_OK)
  189. , mIsPending(false)
  190. , mIsUnsafe(true)
  191. , mJarInput(nsnull)
  192. {
  193. #if defined(PR_LOGGING)
  194. if (!gJarProtocolLog)
  195. gJarProtocolLog = PR_NewLogModule("nsJarProtocol");
  196. #endif
  197. // hold an owning reference to the jar handler
  198. NS_ADDREF(gJarHandler);
  199. }
  200. nsJARChannel::~nsJARChannel()
  201. {
  202. // with the exception of certain error cases mJarInput will already be null.
  203. NS_IF_RELEASE(mJarInput);
  204. // release owning reference to the jar handler
  205. nsJARProtocolHandler *handler = gJarHandler;
  206. NS_RELEASE(handler); // NULL parameter
  207. }
  208. NS_IMPL_ISUPPORTS_INHERITED6(nsJARChannel,
  209. nsHashPropertyBag,
  210. nsIRequest,
  211. nsIChannel,
  212. nsIStreamListener,
  213. nsIRequestObserver,
  214. nsIDownloadObserver,
  215. nsIJARChannel)
  216. nsresult
  217. nsJARChannel::Init(nsIURI *uri)
  218. {
  219. nsresult rv;
  220. rv = nsHashPropertyBag::Init();
  221. if (NS_FAILED(rv))
  222. return rv;
  223. mJarURI = do_QueryInterface(uri, &rv);
  224. if (NS_FAILED(rv))
  225. return rv;
  226. mOriginalURI = mJarURI;
  227. // Prevent loading jar:javascript URIs (see bug 290982).
  228. nsCOMPtr<nsIURI> innerURI;
  229. rv = mJarURI->GetJARFile(getter_AddRefs(innerURI));
  230. if (NS_FAILED(rv))
  231. return rv;
  232. bool isJS;
  233. rv = innerURI->SchemeIs("javascript", &isJS);
  234. if (NS_FAILED(rv))
  235. return rv;
  236. if (isJS) {
  237. NS_WARNING("blocking jar:javascript:");
  238. return NS_ERROR_INVALID_ARG;
  239. }
  240. #if defined(PR_LOGGING)
  241. mJarURI->GetSpec(mSpec);
  242. #endif
  243. return rv;
  244. }
  245. nsresult
  246. nsJARChannel::CreateJarInput(nsIZipReaderCache *jarCache)
  247. {
  248. // important to pass a clone of the file since the nsIFile impl is not
  249. // necessarily MT-safe
  250. nsCOMPtr<nsIFile> clonedFile;
  251. nsresult rv = mJarFile->Clone(getter_AddRefs(clonedFile));
  252. if (NS_FAILED(rv))
  253. return rv;
  254. nsCOMPtr<nsIZipReader> reader;
  255. if (jarCache) {
  256. if (mInnerJarEntry.IsEmpty())
  257. rv = jarCache->GetZip(mJarFile, getter_AddRefs(reader));
  258. else
  259. rv = jarCache->GetInnerZip(mJarFile, mInnerJarEntry,
  260. getter_AddRefs(reader));
  261. } else {
  262. // create an uncached jar reader
  263. nsCOMPtr<nsIZipReader> outerReader = do_CreateInstance(kZipReaderCID, &rv);
  264. if (NS_FAILED(rv))
  265. return rv;
  266. rv = outerReader->Open(mJarFile);
  267. if (NS_FAILED(rv))
  268. return rv;
  269. if (mInnerJarEntry.IsEmpty())
  270. reader = outerReader;
  271. else {
  272. reader = do_CreateInstance(kZipReaderCID, &rv);
  273. if (NS_FAILED(rv))
  274. return rv;
  275. rv = reader->OpenInner(outerReader, mInnerJarEntry);
  276. }
  277. }
  278. if (NS_FAILED(rv))
  279. return rv;
  280. mJarInput = new nsJARInputThunk(reader, mJarURI, mJarEntry, jarCache);
  281. if (!mJarInput)
  282. return NS_ERROR_OUT_OF_MEMORY;
  283. NS_ADDREF(mJarInput);
  284. return NS_OK;
  285. }
  286. nsresult
  287. nsJARChannel::EnsureJarInput(bool blocking)
  288. {
  289. LOG(("nsJARChannel::EnsureJarInput [this=%x %s]\n", this, mSpec.get()));
  290. nsresult rv;
  291. nsCOMPtr<nsIURI> uri;
  292. rv = mJarURI->GetJARFile(getter_AddRefs(mJarBaseURI));
  293. if (NS_FAILED(rv)) return rv;
  294. rv = mJarURI->GetJAREntry(mJarEntry);
  295. if (NS_FAILED(rv)) return rv;
  296. // The name of the JAR entry must not contain URL-escaped characters:
  297. // we're moving from URL domain to a filename domain here. nsStandardURL
  298. // does basic escaping by default, which breaks reading zipped files which
  299. // have e.g. spaces in their filenames.
  300. NS_UnescapeURL(mJarEntry);
  301. // try to get a nsIFile directly from the url, which will often succeed.
  302. {
  303. nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mJarBaseURI);
  304. if (fileURL)
  305. fileURL->GetFile(getter_AddRefs(mJarFile));
  306. }
  307. // try to handle a nested jar
  308. if (!mJarFile) {
  309. nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(mJarBaseURI);
  310. if (jarURI) {
  311. nsCOMPtr<nsIFileURL> fileURL;
  312. nsCOMPtr<nsIURI> innerJarURI;
  313. rv = jarURI->GetJARFile(getter_AddRefs(innerJarURI));
  314. if (NS_SUCCEEDED(rv))
  315. fileURL = do_QueryInterface(innerJarURI);
  316. if (fileURL) {
  317. fileURL->GetFile(getter_AddRefs(mJarFile));
  318. jarURI->GetJAREntry(mInnerJarEntry);
  319. }
  320. }
  321. }
  322. if (mJarFile) {
  323. mIsUnsafe = false;
  324. // NOTE: we do not need to deal with mSecurityInfo here,
  325. // because we're loading from a local file
  326. rv = CreateJarInput(gJarHandler->JarCache());
  327. }
  328. else if (blocking) {
  329. NS_NOTREACHED("need sync downloader");
  330. rv = NS_ERROR_NOT_IMPLEMENTED;
  331. }
  332. else {
  333. // kick off an async download of the base URI...
  334. rv = NS_NewDownloader(getter_AddRefs(mDownloader), this);
  335. if (NS_SUCCEEDED(rv))
  336. rv = NS_OpenURI(mDownloader, nsnull, mJarBaseURI, nsnull,
  337. mLoadGroup, mCallbacks,
  338. mLoadFlags & ~(LOAD_DOCUMENT_URI | LOAD_CALL_CONTENT_SNIFFERS));
  339. }
  340. return rv;
  341. }
  342. //-----------------------------------------------------------------------------
  343. // nsIRequest
  344. //-----------------------------------------------------------------------------
  345. NS_IMETHODIMP
  346. nsJARChannel::GetName(nsACString &result)
  347. {
  348. return mJarURI->GetSpec(result);
  349. }
  350. NS_IMETHODIMP
  351. nsJARChannel::IsPending(bool *result)
  352. {
  353. *result = mIsPending;
  354. return NS_OK;
  355. }
  356. NS_IMETHODIMP
  357. nsJARChannel::GetStatus(nsresult *status)
  358. {
  359. if (mPump && NS_SUCCEEDED(mStatus))
  360. mPump->GetStatus(status);
  361. else
  362. *status = mStatus;
  363. return NS_OK;
  364. }
  365. NS_IMETHODIMP
  366. nsJARChannel::Cancel(nsresult status)
  367. {
  368. mStatus = status;
  369. if (mPump)
  370. return mPump->Cancel(status);
  371. NS_ASSERTION(!mIsPending, "need to implement cancel when downloading");
  372. return NS_OK;
  373. }
  374. NS_IMETHODIMP
  375. nsJARChannel::Suspend()
  376. {
  377. if (mPump)
  378. return mPump->Suspend();
  379. NS_ASSERTION(!mIsPending, "need to implement suspend when downloading");
  380. return NS_OK;
  381. }
  382. NS_IMETHODIMP
  383. nsJARChannel::Resume()
  384. {
  385. if (mPump)
  386. return mPump->Resume();
  387. NS_ASSERTION(!mIsPending, "need to implement resume when downloading");
  388. return NS_OK;
  389. }
  390. NS_IMETHODIMP
  391. nsJARChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
  392. {
  393. *aLoadFlags = mLoadFlags;
  394. return NS_OK;
  395. }
  396. NS_IMETHODIMP
  397. nsJARChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
  398. {
  399. mLoadFlags = aLoadFlags;
  400. return NS_OK;
  401. }
  402. NS_IMETHODIMP
  403. nsJARChannel::GetLoadGroup(nsILoadGroup **aLoadGroup)
  404. {
  405. NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
  406. return NS_OK;
  407. }
  408. NS_IMETHODIMP
  409. nsJARChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
  410. {
  411. mLoadGroup = aLoadGroup;
  412. return NS_OK;
  413. }
  414. //-----------------------------------------------------------------------------
  415. // nsIChannel
  416. //-----------------------------------------------------------------------------
  417. NS_IMETHODIMP
  418. nsJARChannel::GetOriginalURI(nsIURI **aURI)
  419. {
  420. *aURI = mOriginalURI;
  421. NS_ADDREF(*aURI);
  422. return NS_OK;
  423. }
  424. NS_IMETHODIMP
  425. nsJARChannel::SetOriginalURI(nsIURI *aURI)
  426. {
  427. NS_ENSURE_ARG_POINTER(aURI);
  428. mOriginalURI = aURI;
  429. return NS_OK;
  430. }
  431. NS_IMETHODIMP
  432. nsJARChannel::GetURI(nsIURI **aURI)
  433. {
  434. NS_IF_ADDREF(*aURI = mJarURI);
  435. return NS_OK;
  436. }
  437. NS_IMETHODIMP
  438. nsJARChannel::GetOwner(nsISupports **result)
  439. {
  440. nsresult rv;
  441. if (mOwner) {
  442. NS_ADDREF(*result = mOwner);
  443. return NS_OK;
  444. }
  445. if (!mJarInput) {
  446. *result = nsnull;
  447. return NS_OK;
  448. }
  449. //-- Verify signature, if one is present, and set owner accordingly
  450. nsCOMPtr<nsIZipReader> jarReader;
  451. mJarInput->GetJarReader(getter_AddRefs(jarReader));
  452. if (!jarReader)
  453. return NS_ERROR_NOT_INITIALIZED;
  454. nsCOMPtr<nsIPrincipal> cert;
  455. rv = jarReader->GetCertificatePrincipal(mJarEntry, getter_AddRefs(cert));
  456. if (NS_FAILED(rv)) return rv;
  457. if (cert) {
  458. nsCAutoString certFingerprint;
  459. rv = cert->GetFingerprint(certFingerprint);
  460. if (NS_FAILED(rv)) return rv;
  461. nsCAutoString subjectName;
  462. rv = cert->GetSubjectName(subjectName);
  463. if (NS_FAILED(rv)) return rv;
  464. nsCAutoString prettyName;
  465. rv = cert->GetPrettyName(prettyName);
  466. if (NS_FAILED(rv)) return rv;
  467. nsCOMPtr<nsISupports> certificate;
  468. rv = cert->GetCertificate(getter_AddRefs(certificate));
  469. if (NS_FAILED(rv)) return rv;
  470. nsCOMPtr<nsIScriptSecurityManager> secMan =
  471. do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
  472. if (NS_FAILED(rv)) return rv;
  473. rv = secMan->GetCertificatePrincipal(certFingerprint, subjectName,
  474. prettyName, certificate,
  475. mJarBaseURI,
  476. getter_AddRefs(cert));
  477. if (NS_FAILED(rv)) return rv;
  478. mOwner = do_QueryInterface(cert, &rv);
  479. if (NS_FAILED(rv)) return rv;
  480. NS_ADDREF(*result = mOwner);
  481. }
  482. return NS_OK;
  483. }
  484. NS_IMETHODIMP
  485. nsJARChannel::SetOwner(nsISupports *aOwner)
  486. {
  487. mOwner = aOwner;
  488. return NS_OK;
  489. }
  490. NS_IMETHODIMP
  491. nsJARChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
  492. {
  493. NS_IF_ADDREF(*aCallbacks = mCallbacks);
  494. return NS_OK;
  495. }
  496. NS_IMETHODIMP
  497. nsJARChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
  498. {
  499. mCallbacks = aCallbacks;
  500. return NS_OK;
  501. }
  502. NS_IMETHODIMP
  503. nsJARChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
  504. {
  505. NS_PRECONDITION(aSecurityInfo, "Null out param");
  506. NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
  507. return NS_OK;
  508. }
  509. NS_IMETHODIMP
  510. nsJARChannel::GetContentType(nsACString &result)
  511. {
  512. if (mContentType.IsEmpty()) {
  513. //
  514. // generate content type and set it
  515. //
  516. const char *ext = nsnull, *fileName = mJarEntry.get();
  517. PRInt32 len = mJarEntry.Length();
  518. // check if we're displaying a directory
  519. // mJarEntry will be empty if we're trying to display
  520. // the topmost directory in a zip, e.g. jar:foo.zip!/
  521. if (ENTRY_IS_DIRECTORY(mJarEntry)) {
  522. mContentType.AssignLiteral(APPLICATION_HTTP_INDEX_FORMAT);
  523. }
  524. else {
  525. // not a directory, take a guess by its extension
  526. for (PRInt32 i = len-1; i >= 0; i--) {
  527. if (fileName[i] == '.') {
  528. ext = &fileName[i + 1];
  529. break;
  530. }
  531. }
  532. if (ext) {
  533. nsIMIMEService *mimeServ = gJarHandler->MimeService();
  534. if (mimeServ)
  535. mimeServ->GetTypeFromExtension(nsDependentCString(ext), mContentType);
  536. }
  537. if (mContentType.IsEmpty())
  538. mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
  539. }
  540. }
  541. result = mContentType;
  542. return NS_OK;
  543. }
  544. NS_IMETHODIMP
  545. nsJARChannel::SetContentType(const nsACString &aContentType)
  546. {
  547. // If someone gives us a type hint we should just use that type instead of
  548. // doing our guessing. So we don't care when this is being called.
  549. // mContentCharset is unchanged if not parsed
  550. NS_ParseContentType(aContentType, mContentType, mContentCharset);
  551. return NS_OK;
  552. }
  553. NS_IMETHODIMP
  554. nsJARChannel::GetContentCharset(nsACString &aContentCharset)
  555. {
  556. // If someone gives us a charset hint we should just use that charset.
  557. // So we don't care when this is being called.
  558. aContentCharset = mContentCharset;
  559. return NS_OK;
  560. }
  561. NS_IMETHODIMP
  562. nsJARChannel::SetContentCharset(const nsACString &aContentCharset)
  563. {
  564. mContentCharset = aContentCharset;
  565. return NS_OK;
  566. }
  567. NS_IMETHODIMP
  568. nsJARChannel::GetContentDisposition(PRUint32 *aContentDisposition)
  569. {
  570. if (mContentDispositionHeader.IsEmpty())
  571. return NS_ERROR_NOT_AVAILABLE;
  572. *aContentDisposition = mContentDisposition;
  573. return NS_OK;
  574. }
  575. NS_IMETHODIMP
  576. nsJARChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
  577. {
  578. return NS_ERROR_NOT_AVAILABLE;
  579. }
  580. NS_IMETHODIMP
  581. nsJARChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
  582. {
  583. if (mContentDispositionHeader.IsEmpty())
  584. return NS_ERROR_NOT_AVAILABLE;
  585. aContentDispositionHeader = mContentDispositionHeader;
  586. return NS_OK;
  587. }
  588. NS_IMETHODIMP
  589. nsJARChannel::GetContentLength(PRInt32 *result)
  590. {
  591. // if content length is unknown, query mJarInput...
  592. if (mContentLength < 0 && mJarInput)
  593. mContentLength = mJarInput->GetContentLength();
  594. *result = mContentLength;
  595. return NS_OK;
  596. }
  597. NS_IMETHODIMP
  598. nsJARChannel::SetContentLength(PRInt32 aContentLength)
  599. {
  600. // XXX does this really make any sense at all?
  601. mContentLength = aContentLength;
  602. return NS_OK;
  603. }
  604. NS_IMETHODIMP
  605. nsJARChannel::Open(nsIInputStream **stream)
  606. {
  607. LOG(("nsJARChannel::Open [this=%x]\n", this));
  608. NS_ENSURE_TRUE(!mJarInput, NS_ERROR_IN_PROGRESS);
  609. NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
  610. mJarFile = nsnull;
  611. mIsUnsafe = true;
  612. nsresult rv = EnsureJarInput(true);
  613. if (NS_FAILED(rv)) return rv;
  614. if (!mJarInput)
  615. return NS_ERROR_UNEXPECTED;
  616. // force load the jar file now so GetContentLength will return a
  617. // meaningful value once we return.
  618. rv = mJarInput->EnsureJarStream();
  619. if (NS_FAILED(rv)) return rv;
  620. NS_ADDREF(*stream = mJarInput);
  621. return NS_OK;
  622. }
  623. NS_IMETHODIMP
  624. nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
  625. {
  626. LOG(("nsJARChannel::AsyncOpen [this=%x]\n", this));
  627. NS_ENSURE_ARG_POINTER(listener);
  628. NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
  629. mJarFile = nsnull;
  630. mIsUnsafe = true;
  631. // Initialize mProgressSink
  632. NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink);
  633. nsresult rv = EnsureJarInput(false);
  634. if (NS_FAILED(rv)) return rv;
  635. // These variables must only be set if we're going to trigger an
  636. // OnStartRequest, either from AsyncRead or OnDownloadComplete.
  637. mListener = listener;
  638. mListenerContext = ctx;
  639. mIsPending = true;
  640. if (mJarInput) {
  641. // create input stream pump and call AsyncRead as a block
  642. rv = NS_NewInputStreamPump(getter_AddRefs(mPump), mJarInput);
  643. if (NS_SUCCEEDED(rv))
  644. rv = mPump->AsyncRead(this, nsnull);
  645. // If we failed to create the pump or initiate the AsyncRead,
  646. // then we need to clear these variables.
  647. if (NS_FAILED(rv)) {
  648. mIsPending = false;
  649. mListenerContext = nsnull;
  650. mListener = nsnull;
  651. return rv;
  652. }
  653. }
  654. if (mLoadGroup)
  655. mLoadGroup->AddRequest(this, nsnull);
  656. return NS_OK;
  657. }
  658. //-----------------------------------------------------------------------------
  659. // nsIJARChannel
  660. //-----------------------------------------------------------------------------
  661. NS_IMETHODIMP
  662. nsJARChannel::GetIsUnsafe(bool *isUnsafe)
  663. {
  664. *isUnsafe = mIsUnsafe;
  665. return NS_OK;
  666. }
  667. //-----------------------------------------------------------------------------
  668. // nsIDownloadObserver
  669. //-----------------------------------------------------------------------------
  670. NS_IMETHODIMP
  671. nsJARChannel::OnDownloadComplete(nsIDownloader *downloader,
  672. nsIRequest *request,
  673. nsISupports *context,
  674. nsresult status,
  675. nsIFile *file)
  676. {
  677. nsresult rv;
  678. nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
  679. if (channel) {
  680. PRUint32 loadFlags;
  681. channel->GetLoadFlags(&loadFlags);
  682. if (loadFlags & LOAD_REPLACE) {
  683. mLoadFlags |= LOAD_REPLACE;
  684. if (!mOriginalURI) {
  685. SetOriginalURI(mJarURI);
  686. }
  687. nsCOMPtr<nsIURI> innerURI;
  688. rv = channel->GetURI(getter_AddRefs(innerURI));
  689. if (NS_SUCCEEDED(rv)) {
  690. nsCOMPtr<nsIJARURI> newURI;
  691. rv = mJarURI->CloneWithJARFile(innerURI,
  692. getter_AddRefs(newURI));
  693. if (NS_SUCCEEDED(rv)) {
  694. mJarURI = newURI;
  695. }
  696. }
  697. if (NS_SUCCEEDED(status)) {
  698. status = rv;
  699. }
  700. }
  701. }
  702. if (NS_SUCCEEDED(status) && channel) {
  703. // Grab the security info from our base channel
  704. channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
  705. nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
  706. if (httpChannel) {
  707. // We only want to run scripts if the server really intended to
  708. // send us a JAR file. Check the server-supplied content type for
  709. // a JAR type.
  710. nsCAutoString header;
  711. httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Type"),
  712. header);
  713. nsCAutoString contentType;
  714. nsCAutoString charset;
  715. NS_ParseContentType(header, contentType, charset);
  716. nsCAutoString channelContentType;
  717. channel->GetContentType(channelContentType);
  718. mIsUnsafe = !(contentType.Equals(channelContentType) &&
  719. (contentType.EqualsLiteral("application/java-archive") ||
  720. contentType.EqualsLiteral("application/x-jar")));
  721. } else {
  722. nsCOMPtr<nsIJARChannel> innerJARChannel(do_QueryInterface(channel));
  723. if (innerJARChannel) {
  724. bool unsafe;
  725. innerJARChannel->GetIsUnsafe(&unsafe);
  726. mIsUnsafe = unsafe;
  727. }
  728. }
  729. channel->GetContentDispositionHeader(mContentDispositionHeader);
  730. mContentDisposition = NS_GetContentDispositionFromHeader(mContentDispositionHeader, this);
  731. }
  732. if (NS_SUCCEEDED(status) && mIsUnsafe &&
  733. !Preferences::GetBool("network.jar.open-unsafe-types", false)) {
  734. status = NS_ERROR_UNSAFE_CONTENT_TYPE;
  735. }
  736. if (NS_SUCCEEDED(status)) {
  737. // Refuse to unpack view-source: jars even if open-unsafe-types is set.
  738. nsCOMPtr<nsIViewSourceChannel> viewSource = do_QueryInterface(channel);
  739. if (viewSource) {
  740. status = NS_ERROR_UNSAFE_CONTENT_TYPE;
  741. }
  742. }
  743. if (NS_SUCCEEDED(status)) {
  744. mJarFile = file;
  745. rv = CreateJarInput(nsnull);
  746. if (NS_SUCCEEDED(rv)) {
  747. // create input stream pump
  748. rv = NS_NewInputStreamPump(getter_AddRefs(mPump), mJarInput);
  749. if (NS_SUCCEEDED(rv))
  750. rv = mPump->AsyncRead(this, nsnull);
  751. }
  752. status = rv;
  753. }
  754. if (NS_FAILED(status)) {
  755. mStatus = status;
  756. OnStartRequest(nsnull, nsnull);
  757. OnStopRequest(nsnull, nsnull, status);
  758. }
  759. return NS_OK;
  760. }
  761. //-----------------------------------------------------------------------------
  762. // nsIStreamListener
  763. //-----------------------------------------------------------------------------
  764. NS_IMETHODIMP
  765. nsJARChannel::OnStartRequest(nsIRequest *req, nsISupports *ctx)
  766. {
  767. LOG(("nsJARChannel::OnStartRequest [this=%x %s]\n", this, mSpec.get()));
  768. return mListener->OnStartRequest(this, mListenerContext);
  769. }
  770. NS_IMETHODIMP
  771. nsJARChannel::OnStopRequest(nsIRequest *req, nsISupports *ctx, nsresult status)
  772. {
  773. LOG(("nsJARChannel::OnStopRequest [this=%x %s status=%x]\n",
  774. this, mSpec.get(), status));
  775. if (NS_SUCCEEDED(mStatus))
  776. mStatus = status;
  777. if (mListener) {
  778. mListener->OnStopRequest(this, mListenerContext, status);
  779. mListener = 0;
  780. mListenerContext = 0;
  781. }
  782. if (mLoadGroup)
  783. mLoadGroup->RemoveRequest(this, nsnull, status);
  784. mPump = 0;
  785. NS_IF_RELEASE(mJarInput);
  786. mIsPending = false;
  787. mDownloader = 0; // this may delete the underlying jar file
  788. // Drop notification callbacks to prevent cycles.
  789. mCallbacks = 0;
  790. mProgressSink = 0;
  791. return NS_OK;
  792. }
  793. NS_IMETHODIMP
  794. nsJARChannel::OnDataAvailable(nsIRequest *req, nsISupports *ctx,
  795. nsIInputStream *stream,
  796. PRUint32 offset, PRUint32 count)
  797. {
  798. #if defined(PR_LOGGING)
  799. LOG(("nsJARChannel::OnDataAvailable [this=%x %s]\n", this, mSpec.get()));
  800. #endif
  801. nsresult rv;
  802. rv = mListener->OnDataAvailable(this, mListenerContext, stream, offset, count);
  803. // simply report progress here instead of hooking ourselves up as a
  804. // nsITransportEventSink implementation.
  805. // XXX do the 64-bit stuff for real
  806. if (mProgressSink && NS_SUCCEEDED(rv) && !(mLoadFlags & LOAD_BACKGROUND))
  807. mProgressSink->OnProgress(this, nsnull, PRUint64(offset + count),
  808. PRUint64(mContentLength));
  809. return rv; // let the pump cancel on failure
  810. }