netwerk/protocol/about/nsAboutCacheEntry.cpp

branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
equal deleted inserted replaced
-1:000000000000 0:023d4c851c94
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 "nsAboutCacheEntry.h"
7 #include "nsICacheService.h"
8 #include "nsICacheSession.h"
9 #include "nsNetUtil.h"
10 #include "prprf.h"
11 #include "nsEscape.h"
12 #include "nsIAsyncInputStream.h"
13 #include "nsIAsyncOutputStream.h"
14 #include "nsAboutProtocolUtils.h"
15 #include <algorithm>
16
17 #define HEXDUMP_MAX_ROWS 16
18
19 static void
20 HexDump(uint32_t *state, const char *buf, int32_t n, nsCString &result)
21 {
22 char temp[16];
23
24 const unsigned char *p;
25 while (n) {
26 PR_snprintf(temp, sizeof(temp), "%08x: ", *state);
27 result.Append(temp);
28 *state += HEXDUMP_MAX_ROWS;
29
30 p = (const unsigned char *) buf;
31
32 int32_t i, row_max = std::min(HEXDUMP_MAX_ROWS, n);
33
34 // print hex codes:
35 for (i = 0; i < row_max; ++i) {
36 PR_snprintf(temp, sizeof(temp), "%02x ", *p++);
37 result.Append(temp);
38 }
39 for (i = row_max; i < HEXDUMP_MAX_ROWS; ++i) {
40 result.AppendLiteral(" ");
41 }
42
43 // print ASCII glyphs if possible:
44 p = (const unsigned char *) buf;
45 for (i = 0; i < row_max; ++i, ++p) {
46 switch (*p) {
47 case '<':
48 result.AppendLiteral("&lt;");
49 break;
50 case '>':
51 result.AppendLiteral("&gt;");
52 break;
53 case '&':
54 result.AppendLiteral("&amp;");
55 break;
56 default:
57 if (*p < 0x7F && *p > 0x1F) {
58 result.Append(*p);
59 } else {
60 result.Append('.');
61 }
62 }
63 }
64
65 result.Append('\n');
66
67 buf += row_max;
68 n -= row_max;
69 }
70 }
71
72 //-----------------------------------------------------------------------------
73 // nsAboutCacheEntry::nsISupports
74
75 NS_IMPL_ISUPPORTS(nsAboutCacheEntry,
76 nsIAboutModule,
77 nsICacheMetaDataVisitor)
78
79 //-----------------------------------------------------------------------------
80 // nsAboutCacheEntry::nsIAboutModule
81
82 NS_IMETHODIMP
83 nsAboutCacheEntry::NewChannel(nsIURI *uri, nsIChannel **result)
84 {
85 NS_ENSURE_ARG_POINTER(uri);
86 nsresult rv;
87
88 nsCOMPtr<nsIInputStream> stream;
89 rv = GetContentStream(uri, getter_AddRefs(stream));
90 if (NS_FAILED(rv)) return rv;
91
92 return NS_NewInputStreamChannel(result, uri, stream,
93 NS_LITERAL_CSTRING("text/html"),
94 NS_LITERAL_CSTRING("utf-8"));
95 }
96
97 NS_IMETHODIMP
98 nsAboutCacheEntry::GetURIFlags(nsIURI *aURI, uint32_t *result)
99 {
100 *result = nsIAboutModule::HIDE_FROM_ABOUTABOUT;
101 return NS_OK;
102 }
103
104 //-----------------------------------------------------------------------------
105 // nsAboutCacheEntry
106
107 nsresult
108 nsAboutCacheEntry::GetContentStream(nsIURI *uri, nsIInputStream **result)
109 {
110 nsresult rv;
111
112 // Init: (block size, maximum length)
113 nsCOMPtr<nsIAsyncInputStream> inputStream;
114 rv = NS_NewPipe2(getter_AddRefs(inputStream),
115 getter_AddRefs(mOutputStream),
116 true, false,
117 256, UINT32_MAX);
118 if (NS_FAILED(rv)) return rv;
119
120 NS_NAMED_LITERAL_CSTRING(
121 buffer,
122 "<!DOCTYPE html>\n"
123 "<html>\n"
124 "<head>\n"
125 " <title>Cache entry information</title>\n"
126 " <link rel=\"stylesheet\" "
127 "href=\"chrome://global/skin/about.css\" type=\"text/css\"/>\n"
128 " <link rel=\"stylesheet\" "
129 "href=\"chrome://global/skin/aboutCacheEntry.css\" type=\"text/css\"/>\n"
130 "</head>\n"
131 "<body>\n"
132 "<h1>Cache entry information</h1>\n");
133 uint32_t n;
134 rv = mOutputStream->Write(buffer.get(), buffer.Length(), &n);
135 if (NS_FAILED(rv)) return rv;
136 if (n != buffer.Length()) return NS_ERROR_UNEXPECTED;
137
138 rv = OpenCacheEntry(uri);
139 if (NS_FAILED(rv)) return rv;
140
141 inputStream.forget(result);
142 return NS_OK;
143 }
144
145 nsresult
146 nsAboutCacheEntry::OpenCacheEntry(nsIURI *uri)
147 {
148 nsresult rv;
149 nsAutoCString clientID, key;
150 bool streamBased = true;
151
152 rv = ParseURI(uri, clientID, streamBased, key);
153 if (NS_FAILED(rv)) return rv;
154
155 nsCOMPtr<nsICacheService> serv =
156 do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
157 if (NS_FAILED(rv)) return rv;
158
159 nsCOMPtr<nsICacheSession> session;
160 rv = serv->CreateSession(clientID.get(),
161 nsICache::STORE_ANYWHERE,
162 streamBased,
163 getter_AddRefs(session));
164 if (NS_FAILED(rv)) return rv;
165
166 rv = session->SetDoomEntriesIfExpired(false);
167 if (NS_FAILED(rv)) return rv;
168
169 return session->AsyncOpenCacheEntry(key, nsICache::ACCESS_READ, this, true);
170 }
171
172
173 //-----------------------------------------------------------------------------
174 // helper methods
175 //-----------------------------------------------------------------------------
176
177 #define APPEND_ROW(label, value) \
178 PR_BEGIN_MACRO \
179 buffer.AppendLiteral(" <tr>\n" \
180 " <th>"); \
181 buffer.AppendLiteral(label); \
182 buffer.AppendLiteral(":</th>\n" \
183 " <td>"); \
184 buffer.Append(value); \
185 buffer.AppendLiteral("</td>\n" \
186 " </tr>\n"); \
187 PR_END_MACRO
188
189 nsresult
190 nsAboutCacheEntry::WriteCacheEntryDescription(nsICacheEntryDescriptor *descriptor)
191 {
192 nsresult rv;
193 nsCString buffer;
194 uint32_t n;
195
196 nsAutoCString str;
197
198 rv = descriptor->GetKey(str);
199 if (NS_FAILED(rv)) return rv;
200
201 buffer.SetCapacity(4096);
202 buffer.AssignLiteral("<table>\n"
203 " <tr>\n"
204 " <th>key:</th>\n"
205 " <td id=\"td-key\">");
206
207 // Test if the key is actually a URI
208 nsCOMPtr<nsIURI> uri;
209 bool isJS = false;
210 bool isData = false;
211
212 rv = NS_NewURI(getter_AddRefs(uri), str);
213 // javascript: and data: URLs should not be linkified
214 // since clicking them can cause scripts to run - bug 162584
215 if (NS_SUCCEEDED(rv)) {
216 uri->SchemeIs("javascript", &isJS);
217 uri->SchemeIs("data", &isData);
218 }
219 char* escapedStr = nsEscapeHTML(str.get());
220 if (NS_SUCCEEDED(rv) && !(isJS || isData)) {
221 buffer.AppendLiteral("<a href=\"");
222 buffer.Append(escapedStr);
223 buffer.AppendLiteral("\">");
224 buffer.Append(escapedStr);
225 buffer.AppendLiteral("</a>");
226 uri = 0;
227 }
228 else
229 buffer.Append(escapedStr);
230 nsMemory::Free(escapedStr);
231 buffer.AppendLiteral("</td>\n"
232 " </tr>\n");
233
234 // temp vars for reporting
235 char timeBuf[255];
236 uint32_t u = 0;
237 int32_t i = 0;
238 nsAutoCString s;
239
240 // Fetch Count
241 s.Truncate();
242 descriptor->GetFetchCount(&i);
243 s.AppendInt(i);
244 APPEND_ROW("fetch count", s);
245
246 // Last Fetched
247 descriptor->GetLastFetched(&u);
248 if (u) {
249 PrintTimeString(timeBuf, sizeof(timeBuf), u);
250 APPEND_ROW("last fetched", timeBuf);
251 } else {
252 APPEND_ROW("last fetched", "No last fetch time");
253 }
254
255 // Last Modified
256 descriptor->GetLastModified(&u);
257 if (u) {
258 PrintTimeString(timeBuf, sizeof(timeBuf), u);
259 APPEND_ROW("last modified", timeBuf);
260 } else {
261 APPEND_ROW("last modified", "No last modified time");
262 }
263
264 // Expiration Time
265 descriptor->GetExpirationTime(&u);
266 if (u < 0xFFFFFFFF) {
267 PrintTimeString(timeBuf, sizeof(timeBuf), u);
268 APPEND_ROW("expires", timeBuf);
269 } else {
270 APPEND_ROW("expires", "No expiration time");
271 }
272
273 // Data Size
274 s.Truncate();
275 uint32_t dataSize;
276 descriptor->GetStorageDataSize(&dataSize);
277 s.AppendInt((int32_t)dataSize); // XXX nsICacheEntryInfo interfaces should be fixed.
278 APPEND_ROW("Data size", s);
279
280 // Storage Policy
281
282 // XXX Stream Based?
283
284 // XXX Cache Device
285 // File on disk
286 nsCOMPtr<nsIFile> cacheFile;
287 rv = descriptor->GetFile(getter_AddRefs(cacheFile));
288 if (NS_SUCCEEDED(rv)) {
289 nsAutoString filePath;
290 cacheFile->GetPath(filePath);
291 APPEND_ROW("file on disk", NS_ConvertUTF16toUTF8(filePath));
292 }
293 else
294 APPEND_ROW("file on disk", "none");
295
296 // Security Info
297 nsCOMPtr<nsISupports> securityInfo;
298 descriptor->GetSecurityInfo(getter_AddRefs(securityInfo));
299 if (securityInfo) {
300 APPEND_ROW("Security", "This is a secure document.");
301 } else {
302 APPEND_ROW("Security",
303 "This document does not have any security info associated with it.");
304 }
305
306 buffer.AppendLiteral("</table>\n"
307 "<hr/>\n"
308 "<table>\n");
309 // Meta Data
310 // let's just look for some well known (HTTP) meta data tags, for now.
311
312 // Client ID
313 nsXPIDLCString str2;
314 descriptor->GetClientID(getter_Copies(str2));
315 if (!str2.IsEmpty()) APPEND_ROW("Client", str2);
316
317
318 mBuffer = &buffer; // make it available for VisitMetaDataElement().
319 // nsCacheEntryDescriptor::VisitMetaData calls
320 // nsCacheEntry.h VisitMetaDataElements, which returns
321 // nsCacheMetaData::VisitElements, which calls
322 // nsAboutCacheEntry::VisitMetaDataElement (below) in a loop.
323 descriptor->VisitMetaData(this);
324 mBuffer = nullptr;
325
326 buffer.AppendLiteral("</table>\n");
327 mOutputStream->Write(buffer.get(), buffer.Length(), &n);
328
329 buffer.Truncate();
330
331 // Provide a hexdump of the data
332 if (dataSize) { // don't draw an <hr> if the Data Size is 0.
333 nsCOMPtr<nsIInputStream> stream;
334 descriptor->OpenInputStream(0, getter_AddRefs(stream));
335 if (stream) {
336 buffer.AssignLiteral("<hr/>\n"
337 "<pre>");
338 uint32_t hexDumpState = 0;
339 char chunk[4096];
340 while(NS_SUCCEEDED(stream->Read(chunk, sizeof(chunk), &n)) &&
341 n > 0) {
342 HexDump(&hexDumpState, chunk, n, buffer);
343 mOutputStream->Write(buffer.get(), buffer.Length(), &n);
344 buffer.Truncate();
345 }
346 buffer.AssignLiteral("</pre>\n");
347 mOutputStream->Write(buffer.get(), buffer.Length(), &n);
348 }
349 }
350 return NS_OK;
351 }
352
353 nsresult
354 nsAboutCacheEntry::WriteCacheEntryUnavailable()
355 {
356 uint32_t n;
357 NS_NAMED_LITERAL_CSTRING(buffer,
358 "The cache entry you selected is not available.");
359 mOutputStream->Write(buffer.get(), buffer.Length(), &n);
360 return NS_OK;
361 }
362
363 nsresult
364 nsAboutCacheEntry::ParseURI(nsIURI *uri, nsCString &clientID,
365 bool &streamBased, nsCString &key)
366 {
367 //
368 // about:cache-entry?client=[string]&sb=[boolean]&key=[string]
369 //
370 nsresult rv;
371
372 nsAutoCString path;
373 rv = uri->GetPath(path);
374 if (NS_FAILED(rv)) return rv;
375
376 nsACString::const_iterator i1, i2, i3, end;
377 path.BeginReading(i1);
378 path.EndReading(end);
379
380 i2 = end;
381 if (!FindInReadable(NS_LITERAL_CSTRING("?client="), i1, i2))
382 return NS_ERROR_FAILURE;
383 // i2 points to the start of clientID
384
385 i1 = i2;
386 i3 = end;
387 if (!FindInReadable(NS_LITERAL_CSTRING("&sb="), i1, i3))
388 return NS_ERROR_FAILURE;
389 // i1 points to the end of clientID
390 // i3 points to the start of isStreamBased
391
392 clientID.Assign(Substring(i2, i1));
393
394 i1 = i3;
395 i2 = end;
396 if (!FindInReadable(NS_LITERAL_CSTRING("&key="), i1, i2))
397 return NS_ERROR_FAILURE;
398 // i1 points to the end of isStreamBased
399 // i2 points to the start of key
400
401 streamBased = FindCharInReadable('1', i3, i1);
402 key.Assign(Substring(i2, end));
403
404 return NS_OK;
405 }
406
407
408 //-----------------------------------------------------------------------------
409 // nsICacheMetaDataVisitor implementation
410 //-----------------------------------------------------------------------------
411
412 NS_IMETHODIMP
413 nsAboutCacheEntry::VisitMetaDataElement(const char * key,
414 const char * value,
415 bool * keepGoing)
416 {
417 mBuffer->AppendLiteral(" <tr>\n"
418 " <th>");
419 mBuffer->Append(key);
420 mBuffer->AppendLiteral(":</th>\n"
421 " <td>");
422 char* escapedValue = nsEscapeHTML(value);
423 mBuffer->Append(escapedValue);
424 nsMemory::Free(escapedValue);
425 mBuffer->AppendLiteral("</td>\n"
426 " </tr>\n");
427
428 *keepGoing = true;
429 return NS_OK;
430 }
431
432 //-----------------------------------------------------------------------------
433 // nsICacheListener implementation
434 //-----------------------------------------------------------------------------
435
436 NS_IMETHODIMP
437 nsAboutCacheEntry::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry,
438 nsCacheAccessMode access,
439 nsresult status)
440 {
441 nsresult rv;
442
443 if (entry)
444 rv = WriteCacheEntryDescription(entry);
445 else
446 rv = WriteCacheEntryUnavailable();
447 if (NS_FAILED(rv)) return rv;
448
449 uint32_t n;
450 NS_NAMED_LITERAL_CSTRING(buffer, "</body>\n</html>\n");
451 mOutputStream->Write(buffer.get(), buffer.Length(), &n);
452 mOutputStream->Close();
453 mOutputStream = nullptr;
454
455 return NS_OK;
456 }
457
458 NS_IMETHODIMP
459 nsAboutCacheEntry::OnCacheEntryDoomed(nsresult status)
460 {
461 return NS_ERROR_NOT_IMPLEMENTED;
462 }

mercurial