|
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("<"); |
|
49 break; |
|
50 case '>': |
|
51 result.AppendLiteral(">"); |
|
52 break; |
|
53 case '&': |
|
54 result.AppendLiteral("&"); |
|
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 } |