diff -r 000000000000 -r 6474c204b198 netwerk/protocol/about/nsAboutCacheEntry.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/netwerk/protocol/about/nsAboutCacheEntry.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,462 @@ +/* -*- 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 "nsICacheService.h" +#include "nsICacheSession.h" +#include "nsNetUtil.h" +#include "prprf.h" +#include "nsEscape.h" +#include "nsIAsyncInputStream.h" +#include "nsIAsyncOutputStream.h" +#include "nsAboutProtocolUtils.h" +#include + +#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) { + PR_snprintf(temp, sizeof(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) { + PR_snprintf(temp, sizeof(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, + nsICacheMetaDataVisitor) + +//----------------------------------------------------------------------------- +// nsAboutCacheEntry::nsIAboutModule + +NS_IMETHODIMP +nsAboutCacheEntry::NewChannel(nsIURI *uri, nsIChannel **result) +{ + NS_ENSURE_ARG_POINTER(uri); + nsresult rv; + + nsCOMPtr stream; + rv = GetContentStream(uri, getter_AddRefs(stream)); + if (NS_FAILED(rv)) return rv; + + return NS_NewInputStreamChannel(result, uri, stream, + NS_LITERAL_CSTRING("text/html"), + NS_LITERAL_CSTRING("utf-8")); +} + +NS_IMETHODIMP +nsAboutCacheEntry::GetURIFlags(nsIURI *aURI, uint32_t *result) +{ + *result = nsIAboutModule::HIDE_FROM_ABOUTABOUT; + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsAboutCacheEntry + +nsresult +nsAboutCacheEntry::GetContentStream(nsIURI *uri, nsIInputStream **result) +{ + nsresult rv; + + // Init: (block size, maximum length) + nsCOMPtr 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, + "\n" + "\n" + "\n" + " Cache entry information\n" + " \n" + " \n" + "\n" + "\n" + "

Cache entry information

\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::OpenCacheEntry(nsIURI *uri) +{ + nsresult rv; + nsAutoCString clientID, key; + bool streamBased = true; + + rv = ParseURI(uri, clientID, streamBased, key); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr serv = + do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr session; + rv = serv->CreateSession(clientID.get(), + nsICache::STORE_ANYWHERE, + streamBased, + getter_AddRefs(session)); + if (NS_FAILED(rv)) return rv; + + rv = session->SetDoomEntriesIfExpired(false); + if (NS_FAILED(rv)) return rv; + + return session->AsyncOpenCacheEntry(key, nsICache::ACCESS_READ, this, true); +} + + +//----------------------------------------------------------------------------- +// helper methods +//----------------------------------------------------------------------------- + +#define APPEND_ROW(label, value) \ + PR_BEGIN_MACRO \ + buffer.AppendLiteral(" \n" \ + " "); \ + buffer.AppendLiteral(label); \ + buffer.AppendLiteral(":\n" \ + " "); \ + buffer.Append(value); \ + buffer.AppendLiteral("\n" \ + " \n"); \ + PR_END_MACRO + +nsresult +nsAboutCacheEntry::WriteCacheEntryDescription(nsICacheEntryDescriptor *descriptor) +{ + nsresult rv; + nsCString buffer; + uint32_t n; + + nsAutoCString str; + + rv = descriptor->GetKey(str); + if (NS_FAILED(rv)) return rv; + + buffer.SetCapacity(4096); + buffer.AssignLiteral("\n" + " \n" + " \n" + " \n" + " \n"); + + // temp vars for reporting + char timeBuf[255]; + uint32_t u = 0; + int32_t i = 0; + nsAutoCString s; + + // Fetch Count + s.Truncate(); + descriptor->GetFetchCount(&i); + s.AppendInt(i); + APPEND_ROW("fetch count", s); + + // Last Fetched + descriptor->GetLastFetched(&u); + if (u) { + PrintTimeString(timeBuf, sizeof(timeBuf), u); + APPEND_ROW("last fetched", timeBuf); + } else { + APPEND_ROW("last fetched", "No last fetch time"); + } + + // Last Modified + descriptor->GetLastModified(&u); + if (u) { + PrintTimeString(timeBuf, sizeof(timeBuf), u); + APPEND_ROW("last modified", timeBuf); + } else { + APPEND_ROW("last modified", "No last modified time"); + } + + // Expiration Time + descriptor->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; + descriptor->GetStorageDataSize(&dataSize); + s.AppendInt((int32_t)dataSize); // XXX nsICacheEntryInfo interfaces should be fixed. + APPEND_ROW("Data size", s); + + // Storage Policy + + // XXX Stream Based? + + // XXX Cache Device + // File on disk + nsCOMPtr cacheFile; + rv = descriptor->GetFile(getter_AddRefs(cacheFile)); + if (NS_SUCCEEDED(rv)) { + nsAutoString filePath; + cacheFile->GetPath(filePath); + APPEND_ROW("file on disk", NS_ConvertUTF16toUTF8(filePath)); + } + else + APPEND_ROW("file on disk", "none"); + + // Security Info + nsCOMPtr securityInfo; + descriptor->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("
key:"); + + // Test if the key is actually a URI + nsCOMPtr 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(""); + buffer.Append(escapedStr); + buffer.AppendLiteral(""); + uri = 0; + } + else + buffer.Append(escapedStr); + nsMemory::Free(escapedStr); + buffer.AppendLiteral("
\n" + "
\n" + "\n"); + // Meta Data + // let's just look for some well known (HTTP) meta data tags, for now. + + // Client ID + nsXPIDLCString str2; + descriptor->GetClientID(getter_Copies(str2)); + if (!str2.IsEmpty()) APPEND_ROW("Client", str2); + + + mBuffer = &buffer; // make it available for VisitMetaDataElement(). + // nsCacheEntryDescriptor::VisitMetaData calls + // nsCacheEntry.h VisitMetaDataElements, which returns + // nsCacheMetaData::VisitElements, which calls + // nsAboutCacheEntry::VisitMetaDataElement (below) in a loop. + descriptor->VisitMetaData(this); + mBuffer = nullptr; + + buffer.AppendLiteral("
\n"); + mOutputStream->Write(buffer.get(), buffer.Length(), &n); + + buffer.Truncate(); + + // Provide a hexdump of the data + if (dataSize) { // don't draw an
if the Data Size is 0. + nsCOMPtr stream; + descriptor->OpenInputStream(0, getter_AddRefs(stream)); + if (stream) { + buffer.AssignLiteral("
\n" + "
");
+            uint32_t hexDumpState = 0;
+            char chunk[4096];
+            while(NS_SUCCEEDED(stream->Read(chunk, sizeof(chunk), &n)) && 
+                  n > 0) {
+                HexDump(&hexDumpState, chunk, n, buffer);
+                mOutputStream->Write(buffer.get(), buffer.Length(), &n);
+                buffer.Truncate();
+            }
+            buffer.AssignLiteral("
\n"); + mOutputStream->Write(buffer.get(), buffer.Length(), &n); + } + } + return NS_OK; +} + +nsresult +nsAboutCacheEntry::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; +} + +nsresult +nsAboutCacheEntry::ParseURI(nsIURI *uri, nsCString &clientID, + bool &streamBased, nsCString &key) +{ + // + // about:cache-entry?client=[string]&sb=[boolean]&key=[string] + // + nsresult rv; + + nsAutoCString path; + rv = uri->GetPath(path); + if (NS_FAILED(rv)) return rv; + + nsACString::const_iterator i1, i2, i3, end; + path.BeginReading(i1); + path.EndReading(end); + + i2 = end; + if (!FindInReadable(NS_LITERAL_CSTRING("?client="), i1, i2)) + return NS_ERROR_FAILURE; + // i2 points to the start of clientID + + i1 = i2; + i3 = end; + if (!FindInReadable(NS_LITERAL_CSTRING("&sb="), i1, i3)) + return NS_ERROR_FAILURE; + // i1 points to the end of clientID + // i3 points to the start of isStreamBased + + clientID.Assign(Substring(i2, i1)); + + i1 = i3; + i2 = end; + if (!FindInReadable(NS_LITERAL_CSTRING("&key="), i1, i2)) + return NS_ERROR_FAILURE; + // i1 points to the end of isStreamBased + // i2 points to the start of key + + streamBased = FindCharInReadable('1', i3, i1); + key.Assign(Substring(i2, end)); + + return NS_OK; +} + + +//----------------------------------------------------------------------------- +// nsICacheMetaDataVisitor implementation +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsAboutCacheEntry::VisitMetaDataElement(const char * key, + const char * value, + bool * keepGoing) +{ + mBuffer->AppendLiteral(" \n" + " "); + mBuffer->Append(key); + mBuffer->AppendLiteral(":\n" + " "); + char* escapedValue = nsEscapeHTML(value); + mBuffer->Append(escapedValue); + nsMemory::Free(escapedValue); + mBuffer->AppendLiteral("\n" + " \n"); + + *keepGoing = true; + return NS_OK; +} + +//----------------------------------------------------------------------------- +// nsICacheListener implementation +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +nsAboutCacheEntry::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry, + nsCacheAccessMode access, + nsresult status) +{ + nsresult rv; + + if (entry) + rv = WriteCacheEntryDescription(entry); + else + rv = WriteCacheEntryUnavailable(); + if (NS_FAILED(rv)) return rv; + + uint32_t n; + NS_NAMED_LITERAL_CSTRING(buffer, "\n\n"); + mOutputStream->Write(buffer.get(), buffer.Length(), &n); + mOutputStream->Close(); + mOutputStream = nullptr; + + return NS_OK; +} + +NS_IMETHODIMP +nsAboutCacheEntry::OnCacheEntryDoomed(nsresult status) +{ + return NS_ERROR_NOT_IMPLEMENTED; +}