Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/protocol/about/nsAboutCacheEntry.cpp
Line
Count
Source (jump to first uncovered line)
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
8
#include "mozilla/Sprintf.h"
9
10
#include "nsAboutCache.h"
11
#include "nsICacheStorage.h"
12
#include "CacheObserver.h"
13
#include "nsNetUtil.h"
14
#include "nsEscape.h"
15
#include "nsIAsyncInputStream.h"
16
#include "nsIAsyncOutputStream.h"
17
#include "nsAboutProtocolUtils.h"
18
#include "nsContentUtils.h"
19
#include "nsInputStreamPump.h"
20
#include "CacheFileUtils.h"
21
#include <algorithm>
22
#include "nsIPipe.h"
23
24
using namespace mozilla::net;
25
26
0
#define HEXDUMP_MAX_ROWS 16
27
28
static void
29
HexDump(uint32_t *state, const char *buf, int32_t n, nsCString &result)
30
0
{
31
0
  char temp[16];
32
0
33
0
  const unsigned char *p;
34
0
  while (n) {
35
0
    SprintfLiteral(temp, "%08x:  ", *state);
36
0
    result.Append(temp);
37
0
    *state += HEXDUMP_MAX_ROWS;
38
0
39
0
    p = (const unsigned char *) buf;
40
0
41
0
    int32_t i, row_max = std::min(HEXDUMP_MAX_ROWS, n);
42
0
43
0
    // print hex codes:
44
0
    for (i = 0; i < row_max; ++i) {
45
0
      SprintfLiteral(temp, "%02x  ", *p++);
46
0
      result.Append(temp);
47
0
    }
48
0
    for (i = row_max; i < HEXDUMP_MAX_ROWS; ++i) {
49
0
      result.AppendLiteral("    ");
50
0
    }
51
0
52
0
    // print ASCII glyphs if possible:
53
0
    p = (const unsigned char *) buf;
54
0
    for (i = 0; i < row_max; ++i, ++p) {
55
0
      switch (*p) {
56
0
      case '<':
57
0
        result.AppendLiteral("&lt;");
58
0
        break;
59
0
      case '>':
60
0
        result.AppendLiteral("&gt;");
61
0
        break;
62
0
      case '&':
63
0
        result.AppendLiteral("&amp;");
64
0
        break;
65
0
      default:
66
0
        if (*p < 0x7F && *p > 0x1F) {
67
0
          result.Append(*p);
68
0
        } else {
69
0
          result.Append('.');
70
0
        }
71
0
      }
72
0
    }
73
0
74
0
    result.Append('\n');
75
0
76
0
    buf += row_max;
77
0
    n -= row_max;
78
0
  }
79
0
}
80
81
//-----------------------------------------------------------------------------
82
// nsAboutCacheEntry::nsISupports
83
84
NS_IMPL_ISUPPORTS(nsAboutCacheEntry,
85
                  nsIAboutModule)
86
NS_IMPL_ISUPPORTS(nsAboutCacheEntry::Channel,
87
                  nsICacheEntryOpenCallback,
88
                  nsICacheEntryMetaDataVisitor,
89
                  nsIStreamListener,
90
                  nsIRequest,
91
                  nsIChannel)
92
93
//-----------------------------------------------------------------------------
94
// nsAboutCacheEntry::nsIAboutModule
95
96
NS_IMETHODIMP
97
nsAboutCacheEntry::NewChannel(nsIURI* uri,
98
                              nsILoadInfo* aLoadInfo,
99
                              nsIChannel** result)
100
0
{
101
0
    NS_ENSURE_ARG_POINTER(uri);
102
0
    nsresult rv;
103
0
104
0
    RefPtr<Channel> channel = new Channel();
105
0
    rv = channel->Init(uri, aLoadInfo);
106
0
    if (NS_FAILED(rv)) return rv;
107
0
108
0
    channel.forget(result);
109
0
110
0
    return NS_OK;
111
0
}
112
113
NS_IMETHODIMP
114
nsAboutCacheEntry::GetURIFlags(nsIURI *aURI, uint32_t *result)
115
0
{
116
0
    *result = nsIAboutModule::HIDE_FROM_ABOUTABOUT |
117
0
              nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT;
118
0
    return NS_OK;
119
0
}
120
121
//-----------------------------------------------------------------------------
122
// nsAboutCacheEntry::Channel
123
124
nsresult
125
nsAboutCacheEntry::Channel::Init(nsIURI* uri, nsILoadInfo* aLoadInfo)
126
0
{
127
0
    nsresult rv;
128
0
129
0
    nsCOMPtr<nsIInputStream> stream;
130
0
    rv = GetContentStream(uri, getter_AddRefs(stream));
131
0
    if (NS_FAILED(rv)) return rv;
132
0
133
0
    rv =  NS_NewInputStreamChannelInternal(getter_AddRefs(mChannel),
134
0
                                           uri,
135
0
                                           stream.forget(),
136
0
                                           NS_LITERAL_CSTRING("text/html"),
137
0
                                           NS_LITERAL_CSTRING("utf-8"),
138
0
                                           aLoadInfo);
139
0
    if (NS_FAILED(rv)) return rv;
140
0
141
0
    return NS_OK;
142
0
}
143
144
nsresult
145
nsAboutCacheEntry::Channel::GetContentStream(nsIURI *uri, nsIInputStream **result)
146
0
{
147
0
    nsresult rv;
148
0
149
0
    // Init: (block size, maximum length)
150
0
    nsCOMPtr<nsIAsyncInputStream> inputStream;
151
0
    rv = NS_NewPipe2(getter_AddRefs(inputStream),
152
0
                     getter_AddRefs(mOutputStream),
153
0
                     true, false,
154
0
                     256, UINT32_MAX);
155
0
    if (NS_FAILED(rv)) return rv;
156
0
157
0
    NS_NAMED_LITERAL_CSTRING(
158
0
      buffer,
159
0
      "<!DOCTYPE html>\n"
160
0
      "<html>\n"
161
0
      "<head>\n"
162
0
      "  <title>Cache entry information</title>\n"
163
0
      "  <link rel=\"stylesheet\" "
164
0
      "href=\"chrome://global/skin/about.css\" type=\"text/css\"/>\n"
165
0
      "  <link rel=\"stylesheet\" "
166
0
      "href=\"chrome://global/skin/aboutCacheEntry.css\" type=\"text/css\"/>\n"
167
0
      "</head>\n"
168
0
      "<body>\n"
169
0
      "<h1>Cache entry information</h1>\n");
170
0
    uint32_t n;
171
0
    rv = mOutputStream->Write(buffer.get(), buffer.Length(), &n);
172
0
    if (NS_FAILED(rv)) return rv;
173
0
    if (n != buffer.Length()) return NS_ERROR_UNEXPECTED;
174
0
175
0
    rv = OpenCacheEntry(uri);
176
0
    if (NS_FAILED(rv)) return rv;
177
0
178
0
    inputStream.forget(result);
179
0
    return NS_OK;
180
0
}
181
182
nsresult
183
nsAboutCacheEntry::Channel::OpenCacheEntry(nsIURI *uri)
184
0
{
185
0
    nsresult rv;
186
0
187
0
    rv = ParseURI(uri, mStorageName, getter_AddRefs(mLoadInfo),
188
0
                       mEnhanceId, getter_AddRefs(mCacheURI));
189
0
    if (NS_FAILED(rv)) return rv;
190
0
191
0
    return OpenCacheEntry();
192
0
}
193
194
nsresult
195
nsAboutCacheEntry::Channel::OpenCacheEntry()
196
0
{
197
0
    nsresult rv;
198
0
199
0
    nsCOMPtr<nsICacheStorage> storage;
200
0
    rv = nsAboutCache::GetStorage(mStorageName, mLoadInfo, getter_AddRefs(storage));
201
0
    if (NS_FAILED(rv)) return rv;
202
0
203
0
    // Invokes OnCacheEntryAvailable()
204
0
    rv = storage->AsyncOpenURI(mCacheURI, mEnhanceId,
205
0
                               nsICacheStorage::OPEN_READONLY |
206
0
                               nsICacheStorage::OPEN_SECRETLY,
207
0
                               this);
208
0
    if (NS_FAILED(rv)) return rv;
209
0
210
0
    return NS_OK;
211
0
}
212
213
nsresult
214
nsAboutCacheEntry::Channel::ParseURI(nsIURI *uri,
215
                                     nsACString &storageName,
216
                                     nsILoadContextInfo **loadInfo,
217
                                     nsCString &enahnceID,
218
                                     nsIURI **cacheUri)
219
0
{
220
0
    //
221
0
    // about:cache-entry?storage=[string]&contenxt=[string]&eid=[string]&uri=[string]
222
0
    //
223
0
    nsresult rv;
224
0
225
0
    nsAutoCString path;
226
0
    rv = uri->GetPathQueryRef(path);
227
0
    if (NS_FAILED(rv))
228
0
        return rv;
229
0
230
0
    nsACString::const_iterator keyBegin, keyEnd, valBegin, begin, end;
231
0
    path.BeginReading(begin);
232
0
    path.EndReading(end);
233
0
234
0
    keyBegin = begin; keyEnd = end;
235
0
    if (!FindInReadable(NS_LITERAL_CSTRING("?storage="), keyBegin, keyEnd))
236
0
        return NS_ERROR_FAILURE;
237
0
238
0
    valBegin = keyEnd; // the value of the storage key starts after the key
239
0
240
0
    keyBegin = keyEnd; keyEnd = end;
241
0
    if (!FindInReadable(NS_LITERAL_CSTRING("&context="), keyBegin, keyEnd))
242
0
        return NS_ERROR_FAILURE;
243
0
244
0
    storageName.Assign(Substring(valBegin, keyBegin));
245
0
    valBegin = keyEnd; // the value of the context key starts after the key
246
0
247
0
    keyBegin = keyEnd; keyEnd = end;
248
0
    if (!FindInReadable(NS_LITERAL_CSTRING("&eid="), keyBegin, keyEnd))
249
0
        return NS_ERROR_FAILURE;
250
0
251
0
    nsAutoCString contextKey(Substring(valBegin, keyBegin));
252
0
    valBegin = keyEnd; // the value of the eid key starts after the key
253
0
254
0
    keyBegin = keyEnd; keyEnd = end;
255
0
    if (!FindInReadable(NS_LITERAL_CSTRING("&uri="), keyBegin, keyEnd))
256
0
        return NS_ERROR_FAILURE;
257
0
258
0
    enahnceID.Assign(Substring(valBegin, keyBegin));
259
0
260
0
    valBegin = keyEnd; // the value of the uri key starts after the key
261
0
    nsAutoCString uriSpec(Substring(valBegin, end)); // uri is the last one
262
0
263
0
    // Uf... parsing done, now get some objects from it...
264
0
265
0
    nsCOMPtr<nsILoadContextInfo> info =
266
0
      CacheFileUtils::ParseKey(contextKey);
267
0
    if (!info)
268
0
        return NS_ERROR_FAILURE;
269
0
    info.forget(loadInfo);
270
0
271
0
    rv = NS_NewURI(cacheUri, uriSpec);
272
0
    if (NS_FAILED(rv))
273
0
        return rv;
274
0
275
0
    return NS_OK;
276
0
}
277
278
//-----------------------------------------------------------------------------
279
// nsICacheEntryOpenCallback implementation
280
//-----------------------------------------------------------------------------
281
282
NS_IMETHODIMP
283
nsAboutCacheEntry::Channel::OnCacheEntryCheck(nsICacheEntry *aEntry,
284
                                              nsIApplicationCache *aApplicationCache,
285
                                              uint32_t *result)
286
0
{
287
0
    *result = nsICacheEntryOpenCallback::ENTRY_WANTED;
288
0
    return NS_OK;
289
0
}
290
291
NS_IMETHODIMP
292
nsAboutCacheEntry::Channel::OnCacheEntryAvailable(nsICacheEntry *entry,
293
                                                  bool isNew,
294
                                                  nsIApplicationCache *aApplicationCache,
295
                                                  nsresult status)
296
0
{
297
0
    nsresult rv;
298
0
299
0
    mWaitingForData = false;
300
0
    if (entry) {
301
0
        rv = WriteCacheEntryDescription(entry);
302
0
    } else {
303
0
        rv = WriteCacheEntryUnavailable();
304
0
    }
305
0
    if (NS_FAILED(rv)) return rv;
306
0
307
0
308
0
    if (!mWaitingForData) {
309
0
        // Data is not expected, close the output of content now.
310
0
        CloseContent();
311
0
    }
312
0
313
0
    return NS_OK;
314
0
}
315
316
//-----------------------------------------------------------------------------
317
// Print-out helper methods
318
//-----------------------------------------------------------------------------
319
320
#define APPEND_ROW(label, value) \
321
0
    PR_BEGIN_MACRO \
322
0
    buffer.AppendLiteral("  <tr>\n" \
323
0
                         "    <th>"); \
324
0
    buffer.AppendLiteral(label); \
325
0
    buffer.AppendLiteral(":</th>\n" \
326
0
                         "    <td>"); \
327
0
    buffer.Append(value); \
328
0
    buffer.AppendLiteral("</td>\n" \
329
0
                         "  </tr>\n"); \
330
0
    PR_END_MACRO
331
332
nsresult
333
nsAboutCacheEntry::Channel::WriteCacheEntryDescription(nsICacheEntry *entry)
334
0
{
335
0
    nsresult rv;
336
0
    nsCString buffer;
337
0
    uint32_t n;
338
0
339
0
    nsAutoCString str;
340
0
341
0
    rv = entry->GetKey(str);
342
0
    if (NS_FAILED(rv)) return rv;
343
0
344
0
    buffer.SetCapacity(4096);
345
0
    buffer.AssignLiteral("<table>\n"
346
0
                         "  <tr>\n"
347
0
                         "    <th>key:</th>\n"
348
0
                         "    <td id=\"td-key\">");
349
0
350
0
    // Test if the key is actually a URI
351
0
    nsCOMPtr<nsIURI> uri;
352
0
    bool isJS = false;
353
0
    bool isData = false;
354
0
355
0
    rv = NS_NewURI(getter_AddRefs(uri), str);
356
0
    // javascript: and data: URLs should not be linkified
357
0
    // since clicking them can cause scripts to run - bug 162584
358
0
    if (NS_SUCCEEDED(rv)) {
359
0
        uri->SchemeIs("javascript", &isJS);
360
0
        uri->SchemeIs("data", &isData);
361
0
    }
362
0
    nsAutoCString escapedStr;
363
0
    nsAppendEscapedHTML(str, escapedStr);
364
0
    if (NS_SUCCEEDED(rv) && !(isJS || isData)) {
365
0
        buffer.AppendLiteral("<a href=\"");
366
0
        buffer.Append(escapedStr);
367
0
        buffer.AppendLiteral("\">");
368
0
        buffer.Append(escapedStr);
369
0
        buffer.AppendLiteral("</a>");
370
0
        uri = nullptr;
371
0
    } else {
372
0
        buffer.Append(escapedStr);
373
0
    }
374
0
    buffer.AppendLiteral("</td>\n"
375
0
                         "  </tr>\n");
376
0
377
0
    // temp vars for reporting
378
0
    char timeBuf[255];
379
0
    uint32_t u = 0;
380
0
    int32_t  i = 0;
381
0
    nsAutoCString s;
382
0
383
0
    // Fetch Count
384
0
    s.Truncate();
385
0
    entry->GetFetchCount(&i);
386
0
    s.AppendInt(i);
387
0
    APPEND_ROW("fetch count", s);
388
0
389
0
    // Last Fetched
390
0
    entry->GetLastFetched(&u);
391
0
    if (u) {
392
0
        PrintTimeString(timeBuf, sizeof(timeBuf), u);
393
0
        APPEND_ROW("last fetched", timeBuf);
394
0
    } else {
395
0
        APPEND_ROW("last fetched", "No last fetch time (bug 1000338)");
396
0
    }
397
0
398
0
    // Last Modified
399
0
    entry->GetLastModified(&u);
400
0
    if (u) {
401
0
        PrintTimeString(timeBuf, sizeof(timeBuf), u);
402
0
        APPEND_ROW("last modified", timeBuf);
403
0
    } else {
404
0
        APPEND_ROW("last modified", "No last modified time (bug 1000338)");
405
0
    }
406
0
407
0
    // Expiration Time
408
0
    entry->GetExpirationTime(&u);
409
0
410
0
    // Bug - 633747.
411
0
    // When expiration time is 0, we show 1970-01-01 01:00:00 which is confusing.
412
0
    // So we check if time is 0, then we show a message, "Expired Immediately"
413
0
    if (u == 0) {
414
0
        APPEND_ROW("expires", "Expired Immediately");
415
0
    } else if (u < 0xFFFFFFFF) {
416
0
        PrintTimeString(timeBuf, sizeof(timeBuf), u);
417
0
        APPEND_ROW("expires", timeBuf);
418
0
    } else {
419
0
        APPEND_ROW("expires", "No expiration time");
420
0
    }
421
0
422
0
    // Data Size
423
0
    s.Truncate();
424
0
    uint32_t dataSize;
425
0
    if (NS_FAILED(entry->GetStorageDataSize(&dataSize)))
426
0
        dataSize = 0;
427
0
    s.AppendInt((int32_t)dataSize);     // XXX nsICacheEntryInfo interfaces should be fixed.
428
0
    s.AppendLiteral(" B");
429
0
    APPEND_ROW("Data size", s);
430
0
431
0
    // TODO - mayhemer
432
0
    // Here used to be a link to the disk file (in the old cache for entries that
433
0
    // did not fit any of the block files, in the new cache every time).
434
0
    // I'd rather have a small set of buttons here to action on the entry:
435
0
    // 1. save the content
436
0
    // 2. save as a complete HTTP response (response head, headers, content)
437
0
    // 3. doom the entry
438
0
    // A new bug(s) should be filed here.
439
0
440
0
    // Security Info
441
0
    nsCOMPtr<nsISupports> securityInfo;
442
0
    entry->GetSecurityInfo(getter_AddRefs(securityInfo));
443
0
    if (securityInfo) {
444
0
        APPEND_ROW("Security", "This is a secure document.");
445
0
    } else {
446
0
        APPEND_ROW("Security",
447
0
                   "This document does not have any security info associated with it.");
448
0
    }
449
0
450
0
    buffer.AppendLiteral("</table>\n"
451
0
                         "<hr/>\n"
452
0
                         "<table>\n");
453
0
454
0
    mBuffer = &buffer;  // make it available for OnMetaDataElement().
455
0
    entry->VisitMetaData(this);
456
0
    mBuffer = nullptr;
457
0
458
0
    buffer.AppendLiteral("</table>\n");
459
0
    mOutputStream->Write(buffer.get(), buffer.Length(), &n);
460
0
    buffer.Truncate();
461
0
462
0
    // Provide a hexdump of the data
463
0
    if (!dataSize) {
464
0
        return NS_OK;
465
0
    }
466
0
467
0
    nsCOMPtr<nsIInputStream> stream;
468
0
    entry->OpenInputStream(0, getter_AddRefs(stream));
469
0
    if (!stream) {
470
0
        return NS_OK;
471
0
    }
472
0
473
0
    RefPtr<nsInputStreamPump> pump;
474
0
    rv = nsInputStreamPump::Create(getter_AddRefs(pump), stream);
475
0
    if (NS_FAILED(rv)) {
476
0
        return NS_OK; // just ignore
477
0
    }
478
0
479
0
    rv = pump->AsyncRead(this, nullptr);
480
0
    if (NS_FAILED(rv)) {
481
0
        return NS_OK; // just ignore
482
0
    }
483
0
484
0
    mWaitingForData = true;
485
0
    return NS_OK;
486
0
}
487
488
nsresult
489
nsAboutCacheEntry::Channel::WriteCacheEntryUnavailable()
490
0
{
491
0
    uint32_t n;
492
0
    NS_NAMED_LITERAL_CSTRING(buffer,
493
0
        "The cache entry you selected is not available.");
494
0
    mOutputStream->Write(buffer.get(), buffer.Length(), &n);
495
0
    return NS_OK;
496
0
}
497
498
//-----------------------------------------------------------------------------
499
// nsICacheEntryMetaDataVisitor implementation
500
//-----------------------------------------------------------------------------
501
502
NS_IMETHODIMP
503
nsAboutCacheEntry::Channel::OnMetaDataElement(char const * key, char const * value)
504
0
{
505
0
    mBuffer->AppendLiteral("  <tr>\n"
506
0
                           "    <th>");
507
0
    mBuffer->Append(key);
508
0
    mBuffer->AppendLiteral(":</th>\n"
509
0
                           "    <td>");
510
0
    nsAppendEscapedHTML(nsDependentCString(value), *mBuffer);
511
0
    mBuffer->AppendLiteral("</td>\n"
512
0
                           "  </tr>\n");
513
0
514
0
    return NS_OK;
515
0
}
516
517
//-----------------------------------------------------------------------------
518
// nsIStreamListener implementation
519
//-----------------------------------------------------------------------------
520
521
NS_IMETHODIMP
522
nsAboutCacheEntry::Channel::OnStartRequest(nsIRequest *request, nsISupports *ctx)
523
0
{
524
0
    mHexDumpState = 0;
525
0
526
0
    NS_NAMED_LITERAL_CSTRING(buffer, "<hr/>\n<pre>");
527
0
    uint32_t n;
528
0
    return mOutputStream->Write(buffer.get(), buffer.Length(), &n);
529
0
}
530
531
NS_IMETHODIMP
532
nsAboutCacheEntry::Channel::OnDataAvailable(nsIRequest *request, nsISupports *ctx,
533
                                   nsIInputStream *aInputStream,
534
                                   uint64_t aOffset,
535
                                   uint32_t aCount)
536
0
{
537
0
    uint32_t n;
538
0
    return aInputStream->ReadSegments(
539
0
        &nsAboutCacheEntry::Channel::PrintCacheData, this, aCount, &n);
540
0
}
541
542
/* static */ nsresult
543
nsAboutCacheEntry::Channel::PrintCacheData(nsIInputStream *aInStream,
544
                                           void *aClosure,
545
                                           const char *aFromSegment,
546
                                           uint32_t aToOffset,
547
                                           uint32_t aCount,
548
                                           uint32_t *aWriteCount)
549
0
{
550
0
    nsAboutCacheEntry::Channel *a =
551
0
      static_cast<nsAboutCacheEntry::Channel*>(aClosure);
552
0
553
0
    nsCString buffer;
554
0
    HexDump(&a->mHexDumpState, aFromSegment, aCount, buffer);
555
0
556
0
    uint32_t n;
557
0
    a->mOutputStream->Write(buffer.get(), buffer.Length(), &n);
558
0
559
0
    *aWriteCount = aCount;
560
0
561
0
    return NS_OK;
562
0
}
563
564
NS_IMETHODIMP
565
nsAboutCacheEntry::Channel::OnStopRequest(nsIRequest *request, nsISupports *ctx,
566
                                          nsresult result)
567
0
{
568
0
    NS_NAMED_LITERAL_CSTRING(buffer, "</pre>\n");
569
0
    uint32_t n;
570
0
    mOutputStream->Write(buffer.get(), buffer.Length(), &n);
571
0
572
0
    CloseContent();
573
0
574
0
    return NS_OK;
575
0
}
576
577
void
578
nsAboutCacheEntry::Channel::CloseContent()
579
0
{
580
0
    NS_NAMED_LITERAL_CSTRING(buffer, "</body>\n</html>\n");
581
0
    uint32_t n;
582
0
    mOutputStream->Write(buffer.get(), buffer.Length(), &n);
583
0
584
0
    mOutputStream->Close();
585
0
    mOutputStream = nullptr;
586
0
}