tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsAboutCache.cpp (14879B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      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 
      6 #include "nsAboutCache.h"
      7 #include "nsIInputStream.h"
      8 #include "nsIURI.h"
      9 #include "nsCOMPtr.h"
     10 #include "nsNetUtil.h"
     11 #include "nsIPipe.h"
     12 #include "nsContentUtils.h"
     13 #include "nsEscape.h"
     14 #include "nsAboutProtocolUtils.h"
     15 #include "nsPrintfCString.h"
     16 
     17 #include "nsICacheStorageService.h"
     18 #include "nsICacheStorage.h"
     19 #include "CacheFileUtils.h"
     20 #include "CacheObserver.h"
     21 
     22 #include "nsThreadUtils.h"
     23 
     24 #include "mozilla/Components.h"
     25 
     26 using namespace mozilla::net;
     27 
     28 NS_IMPL_ISUPPORTS(nsAboutCache, nsIAboutModule)
     29 NS_IMPL_ISUPPORTS(nsAboutCache::Channel, nsIChannel, nsIRequest,
     30                  nsICacheStorageVisitor)
     31 
     32 NS_IMETHODIMP
     33 nsAboutCache::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo,
     34                         nsIChannel** result) {
     35  nsresult rv;
     36 
     37  NS_ENSURE_ARG_POINTER(aURI);
     38 
     39  RefPtr<Channel> channel = new Channel();
     40  rv = channel->Init(aURI, aLoadInfo);
     41  if (NS_FAILED(rv)) return rv;
     42 
     43  channel.forget(result);
     44 
     45  return NS_OK;
     46 }
     47 
     48 nsresult nsAboutCache::Channel::Init(nsIURI* aURI, nsILoadInfo* aLoadInfo) {
     49  nsresult rv;
     50 
     51  mCancel = false;
     52 
     53  nsCOMPtr<nsIInputStream> inputStream;
     54  NS_NewPipe(getter_AddRefs(inputStream), getter_AddRefs(mStream), 16384,
     55             (uint32_t)-1,
     56             true,  // non-blocking input
     57             false  // blocking output
     58  );
     59 
     60  nsAutoCString storageName;
     61  rv = ParseURI(aURI, storageName);
     62  if (NS_FAILED(rv)) return rv;
     63 
     64  mOverview = storageName.IsEmpty();
     65  if (mOverview) {
     66    // ...and visit all we can
     67    mStorageList.AppendElement("memory"_ns);
     68    mStorageList.AppendElement("disk"_ns);
     69  } else {
     70    // ...and visit just the specified storage, entries will output too
     71    mStorageList.AppendElement(storageName);
     72  }
     73 
     74  // The entries header is added on encounter of the first entry
     75  mEntriesHeaderAdded = false;
     76 
     77  rv = NS_NewInputStreamChannelInternal(getter_AddRefs(mChannel), aURI,
     78                                        inputStream.forget(), "text/html"_ns,
     79                                        "utf-8"_ns, aLoadInfo);
     80  if (NS_FAILED(rv)) return rv;
     81 
     82  mBuffer.AssignLiteral(
     83      "<!DOCTYPE html>\n"
     84      "<html>\n"
     85      "<head>\n"
     86      "  <title>Network Cache Storage Information</title>\n"
     87      "  <meta charset=\"utf-8\">\n"
     88      "  <meta name=\"color-scheme\" content=\"light dark\">\n"
     89      "  <meta http-equiv=\"Content-Security-Policy\" content=\"default-src "
     90      "chrome:; object-src 'none'\"/>\n"
     91      "  <link rel=\"stylesheet\" "
     92      "href=\"chrome://global/skin/in-content/info-pages.css\"/>\n"
     93      "  <link rel=\"stylesheet\" "
     94      "href=\"chrome://global/skin/aboutCache.css\"/>\n"
     95      "</head>\n"
     96      "<body class=\"aboutPageWideContainer\">\n"
     97      "<h1>Information about the Network Cache Storage Service</h1>\n");
     98 
     99  if (!mOverview) {
    100    mBuffer.AppendLiteral(
    101        "<a href=\"about:cache?storage=\">Back to overview</a>\n");
    102    mBuffer.AppendLiteral(
    103        "<p id=\"explanation-dataSize\">Data sizes refer to the size of the "
    104        "response body and do not reflect the amount of disk space that the "
    105        "file occupies.</p>\n");
    106  }
    107 
    108  rv = FlushBuffer();
    109  if (NS_FAILED(rv)) {
    110    NS_WARNING("Failed to flush buffer");
    111  }
    112 
    113  return NS_OK;
    114 }
    115 
    116 NS_IMETHODIMP nsAboutCache::Channel::AsyncOpen(nsIStreamListener* aListener) {
    117  nsresult rv;
    118 
    119  if (!mChannel) {
    120    return NS_ERROR_UNEXPECTED;
    121  }
    122 
    123  // Kick the walk loop.
    124  rv = VisitNextStorage();
    125  if (NS_FAILED(rv)) return rv;
    126 
    127  rv = mChannel->AsyncOpen(aListener);
    128  if (NS_FAILED(rv)) return rv;
    129 
    130  return NS_OK;
    131 }
    132 
    133 NS_IMETHODIMP nsAboutCache::Channel::Open(nsIInputStream** _retval) {
    134  return NS_ERROR_NOT_IMPLEMENTED;
    135 }
    136 
    137 nsresult nsAboutCache::Channel::ParseURI(nsIURI* uri, nsACString& storage) {
    138  //
    139  // about:cache[?storage=<storage-name>[&context=<context-key>]]
    140  //
    141  nsresult rv;
    142 
    143  nsAutoCString path;
    144  rv = uri->GetPathQueryRef(path);
    145  if (NS_FAILED(rv)) return rv;
    146 
    147  storage.Truncate();
    148 
    149  nsACString::const_iterator start, valueStart, end;
    150  path.BeginReading(start);
    151  path.EndReading(end);
    152 
    153  valueStart = end;
    154  if (!FindInReadable("?storage="_ns, start, valueStart)) {
    155    return NS_OK;
    156  }
    157 
    158  storage.Assign(Substring(valueStart, end));
    159 
    160  return NS_OK;
    161 }
    162 
    163 nsresult nsAboutCache::Channel::VisitNextStorage() {
    164  if (!mStorageList.Length()) return NS_ERROR_NOT_AVAILABLE;
    165 
    166  mStorageName = mStorageList[0];
    167  mStorageList.RemoveElementAt(0);
    168 
    169  // Must re-dispatch since we cannot start another visit cycle
    170  // from visitor callback.  The cache v1 service doesn't like it.
    171  // TODO - mayhemer, bug 913828, remove this dispatch and call
    172  // directly.
    173  return NS_DispatchToMainThread(mozilla::NewRunnableMethod(
    174      "nsAboutCache::Channel::FireVisitStorage", this,
    175      &nsAboutCache::Channel::FireVisitStorage));
    176 }
    177 
    178 void nsAboutCache::Channel::FireVisitStorage() {
    179  nsresult rv;
    180 
    181  rv = VisitStorage(mStorageName);
    182  if (NS_FAILED(rv)) {
    183    nsAutoCString escaped;
    184    nsAppendEscapedHTML(mStorageName, escaped);
    185    mBuffer.Append(nsPrintfCString(
    186        "<p>Unrecognized storage name '%s' in about:cache URL</p>",
    187        escaped.get()));
    188 
    189    rv = FlushBuffer();
    190    if (NS_FAILED(rv)) {
    191      NS_WARNING("Failed to flush buffer");
    192    }
    193 
    194    // Simulate finish of a visit cycle, this tries the next storage
    195    // or closes the output stream (i.e. the UI loader will stop spinning)
    196    OnCacheEntryVisitCompleted();
    197  }
    198 }
    199 
    200 nsresult nsAboutCache::Channel::VisitStorage(nsACString const& storageName) {
    201  nsresult rv;
    202 
    203  rv = GetStorage(storageName, nullptr, getter_AddRefs(mStorage));
    204  if (NS_FAILED(rv)) return rv;
    205 
    206  rv = mStorage->AsyncVisitStorage(this, !mOverview);
    207  if (NS_FAILED(rv)) return rv;
    208 
    209  return NS_OK;
    210 }
    211 
    212 // static
    213 nsresult nsAboutCache::GetStorage(nsACString const& storageName,
    214                                  nsILoadContextInfo* loadInfo,
    215                                  nsICacheStorage** storage) {
    216  nsresult rv;
    217 
    218  nsCOMPtr<nsICacheStorageService> cacheService;
    219  cacheService = mozilla::components::CacheStorage::Service(&rv);
    220  if (NS_FAILED(rv)) return rv;
    221 
    222  nsCOMPtr<nsICacheStorage> cacheStorage;
    223  if (storageName == "disk") {
    224    rv = cacheService->DiskCacheStorage(loadInfo, getter_AddRefs(cacheStorage));
    225  } else if (storageName == "memory") {
    226    rv = cacheService->MemoryCacheStorage(loadInfo,
    227                                          getter_AddRefs(cacheStorage));
    228  } else {
    229    rv = NS_ERROR_UNEXPECTED;
    230  }
    231  if (NS_FAILED(rv)) return rv;
    232 
    233  cacheStorage.forget(storage);
    234  return NS_OK;
    235 }
    236 
    237 NS_IMETHODIMP
    238 nsAboutCache::Channel::OnCacheStorageInfo(uint32_t aEntryCount,
    239                                          uint64_t aConsumption,
    240                                          uint64_t aCapacity,
    241                                          nsIFile* aDirectory) {
    242  // We need mStream for this
    243  if (!mStream) {
    244    return NS_ERROR_FAILURE;
    245  }
    246 
    247  mBuffer.AssignLiteral("<h2>");
    248  nsAppendEscapedHTML(mStorageName, mBuffer);
    249  mBuffer.AppendLiteral(
    250      "</h2>\n"
    251      "<table id=\"");
    252  mBuffer.AppendLiteral("\">\n");
    253 
    254  // Write out cache info
    255  // Number of entries
    256  mBuffer.AppendLiteral(
    257      "  <tr>\n"
    258      "    <th>Number of entries:</th>\n"
    259      "    <td>");
    260  mBuffer.AppendInt(aEntryCount);
    261  mBuffer.AppendLiteral(
    262      "</td>\n"
    263      "  </tr>\n");
    264 
    265  // Maximum storage size
    266  mBuffer.AppendLiteral(
    267      "  <tr>\n"
    268      "    <th>Maximum storage size:</th>\n"
    269      "    <td>");
    270  mBuffer.AppendInt(aCapacity / 1024);
    271  mBuffer.AppendLiteral(
    272      " KiB</td>\n"
    273      "  </tr>\n");
    274 
    275  // Storage in use
    276  mBuffer.AppendLiteral(
    277      "  <tr>\n"
    278      "    <th>Storage in use:</th>\n"
    279      "    <td>");
    280  mBuffer.AppendInt(aConsumption / 1024);
    281  mBuffer.AppendLiteral(
    282      " KiB</td>\n"
    283      "  </tr>\n");
    284 
    285  // Storage disk location
    286  mBuffer.AppendLiteral(
    287      "  <tr>\n"
    288      "    <th>Storage disk location:</th>\n"
    289      "    <td>");
    290  if (aDirectory) {
    291    nsAutoString path;
    292    aDirectory->GetPath(path);
    293    mBuffer.Append(NS_ConvertUTF16toUTF8(path));
    294  } else {
    295    mBuffer.AppendLiteral("none, only stored in memory");
    296  }
    297  mBuffer.AppendLiteral(
    298      "    </td>\n"
    299      "  </tr>\n");
    300 
    301  if (mOverview) {           // The about:cache case
    302    if (aEntryCount != 0) {  // Add the "List Cache Entries" link
    303      mBuffer.AppendLiteral(
    304          "  <tr>\n"
    305          "    <td colspan=\"2\"><a href=\"about:cache?storage=");
    306      nsAppendEscapedHTML(mStorageName, mBuffer);
    307      mBuffer.AppendLiteral(
    308          "\">List Cache Entries</a></td>\n"
    309          "  </tr>\n");
    310    }
    311  }
    312 
    313  mBuffer.AppendLiteral("</table>\n");
    314 
    315  // The entries header is added on encounter of the first entry
    316  mEntriesHeaderAdded = false;
    317 
    318  nsresult rv = FlushBuffer();
    319  if (NS_FAILED(rv)) {
    320    NS_WARNING("Failed to flush buffer");
    321  }
    322 
    323  if (mOverview) {
    324    // OnCacheEntryVisitCompleted() is not called when we do not iterate
    325    // cache entries.  Since this moves forward to the next storage in
    326    // the list we want to visit, artificially call it here.
    327    OnCacheEntryVisitCompleted();
    328  }
    329 
    330  return NS_OK;
    331 }
    332 
    333 NS_IMETHODIMP
    334 nsAboutCache::Channel::OnCacheEntryInfo(
    335    nsIURI* aURI, const nsACString& aIdEnhance, int64_t aDataSize,
    336    int64_t aAltDataSize, uint32_t aFetchCount, uint32_t aLastModified,
    337    uint32_t aExpirationTime, bool aPinned, nsILoadContextInfo* aInfo) {
    338  // We need mStream for this
    339  if (!mStream || mCancel) {
    340    // Returning a failure from this callback stops the iteration
    341    return NS_ERROR_FAILURE;
    342  }
    343 
    344  if (!mEntriesHeaderAdded) {
    345    mBuffer.AppendLiteral(
    346        "<hr/>\n"
    347        "<table id=\"entries\">\n"
    348        "  <colgroup>\n"
    349        "   <col id=\"col-key\">\n"
    350        "   <col id=\"col-dataSize\">\n"
    351        "   <col id=\"col-altDataSize\">\n"
    352        "   <col id=\"col-fetchCount\">\n"
    353        "   <col id=\"col-lastModified\">\n"
    354        "   <col id=\"col-expires\">\n"
    355        "   <col id=\"col-pinned\">\n"
    356        "  </colgroup>\n"
    357        "  <thead>\n"
    358        "    <tr>\n"
    359        "      <th>Key</th>\n"
    360        "      <th>Data size</th>\n"
    361        "      <th>Alternative Data size</th>\n"
    362        "      <th>Fetch count</th>\n"
    363        "      <th>Last Modifed</th>\n"
    364        "      <th>Expires</th>\n"
    365        "      <th>Pinning</th>\n"
    366        "    </tr>\n"
    367        "  </thead>\n");
    368    mEntriesHeaderAdded = true;
    369  }
    370 
    371  // Generate a about:cache-entry URL for this entry...
    372 
    373  nsAutoCString url;
    374  url.AssignLiteral("about:cache-entry?storage=");
    375  nsAppendEscapedHTML(mStorageName, url);
    376 
    377  nsAutoCString context;
    378  CacheFileUtils::AppendKeyPrefix(aInfo, context);
    379  url.AppendLiteral("&amp;context=");
    380  nsAppendEscapedHTML(context, url);
    381 
    382  url.AppendLiteral("&amp;eid=");
    383  nsAppendEscapedHTML(aIdEnhance, url);
    384 
    385  nsAutoCString cacheUriSpec;
    386  aURI->GetAsciiSpec(cacheUriSpec);
    387  nsAutoCString escapedCacheURI;
    388  nsAppendEscapedHTML(cacheUriSpec, escapedCacheURI);
    389  url.AppendLiteral("&amp;uri=");
    390  url += escapedCacheURI;
    391 
    392  // Entry start...
    393  mBuffer.AppendLiteral("  <tr>\n");
    394 
    395  // URI
    396  mBuffer.AppendLiteral("    <td><a href=\"");
    397  mBuffer.Append(url);
    398  mBuffer.AppendLiteral("\">");
    399  if (!aIdEnhance.IsEmpty()) {
    400    nsAppendEscapedHTML(aIdEnhance, mBuffer);
    401    mBuffer.Append(':');
    402  }
    403  mBuffer.Append(escapedCacheURI);
    404  mBuffer.AppendLiteral("</a>");
    405 
    406  if (!context.IsEmpty()) {
    407    mBuffer.AppendLiteral("<br><span title=\"Context separation key\">");
    408    nsAutoCString escapedContext;
    409    nsAppendEscapedHTML(context, escapedContext);
    410    mBuffer.Append(escapedContext);
    411    mBuffer.AppendLiteral("</span>");
    412  }
    413 
    414  mBuffer.AppendLiteral("</td>\n");
    415 
    416  // Content length
    417  mBuffer.AppendLiteral("    <td>");
    418  mBuffer.AppendInt(aDataSize);
    419  mBuffer.AppendLiteral(" bytes</td>\n");
    420 
    421  // Length of alternative content
    422  mBuffer.AppendLiteral("    <td>");
    423  mBuffer.AppendInt(aAltDataSize);
    424  mBuffer.AppendLiteral(" bytes</td>\n");
    425 
    426  // Number of accesses
    427  mBuffer.AppendLiteral("    <td>");
    428  mBuffer.AppendInt(aFetchCount);
    429  mBuffer.AppendLiteral("</td>\n");
    430 
    431  // vars for reporting time
    432  char buf[255];
    433 
    434  // Last modified time
    435  mBuffer.AppendLiteral("    <td>");
    436  if (aLastModified) {
    437    PrintTimeString(buf, sizeof(buf), aLastModified);
    438    mBuffer.Append(buf);
    439  } else {
    440    mBuffer.AppendLiteral("No last modified time");
    441  }
    442  mBuffer.AppendLiteral("</td>\n");
    443 
    444  // Expires time
    445  mBuffer.AppendLiteral("    <td>");
    446 
    447  // Bug - 633747.
    448  // When expiration time is 0, we show 1970-01-01 01:00:00 which is confusing.
    449  // So we check if time is 0, then we show a message, "Expired Immediately"
    450  if (aExpirationTime == 0) {
    451    mBuffer.AppendLiteral("Expired Immediately");
    452  } else if (aExpirationTime < 0xFFFFFFFF) {
    453    PrintTimeString(buf, sizeof(buf), aExpirationTime);
    454    mBuffer.Append(buf);
    455  } else {
    456    mBuffer.AppendLiteral("No expiration time");
    457  }
    458  mBuffer.AppendLiteral("</td>\n");
    459 
    460  // Pinning
    461  mBuffer.AppendLiteral("    <td>");
    462  if (aPinned) {
    463    mBuffer.AppendLiteral("Pinned");
    464  } else {
    465    mBuffer.AppendLiteral("&nbsp;");
    466  }
    467  mBuffer.AppendLiteral("</td>\n");
    468 
    469  // Entry is done...
    470  mBuffer.AppendLiteral("  </tr>\n");
    471 
    472  return FlushBuffer();
    473 }
    474 
    475 NS_IMETHODIMP
    476 nsAboutCache::Channel::OnCacheEntryVisitCompleted() {
    477  if (!mStream) {
    478    return NS_ERROR_FAILURE;
    479  }
    480 
    481  if (mEntriesHeaderAdded) {
    482    mBuffer.AppendLiteral("</table>\n");
    483  }
    484 
    485  // Kick another storage visiting (from a storage that allows us.)
    486  while (mStorageList.Length()) {
    487    nsresult rv = VisitNextStorage();
    488    if (NS_SUCCEEDED(rv)) {
    489      // Expecting new round of OnCache* calls.
    490      return NS_OK;
    491    }
    492  }
    493 
    494  // We are done!
    495  mBuffer.AppendLiteral(
    496      "</body>\n"
    497      "</html>\n");
    498  nsresult rv = FlushBuffer();
    499  if (NS_FAILED(rv)) {
    500    NS_WARNING("Failed to flush buffer");
    501  }
    502  mStream->Close();
    503 
    504  return NS_OK;
    505 }
    506 
    507 nsresult nsAboutCache::Channel::FlushBuffer() {
    508  nsresult rv;
    509 
    510  uint32_t bytesWritten;
    511  rv = mStream->Write(mBuffer.get(), mBuffer.Length(), &bytesWritten);
    512  mBuffer.Truncate();
    513 
    514  if (NS_FAILED(rv)) {
    515    mCancel = true;
    516  }
    517 
    518  return rv;
    519 }
    520 
    521 NS_IMETHODIMP
    522 nsAboutCache::GetURIFlags(nsIURI* aURI, uint32_t* result) {
    523  *result = nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
    524            nsIAboutModule::IS_SECURE_CHROME_UI;
    525  return NS_OK;
    526 }
    527 
    528 // static
    529 nsresult nsAboutCache::Create(REFNSIID aIID, void** aResult) {
    530  RefPtr<nsAboutCache> about = new nsAboutCache();
    531  return about->QueryInterface(aIID, aResult);
    532 }
    533 
    534 NS_IMETHODIMP
    535 nsAboutCache::GetChromeURI(nsIURI* aURI, nsIURI** chromeURI) {
    536  return NS_ERROR_ILLEGAL_VALUE;
    537 }
    538 
    539 ////////////////////////////////////////////////////////////////////////////////