/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 | } |