nsAboutCacheEntry.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "nsAboutCacheEntry.h"
  6. #include "mozilla/Sprintf.h"
  7. #include "nsAboutCache.h"
  8. #include "nsICacheStorage.h"
  9. #include "CacheObserver.h"
  10. #include "nsNetUtil.h"
  11. #include "nsEscape.h"
  12. #include "nsIAsyncInputStream.h"
  13. #include "nsIAsyncOutputStream.h"
  14. #include "nsAboutProtocolUtils.h"
  15. #include "nsContentUtils.h"
  16. #include "nsInputStreamPump.h"
  17. #include "CacheFileUtils.h"
  18. #include <algorithm>
  19. #include "nsIPipe.h"
  20. using namespace mozilla::net;
  21. #define HEXDUMP_MAX_ROWS 16
  22. static void
  23. HexDump(uint32_t *state, const char *buf, int32_t n, nsCString &result)
  24. {
  25. char temp[16];
  26. const unsigned char *p;
  27. while (n) {
  28. SprintfLiteral(temp, "%08x: ", *state);
  29. result.Append(temp);
  30. *state += HEXDUMP_MAX_ROWS;
  31. p = (const unsigned char *) buf;
  32. int32_t i, row_max = std::min(HEXDUMP_MAX_ROWS, n);
  33. // print hex codes:
  34. for (i = 0; i < row_max; ++i) {
  35. SprintfLiteral(temp, "%02x ", *p++);
  36. result.Append(temp);
  37. }
  38. for (i = row_max; i < HEXDUMP_MAX_ROWS; ++i) {
  39. result.AppendLiteral(" ");
  40. }
  41. // print ASCII glyphs if possible:
  42. p = (const unsigned char *) buf;
  43. for (i = 0; i < row_max; ++i, ++p) {
  44. switch (*p) {
  45. case '<':
  46. result.AppendLiteral("&lt;");
  47. break;
  48. case '>':
  49. result.AppendLiteral("&gt;");
  50. break;
  51. case '&':
  52. result.AppendLiteral("&amp;");
  53. break;
  54. default:
  55. if (*p < 0x7F && *p > 0x1F) {
  56. result.Append(*p);
  57. } else {
  58. result.Append('.');
  59. }
  60. }
  61. }
  62. result.Append('\n');
  63. buf += row_max;
  64. n -= row_max;
  65. }
  66. }
  67. //-----------------------------------------------------------------------------
  68. // nsAboutCacheEntry::nsISupports
  69. NS_IMPL_ISUPPORTS(nsAboutCacheEntry,
  70. nsIAboutModule)
  71. NS_IMPL_ISUPPORTS(nsAboutCacheEntry::Channel,
  72. nsICacheEntryOpenCallback,
  73. nsICacheEntryMetaDataVisitor,
  74. nsIStreamListener,
  75. nsIChannel)
  76. //-----------------------------------------------------------------------------
  77. // nsAboutCacheEntry::nsIAboutModule
  78. NS_IMETHODIMP
  79. nsAboutCacheEntry::NewChannel(nsIURI* uri,
  80. nsILoadInfo* aLoadInfo,
  81. nsIChannel** result)
  82. {
  83. NS_ENSURE_ARG_POINTER(uri);
  84. nsresult rv;
  85. RefPtr<Channel> channel = new Channel();
  86. rv = channel->Init(uri, aLoadInfo);
  87. if (NS_FAILED(rv)) return rv;
  88. channel.forget(result);
  89. return NS_OK;
  90. }
  91. NS_IMETHODIMP
  92. nsAboutCacheEntry::GetURIFlags(nsIURI *aURI, uint32_t *result)
  93. {
  94. *result = nsIAboutModule::HIDE_FROM_ABOUTABOUT |
  95. nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT;
  96. return NS_OK;
  97. }
  98. //-----------------------------------------------------------------------------
  99. // nsAboutCacheEntry::Channel
  100. nsresult
  101. nsAboutCacheEntry::Channel::Init(nsIURI* uri, nsILoadInfo* aLoadInfo)
  102. {
  103. nsresult rv;
  104. nsCOMPtr<nsIInputStream> stream;
  105. rv = GetContentStream(uri, getter_AddRefs(stream));
  106. if (NS_FAILED(rv)) return rv;
  107. rv = NS_NewInputStreamChannelInternal(getter_AddRefs(mChannel),
  108. uri,
  109. stream,
  110. NS_LITERAL_CSTRING("text/html"),
  111. NS_LITERAL_CSTRING("utf-8"),
  112. aLoadInfo);
  113. if (NS_FAILED(rv)) return rv;
  114. return NS_OK;
  115. }
  116. nsresult
  117. nsAboutCacheEntry::Channel::GetContentStream(nsIURI *uri, nsIInputStream **result)
  118. {
  119. nsresult rv;
  120. // Init: (block size, maximum length)
  121. nsCOMPtr<nsIAsyncInputStream> inputStream;
  122. rv = NS_NewPipe2(getter_AddRefs(inputStream),
  123. getter_AddRefs(mOutputStream),
  124. true, false,
  125. 256, UINT32_MAX);
  126. if (NS_FAILED(rv)) return rv;
  127. NS_NAMED_LITERAL_CSTRING(
  128. buffer,
  129. "<!DOCTYPE html>\n"
  130. "<html>\n"
  131. "<head>\n"
  132. " <title>Cache entry information</title>\n"
  133. " <link rel=\"stylesheet\" "
  134. "href=\"chrome://global/skin/about.css\" type=\"text/css\"/>\n"
  135. " <link rel=\"stylesheet\" "
  136. "href=\"chrome://global/skin/aboutCacheEntry.css\" type=\"text/css\"/>\n"
  137. "</head>\n"
  138. "<body>\n"
  139. "<h1>Cache entry information</h1>\n");
  140. uint32_t n;
  141. rv = mOutputStream->Write(buffer.get(), buffer.Length(), &n);
  142. if (NS_FAILED(rv)) return rv;
  143. if (n != buffer.Length()) return NS_ERROR_UNEXPECTED;
  144. rv = OpenCacheEntry(uri);
  145. if (NS_FAILED(rv)) return rv;
  146. inputStream.forget(result);
  147. return NS_OK;
  148. }
  149. nsresult
  150. nsAboutCacheEntry::Channel::OpenCacheEntry(nsIURI *uri)
  151. {
  152. nsresult rv;
  153. rv = ParseURI(uri, mStorageName, getter_AddRefs(mLoadInfo),
  154. mEnhanceId, getter_AddRefs(mCacheURI));
  155. if (NS_FAILED(rv)) return rv;
  156. if (!CacheObserver::UseNewCache() &&
  157. mLoadInfo->IsPrivate() &&
  158. mStorageName.EqualsLiteral("disk")) {
  159. // The cache v1 is storing all private entries in the memory-only
  160. // cache, so it would not be found in the v1 disk cache.
  161. mStorageName = NS_LITERAL_CSTRING("memory");
  162. }
  163. return OpenCacheEntry();
  164. }
  165. nsresult
  166. nsAboutCacheEntry::Channel::OpenCacheEntry()
  167. {
  168. nsresult rv;
  169. nsCOMPtr<nsICacheStorage> storage;
  170. rv = nsAboutCache::GetStorage(mStorageName, mLoadInfo, getter_AddRefs(storage));
  171. if (NS_FAILED(rv)) return rv;
  172. // Invokes OnCacheEntryAvailable()
  173. rv = storage->AsyncOpenURI(mCacheURI, mEnhanceId,
  174. nsICacheStorage::OPEN_READONLY |
  175. nsICacheStorage::OPEN_SECRETLY,
  176. this);
  177. if (NS_FAILED(rv)) return rv;
  178. return NS_OK;
  179. }
  180. nsresult
  181. nsAboutCacheEntry::Channel::ParseURI(nsIURI *uri,
  182. nsACString &storageName,
  183. nsILoadContextInfo **loadInfo,
  184. nsCString &enahnceID,
  185. nsIURI **cacheUri)
  186. {
  187. //
  188. // about:cache-entry?storage=[string]&contenxt=[string]&eid=[string]&uri=[string]
  189. //
  190. nsresult rv;
  191. nsAutoCString path;
  192. rv = uri->GetPath(path);
  193. if (NS_FAILED(rv))
  194. return rv;
  195. nsACString::const_iterator keyBegin, keyEnd, valBegin, begin, end;
  196. path.BeginReading(begin);
  197. path.EndReading(end);
  198. keyBegin = begin; keyEnd = end;
  199. if (!FindInReadable(NS_LITERAL_CSTRING("?storage="), keyBegin, keyEnd))
  200. return NS_ERROR_FAILURE;
  201. valBegin = keyEnd; // the value of the storage key starts after the key
  202. keyBegin = keyEnd; keyEnd = end;
  203. if (!FindInReadable(NS_LITERAL_CSTRING("&context="), keyBegin, keyEnd))
  204. return NS_ERROR_FAILURE;
  205. storageName.Assign(Substring(valBegin, keyBegin));
  206. valBegin = keyEnd; // the value of the context key starts after the key
  207. keyBegin = keyEnd; keyEnd = end;
  208. if (!FindInReadable(NS_LITERAL_CSTRING("&eid="), keyBegin, keyEnd))
  209. return NS_ERROR_FAILURE;
  210. nsAutoCString contextKey(Substring(valBegin, keyBegin));
  211. valBegin = keyEnd; // the value of the eid key starts after the key
  212. keyBegin = keyEnd; keyEnd = end;
  213. if (!FindInReadable(NS_LITERAL_CSTRING("&uri="), keyBegin, keyEnd))
  214. return NS_ERROR_FAILURE;
  215. enahnceID.Assign(Substring(valBegin, keyBegin));
  216. valBegin = keyEnd; // the value of the uri key starts after the key
  217. nsAutoCString uriSpec(Substring(valBegin, end)); // uri is the last one
  218. // Uf... parsing done, now get some objects from it...
  219. nsCOMPtr<nsILoadContextInfo> info =
  220. CacheFileUtils::ParseKey(contextKey);
  221. if (!info)
  222. return NS_ERROR_FAILURE;
  223. info.forget(loadInfo);
  224. rv = NS_NewURI(cacheUri, uriSpec);
  225. if (NS_FAILED(rv))
  226. return rv;
  227. return NS_OK;
  228. }
  229. //-----------------------------------------------------------------------------
  230. // nsICacheEntryOpenCallback implementation
  231. //-----------------------------------------------------------------------------
  232. NS_IMETHODIMP
  233. nsAboutCacheEntry::Channel::OnCacheEntryCheck(nsICacheEntry *aEntry,
  234. nsIApplicationCache *aApplicationCache,
  235. uint32_t *result)
  236. {
  237. *result = nsICacheEntryOpenCallback::ENTRY_WANTED;
  238. return NS_OK;
  239. }
  240. NS_IMETHODIMP
  241. nsAboutCacheEntry::Channel::OnCacheEntryAvailable(nsICacheEntry *entry,
  242. bool isNew,
  243. nsIApplicationCache *aApplicationCache,
  244. nsresult status)
  245. {
  246. nsresult rv;
  247. mWaitingForData = false;
  248. if (entry) {
  249. rv = WriteCacheEntryDescription(entry);
  250. } else if (!CacheObserver::UseNewCache() &&
  251. !mLoadInfo->IsPrivate() &&
  252. mStorageName.EqualsLiteral("memory")) {
  253. // If we were not able to find the entry in the memory storage
  254. // try again in the disk storage.
  255. // This is a workaround for cache v1: when an originally disk
  256. // cache entry is recreated as memory-only, it's clientID doesn't
  257. // change and we cannot find it in "HTTP-memory-only" session.
  258. // "Disk" cache storage looks at "HTTP".
  259. mStorageName = NS_LITERAL_CSTRING("disk");
  260. rv = OpenCacheEntry();
  261. if (NS_SUCCEEDED(rv)) {
  262. return NS_OK;
  263. }
  264. } else {
  265. rv = WriteCacheEntryUnavailable();
  266. }
  267. if (NS_FAILED(rv)) return rv;
  268. if (!mWaitingForData) {
  269. // Data is not expected, close the output of content now.
  270. CloseContent();
  271. }
  272. return NS_OK;
  273. }
  274. //-----------------------------------------------------------------------------
  275. // Print-out helper methods
  276. //-----------------------------------------------------------------------------
  277. #define APPEND_ROW(label, value) \
  278. PR_BEGIN_MACRO \
  279. buffer.AppendLiteral(" <tr>\n" \
  280. " <th>"); \
  281. buffer.AppendLiteral(label); \
  282. buffer.AppendLiteral(":</th>\n" \
  283. " <td>"); \
  284. buffer.Append(value); \
  285. buffer.AppendLiteral("</td>\n" \
  286. " </tr>\n"); \
  287. PR_END_MACRO
  288. nsresult
  289. nsAboutCacheEntry::Channel::WriteCacheEntryDescription(nsICacheEntry *entry)
  290. {
  291. nsresult rv;
  292. nsCString buffer;
  293. uint32_t n;
  294. nsAutoCString str;
  295. rv = entry->GetKey(str);
  296. if (NS_FAILED(rv)) return rv;
  297. buffer.SetCapacity(4096);
  298. buffer.AssignLiteral("<table>\n"
  299. " <tr>\n"
  300. " <th>key:</th>\n"
  301. " <td id=\"td-key\">");
  302. // Test if the key is actually a URI
  303. nsCOMPtr<nsIURI> uri;
  304. bool isJS = false;
  305. bool isData = false;
  306. rv = NS_NewURI(getter_AddRefs(uri), str);
  307. // javascript: and data: URLs should not be linkified
  308. // since clicking them can cause scripts to run - bug 162584
  309. if (NS_SUCCEEDED(rv)) {
  310. uri->SchemeIs("javascript", &isJS);
  311. uri->SchemeIs("data", &isData);
  312. }
  313. char* escapedStr = nsEscapeHTML(str.get());
  314. if (NS_SUCCEEDED(rv) && !(isJS || isData)) {
  315. buffer.AppendLiteral("<a href=\"");
  316. buffer.Append(escapedStr);
  317. buffer.AppendLiteral("\">");
  318. buffer.Append(escapedStr);
  319. buffer.AppendLiteral("</a>");
  320. uri = nullptr;
  321. } else {
  322. buffer.Append(escapedStr);
  323. }
  324. free(escapedStr);
  325. buffer.AppendLiteral("</td>\n"
  326. " </tr>\n");
  327. // temp vars for reporting
  328. char timeBuf[255];
  329. uint32_t u = 0;
  330. int32_t i = 0;
  331. nsAutoCString s;
  332. // Fetch Count
  333. s.Truncate();
  334. entry->GetFetchCount(&i);
  335. s.AppendInt(i);
  336. APPEND_ROW("fetch count", s);
  337. // Last Fetched
  338. entry->GetLastFetched(&u);
  339. if (u) {
  340. PrintTimeString(timeBuf, sizeof(timeBuf), u);
  341. APPEND_ROW("last fetched", timeBuf);
  342. } else {
  343. APPEND_ROW("last fetched", "No last fetch time (bug 1000338)");
  344. }
  345. // Last Modified
  346. entry->GetLastModified(&u);
  347. if (u) {
  348. PrintTimeString(timeBuf, sizeof(timeBuf), u);
  349. APPEND_ROW("last modified", timeBuf);
  350. } else {
  351. APPEND_ROW("last modified", "No last modified time (bug 1000338)");
  352. }
  353. // Expiration Time
  354. entry->GetExpirationTime(&u);
  355. if (u < 0xFFFFFFFF) {
  356. PrintTimeString(timeBuf, sizeof(timeBuf), u);
  357. APPEND_ROW("expires", timeBuf);
  358. } else {
  359. APPEND_ROW("expires", "No expiration time");
  360. }
  361. // Data Size
  362. s.Truncate();
  363. uint32_t dataSize;
  364. if (NS_FAILED(entry->GetStorageDataSize(&dataSize)))
  365. dataSize = 0;
  366. s.AppendInt((int32_t)dataSize); // XXX nsICacheEntryInfo interfaces should be fixed.
  367. s.AppendLiteral(" B");
  368. APPEND_ROW("Data size", s);
  369. // TODO - mayhemer
  370. // Here used to be a link to the disk file (in the old cache for entries that
  371. // did not fit any of the block files, in the new cache every time).
  372. // I'd rather have a small set of buttons here to action on the entry:
  373. // 1. save the content
  374. // 2. save as a complete HTTP response (response head, headers, content)
  375. // 3. doom the entry
  376. // A new bug(s) should be filed here.
  377. // Security Info
  378. nsCOMPtr<nsISupports> securityInfo;
  379. entry->GetSecurityInfo(getter_AddRefs(securityInfo));
  380. if (securityInfo) {
  381. APPEND_ROW("Security", "This is a secure document.");
  382. } else {
  383. APPEND_ROW("Security",
  384. "This document does not have any security info associated with it.");
  385. }
  386. buffer.AppendLiteral("</table>\n"
  387. "<hr/>\n"
  388. "<table>\n");
  389. mBuffer = &buffer; // make it available for OnMetaDataElement().
  390. entry->VisitMetaData(this);
  391. mBuffer = nullptr;
  392. buffer.AppendLiteral("</table>\n");
  393. mOutputStream->Write(buffer.get(), buffer.Length(), &n);
  394. buffer.Truncate();
  395. // Provide a hexdump of the data
  396. if (!dataSize) {
  397. return NS_OK;
  398. }
  399. nsCOMPtr<nsIInputStream> stream;
  400. entry->OpenInputStream(0, getter_AddRefs(stream));
  401. if (!stream) {
  402. return NS_OK;
  403. }
  404. RefPtr<nsInputStreamPump> pump;
  405. rv = nsInputStreamPump::Create(getter_AddRefs(pump), stream);
  406. if (NS_FAILED(rv)) {
  407. return NS_OK; // just ignore
  408. }
  409. rv = pump->AsyncRead(this, nullptr);
  410. if (NS_FAILED(rv)) {
  411. return NS_OK; // just ignore
  412. }
  413. mWaitingForData = true;
  414. return NS_OK;
  415. }
  416. nsresult
  417. nsAboutCacheEntry::Channel::WriteCacheEntryUnavailable()
  418. {
  419. uint32_t n;
  420. NS_NAMED_LITERAL_CSTRING(buffer,
  421. "The cache entry you selected is not available.");
  422. mOutputStream->Write(buffer.get(), buffer.Length(), &n);
  423. return NS_OK;
  424. }
  425. //-----------------------------------------------------------------------------
  426. // nsICacheEntryMetaDataVisitor implementation
  427. //-----------------------------------------------------------------------------
  428. NS_IMETHODIMP
  429. nsAboutCacheEntry::Channel::OnMetaDataElement(char const * key, char const * value)
  430. {
  431. mBuffer->AppendLiteral(" <tr>\n"
  432. " <th>");
  433. mBuffer->Append(key);
  434. mBuffer->AppendLiteral(":</th>\n"
  435. " <td>");
  436. char* escapedValue = nsEscapeHTML(value);
  437. mBuffer->Append(escapedValue);
  438. free(escapedValue);
  439. mBuffer->AppendLiteral("</td>\n"
  440. " </tr>\n");
  441. return NS_OK;
  442. }
  443. //-----------------------------------------------------------------------------
  444. // nsIStreamListener implementation
  445. //-----------------------------------------------------------------------------
  446. NS_IMETHODIMP
  447. nsAboutCacheEntry::Channel::OnStartRequest(nsIRequest *request, nsISupports *ctx)
  448. {
  449. mHexDumpState = 0;
  450. NS_NAMED_LITERAL_CSTRING(buffer, "<hr/>\n<pre>");
  451. uint32_t n;
  452. return mOutputStream->Write(buffer.get(), buffer.Length(), &n);
  453. }
  454. NS_IMETHODIMP
  455. nsAboutCacheEntry::Channel::OnDataAvailable(nsIRequest *request, nsISupports *ctx,
  456. nsIInputStream *aInputStream,
  457. uint64_t aOffset,
  458. uint32_t aCount)
  459. {
  460. uint32_t n;
  461. return aInputStream->ReadSegments(
  462. &nsAboutCacheEntry::Channel::PrintCacheData, this, aCount, &n);
  463. }
  464. /* static */ nsresult
  465. nsAboutCacheEntry::Channel::PrintCacheData(nsIInputStream *aInStream,
  466. void *aClosure,
  467. const char *aFromSegment,
  468. uint32_t aToOffset,
  469. uint32_t aCount,
  470. uint32_t *aWriteCount)
  471. {
  472. nsAboutCacheEntry::Channel *a =
  473. static_cast<nsAboutCacheEntry::Channel*>(aClosure);
  474. nsCString buffer;
  475. HexDump(&a->mHexDumpState, aFromSegment, aCount, buffer);
  476. uint32_t n;
  477. a->mOutputStream->Write(buffer.get(), buffer.Length(), &n);
  478. *aWriteCount = aCount;
  479. return NS_OK;
  480. }
  481. NS_IMETHODIMP
  482. nsAboutCacheEntry::Channel::OnStopRequest(nsIRequest *request, nsISupports *ctx,
  483. nsresult result)
  484. {
  485. NS_NAMED_LITERAL_CSTRING(buffer, "</pre>\n");
  486. uint32_t n;
  487. mOutputStream->Write(buffer.get(), buffer.Length(), &n);
  488. CloseContent();
  489. return NS_OK;
  490. }
  491. void
  492. nsAboutCacheEntry::Channel::CloseContent()
  493. {
  494. NS_NAMED_LITERAL_CSTRING(buffer, "</body>\n</html>\n");
  495. uint32_t n;
  496. mOutputStream->Write(buffer.get(), buffer.Length(), &n);
  497. mOutputStream->Close();
  498. mOutputStream = nullptr;
  499. }