Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/protocol/data/nsDataHandler.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 2; 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 "nsDataChannel.h"
7
#include "nsDataHandler.h"
8
#include "nsNetCID.h"
9
#include "nsError.h"
10
#include "nsIOService.h"
11
#include "DataChannelChild.h"
12
#include "plstr.h"
13
#include "nsSimpleURI.h"
14
#include "mozilla/dom/MimeType.h"
15
16
////////////////////////////////////////////////////////////////////////////////
17
18
NS_IMPL_ISUPPORTS(nsDataHandler, nsIProtocolHandler, nsISupportsWeakReference)
19
20
nsresult
21
1
nsDataHandler::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult) {
22
1
23
1
    nsDataHandler* ph = new nsDataHandler();
24
1
    if (ph == nullptr)
25
0
        return NS_ERROR_OUT_OF_MEMORY;
26
1
    NS_ADDREF(ph);
27
1
    nsresult rv = ph->QueryInterface(aIID, aResult);
28
1
    NS_RELEASE(ph);
29
1
    return rv;
30
1
}
31
32
////////////////////////////////////////////////////////////////////////////////
33
// nsIProtocolHandler methods:
34
35
NS_IMETHODIMP
36
0
nsDataHandler::GetScheme(nsACString &result) {
37
0
    result.AssignLiteral("data");
38
0
    return NS_OK;
39
0
}
40
41
NS_IMETHODIMP
42
0
nsDataHandler::GetDefaultPort(int32_t *result) {
43
0
    // no ports for data protocol
44
0
    *result = -1;
45
0
    return NS_OK;
46
0
}
47
48
NS_IMETHODIMP
49
0
nsDataHandler::GetProtocolFlags(uint32_t *result) {
50
0
    *result = URI_NORELATIVE | URI_NOAUTH | URI_INHERITS_SECURITY_CONTEXT |
51
0
              URI_LOADABLE_BY_ANYONE | URI_NON_PERSISTABLE | URI_IS_LOCAL_RESOURCE |
52
0
              URI_SYNC_LOAD_IS_OK;
53
0
    return NS_OK;
54
0
}
55
56
NS_IMETHODIMP
57
nsDataHandler::NewURI(const nsACString &aSpec,
58
                      const char *aCharset, // ignore charset info
59
                      nsIURI *aBaseURI,
60
5.17k
                      nsIURI **result) {
61
5.17k
    nsresult rv;
62
5.17k
    nsCOMPtr<nsIURI> uri;
63
5.17k
64
5.17k
    nsCString spec(aSpec);
65
5.17k
66
5.17k
    if (aBaseURI && !spec.IsEmpty() && spec[0] == '#') {
67
0
        // Looks like a reference instead of a fully-specified URI.
68
0
        // --> initialize |uri| as a clone of |aBaseURI|, with ref appended.
69
0
        rv = NS_MutateURI(aBaseURI)
70
0
               .SetRef(spec)
71
0
               .Finalize(uri);
72
5.17k
    } else {
73
5.17k
        // Otherwise, we'll assume |spec| is a fully-specified data URI
74
5.17k
        nsAutoCString contentType;
75
5.17k
        bool base64;
76
5.17k
        rv = ParseURI(spec, contentType, /* contentCharset = */ nullptr,
77
5.17k
                      base64, /* dataBuffer = */ nullptr);
78
5.17k
        if (NS_FAILED(rv))
79
5.17k
            return rv;
80
4.71k
81
4.71k
        // Strip whitespace unless this is text, where whitespace is important
82
4.71k
        // Don't strip escaped whitespace though (bug 391951)
83
4.71k
        if (base64 || (strncmp(contentType.get(),"text/",5) != 0 &&
84
4.71k
                       contentType.Find("xml") == kNotFound)) {
85
633
            // it's ascii encoded binary, don't let any spaces in
86
633
            if (!spec.StripWhitespace(mozilla::fallible)) {
87
0
                return NS_ERROR_OUT_OF_MEMORY;
88
0
            }
89
4.71k
        }
90
4.71k
91
4.71k
        rv = NS_MutateURI(new mozilla::net::nsSimpleURI::Mutator())
92
4.71k
               .SetSpec(spec)
93
4.71k
               .Finalize(uri);
94
4.71k
    }
95
5.17k
96
5.17k
    if (NS_FAILED(rv))
97
4.71k
        return rv;
98
4.71k
99
4.71k
    uri.forget(result);
100
4.71k
    return rv;
101
4.71k
}
102
103
NS_IMETHODIMP
104
nsDataHandler::NewChannel2(nsIURI* uri,
105
                           nsILoadInfo* aLoadInfo,
106
                           nsIChannel** result)
107
0
{
108
0
    NS_ENSURE_ARG_POINTER(uri);
109
0
    nsDataChannel* channel;
110
0
    if (XRE_IsParentProcess()) {
111
0
        channel = new nsDataChannel(uri);
112
0
    } else {
113
0
        channel = new mozilla::net::DataChannelChild(uri);
114
0
    }
115
0
    NS_ADDREF(channel);
116
0
117
0
    nsresult rv = channel->Init();
118
0
    if (NS_FAILED(rv)) {
119
0
        NS_RELEASE(channel);
120
0
        return rv;
121
0
    }
122
0
123
0
    // set the loadInfo on the new channel
124
0
    rv = channel->SetLoadInfo(aLoadInfo);
125
0
    if (NS_FAILED(rv)) {
126
0
        NS_RELEASE(channel);
127
0
        return rv;
128
0
    }
129
0
130
0
    *result = channel;
131
0
    return NS_OK;
132
0
}
133
134
NS_IMETHODIMP
135
nsDataHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
136
0
{
137
0
    return NewChannel2(uri, nullptr, result);
138
0
}
139
140
NS_IMETHODIMP
141
0
nsDataHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) {
142
0
    // don't override anything.
143
0
    *_retval = false;
144
0
    return NS_OK;
145
0
}
146
147
/**
148
 * Helper that performs a case insensitive match to find the offset of a given
149
 * pattern in a nsACString.
150
 * The search is performed starting from the end of the string; if the string
151
 * contains more than one match, the rightmost (last) match will be returned.
152
 */
153
static bool
154
FindOffsetOf(const nsACString& aPattern, const nsACString& aSrc,
155
             nsACString::size_type& aOffset)
156
3.45k
{
157
3.45k
    static const nsCaseInsensitiveCStringComparator kComparator;
158
3.45k
159
3.45k
    nsACString::const_iterator begin, end;
160
3.45k
    aSrc.BeginReading(begin);
161
3.45k
    aSrc.EndReading(end);
162
3.45k
    if (!RFindInReadable(aPattern, begin, end, kComparator)) {
163
3.45k
        return false;
164
3.45k
    }
165
0
166
0
    // FindInReadable updates |begin| and |end| to the match coordinates.
167
0
    aOffset = nsACString::size_type(begin.get() - aSrc.Data());
168
0
    return true;
169
0
}
170
171
nsresult
172
nsDataHandler::ParsePathWithoutRef(
173
    const nsACString& aPath,
174
    nsCString& aContentType,
175
    nsCString* aContentCharset,
176
    bool& aIsBase64,
177
    nsDependentCSubstring* aDataBuffer)
178
5.17k
{
179
5.17k
    static NS_NAMED_LITERAL_CSTRING(kBase64, "base64");
180
5.17k
    static NS_NAMED_LITERAL_CSTRING(kCharset, "charset");
181
5.17k
182
5.17k
    aIsBase64 = false;
183
5.17k
184
5.17k
    // First, find the start of the data
185
5.17k
    int32_t commaIdx = aPath.FindChar(',');
186
5.17k
    if (commaIdx == kNotFound) {
187
454
        return NS_ERROR_MALFORMED_URI;
188
454
    }
189
4.71k
190
4.71k
    if (commaIdx == 0) {
191
1.26k
        // Nothing but data.
192
1.26k
        aContentType.AssignLiteral("text/plain");
193
1.26k
        if (aContentCharset) {
194
0
            aContentCharset->AssignLiteral("US-ASCII");
195
0
        }
196
3.45k
    } else {
197
3.45k
        auto mediaType = Substring(aPath, 0, commaIdx);
198
3.45k
199
3.45k
        // Determine if the data is base64 encoded.
200
3.45k
        nsACString::size_type base64;
201
3.45k
        if (FindOffsetOf(kBase64, mediaType, base64) && base64 > 0) {
202
0
            nsACString::size_type offset = base64 + kBase64.Length();
203
0
            // Per the RFC 2397 grammar, "base64" MUST be at the end of the
204
0
            // non-data part.
205
0
            //
206
0
            // But we also allow it in between parameters so a subsequent ";"
207
0
            // is ok as well (this deals with *broken* data URIs, see bug
208
0
            // 781693 for an example). Anything after "base64" in the non-data
209
0
            // part will be discarded in this case, however.
210
0
            if (offset == mediaType.Length() || mediaType[offset] == ';' || mediaType[offset] == ' ') {
211
0
                MOZ_DIAGNOSTIC_ASSERT(base64 > 0, "Did someone remove the check?");
212
0
                // Index is on the first character of matched "base64" so we
213
0
                // move to the preceding character
214
0
                base64--;
215
0
                // Skip any preceding spaces, searching for a semicolon
216
0
                while (base64 > 0 && mediaType[base64] == ' ') {
217
0
                    base64--;
218
0
                }
219
0
                if (mediaType[base64] == ';') {
220
0
                    aIsBase64 = true;
221
0
                    // Trim the base64 part off.
222
0
                    mediaType.Rebind(aPath, 0, base64);
223
0
                }
224
0
            }
225
0
        }
226
3.45k
227
3.45k
        // Skip any leading spaces
228
3.45k
        nsACString::size_type startIndex = 0;
229
3.45k
        while (startIndex < mediaType.Length() && mediaType[startIndex] == ' ') {
230
0
            startIndex++;
231
0
        }
232
3.45k
233
3.45k
        nsAutoCString mediaTypeBuf;
234
3.45k
        // If the mimetype starts with ';' we assume text/plain
235
3.45k
        if (startIndex < mediaType.Length() && mediaType[startIndex] == ';') {
236
0
            mediaTypeBuf.AssignLiteral("text/plain");
237
0
            mediaTypeBuf.Append(mediaType);
238
0
            mediaType.Rebind(mediaTypeBuf, 0, mediaTypeBuf.Length());
239
0
        }
240
3.45k
241
3.45k
        // Everything else is content type.
242
3.45k
        UniquePtr<CMimeType> parsed = CMimeType::Parse(mediaType);
243
3.45k
        if (parsed) {
244
875
            parsed->GetFullType(aContentType);
245
875
            if (aContentCharset) {
246
0
                parsed->GetParameterValue(kCharset, *aContentCharset);
247
0
            }
248
2.58k
        } else {
249
2.58k
            // Mime Type parsing failed
250
2.58k
            aContentType.AssignLiteral("text/plain");
251
2.58k
            if (aContentCharset) {
252
0
                aContentCharset->AssignLiteral("US-ASCII");
253
0
            }
254
2.58k
        }
255
3.45k
256
3.45k
    }
257
4.71k
258
4.71k
    if (aDataBuffer) {
259
4.71k
        aDataBuffer->Rebind(aPath, commaIdx + 1);
260
4.71k
    }
261
4.71k
262
4.71k
    return NS_OK;
263
4.71k
}
264
265
nsresult
266
nsDataHandler::ParseURI(nsCString& spec,
267
                        nsCString& contentType,
268
                        nsCString* contentCharset,
269
                        bool&    isBase64,
270
                        nsCString* dataBuffer)
271
5.17k
{
272
5.17k
    static NS_NAMED_LITERAL_CSTRING(kDataScheme, "data:");
273
5.17k
274
5.17k
    // move past "data:"
275
5.17k
    int32_t scheme = spec.Find(kDataScheme, /* aIgnoreCase = */ true);
276
5.17k
    if (scheme == kNotFound) {
277
0
        // malformed uri
278
0
        return NS_ERROR_MALFORMED_URI;
279
0
    }
280
5.17k
281
5.17k
    scheme += kDataScheme.Length();
282
5.17k
283
5.17k
    // Find the start of the hash ref if present.
284
5.17k
    int32_t hash = spec.FindChar('#', scheme);
285
5.17k
286
5.17k
    auto pathWithoutRef = Substring(spec, scheme,
287
5.17k
                                    hash != kNotFound ? hash : -1);
288
5.17k
    nsDependentCSubstring dataRange;
289
5.17k
    nsresult rv = ParsePathWithoutRef(pathWithoutRef, contentType,
290
5.17k
                                      contentCharset, isBase64, &dataRange);
291
5.17k
    if (NS_SUCCEEDED(rv) && dataBuffer) {
292
0
        if (!dataBuffer->Assign(dataRange, mozilla::fallible)) {
293
0
            rv = NS_ERROR_OUT_OF_MEMORY;
294
0
        }
295
0
    }
296
5.17k
297
5.17k
    return rv;
298
5.17k
}