/* -*- 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 #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_ISUPPORTS2(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; *result = inputStream.forget().get(); 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 //----------------------------------------------------------------------------- static PRTime SecondsToPRTime(uint32_t t_sec) { PRTime t_usec, usec_per_sec; t_usec = t_sec; usec_per_sec = PR_USEC_PER_SEC; return t_usec *= usec_per_sec; } static void PrintTimeString(char *buf, uint32_t bufsize, uint32_t t_sec) { PRExplodedTime et; PRTime t_usec = SecondsToPRTime(t_sec); PR_ExplodeTime(t_usec, PR_LocalTimeParameters, &et); PR_FormatTime(buf, bufsize, "%Y-%m-%d %H:%M:%S", &et); } #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; }