123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604 |
- /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "nsAboutCacheEntry.h"
- #include "mozilla/Sprintf.h"
- #include "nsAboutCache.h"
- #include "nsICacheStorage.h"
- #include "CacheObserver.h"
- #include "nsNetUtil.h"
- #include "nsEscape.h"
- #include "nsIAsyncInputStream.h"
- #include "nsIAsyncOutputStream.h"
- #include "nsAboutProtocolUtils.h"
- #include "nsContentUtils.h"
- #include "nsInputStreamPump.h"
- #include "CacheFileUtils.h"
- #include <algorithm>
- #include "nsIPipe.h"
- using namespace mozilla::net;
- #define HEXDUMP_MAX_ROWS 16
- static void
- HexDump(uint32_t *state, const char *buf, int32_t n, nsCString &result)
- {
- char temp[16];
- const unsigned char *p;
- while (n) {
- SprintfLiteral(temp, "%08x: ", *state);
- result.Append(temp);
- *state += HEXDUMP_MAX_ROWS;
- p = (const unsigned char *) buf;
- int32_t i, row_max = std::min(HEXDUMP_MAX_ROWS, n);
- // print hex codes:
- for (i = 0; i < row_max; ++i) {
- SprintfLiteral(temp, "%02x ", *p++);
- result.Append(temp);
- }
- for (i = row_max; i < HEXDUMP_MAX_ROWS; ++i) {
- result.AppendLiteral(" ");
- }
- // print ASCII glyphs if possible:
- p = (const unsigned char *) buf;
- for (i = 0; i < row_max; ++i, ++p) {
- switch (*p) {
- case '<':
- result.AppendLiteral("<");
- break;
- case '>':
- result.AppendLiteral(">");
- break;
- case '&':
- result.AppendLiteral("&");
- break;
- default:
- if (*p < 0x7F && *p > 0x1F) {
- result.Append(*p);
- } else {
- result.Append('.');
- }
- }
- }
- result.Append('\n');
- buf += row_max;
- n -= row_max;
- }
- }
- //-----------------------------------------------------------------------------
- // nsAboutCacheEntry::nsISupports
- NS_IMPL_ISUPPORTS(nsAboutCacheEntry,
- nsIAboutModule)
- NS_IMPL_ISUPPORTS(nsAboutCacheEntry::Channel,
- nsICacheEntryOpenCallback,
- nsICacheEntryMetaDataVisitor,
- nsIStreamListener,
- nsIChannel)
- //-----------------------------------------------------------------------------
- // nsAboutCacheEntry::nsIAboutModule
- NS_IMETHODIMP
- nsAboutCacheEntry::NewChannel(nsIURI* uri,
- nsILoadInfo* aLoadInfo,
- nsIChannel** result)
- {
- NS_ENSURE_ARG_POINTER(uri);
- nsresult rv;
- RefPtr<Channel> channel = new Channel();
- rv = channel->Init(uri, aLoadInfo);
- if (NS_FAILED(rv)) return rv;
- channel.forget(result);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsAboutCacheEntry::GetURIFlags(nsIURI *aURI, uint32_t *result)
- {
- *result = nsIAboutModule::HIDE_FROM_ABOUTABOUT |
- nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT;
- return NS_OK;
- }
- //-----------------------------------------------------------------------------
- // nsAboutCacheEntry::Channel
- nsresult
- nsAboutCacheEntry::Channel::Init(nsIURI* uri, nsILoadInfo* aLoadInfo)
- {
- nsresult rv;
- nsCOMPtr<nsIInputStream> stream;
- rv = GetContentStream(uri, getter_AddRefs(stream));
- if (NS_FAILED(rv)) return rv;
- rv = NS_NewInputStreamChannelInternal(getter_AddRefs(mChannel),
- uri,
- stream,
- NS_LITERAL_CSTRING("text/html"),
- NS_LITERAL_CSTRING("utf-8"),
- aLoadInfo);
- if (NS_FAILED(rv)) return rv;
- return NS_OK;
- }
- nsresult
- nsAboutCacheEntry::Channel::GetContentStream(nsIURI *uri, nsIInputStream **result)
- {
- nsresult rv;
- // Init: (block size, maximum length)
- nsCOMPtr<nsIAsyncInputStream> inputStream;
- rv = NS_NewPipe2(getter_AddRefs(inputStream),
- getter_AddRefs(mOutputStream),
- true, false,
- 256, UINT32_MAX);
- if (NS_FAILED(rv)) return rv;
- NS_NAMED_LITERAL_CSTRING(
- buffer,
- "<!DOCTYPE html>\n"
- "<html>\n"
- "<head>\n"
- " <title>Cache entry information</title>\n"
- " <link rel=\"stylesheet\" "
- "href=\"chrome://global/skin/about.css\" type=\"text/css\"/>\n"
- " <link rel=\"stylesheet\" "
- "href=\"chrome://global/skin/aboutCacheEntry.css\" type=\"text/css\"/>\n"
- "</head>\n"
- "<body>\n"
- "<h1>Cache entry information</h1>\n");
- uint32_t n;
- rv = mOutputStream->Write(buffer.get(), buffer.Length(), &n);
- if (NS_FAILED(rv)) return rv;
- if (n != buffer.Length()) return NS_ERROR_UNEXPECTED;
- rv = OpenCacheEntry(uri);
- if (NS_FAILED(rv)) return rv;
- inputStream.forget(result);
- return NS_OK;
- }
- nsresult
- nsAboutCacheEntry::Channel::OpenCacheEntry(nsIURI *uri)
- {
- nsresult rv;
- rv = ParseURI(uri, mStorageName, getter_AddRefs(mLoadInfo),
- mEnhanceId, getter_AddRefs(mCacheURI));
- if (NS_FAILED(rv)) return rv;
- if (!CacheObserver::UseNewCache() &&
- mLoadInfo->IsPrivate() &&
- mStorageName.EqualsLiteral("disk")) {
- // The cache v1 is storing all private entries in the memory-only
- // cache, so it would not be found in the v1 disk cache.
- mStorageName = NS_LITERAL_CSTRING("memory");
- }
- return OpenCacheEntry();
- }
- nsresult
- nsAboutCacheEntry::Channel::OpenCacheEntry()
- {
- nsresult rv;
- nsCOMPtr<nsICacheStorage> storage;
- rv = nsAboutCache::GetStorage(mStorageName, mLoadInfo, getter_AddRefs(storage));
- if (NS_FAILED(rv)) return rv;
- // Invokes OnCacheEntryAvailable()
- rv = storage->AsyncOpenURI(mCacheURI, mEnhanceId,
- nsICacheStorage::OPEN_READONLY |
- nsICacheStorage::OPEN_SECRETLY,
- this);
- if (NS_FAILED(rv)) return rv;
- return NS_OK;
- }
- nsresult
- nsAboutCacheEntry::Channel::ParseURI(nsIURI *uri,
- nsACString &storageName,
- nsILoadContextInfo **loadInfo,
- nsCString &enahnceID,
- nsIURI **cacheUri)
- {
- //
- // about:cache-entry?storage=[string]&contenxt=[string]&eid=[string]&uri=[string]
- //
- nsresult rv;
- nsAutoCString path;
- rv = uri->GetPath(path);
- if (NS_FAILED(rv))
- return rv;
- nsACString::const_iterator keyBegin, keyEnd, valBegin, begin, end;
- path.BeginReading(begin);
- path.EndReading(end);
- keyBegin = begin; keyEnd = end;
- if (!FindInReadable(NS_LITERAL_CSTRING("?storage="), keyBegin, keyEnd))
- return NS_ERROR_FAILURE;
- valBegin = keyEnd; // the value of the storage key starts after the key
- keyBegin = keyEnd; keyEnd = end;
- if (!FindInReadable(NS_LITERAL_CSTRING("&context="), keyBegin, keyEnd))
- return NS_ERROR_FAILURE;
- storageName.Assign(Substring(valBegin, keyBegin));
- valBegin = keyEnd; // the value of the context key starts after the key
- keyBegin = keyEnd; keyEnd = end;
- if (!FindInReadable(NS_LITERAL_CSTRING("&eid="), keyBegin, keyEnd))
- return NS_ERROR_FAILURE;
- nsAutoCString contextKey(Substring(valBegin, keyBegin));
- valBegin = keyEnd; // the value of the eid key starts after the key
- keyBegin = keyEnd; keyEnd = end;
- if (!FindInReadable(NS_LITERAL_CSTRING("&uri="), keyBegin, keyEnd))
- return NS_ERROR_FAILURE;
- enahnceID.Assign(Substring(valBegin, keyBegin));
- valBegin = keyEnd; // the value of the uri key starts after the key
- nsAutoCString uriSpec(Substring(valBegin, end)); // uri is the last one
- // Uf... parsing done, now get some objects from it...
- nsCOMPtr<nsILoadContextInfo> info =
- CacheFileUtils::ParseKey(contextKey);
- if (!info)
- return NS_ERROR_FAILURE;
- info.forget(loadInfo);
- rv = NS_NewURI(cacheUri, uriSpec);
- if (NS_FAILED(rv))
- return rv;
- return NS_OK;
- }
- //-----------------------------------------------------------------------------
- // nsICacheEntryOpenCallback implementation
- //-----------------------------------------------------------------------------
- NS_IMETHODIMP
- nsAboutCacheEntry::Channel::OnCacheEntryCheck(nsICacheEntry *aEntry,
- nsIApplicationCache *aApplicationCache,
- uint32_t *result)
- {
- *result = nsICacheEntryOpenCallback::ENTRY_WANTED;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsAboutCacheEntry::Channel::OnCacheEntryAvailable(nsICacheEntry *entry,
- bool isNew,
- nsIApplicationCache *aApplicationCache,
- nsresult status)
- {
- nsresult rv;
- mWaitingForData = false;
- if (entry) {
- rv = WriteCacheEntryDescription(entry);
- } else if (!CacheObserver::UseNewCache() &&
- !mLoadInfo->IsPrivate() &&
- mStorageName.EqualsLiteral("memory")) {
- // If we were not able to find the entry in the memory storage
- // try again in the disk storage.
- // This is a workaround for cache v1: when an originally disk
- // cache entry is recreated as memory-only, it's clientID doesn't
- // change and we cannot find it in "HTTP-memory-only" session.
- // "Disk" cache storage looks at "HTTP".
- mStorageName = NS_LITERAL_CSTRING("disk");
- rv = OpenCacheEntry();
- if (NS_SUCCEEDED(rv)) {
- return NS_OK;
- }
- } else {
- rv = WriteCacheEntryUnavailable();
- }
- if (NS_FAILED(rv)) return rv;
- if (!mWaitingForData) {
- // Data is not expected, close the output of content now.
- CloseContent();
- }
- return NS_OK;
- }
- //-----------------------------------------------------------------------------
- // Print-out helper methods
- //-----------------------------------------------------------------------------
- #define APPEND_ROW(label, value) \
- PR_BEGIN_MACRO \
- buffer.AppendLiteral(" <tr>\n" \
- " <th>"); \
- buffer.AppendLiteral(label); \
- buffer.AppendLiteral(":</th>\n" \
- " <td>"); \
- buffer.Append(value); \
- buffer.AppendLiteral("</td>\n" \
- " </tr>\n"); \
- PR_END_MACRO
- nsresult
- nsAboutCacheEntry::Channel::WriteCacheEntryDescription(nsICacheEntry *entry)
- {
- nsresult rv;
- nsCString buffer;
- uint32_t n;
- nsAutoCString str;
- rv = entry->GetKey(str);
- if (NS_FAILED(rv)) return rv;
- buffer.SetCapacity(4096);
- buffer.AssignLiteral("<table>\n"
- " <tr>\n"
- " <th>key:</th>\n"
- " <td id=\"td-key\">");
- // Test if the key is actually a URI
- nsCOMPtr<nsIURI> uri;
- bool isJS = false;
- bool isData = false;
- rv = NS_NewURI(getter_AddRefs(uri), str);
- // javascript: and data: URLs should not be linkified
- // since clicking them can cause scripts to run - bug 162584
- if (NS_SUCCEEDED(rv)) {
- uri->SchemeIs("javascript", &isJS);
- uri->SchemeIs("data", &isData);
- }
- char* escapedStr = nsEscapeHTML(str.get());
- if (NS_SUCCEEDED(rv) && !(isJS || isData)) {
- buffer.AppendLiteral("<a href=\"");
- buffer.Append(escapedStr);
- buffer.AppendLiteral("\">");
- buffer.Append(escapedStr);
- buffer.AppendLiteral("</a>");
- uri = nullptr;
- } else {
- buffer.Append(escapedStr);
- }
- free(escapedStr);
- buffer.AppendLiteral("</td>\n"
- " </tr>\n");
- // temp vars for reporting
- char timeBuf[255];
- uint32_t u = 0;
- int32_t i = 0;
- nsAutoCString s;
- // Fetch Count
- s.Truncate();
- entry->GetFetchCount(&i);
- s.AppendInt(i);
- APPEND_ROW("fetch count", s);
- // Last Fetched
- entry->GetLastFetched(&u);
- if (u) {
- PrintTimeString(timeBuf, sizeof(timeBuf), u);
- APPEND_ROW("last fetched", timeBuf);
- } else {
- APPEND_ROW("last fetched", "No last fetch time (bug 1000338)");
- }
- // Last Modified
- entry->GetLastModified(&u);
- if (u) {
- PrintTimeString(timeBuf, sizeof(timeBuf), u);
- APPEND_ROW("last modified", timeBuf);
- } else {
- APPEND_ROW("last modified", "No last modified time (bug 1000338)");
- }
- // Expiration Time
- entry->GetExpirationTime(&u);
- if (u < 0xFFFFFFFF) {
- PrintTimeString(timeBuf, sizeof(timeBuf), u);
- APPEND_ROW("expires", timeBuf);
- } else {
- APPEND_ROW("expires", "No expiration time");
- }
- // Data Size
- s.Truncate();
- uint32_t dataSize;
- if (NS_FAILED(entry->GetStorageDataSize(&dataSize)))
- dataSize = 0;
- s.AppendInt((int32_t)dataSize); // XXX nsICacheEntryInfo interfaces should be fixed.
- s.AppendLiteral(" B");
- APPEND_ROW("Data size", s);
- // TODO - mayhemer
- // Here used to be a link to the disk file (in the old cache for entries that
- // did not fit any of the block files, in the new cache every time).
- // I'd rather have a small set of buttons here to action on the entry:
- // 1. save the content
- // 2. save as a complete HTTP response (response head, headers, content)
- // 3. doom the entry
- // A new bug(s) should be filed here.
- // Security Info
- nsCOMPtr<nsISupports> securityInfo;
- entry->GetSecurityInfo(getter_AddRefs(securityInfo));
- if (securityInfo) {
- APPEND_ROW("Security", "This is a secure document.");
- } else {
- APPEND_ROW("Security",
- "This document does not have any security info associated with it.");
- }
- buffer.AppendLiteral("</table>\n"
- "<hr/>\n"
- "<table>\n");
- mBuffer = &buffer; // make it available for OnMetaDataElement().
- entry->VisitMetaData(this);
- mBuffer = nullptr;
- buffer.AppendLiteral("</table>\n");
- mOutputStream->Write(buffer.get(), buffer.Length(), &n);
- buffer.Truncate();
- // Provide a hexdump of the data
- if (!dataSize) {
- return NS_OK;
- }
- nsCOMPtr<nsIInputStream> stream;
- entry->OpenInputStream(0, getter_AddRefs(stream));
- if (!stream) {
- return NS_OK;
- }
- RefPtr<nsInputStreamPump> pump;
- rv = nsInputStreamPump::Create(getter_AddRefs(pump), stream);
- if (NS_FAILED(rv)) {
- return NS_OK; // just ignore
- }
- rv = pump->AsyncRead(this, nullptr);
- if (NS_FAILED(rv)) {
- return NS_OK; // just ignore
- }
- mWaitingForData = true;
- return NS_OK;
- }
- nsresult
- nsAboutCacheEntry::Channel::WriteCacheEntryUnavailable()
- {
- uint32_t n;
- NS_NAMED_LITERAL_CSTRING(buffer,
- "The cache entry you selected is not available.");
- mOutputStream->Write(buffer.get(), buffer.Length(), &n);
- return NS_OK;
- }
- //-----------------------------------------------------------------------------
- // nsICacheEntryMetaDataVisitor implementation
- //-----------------------------------------------------------------------------
- NS_IMETHODIMP
- nsAboutCacheEntry::Channel::OnMetaDataElement(char const * key, char const * value)
- {
- mBuffer->AppendLiteral(" <tr>\n"
- " <th>");
- mBuffer->Append(key);
- mBuffer->AppendLiteral(":</th>\n"
- " <td>");
- char* escapedValue = nsEscapeHTML(value);
- mBuffer->Append(escapedValue);
- free(escapedValue);
- mBuffer->AppendLiteral("</td>\n"
- " </tr>\n");
- return NS_OK;
- }
- //-----------------------------------------------------------------------------
- // nsIStreamListener implementation
- //-----------------------------------------------------------------------------
- NS_IMETHODIMP
- nsAboutCacheEntry::Channel::OnStartRequest(nsIRequest *request, nsISupports *ctx)
- {
- mHexDumpState = 0;
- NS_NAMED_LITERAL_CSTRING(buffer, "<hr/>\n<pre>");
- uint32_t n;
- return mOutputStream->Write(buffer.get(), buffer.Length(), &n);
- }
- NS_IMETHODIMP
- nsAboutCacheEntry::Channel::OnDataAvailable(nsIRequest *request, nsISupports *ctx,
- nsIInputStream *aInputStream,
- uint64_t aOffset,
- uint32_t aCount)
- {
- uint32_t n;
- return aInputStream->ReadSegments(
- &nsAboutCacheEntry::Channel::PrintCacheData, this, aCount, &n);
- }
- /* static */ nsresult
- nsAboutCacheEntry::Channel::PrintCacheData(nsIInputStream *aInStream,
- void *aClosure,
- const char *aFromSegment,
- uint32_t aToOffset,
- uint32_t aCount,
- uint32_t *aWriteCount)
- {
- nsAboutCacheEntry::Channel *a =
- static_cast<nsAboutCacheEntry::Channel*>(aClosure);
- nsCString buffer;
- HexDump(&a->mHexDumpState, aFromSegment, aCount, buffer);
- uint32_t n;
- a->mOutputStream->Write(buffer.get(), buffer.Length(), &n);
- *aWriteCount = aCount;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsAboutCacheEntry::Channel::OnStopRequest(nsIRequest *request, nsISupports *ctx,
- nsresult result)
- {
- NS_NAMED_LITERAL_CSTRING(buffer, "</pre>\n");
- uint32_t n;
- mOutputStream->Write(buffer.get(), buffer.Length(), &n);
- CloseContent();
- return NS_OK;
- }
- void
- nsAboutCacheEntry::Channel::CloseContent()
- {
- NS_NAMED_LITERAL_CSTRING(buffer, "</body>\n</html>\n");
- uint32_t n;
- mOutputStream->Write(buffer.get(), buffer.Length(), &n);
- mOutputStream->Close();
- mOutputStream = nullptr;
- }
|