1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2001
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Darin Fisher <darin@netscape.com> (original author)
24 : * Alexey Chernyak <alexeyc@bigfoot.com> (XHTML 1.1 conversion)
25 : * Steffen Wilberg <steffen.wilberg@web.de> (new layout)
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include <limits.h>
42 :
43 : #include "nsAboutCacheEntry.h"
44 : #include "nsICacheService.h"
45 : #include "nsICacheEntryDescriptor.h"
46 : #include "nsIStorageStream.h"
47 : #include "nsNetUtil.h"
48 : #include "nsAutoPtr.h"
49 : #include "prprf.h"
50 : #include "prtime.h"
51 : #include "nsEscape.h"
52 :
53 : #define HEXDUMP_MAX_ROWS 16
54 :
55 : static void
56 0 : HexDump(PRUint32 *state, const char *buf, PRInt32 n, nsCString &result)
57 : {
58 : char temp[16];
59 :
60 : const unsigned char *p;
61 0 : while (n) {
62 0 : PR_snprintf(temp, sizeof(temp), "%08x: ", *state);
63 0 : result.Append(temp);
64 0 : *state += HEXDUMP_MAX_ROWS;
65 :
66 0 : p = (const unsigned char *) buf;
67 :
68 0 : PRInt32 i, row_max = NS_MIN(HEXDUMP_MAX_ROWS, n);
69 :
70 : // print hex codes:
71 0 : for (i = 0; i < row_max; ++i) {
72 0 : PR_snprintf(temp, sizeof(temp), "%02x ", *p++);
73 0 : result.Append(temp);
74 : }
75 0 : for (i = row_max; i < HEXDUMP_MAX_ROWS; ++i) {
76 0 : result.AppendLiteral(" ");
77 : }
78 :
79 : // print ASCII glyphs if possible:
80 0 : p = (const unsigned char *) buf;
81 0 : for (i = 0; i < row_max; ++i, ++p) {
82 0 : switch (*p) {
83 : case '<':
84 0 : result.AppendLiteral("<");
85 0 : break;
86 : case '>':
87 0 : result.AppendLiteral(">");
88 0 : break;
89 : case '&':
90 0 : result.AppendLiteral("&");
91 0 : break;
92 : default:
93 0 : if (*p < 0x7F && *p > 0x1F) {
94 0 : result.Append(*p);
95 : } else {
96 0 : result.Append('.');
97 : }
98 : }
99 : }
100 :
101 0 : result.Append('\n');
102 :
103 0 : buf += row_max;
104 0 : n -= row_max;
105 : }
106 0 : }
107 :
108 : //-----------------------------------------------------------------------------
109 : // nsAboutCacheEntry::nsISupports
110 :
111 0 : NS_IMPL_ISUPPORTS2(nsAboutCacheEntry,
112 : nsIAboutModule,
113 : nsICacheMetaDataVisitor)
114 :
115 : //-----------------------------------------------------------------------------
116 : // nsAboutCacheEntry::nsIAboutModule
117 :
118 : NS_IMETHODIMP
119 0 : nsAboutCacheEntry::NewChannel(nsIURI *uri, nsIChannel **result)
120 : {
121 0 : NS_ENSURE_ARG_POINTER(uri);
122 : nsresult rv;
123 :
124 0 : nsCOMPtr<nsIInputStream> stream;
125 0 : rv = GetContentStream(uri, getter_AddRefs(stream));
126 0 : if (NS_FAILED(rv)) return rv;
127 :
128 : return NS_NewInputStreamChannel(result, uri, stream,
129 0 : NS_LITERAL_CSTRING("text/html"),
130 0 : NS_LITERAL_CSTRING("utf-8"));
131 : }
132 :
133 : NS_IMETHODIMP
134 0 : nsAboutCacheEntry::GetURIFlags(nsIURI *aURI, PRUint32 *result)
135 : {
136 0 : *result = nsIAboutModule::HIDE_FROM_ABOUTABOUT;
137 0 : return NS_OK;
138 : }
139 :
140 : //-----------------------------------------------------------------------------
141 : // nsAboutCacheEntry
142 :
143 : nsresult
144 0 : nsAboutCacheEntry::GetContentStream(nsIURI *uri, nsIInputStream **result)
145 : {
146 0 : nsCOMPtr<nsIStorageStream> storageStream;
147 0 : nsCOMPtr<nsIOutputStream> outputStream;
148 : PRUint32 n;
149 0 : nsCString buffer;
150 : nsresult rv;
151 :
152 0 : nsCOMPtr<nsICacheEntryDescriptor> descriptor;
153 0 : OpenCacheEntry(uri, getter_AddRefs(descriptor));
154 :
155 : // Init: (block size, maximum length)
156 0 : rv = NS_NewStorageStream(256, PRUint32(-1), getter_AddRefs(storageStream));
157 0 : if (NS_FAILED(rv)) return rv;
158 :
159 0 : rv = storageStream->GetOutputStream(0, getter_AddRefs(outputStream));
160 0 : if (NS_FAILED(rv)) return rv;
161 :
162 : buffer.AssignLiteral(
163 : "<!DOCTYPE html>\n"
164 : "<html>\n"
165 : "<head>\n"
166 : " <title>Cache entry information</title>\n"
167 : " <link rel=\"stylesheet\" "
168 : "href=\"chrome://global/skin/about.css\" type=\"text/css\"/>\n"
169 : " <link rel=\"stylesheet\" "
170 : "href=\"chrome://global/skin/aboutCacheEntry.css\" type=\"text/css\"/>\n"
171 : "</head>\n"
172 : "<body>\n"
173 0 : "<h1>Cache entry information</h1>\n");
174 0 : outputStream->Write(buffer.get(), buffer.Length(), &n);
175 :
176 0 : if (descriptor)
177 0 : rv = WriteCacheEntryDescription(outputStream, descriptor);
178 : else
179 0 : rv = WriteCacheEntryUnavailable(outputStream);
180 0 : if (NS_FAILED(rv)) return rv;
181 :
182 0 : buffer.AssignLiteral("</body>\n</html>\n");
183 0 : outputStream->Write(buffer.get(), buffer.Length(), &n);
184 :
185 0 : nsCOMPtr<nsIInputStream> inStr;
186 : PRUint32 size;
187 :
188 0 : rv = storageStream->GetLength(&size);
189 0 : if (NS_FAILED(rv)) return rv;
190 :
191 0 : return storageStream->NewInputStream(0, result);
192 : }
193 :
194 : nsresult
195 0 : nsAboutCacheEntry::OpenCacheEntry(nsIURI *uri, nsICacheEntryDescriptor **result)
196 : {
197 : nsresult rv;
198 0 : nsCAutoString clientID, key;
199 0 : bool streamBased = true;
200 :
201 0 : rv = ParseURI(uri, clientID, streamBased, key);
202 0 : if (NS_FAILED(rv)) return rv;
203 :
204 : nsCOMPtr<nsICacheService> serv =
205 0 : do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
206 0 : if (NS_FAILED(rv)) return rv;
207 :
208 0 : nsCOMPtr<nsICacheSession> session;
209 0 : rv = serv->CreateSession(clientID.get(),
210 : nsICache::STORE_ANYWHERE,
211 : streamBased,
212 0 : getter_AddRefs(session));
213 0 : if (NS_FAILED(rv)) return rv;
214 :
215 0 : rv = session->SetDoomEntriesIfExpired(false);
216 0 : if (NS_FAILED(rv)) return rv;
217 :
218 0 : rv = session->OpenCacheEntry(key, nsICache::ACCESS_READ, false, result);
219 0 : return rv;
220 : }
221 :
222 :
223 : //-----------------------------------------------------------------------------
224 : // helper methods
225 : //-----------------------------------------------------------------------------
226 :
227 0 : static PRTime SecondsToPRTime(PRUint32 t_sec)
228 : {
229 : PRTime t_usec, usec_per_sec;
230 0 : LL_I2L(t_usec, t_sec);
231 0 : LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
232 0 : LL_MUL(t_usec, t_usec, usec_per_sec);
233 0 : return t_usec;
234 : }
235 0 : static void PrintTimeString(char *buf, PRUint32 bufsize, PRUint32 t_sec)
236 : {
237 : PRExplodedTime et;
238 0 : PRTime t_usec = SecondsToPRTime(t_sec);
239 0 : PR_ExplodeTime(t_usec, PR_LocalTimeParameters, &et);
240 0 : PR_FormatTime(buf, bufsize, "%Y-%m-%d %H:%M:%S", &et);
241 0 : }
242 :
243 : #define APPEND_ROW(label, value) \
244 : PR_BEGIN_MACRO \
245 : buffer.AppendLiteral(" <tr>\n" \
246 : " <th>"); \
247 : buffer.AppendLiteral(label); \
248 : buffer.AppendLiteral(":</th>\n" \
249 : " <td>"); \
250 : buffer.Append(value); \
251 : buffer.AppendLiteral("</td>\n" \
252 : " </tr>\n"); \
253 : PR_END_MACRO
254 :
255 : nsresult
256 0 : nsAboutCacheEntry::WriteCacheEntryDescription(nsIOutputStream *outputStream,
257 : nsICacheEntryDescriptor *descriptor)
258 : {
259 : nsresult rv;
260 0 : nsCString buffer;
261 : PRUint32 n;
262 :
263 0 : nsCAutoString str;
264 :
265 0 : rv = descriptor->GetKey(str);
266 0 : if (NS_FAILED(rv)) return rv;
267 :
268 0 : buffer.SetCapacity(4096);
269 : buffer.AssignLiteral("<table>\n"
270 : " <tr>\n"
271 : " <th>key:</th>\n"
272 0 : " <td id=\"td-key\">");
273 :
274 : // Test if the key is actually a URI
275 0 : nsCOMPtr<nsIURI> uri;
276 0 : bool isJS = false;
277 0 : bool isData = false;
278 :
279 0 : rv = NS_NewURI(getter_AddRefs(uri), str);
280 : // javascript: and data: URLs should not be linkified
281 : // since clicking them can cause scripts to run - bug 162584
282 0 : if (NS_SUCCEEDED(rv)) {
283 0 : uri->SchemeIs("javascript", &isJS);
284 0 : uri->SchemeIs("data", &isData);
285 : }
286 0 : char* escapedStr = nsEscapeHTML(str.get());
287 0 : if (NS_SUCCEEDED(rv) && !(isJS || isData)) {
288 0 : buffer.AppendLiteral("<a href=\"");
289 0 : buffer.Append(escapedStr);
290 0 : buffer.AppendLiteral("\">");
291 0 : buffer.Append(escapedStr);
292 0 : buffer.AppendLiteral("</a>");
293 0 : uri = 0;
294 : }
295 : else
296 0 : buffer.Append(escapedStr);
297 0 : nsMemory::Free(escapedStr);
298 : buffer.AppendLiteral("</td>\n"
299 0 : " </tr>\n");
300 :
301 : // temp vars for reporting
302 : char timeBuf[255];
303 0 : PRUint32 u = 0;
304 0 : PRInt32 i = 0;
305 0 : nsCAutoString s;
306 :
307 : // Fetch Count
308 0 : s.Truncate();
309 0 : descriptor->GetFetchCount(&i);
310 0 : s.AppendInt(i);
311 0 : APPEND_ROW("fetch count", s);
312 :
313 : // Last Fetched
314 0 : descriptor->GetLastFetched(&u);
315 0 : if (u) {
316 0 : PrintTimeString(timeBuf, sizeof(timeBuf), u);
317 0 : APPEND_ROW("last fetched", timeBuf);
318 : } else {
319 0 : APPEND_ROW("last fetched", "No last fetch time");
320 : }
321 :
322 : // Last Modified
323 0 : descriptor->GetLastModified(&u);
324 0 : if (u) {
325 0 : PrintTimeString(timeBuf, sizeof(timeBuf), u);
326 0 : APPEND_ROW("last modified", timeBuf);
327 : } else {
328 0 : APPEND_ROW("last modified", "No last modified time");
329 : }
330 :
331 : // Expiration Time
332 0 : descriptor->GetExpirationTime(&u);
333 0 : if (u < 0xFFFFFFFF) {
334 0 : PrintTimeString(timeBuf, sizeof(timeBuf), u);
335 0 : APPEND_ROW("expires", timeBuf);
336 : } else {
337 0 : APPEND_ROW("expires", "No expiration time");
338 : }
339 :
340 : // Data Size
341 0 : s.Truncate();
342 : PRUint32 dataSize;
343 0 : descriptor->GetStorageDataSize(&dataSize);
344 0 : s.AppendInt((PRInt32)dataSize); // XXX nsICacheEntryInfo interfaces should be fixed.
345 0 : APPEND_ROW("Data size", s);
346 :
347 : // Storage Policy
348 :
349 : // XXX Stream Based?
350 :
351 : // XXX Cache Device
352 : // File on disk
353 0 : nsCOMPtr<nsIFile> cacheFile;
354 0 : rv = descriptor->GetFile(getter_AddRefs(cacheFile));
355 0 : if (NS_SUCCEEDED(rv)) {
356 0 : nsAutoString filePath;
357 0 : cacheFile->GetPath(filePath);
358 0 : APPEND_ROW("file on disk", NS_ConvertUTF16toUTF8(filePath));
359 : }
360 : else
361 0 : APPEND_ROW("file on disk", "none");
362 :
363 : // Security Info
364 0 : nsCOMPtr<nsISupports> securityInfo;
365 0 : descriptor->GetSecurityInfo(getter_AddRefs(securityInfo));
366 0 : if (securityInfo) {
367 0 : APPEND_ROW("Security", "This is a secure document.");
368 : } else {
369 0 : APPEND_ROW("Security",
370 : "This document does not have any security info associated with it.");
371 : }
372 :
373 : buffer.AppendLiteral("</table>\n"
374 : "<hr/>\n"
375 0 : "<table>\n");
376 : // Meta Data
377 : // let's just look for some well known (HTTP) meta data tags, for now.
378 :
379 : // Client ID
380 0 : nsXPIDLCString str2;
381 0 : descriptor->GetClientID(getter_Copies(str2));
382 0 : if (!str2.IsEmpty()) APPEND_ROW("Client", str2);
383 :
384 :
385 0 : mBuffer = &buffer; // make it available for VisitMetaDataElement().
386 : // nsCacheEntryDescriptor::VisitMetaData calls
387 : // nsCacheEntry.h VisitMetaDataElements, which returns
388 : // nsCacheMetaData::VisitElements, which calls
389 : // nsAboutCacheEntry::VisitMetaDataElement (below) in a loop.
390 0 : descriptor->VisitMetaData(this);
391 0 : mBuffer = nsnull;
392 :
393 0 : buffer.AppendLiteral("</table>\n");
394 0 : outputStream->Write(buffer.get(), buffer.Length(), &n);
395 :
396 0 : buffer.Truncate();
397 :
398 : // Provide a hexdump of the data
399 0 : if (dataSize) { // don't draw an <hr> if the Data Size is 0.
400 0 : nsCOMPtr<nsIInputStream> stream;
401 0 : descriptor->OpenInputStream(0, getter_AddRefs(stream));
402 0 : if (stream) {
403 : buffer.AssignLiteral("<hr/>\n"
404 0 : "<pre>");
405 0 : PRUint32 hexDumpState = 0;
406 : char chunk[4096];
407 0 : while(NS_SUCCEEDED(stream->Read(chunk, sizeof(chunk), &n)) &&
408 : n > 0) {
409 0 : HexDump(&hexDumpState, chunk, n, buffer);
410 0 : outputStream->Write(buffer.get(), buffer.Length(), &n);
411 0 : buffer.Truncate();
412 : }
413 0 : buffer.AssignLiteral("</pre>\n");
414 0 : outputStream->Write(buffer.get(), buffer.Length(), &n);
415 : }
416 : }
417 0 : return NS_OK;
418 : }
419 :
420 : nsresult
421 0 : nsAboutCacheEntry::WriteCacheEntryUnavailable(nsIOutputStream *outputStream)
422 : {
423 : PRUint32 n;
424 0 : NS_NAMED_LITERAL_CSTRING(buffer,
425 : "The cache entry you selected is not available.");
426 0 : outputStream->Write(buffer.get(), buffer.Length(), &n);
427 0 : return NS_OK;
428 : }
429 :
430 : nsresult
431 0 : nsAboutCacheEntry::ParseURI(nsIURI *uri, nsCString &clientID,
432 : bool &streamBased, nsCString &key)
433 : {
434 : //
435 : // about:cache-entry?client=[string]&sb=[boolean]&key=[string]
436 : //
437 : nsresult rv;
438 :
439 0 : nsCAutoString path;
440 0 : rv = uri->GetPath(path);
441 0 : if (NS_FAILED(rv)) return rv;
442 :
443 0 : nsACString::const_iterator i1, i2, i3, end;
444 0 : path.BeginReading(i1);
445 0 : path.EndReading(end);
446 :
447 0 : i2 = end;
448 0 : if (!FindInReadable(NS_LITERAL_CSTRING("?client="), i1, i2))
449 0 : return NS_ERROR_FAILURE;
450 : // i2 points to the start of clientID
451 :
452 0 : i1 = i2;
453 0 : i3 = end;
454 0 : if (!FindInReadable(NS_LITERAL_CSTRING("&sb="), i1, i3))
455 0 : return NS_ERROR_FAILURE;
456 : // i1 points to the end of clientID
457 : // i3 points to the start of isStreamBased
458 :
459 0 : clientID.Assign(Substring(i2, i1));
460 :
461 0 : i1 = i3;
462 0 : i2 = end;
463 0 : if (!FindInReadable(NS_LITERAL_CSTRING("&key="), i1, i2))
464 0 : return NS_ERROR_FAILURE;
465 : // i1 points to the end of isStreamBased
466 : // i2 points to the start of key
467 :
468 0 : streamBased = FindCharInReadable('1', i3, i1);
469 0 : key.Assign(Substring(i2, end));
470 :
471 0 : return NS_OK;
472 : }
473 :
474 :
475 : //-----------------------------------------------------------------------------
476 : // nsICacheMetaDataVisitor implementation
477 : //-----------------------------------------------------------------------------
478 :
479 : NS_IMETHODIMP
480 0 : nsAboutCacheEntry::VisitMetaDataElement(const char * key,
481 : const char * value,
482 : bool * keepGoing)
483 : {
484 : mBuffer->AppendLiteral(" <tr>\n"
485 0 : " <th>");
486 0 : mBuffer->Append(key);
487 : mBuffer->AppendLiteral(":</th>\n"
488 0 : " <td>");
489 0 : char* escapedValue = nsEscapeHTML(value);
490 0 : mBuffer->Append(escapedValue);
491 0 : nsMemory::Free(escapedValue);
492 : mBuffer->AppendLiteral("</td>\n"
493 0 : " </tr>\n");
494 :
495 0 : *keepGoing = true;
496 0 : return NS_OK;
497 : }
|