1 : /* -*- Mode: C++; tab-width: 2; 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) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Henrik Gemal <mozilla@gemal.dk>
24 : * Darin Fisher <darin@netscape.com>
25 : * Alexey Chernyak <alexeyc@bigfoot.com> (XHTML 1.1 conversion)
26 : * Steffen Wilberg <steffen.wilberg@web.de> (new layout)
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either the GNU General Public License Version 2 or later (the "GPL"), or
30 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #include "nsAboutCache.h"
43 : #include "nsIIOService.h"
44 : #include "nsIServiceManager.h"
45 : #include "nsIInputStream.h"
46 : #include "nsIStorageStream.h"
47 : #include "nsISimpleEnumerator.h"
48 : #include "nsXPIDLString.h"
49 : #include "nsIURI.h"
50 : #include "nsCOMPtr.h"
51 : #include "nsNetUtil.h"
52 : #include "prtime.h"
53 : #include "nsEscape.h"
54 :
55 : #include "nsICacheService.h"
56 :
57 0 : static PRTime SecondsToPRTime(PRUint32 t_sec)
58 : {
59 : PRTime t_usec, usec_per_sec;
60 0 : LL_I2L(t_usec, t_sec);
61 0 : LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
62 0 : LL_MUL(t_usec, t_usec, usec_per_sec);
63 0 : return t_usec;
64 : }
65 0 : static void PrintTimeString(char *buf, PRUint32 bufsize, PRUint32 t_sec)
66 : {
67 : PRExplodedTime et;
68 0 : PRTime t_usec = SecondsToPRTime(t_sec);
69 0 : PR_ExplodeTime(t_usec, PR_LocalTimeParameters, &et);
70 0 : PR_FormatTime(buf, bufsize, "%Y-%m-%d %H:%M:%S", &et);
71 0 : }
72 :
73 :
74 0 : NS_IMPL_ISUPPORTS2(nsAboutCache, nsIAboutModule, nsICacheVisitor)
75 :
76 : NS_IMETHODIMP
77 0 : nsAboutCache::NewChannel(nsIURI *aURI, nsIChannel **result)
78 : {
79 0 : NS_ENSURE_ARG_POINTER(aURI);
80 : nsresult rv;
81 : PRUint32 bytesWritten;
82 :
83 0 : *result = nsnull;
84 : // Get the cache manager service
85 : nsCOMPtr<nsICacheService> cacheService =
86 0 : do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
87 0 : if (NS_FAILED(rv)) return rv;
88 :
89 0 : nsCOMPtr<nsIStorageStream> storageStream;
90 0 : nsCOMPtr<nsIOutputStream> outputStream;
91 :
92 : // Init: (block size, maximum length)
93 0 : rv = NS_NewStorageStream(256, (PRUint32)-1, getter_AddRefs(storageStream));
94 0 : if (NS_FAILED(rv)) return rv;
95 :
96 0 : rv = storageStream->GetOutputStream(0, getter_AddRefs(outputStream));
97 0 : if (NS_FAILED(rv)) return rv;
98 :
99 : mBuffer.AssignLiteral(
100 : "<!DOCTYPE html>\n"
101 : "<html>\n"
102 : "<head>\n"
103 : " <title>Information about the Cache Service</title>\n"
104 : " <link rel=\"stylesheet\" "
105 : "href=\"chrome://global/skin/about.css\" type=\"text/css\"/>\n"
106 : " <link rel=\"stylesheet\" "
107 : "href=\"chrome://global/skin/aboutCache.css\" type=\"text/css\"/>\n"
108 : "</head>\n"
109 : "<body class=\"aboutPageWideContainer\">\n"
110 0 : "<h1>Information about the Cache Service</h1>\n");
111 :
112 0 : outputStream->Write(mBuffer.get(), mBuffer.Length(), &bytesWritten);
113 :
114 0 : rv = ParseURI(aURI, mDeviceID);
115 0 : if (NS_FAILED(rv)) return rv;
116 :
117 0 : mStream = outputStream;
118 :
119 : // nsCacheService::VisitEntries calls nsMemoryCacheDevice::Visit,
120 : // nsDiskCacheDevice::Visit and nsOfflineCacheDevice::Visit,
121 : // each of which call
122 : // 1. VisitDevice (for about:cache),
123 : // 2. VisitEntry in a loop (for about:cache?device=disk etc.)
124 0 : rv = cacheService->VisitEntries(this);
125 0 : mBuffer.Truncate();
126 0 : if (rv == NS_ERROR_NOT_AVAILABLE) {
127 0 : mBuffer.AppendLiteral("<h2>The cache is disabled.</h2>\n");
128 : }
129 0 : else if (NS_FAILED(rv)) {
130 0 : return rv;
131 : }
132 :
133 0 : if (!mDeviceID.IsEmpty()) {
134 0 : mBuffer.AppendLiteral("</table>\n");
135 : }
136 : mBuffer.AppendLiteral("</body>\n"
137 0 : "</html>\n");
138 0 : outputStream->Write(mBuffer.get(), mBuffer.Length(), &bytesWritten);
139 :
140 0 : nsCOMPtr<nsIInputStream> inStr;
141 :
142 0 : rv = storageStream->NewInputStream(0, getter_AddRefs(inStr));
143 0 : if (NS_FAILED(rv)) return rv;
144 :
145 : nsIChannel* channel;
146 : rv = NS_NewInputStreamChannel(&channel, aURI, inStr,
147 0 : NS_LITERAL_CSTRING("text/html"),
148 0 : NS_LITERAL_CSTRING("utf-8"));
149 0 : if (NS_FAILED(rv)) return rv;
150 :
151 0 : *result = channel;
152 0 : return rv;
153 : }
154 :
155 : NS_IMETHODIMP
156 0 : nsAboutCache::GetURIFlags(nsIURI *aURI, PRUint32 *result)
157 : {
158 0 : *result = 0;
159 0 : return NS_OK;
160 : }
161 :
162 : NS_IMETHODIMP
163 0 : nsAboutCache::VisitDevice(const char *deviceID,
164 : nsICacheDeviceInfo *deviceInfo,
165 : bool *visitEntries)
166 : {
167 : PRUint32 bytesWritten, value, entryCount;
168 0 : nsXPIDLCString str;
169 :
170 0 : *visitEntries = false;
171 :
172 0 : if (mDeviceID.IsEmpty() || mDeviceID.Equals(deviceID)) {
173 :
174 : // We need mStream for this
175 0 : if (!mStream)
176 0 : return NS_ERROR_FAILURE;
177 :
178 : // Write out the Cache Name
179 0 : deviceInfo->GetDescription(getter_Copies(str));
180 :
181 0 : mBuffer.AssignLiteral("<h2>");
182 0 : mBuffer.Append(str);
183 : mBuffer.AppendLiteral("</h2>\n"
184 0 : "<table id=\"");
185 0 : mBuffer.Append(deviceID);
186 0 : mBuffer.AppendLiteral("\">\n");
187 :
188 : // Write out cache info
189 : // Number of entries
190 : mBuffer.AppendLiteral(" <tr>\n"
191 : " <th>Number of entries:</th>\n"
192 0 : " <td>");
193 0 : entryCount = 0;
194 0 : deviceInfo->GetEntryCount(&entryCount);
195 0 : mBuffer.AppendInt(entryCount);
196 : mBuffer.AppendLiteral("</td>\n"
197 0 : " </tr>\n");
198 :
199 : // Maximum storage size
200 : mBuffer.AppendLiteral(" <tr>\n"
201 : " <th>Maximum storage size:</th>\n"
202 0 : " <td>");
203 0 : value = 0;
204 0 : deviceInfo->GetMaximumSize(&value);
205 0 : mBuffer.AppendInt(value/1024);
206 : mBuffer.AppendLiteral(" KiB</td>\n"
207 0 : " </tr>\n");
208 :
209 : // Storage in use
210 : mBuffer.AppendLiteral(" <tr>\n"
211 : " <th>Storage in use:</th>\n"
212 0 : " <td>");
213 0 : value = 0;
214 0 : deviceInfo->GetTotalSize(&value);
215 0 : mBuffer.AppendInt(value/1024);
216 : mBuffer.AppendLiteral(" KiB</td>\n"
217 0 : " </tr>\n");
218 :
219 0 : deviceInfo->GetUsageReport(getter_Copies(str));
220 0 : mBuffer.Append(str);
221 :
222 0 : if (mDeviceID.IsEmpty()) { // The about:cache case
223 0 : if (entryCount != 0) { // Add the "List Cache Entries" link
224 : mBuffer.AppendLiteral(" <tr>\n"
225 0 : " <th><a href=\"about:cache?device=");
226 0 : mBuffer.Append(deviceID);
227 : mBuffer.AppendLiteral("\">List Cache Entries</a></th>\n"
228 0 : " </tr>\n");
229 : }
230 0 : mBuffer.AppendLiteral("</table>\n");
231 : } else { // The about:cache?device=disk etc. case
232 0 : mBuffer.AppendLiteral("</table>\n");
233 0 : if (entryCount != 0) {
234 0 : *visitEntries = true;
235 : mBuffer.AppendLiteral("<hr/>\n"
236 : "<table id=\"entries\">\n"
237 : " <colgroup>\n"
238 : " <col id=\"col-key\">\n"
239 : " <col id=\"col-dataSize\">\n"
240 : " <col id=\"col-fetchCount\">\n"
241 : " <col id=\"col-lastModified\">\n"
242 : " <col id=\"col-expires\">\n"
243 : " </colgroup>\n"
244 : " <thead>\n"
245 : " <tr>\n"
246 : " <th>Key</th>\n"
247 : " <th>Data size</th>\n"
248 : " <th>Fetch count</th>\n"
249 : " <th>Last modified</th>\n"
250 : " <th>Expires</th>\n"
251 : " </tr>\n"
252 0 : " </thead>\n");
253 : }
254 : }
255 :
256 0 : mStream->Write(mBuffer.get(), mBuffer.Length(), &bytesWritten);
257 : }
258 :
259 0 : return NS_OK;
260 : }
261 :
262 : NS_IMETHODIMP
263 0 : nsAboutCache::VisitEntry(const char *deviceID,
264 : nsICacheEntryInfo *entryInfo,
265 : bool *visitNext)
266 : {
267 : // We need mStream for this
268 0 : if (!mStream)
269 0 : return NS_ERROR_FAILURE;
270 :
271 : nsresult rv;
272 : PRUint32 bytesWritten;
273 0 : nsCAutoString key;
274 0 : nsXPIDLCString clientID;
275 : bool streamBased;
276 :
277 0 : rv = entryInfo->GetKey(key);
278 0 : if (NS_FAILED(rv)) return rv;
279 :
280 0 : rv = entryInfo->GetClientID(getter_Copies(clientID));
281 0 : if (NS_FAILED(rv)) return rv;
282 :
283 0 : rv = entryInfo->IsStreamBased(&streamBased);
284 0 : if (NS_FAILED(rv)) return rv;
285 :
286 : // Generate a about:cache-entry URL for this entry...
287 0 : nsCAutoString url;
288 0 : url.AssignLiteral("about:cache-entry?client=");
289 0 : url += clientID;
290 0 : url.AppendLiteral("&sb=");
291 0 : url += streamBased ? '1' : '0';
292 0 : url.AppendLiteral("&key=");
293 0 : char* escapedKey = nsEscapeHTML(key.get());
294 0 : url += escapedKey; // key
295 :
296 : // Entry start...
297 0 : mBuffer.AssignLiteral(" <tr>\n");
298 :
299 : // URI
300 0 : mBuffer.AppendLiteral(" <td><a href=\"");
301 0 : mBuffer.Append(url);
302 0 : mBuffer.AppendLiteral("\">");
303 0 : mBuffer.Append(escapedKey);
304 0 : nsMemory::Free(escapedKey);
305 0 : mBuffer.AppendLiteral("</a></td>\n");
306 :
307 : // Content length
308 0 : PRUint32 length = 0;
309 0 : entryInfo->GetDataSize(&length);
310 0 : mBuffer.AppendLiteral(" <td>");
311 0 : mBuffer.AppendInt(length);
312 0 : mBuffer.AppendLiteral(" bytes</td>\n");
313 :
314 : // Number of accesses
315 0 : PRInt32 fetchCount = 0;
316 0 : entryInfo->GetFetchCount(&fetchCount);
317 0 : mBuffer.AppendLiteral(" <td>");
318 0 : mBuffer.AppendInt(fetchCount);
319 0 : mBuffer.AppendLiteral("</td>\n");
320 :
321 : // vars for reporting time
322 : char buf[255];
323 : PRUint32 t;
324 :
325 : // Last modified time
326 0 : mBuffer.AppendLiteral(" <td>");
327 0 : entryInfo->GetLastModified(&t);
328 0 : if (t) {
329 0 : PrintTimeString(buf, sizeof(buf), t);
330 0 : mBuffer.Append(buf);
331 : } else
332 0 : mBuffer.AppendLiteral("No last modified time");
333 0 : mBuffer.AppendLiteral("</td>\n");
334 :
335 : // Expires time
336 0 : mBuffer.AppendLiteral(" <td>");
337 0 : entryInfo->GetExpirationTime(&t);
338 0 : if (t < 0xFFFFFFFF) {
339 0 : PrintTimeString(buf, sizeof(buf), t);
340 0 : mBuffer.Append(buf);
341 : } else {
342 0 : mBuffer.AppendLiteral("No expiration time");
343 : }
344 0 : mBuffer.AppendLiteral("</td>\n");
345 :
346 : // Entry is done...
347 0 : mBuffer.AppendLiteral(" </tr>\n");
348 :
349 0 : mStream->Write(mBuffer.get(), mBuffer.Length(), &bytesWritten);
350 :
351 0 : *visitNext = true;
352 0 : return NS_OK;
353 : }
354 :
355 :
356 : nsresult
357 0 : nsAboutCache::ParseURI(nsIURI * uri, nsCString &deviceID)
358 : {
359 : //
360 : // about:cache[?device=string]
361 : //
362 : nsresult rv;
363 :
364 0 : deviceID.Truncate();
365 :
366 0 : nsCAutoString path;
367 0 : rv = uri->GetPath(path);
368 0 : if (NS_FAILED(rv)) return rv;
369 :
370 0 : nsACString::const_iterator start, valueStart, end;
371 0 : path.BeginReading(start);
372 0 : path.EndReading(end);
373 :
374 0 : valueStart = end;
375 0 : if (!FindInReadable(NS_LITERAL_CSTRING("?device="), start, valueStart))
376 0 : return NS_OK;
377 :
378 0 : deviceID.Assign(Substring(valueStart, end));
379 0 : return NS_OK;
380 : }
381 :
382 :
383 : nsresult
384 0 : nsAboutCache::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult)
385 : {
386 0 : nsAboutCache* about = new nsAboutCache();
387 0 : if (about == nsnull)
388 0 : return NS_ERROR_OUT_OF_MEMORY;
389 0 : NS_ADDREF(about);
390 0 : nsresult rv = about->QueryInterface(aIID, aResult);
391 0 : NS_RELEASE(about);
392 0 : return rv;
393 : }
394 :
395 :
396 :
397 : ////////////////////////////////////////////////////////////////////////////////
|