Line data Source code
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 "DataChannelChild.h"
11 : #include "plstr.h"
12 :
13 : static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID);
14 :
15 : ////////////////////////////////////////////////////////////////////////////////
16 :
17 3 : nsDataHandler::nsDataHandler() {
18 3 : }
19 :
20 0 : nsDataHandler::~nsDataHandler() {
21 0 : }
22 :
23 159 : NS_IMPL_ISUPPORTS(nsDataHandler, nsIProtocolHandler, nsISupportsWeakReference)
24 :
25 : nsresult
26 3 : nsDataHandler::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult) {
27 :
28 3 : nsDataHandler* ph = new nsDataHandler();
29 3 : if (ph == nullptr)
30 0 : return NS_ERROR_OUT_OF_MEMORY;
31 3 : NS_ADDREF(ph);
32 3 : nsresult rv = ph->QueryInterface(aIID, aResult);
33 3 : NS_RELEASE(ph);
34 3 : return rv;
35 : }
36 :
37 : ////////////////////////////////////////////////////////////////////////////////
38 : // nsIProtocolHandler methods:
39 :
40 : NS_IMETHODIMP
41 0 : nsDataHandler::GetScheme(nsACString &result) {
42 0 : result.AssignLiteral("data");
43 0 : return NS_OK;
44 : }
45 :
46 : NS_IMETHODIMP
47 0 : nsDataHandler::GetDefaultPort(int32_t *result) {
48 : // no ports for data protocol
49 0 : *result = -1;
50 0 : return NS_OK;
51 : }
52 :
53 : NS_IMETHODIMP
54 12 : nsDataHandler::GetProtocolFlags(uint32_t *result) {
55 12 : *result = URI_NORELATIVE | URI_NOAUTH | URI_INHERITS_SECURITY_CONTEXT |
56 : URI_LOADABLE_BY_ANYONE | URI_NON_PERSISTABLE | URI_IS_LOCAL_RESOURCE |
57 : URI_SYNC_LOAD_IS_OK;
58 12 : return NS_OK;
59 : }
60 :
61 : NS_IMETHODIMP
62 6 : nsDataHandler::NewURI(const nsACString &aSpec,
63 : const char *aCharset, // ignore charset info
64 : nsIURI *aBaseURI,
65 : nsIURI **result) {
66 : nsresult rv;
67 12 : RefPtr<nsIURI> uri;
68 :
69 12 : nsCString spec(aSpec);
70 :
71 6 : if (aBaseURI && !spec.IsEmpty() && spec[0] == '#') {
72 : // Looks like a reference instead of a fully-specified URI.
73 : // --> initialize |uri| as a clone of |aBaseURI|, with ref appended.
74 0 : rv = aBaseURI->Clone(getter_AddRefs(uri));
75 0 : if (NS_FAILED(rv))
76 0 : return rv;
77 0 : rv = uri->SetRef(spec);
78 : } else {
79 : // Otherwise, we'll assume |spec| is a fully-specified data URI
80 12 : nsAutoCString contentType;
81 : bool base64;
82 6 : rv = ParseURI(spec, contentType, /* contentCharset = */ nullptr,
83 : base64, /* dataBuffer = */ nullptr);
84 6 : if (NS_FAILED(rv))
85 0 : return rv;
86 :
87 : // Strip whitespace unless this is text, where whitespace is important
88 : // Don't strip escaped whitespace though (bug 391951)
89 6 : if (base64 || (strncmp(contentType.get(),"text/",5) != 0 &&
90 0 : contentType.Find("xml") == kNotFound)) {
91 : // it's ascii encoded binary, don't let any spaces in
92 0 : if (!spec.StripWhitespace(mozilla::fallible)) {
93 0 : return NS_ERROR_OUT_OF_MEMORY;
94 : }
95 : }
96 :
97 6 : uri = do_CreateInstance(kSimpleURICID, &rv);
98 6 : if (NS_FAILED(rv))
99 0 : return rv;
100 6 : rv = uri->SetSpec(spec);
101 : }
102 :
103 6 : if (NS_FAILED(rv))
104 0 : return rv;
105 :
106 6 : uri.forget(result);
107 6 : return rv;
108 : }
109 :
110 : NS_IMETHODIMP
111 6 : nsDataHandler::NewChannel2(nsIURI* uri,
112 : nsILoadInfo* aLoadInfo,
113 : nsIChannel** result)
114 : {
115 6 : NS_ENSURE_ARG_POINTER(uri);
116 : nsDataChannel* channel;
117 6 : if (XRE_IsParentProcess()) {
118 2 : channel = new nsDataChannel(uri);
119 : } else {
120 4 : channel = new mozilla::net::DataChannelChild(uri);
121 : }
122 6 : NS_ADDREF(channel);
123 :
124 6 : nsresult rv = channel->Init();
125 6 : if (NS_FAILED(rv)) {
126 0 : NS_RELEASE(channel);
127 0 : return rv;
128 : }
129 :
130 : // set the loadInfo on the new channel
131 6 : rv = channel->SetLoadInfo(aLoadInfo);
132 6 : if (NS_FAILED(rv)) {
133 0 : NS_RELEASE(channel);
134 0 : return rv;
135 : }
136 :
137 6 : *result = channel;
138 6 : return NS_OK;
139 : }
140 :
141 : NS_IMETHODIMP
142 0 : nsDataHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
143 : {
144 0 : return NewChannel2(uri, nullptr, result);
145 : }
146 :
147 : NS_IMETHODIMP
148 0 : nsDataHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) {
149 : // don't override anything.
150 0 : *_retval = false;
151 0 : return NS_OK;
152 : }
153 :
154 : #define BASE64_EXTENSION ";base64"
155 :
156 : nsresult
157 12 : nsDataHandler::ParseURI(nsCString& spec,
158 : nsCString& contentType,
159 : nsCString* contentCharset,
160 : bool& isBase64,
161 : nsCString* dataBuffer)
162 : {
163 12 : isBase64 = false;
164 :
165 : // move past "data:"
166 12 : const char* roBuffer = (const char*) PL_strcasestr(spec.get(), "data:");
167 12 : if (!roBuffer) {
168 : // malformed uri
169 0 : return NS_ERROR_MALFORMED_URI;
170 : }
171 12 : roBuffer += sizeof("data:") - 1;
172 :
173 : // First, find the start of the data
174 12 : const char* roComma = strchr(roBuffer, ',');
175 12 : const char* roHash = strchr(roBuffer, '#');
176 12 : if (!roComma || (roHash && roHash < roComma)) {
177 0 : return NS_ERROR_MALFORMED_URI;
178 : }
179 :
180 12 : if (roComma == roBuffer) {
181 : // nothing but data
182 12 : contentType.AssignLiteral("text/plain");
183 12 : if (contentCharset) {
184 6 : contentCharset->AssignLiteral("US-ASCII");
185 : }
186 : } else {
187 : // Make a copy of the non-data part so we can null out parts of it as
188 : // we go. This copy will be a small number of chars, in contrast to the
189 : // data which may be large.
190 0 : char* buffer = PL_strndup(roBuffer, roComma - roBuffer);
191 :
192 : // determine if the data is base64 encoded.
193 0 : char* base64 = PL_strcasestr(buffer, BASE64_EXTENSION);
194 0 : if (base64) {
195 0 : char *beyond = base64 + sizeof(BASE64_EXTENSION) - 1;
196 : // Per the RFC 2397 grammar, "base64" MUST be at the end of the
197 : // non-data part.
198 : //
199 : // But we also allow it in between parameters so a subsequent ";"
200 : // is ok as well (this deals with *broken* data URIs, see bug
201 : // 781693 for an example). Anything after "base64" in the non-data
202 : // part will be discarded in this case, however.
203 0 : if (*beyond == '\0' || *beyond == ';') {
204 0 : isBase64 = true;
205 0 : *base64 = '\0';
206 : }
207 : }
208 :
209 : // everything else is content type
210 0 : char *semiColon = (char *) strchr(buffer, ';');
211 0 : if (semiColon)
212 0 : *semiColon = '\0';
213 :
214 0 : if (semiColon == buffer || base64 == buffer) {
215 : // there is no content type, but there are other parameters
216 0 : contentType.AssignLiteral("text/plain");
217 : } else {
218 0 : contentType.Assign(buffer);
219 0 : ToLowerCase(contentType);
220 0 : if (!contentType.StripWhitespace(mozilla::fallible)) {
221 0 : return NS_ERROR_OUT_OF_MEMORY;
222 : }
223 : }
224 :
225 0 : if (semiColon && contentCharset) {
226 0 : char *charset = PL_strcasestr(semiColon + 1, "charset=");
227 0 : if (charset) {
228 0 : contentCharset->Assign(charset + sizeof("charset=") - 1);
229 0 : if (!contentCharset->StripWhitespace(mozilla::fallible)) {
230 0 : return NS_ERROR_OUT_OF_MEMORY;
231 : }
232 : }
233 : }
234 :
235 0 : free(buffer);
236 : }
237 :
238 12 : if (dataBuffer) {
239 : // Split encoded data from terminal "#ref" (if present)
240 6 : const char* roData = roComma + 1;
241 : bool ok = !roHash
242 6 : ? dataBuffer->Assign(roData, mozilla::fallible)
243 6 : : dataBuffer->Assign(roData, roHash - roData, mozilla::fallible);
244 6 : if (!ok) {
245 0 : return NS_ERROR_OUT_OF_MEMORY;
246 : }
247 : }
248 :
249 12 : return NS_OK;
250 : }
|