1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/protocol/about/nsAboutCacheEntry.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,462 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsAboutCacheEntry.h" 1.10 +#include "nsICacheService.h" 1.11 +#include "nsICacheSession.h" 1.12 +#include "nsNetUtil.h" 1.13 +#include "prprf.h" 1.14 +#include "nsEscape.h" 1.15 +#include "nsIAsyncInputStream.h" 1.16 +#include "nsIAsyncOutputStream.h" 1.17 +#include "nsAboutProtocolUtils.h" 1.18 +#include <algorithm> 1.19 + 1.20 +#define HEXDUMP_MAX_ROWS 16 1.21 + 1.22 +static void 1.23 +HexDump(uint32_t *state, const char *buf, int32_t n, nsCString &result) 1.24 +{ 1.25 + char temp[16]; 1.26 + 1.27 + const unsigned char *p; 1.28 + while (n) { 1.29 + PR_snprintf(temp, sizeof(temp), "%08x: ", *state); 1.30 + result.Append(temp); 1.31 + *state += HEXDUMP_MAX_ROWS; 1.32 + 1.33 + p = (const unsigned char *) buf; 1.34 + 1.35 + int32_t i, row_max = std::min(HEXDUMP_MAX_ROWS, n); 1.36 + 1.37 + // print hex codes: 1.38 + for (i = 0; i < row_max; ++i) { 1.39 + PR_snprintf(temp, sizeof(temp), "%02x ", *p++); 1.40 + result.Append(temp); 1.41 + } 1.42 + for (i = row_max; i < HEXDUMP_MAX_ROWS; ++i) { 1.43 + result.AppendLiteral(" "); 1.44 + } 1.45 + 1.46 + // print ASCII glyphs if possible: 1.47 + p = (const unsigned char *) buf; 1.48 + for (i = 0; i < row_max; ++i, ++p) { 1.49 + switch (*p) { 1.50 + case '<': 1.51 + result.AppendLiteral("<"); 1.52 + break; 1.53 + case '>': 1.54 + result.AppendLiteral(">"); 1.55 + break; 1.56 + case '&': 1.57 + result.AppendLiteral("&"); 1.58 + break; 1.59 + default: 1.60 + if (*p < 0x7F && *p > 0x1F) { 1.61 + result.Append(*p); 1.62 + } else { 1.63 + result.Append('.'); 1.64 + } 1.65 + } 1.66 + } 1.67 + 1.68 + result.Append('\n'); 1.69 + 1.70 + buf += row_max; 1.71 + n -= row_max; 1.72 + } 1.73 +} 1.74 + 1.75 +//----------------------------------------------------------------------------- 1.76 +// nsAboutCacheEntry::nsISupports 1.77 + 1.78 +NS_IMPL_ISUPPORTS(nsAboutCacheEntry, 1.79 + nsIAboutModule, 1.80 + nsICacheMetaDataVisitor) 1.81 + 1.82 +//----------------------------------------------------------------------------- 1.83 +// nsAboutCacheEntry::nsIAboutModule 1.84 + 1.85 +NS_IMETHODIMP 1.86 +nsAboutCacheEntry::NewChannel(nsIURI *uri, nsIChannel **result) 1.87 +{ 1.88 + NS_ENSURE_ARG_POINTER(uri); 1.89 + nsresult rv; 1.90 + 1.91 + nsCOMPtr<nsIInputStream> stream; 1.92 + rv = GetContentStream(uri, getter_AddRefs(stream)); 1.93 + if (NS_FAILED(rv)) return rv; 1.94 + 1.95 + return NS_NewInputStreamChannel(result, uri, stream, 1.96 + NS_LITERAL_CSTRING("text/html"), 1.97 + NS_LITERAL_CSTRING("utf-8")); 1.98 +} 1.99 + 1.100 +NS_IMETHODIMP 1.101 +nsAboutCacheEntry::GetURIFlags(nsIURI *aURI, uint32_t *result) 1.102 +{ 1.103 + *result = nsIAboutModule::HIDE_FROM_ABOUTABOUT; 1.104 + return NS_OK; 1.105 +} 1.106 + 1.107 +//----------------------------------------------------------------------------- 1.108 +// nsAboutCacheEntry 1.109 + 1.110 +nsresult 1.111 +nsAboutCacheEntry::GetContentStream(nsIURI *uri, nsIInputStream **result) 1.112 +{ 1.113 + nsresult rv; 1.114 + 1.115 + // Init: (block size, maximum length) 1.116 + nsCOMPtr<nsIAsyncInputStream> inputStream; 1.117 + rv = NS_NewPipe2(getter_AddRefs(inputStream), 1.118 + getter_AddRefs(mOutputStream), 1.119 + true, false, 1.120 + 256, UINT32_MAX); 1.121 + if (NS_FAILED(rv)) return rv; 1.122 + 1.123 + NS_NAMED_LITERAL_CSTRING( 1.124 + buffer, 1.125 + "<!DOCTYPE html>\n" 1.126 + "<html>\n" 1.127 + "<head>\n" 1.128 + " <title>Cache entry information</title>\n" 1.129 + " <link rel=\"stylesheet\" " 1.130 + "href=\"chrome://global/skin/about.css\" type=\"text/css\"/>\n" 1.131 + " <link rel=\"stylesheet\" " 1.132 + "href=\"chrome://global/skin/aboutCacheEntry.css\" type=\"text/css\"/>\n" 1.133 + "</head>\n" 1.134 + "<body>\n" 1.135 + "<h1>Cache entry information</h1>\n"); 1.136 + uint32_t n; 1.137 + rv = mOutputStream->Write(buffer.get(), buffer.Length(), &n); 1.138 + if (NS_FAILED(rv)) return rv; 1.139 + if (n != buffer.Length()) return NS_ERROR_UNEXPECTED; 1.140 + 1.141 + rv = OpenCacheEntry(uri); 1.142 + if (NS_FAILED(rv)) return rv; 1.143 + 1.144 + inputStream.forget(result); 1.145 + return NS_OK; 1.146 +} 1.147 + 1.148 +nsresult 1.149 +nsAboutCacheEntry::OpenCacheEntry(nsIURI *uri) 1.150 +{ 1.151 + nsresult rv; 1.152 + nsAutoCString clientID, key; 1.153 + bool streamBased = true; 1.154 + 1.155 + rv = ParseURI(uri, clientID, streamBased, key); 1.156 + if (NS_FAILED(rv)) return rv; 1.157 + 1.158 + nsCOMPtr<nsICacheService> serv = 1.159 + do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); 1.160 + if (NS_FAILED(rv)) return rv; 1.161 + 1.162 + nsCOMPtr<nsICacheSession> session; 1.163 + rv = serv->CreateSession(clientID.get(), 1.164 + nsICache::STORE_ANYWHERE, 1.165 + streamBased, 1.166 + getter_AddRefs(session)); 1.167 + if (NS_FAILED(rv)) return rv; 1.168 + 1.169 + rv = session->SetDoomEntriesIfExpired(false); 1.170 + if (NS_FAILED(rv)) return rv; 1.171 + 1.172 + return session->AsyncOpenCacheEntry(key, nsICache::ACCESS_READ, this, true); 1.173 +} 1.174 + 1.175 + 1.176 +//----------------------------------------------------------------------------- 1.177 +// helper methods 1.178 +//----------------------------------------------------------------------------- 1.179 + 1.180 +#define APPEND_ROW(label, value) \ 1.181 + PR_BEGIN_MACRO \ 1.182 + buffer.AppendLiteral(" <tr>\n" \ 1.183 + " <th>"); \ 1.184 + buffer.AppendLiteral(label); \ 1.185 + buffer.AppendLiteral(":</th>\n" \ 1.186 + " <td>"); \ 1.187 + buffer.Append(value); \ 1.188 + buffer.AppendLiteral("</td>\n" \ 1.189 + " </tr>\n"); \ 1.190 + PR_END_MACRO 1.191 + 1.192 +nsresult 1.193 +nsAboutCacheEntry::WriteCacheEntryDescription(nsICacheEntryDescriptor *descriptor) 1.194 +{ 1.195 + nsresult rv; 1.196 + nsCString buffer; 1.197 + uint32_t n; 1.198 + 1.199 + nsAutoCString str; 1.200 + 1.201 + rv = descriptor->GetKey(str); 1.202 + if (NS_FAILED(rv)) return rv; 1.203 + 1.204 + buffer.SetCapacity(4096); 1.205 + buffer.AssignLiteral("<table>\n" 1.206 + " <tr>\n" 1.207 + " <th>key:</th>\n" 1.208 + " <td id=\"td-key\">"); 1.209 + 1.210 + // Test if the key is actually a URI 1.211 + nsCOMPtr<nsIURI> uri; 1.212 + bool isJS = false; 1.213 + bool isData = false; 1.214 + 1.215 + rv = NS_NewURI(getter_AddRefs(uri), str); 1.216 + // javascript: and data: URLs should not be linkified 1.217 + // since clicking them can cause scripts to run - bug 162584 1.218 + if (NS_SUCCEEDED(rv)) { 1.219 + uri->SchemeIs("javascript", &isJS); 1.220 + uri->SchemeIs("data", &isData); 1.221 + } 1.222 + char* escapedStr = nsEscapeHTML(str.get()); 1.223 + if (NS_SUCCEEDED(rv) && !(isJS || isData)) { 1.224 + buffer.AppendLiteral("<a href=\""); 1.225 + buffer.Append(escapedStr); 1.226 + buffer.AppendLiteral("\">"); 1.227 + buffer.Append(escapedStr); 1.228 + buffer.AppendLiteral("</a>"); 1.229 + uri = 0; 1.230 + } 1.231 + else 1.232 + buffer.Append(escapedStr); 1.233 + nsMemory::Free(escapedStr); 1.234 + buffer.AppendLiteral("</td>\n" 1.235 + " </tr>\n"); 1.236 + 1.237 + // temp vars for reporting 1.238 + char timeBuf[255]; 1.239 + uint32_t u = 0; 1.240 + int32_t i = 0; 1.241 + nsAutoCString s; 1.242 + 1.243 + // Fetch Count 1.244 + s.Truncate(); 1.245 + descriptor->GetFetchCount(&i); 1.246 + s.AppendInt(i); 1.247 + APPEND_ROW("fetch count", s); 1.248 + 1.249 + // Last Fetched 1.250 + descriptor->GetLastFetched(&u); 1.251 + if (u) { 1.252 + PrintTimeString(timeBuf, sizeof(timeBuf), u); 1.253 + APPEND_ROW("last fetched", timeBuf); 1.254 + } else { 1.255 + APPEND_ROW("last fetched", "No last fetch time"); 1.256 + } 1.257 + 1.258 + // Last Modified 1.259 + descriptor->GetLastModified(&u); 1.260 + if (u) { 1.261 + PrintTimeString(timeBuf, sizeof(timeBuf), u); 1.262 + APPEND_ROW("last modified", timeBuf); 1.263 + } else { 1.264 + APPEND_ROW("last modified", "No last modified time"); 1.265 + } 1.266 + 1.267 + // Expiration Time 1.268 + descriptor->GetExpirationTime(&u); 1.269 + if (u < 0xFFFFFFFF) { 1.270 + PrintTimeString(timeBuf, sizeof(timeBuf), u); 1.271 + APPEND_ROW("expires", timeBuf); 1.272 + } else { 1.273 + APPEND_ROW("expires", "No expiration time"); 1.274 + } 1.275 + 1.276 + // Data Size 1.277 + s.Truncate(); 1.278 + uint32_t dataSize; 1.279 + descriptor->GetStorageDataSize(&dataSize); 1.280 + s.AppendInt((int32_t)dataSize); // XXX nsICacheEntryInfo interfaces should be fixed. 1.281 + APPEND_ROW("Data size", s); 1.282 + 1.283 + // Storage Policy 1.284 + 1.285 + // XXX Stream Based? 1.286 + 1.287 + // XXX Cache Device 1.288 + // File on disk 1.289 + nsCOMPtr<nsIFile> cacheFile; 1.290 + rv = descriptor->GetFile(getter_AddRefs(cacheFile)); 1.291 + if (NS_SUCCEEDED(rv)) { 1.292 + nsAutoString filePath; 1.293 + cacheFile->GetPath(filePath); 1.294 + APPEND_ROW("file on disk", NS_ConvertUTF16toUTF8(filePath)); 1.295 + } 1.296 + else 1.297 + APPEND_ROW("file on disk", "none"); 1.298 + 1.299 + // Security Info 1.300 + nsCOMPtr<nsISupports> securityInfo; 1.301 + descriptor->GetSecurityInfo(getter_AddRefs(securityInfo)); 1.302 + if (securityInfo) { 1.303 + APPEND_ROW("Security", "This is a secure document."); 1.304 + } else { 1.305 + APPEND_ROW("Security", 1.306 + "This document does not have any security info associated with it."); 1.307 + } 1.308 + 1.309 + buffer.AppendLiteral("</table>\n" 1.310 + "<hr/>\n" 1.311 + "<table>\n"); 1.312 + // Meta Data 1.313 + // let's just look for some well known (HTTP) meta data tags, for now. 1.314 + 1.315 + // Client ID 1.316 + nsXPIDLCString str2; 1.317 + descriptor->GetClientID(getter_Copies(str2)); 1.318 + if (!str2.IsEmpty()) APPEND_ROW("Client", str2); 1.319 + 1.320 + 1.321 + mBuffer = &buffer; // make it available for VisitMetaDataElement(). 1.322 + // nsCacheEntryDescriptor::VisitMetaData calls 1.323 + // nsCacheEntry.h VisitMetaDataElements, which returns 1.324 + // nsCacheMetaData::VisitElements, which calls 1.325 + // nsAboutCacheEntry::VisitMetaDataElement (below) in a loop. 1.326 + descriptor->VisitMetaData(this); 1.327 + mBuffer = nullptr; 1.328 + 1.329 + buffer.AppendLiteral("</table>\n"); 1.330 + mOutputStream->Write(buffer.get(), buffer.Length(), &n); 1.331 + 1.332 + buffer.Truncate(); 1.333 + 1.334 + // Provide a hexdump of the data 1.335 + if (dataSize) { // don't draw an <hr> if the Data Size is 0. 1.336 + nsCOMPtr<nsIInputStream> stream; 1.337 + descriptor->OpenInputStream(0, getter_AddRefs(stream)); 1.338 + if (stream) { 1.339 + buffer.AssignLiteral("<hr/>\n" 1.340 + "<pre>"); 1.341 + uint32_t hexDumpState = 0; 1.342 + char chunk[4096]; 1.343 + while(NS_SUCCEEDED(stream->Read(chunk, sizeof(chunk), &n)) && 1.344 + n > 0) { 1.345 + HexDump(&hexDumpState, chunk, n, buffer); 1.346 + mOutputStream->Write(buffer.get(), buffer.Length(), &n); 1.347 + buffer.Truncate(); 1.348 + } 1.349 + buffer.AssignLiteral("</pre>\n"); 1.350 + mOutputStream->Write(buffer.get(), buffer.Length(), &n); 1.351 + } 1.352 + } 1.353 + return NS_OK; 1.354 +} 1.355 + 1.356 +nsresult 1.357 +nsAboutCacheEntry::WriteCacheEntryUnavailable() 1.358 +{ 1.359 + uint32_t n; 1.360 + NS_NAMED_LITERAL_CSTRING(buffer, 1.361 + "The cache entry you selected is not available."); 1.362 + mOutputStream->Write(buffer.get(), buffer.Length(), &n); 1.363 + return NS_OK; 1.364 +} 1.365 + 1.366 +nsresult 1.367 +nsAboutCacheEntry::ParseURI(nsIURI *uri, nsCString &clientID, 1.368 + bool &streamBased, nsCString &key) 1.369 +{ 1.370 + // 1.371 + // about:cache-entry?client=[string]&sb=[boolean]&key=[string] 1.372 + // 1.373 + nsresult rv; 1.374 + 1.375 + nsAutoCString path; 1.376 + rv = uri->GetPath(path); 1.377 + if (NS_FAILED(rv)) return rv; 1.378 + 1.379 + nsACString::const_iterator i1, i2, i3, end; 1.380 + path.BeginReading(i1); 1.381 + path.EndReading(end); 1.382 + 1.383 + i2 = end; 1.384 + if (!FindInReadable(NS_LITERAL_CSTRING("?client="), i1, i2)) 1.385 + return NS_ERROR_FAILURE; 1.386 + // i2 points to the start of clientID 1.387 + 1.388 + i1 = i2; 1.389 + i3 = end; 1.390 + if (!FindInReadable(NS_LITERAL_CSTRING("&sb="), i1, i3)) 1.391 + return NS_ERROR_FAILURE; 1.392 + // i1 points to the end of clientID 1.393 + // i3 points to the start of isStreamBased 1.394 + 1.395 + clientID.Assign(Substring(i2, i1)); 1.396 + 1.397 + i1 = i3; 1.398 + i2 = end; 1.399 + if (!FindInReadable(NS_LITERAL_CSTRING("&key="), i1, i2)) 1.400 + return NS_ERROR_FAILURE; 1.401 + // i1 points to the end of isStreamBased 1.402 + // i2 points to the start of key 1.403 + 1.404 + streamBased = FindCharInReadable('1', i3, i1); 1.405 + key.Assign(Substring(i2, end)); 1.406 + 1.407 + return NS_OK; 1.408 +} 1.409 + 1.410 + 1.411 +//----------------------------------------------------------------------------- 1.412 +// nsICacheMetaDataVisitor implementation 1.413 +//----------------------------------------------------------------------------- 1.414 + 1.415 +NS_IMETHODIMP 1.416 +nsAboutCacheEntry::VisitMetaDataElement(const char * key, 1.417 + const char * value, 1.418 + bool * keepGoing) 1.419 +{ 1.420 + mBuffer->AppendLiteral(" <tr>\n" 1.421 + " <th>"); 1.422 + mBuffer->Append(key); 1.423 + mBuffer->AppendLiteral(":</th>\n" 1.424 + " <td>"); 1.425 + char* escapedValue = nsEscapeHTML(value); 1.426 + mBuffer->Append(escapedValue); 1.427 + nsMemory::Free(escapedValue); 1.428 + mBuffer->AppendLiteral("</td>\n" 1.429 + " </tr>\n"); 1.430 + 1.431 + *keepGoing = true; 1.432 + return NS_OK; 1.433 +} 1.434 + 1.435 +//----------------------------------------------------------------------------- 1.436 +// nsICacheListener implementation 1.437 +//----------------------------------------------------------------------------- 1.438 + 1.439 +NS_IMETHODIMP 1.440 +nsAboutCacheEntry::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry, 1.441 + nsCacheAccessMode access, 1.442 + nsresult status) 1.443 +{ 1.444 + nsresult rv; 1.445 + 1.446 + if (entry) 1.447 + rv = WriteCacheEntryDescription(entry); 1.448 + else 1.449 + rv = WriteCacheEntryUnavailable(); 1.450 + if (NS_FAILED(rv)) return rv; 1.451 + 1.452 + uint32_t n; 1.453 + NS_NAMED_LITERAL_CSTRING(buffer, "</body>\n</html>\n"); 1.454 + mOutputStream->Write(buffer.get(), buffer.Length(), &n); 1.455 + mOutputStream->Close(); 1.456 + mOutputStream = nullptr; 1.457 + 1.458 + return NS_OK; 1.459 +} 1.460 + 1.461 +NS_IMETHODIMP 1.462 +nsAboutCacheEntry::OnCacheEntryDoomed(nsresult status) 1.463 +{ 1.464 + return NS_ERROR_NOT_IMPLEMENTED; 1.465 +}