netwerk/protocol/data/nsDataHandler.cpp

branch
TOR_BUG_9701
changeset 11
deefc01c0e14
equal deleted inserted replaced
-1:000000000000 0:fca090313702
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
11 static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID);
12
13 ////////////////////////////////////////////////////////////////////////////////
14
15 nsDataHandler::nsDataHandler() {
16 }
17
18 nsDataHandler::~nsDataHandler() {
19 }
20
21 NS_IMPL_ISUPPORTS(nsDataHandler, nsIProtocolHandler)
22
23 nsresult
24 nsDataHandler::Create(nsISupports* aOuter, const nsIID& aIID, void* *aResult) {
25
26 nsDataHandler* ph = new nsDataHandler();
27 if (ph == nullptr)
28 return NS_ERROR_OUT_OF_MEMORY;
29 NS_ADDREF(ph);
30 nsresult rv = ph->QueryInterface(aIID, aResult);
31 NS_RELEASE(ph);
32 return rv;
33 }
34
35 ////////////////////////////////////////////////////////////////////////////////
36 // nsIProtocolHandler methods:
37
38 NS_IMETHODIMP
39 nsDataHandler::GetScheme(nsACString &result) {
40 result.AssignLiteral("data");
41 return NS_OK;
42 }
43
44 NS_IMETHODIMP
45 nsDataHandler::GetDefaultPort(int32_t *result) {
46 // no ports for data protocol
47 *result = -1;
48 return NS_OK;
49 }
50
51 NS_IMETHODIMP
52 nsDataHandler::GetProtocolFlags(uint32_t *result) {
53 *result = URI_NORELATIVE | URI_NOAUTH | URI_INHERITS_SECURITY_CONTEXT |
54 URI_LOADABLE_BY_ANYONE | URI_NON_PERSISTABLE | URI_IS_LOCAL_RESOURCE |
55 URI_SYNC_LOAD_IS_OK;
56 return NS_OK;
57 }
58
59 NS_IMETHODIMP
60 nsDataHandler::NewURI(const nsACString &aSpec,
61 const char *aCharset, // ignore charset info
62 nsIURI *aBaseURI,
63 nsIURI **result) {
64 nsresult rv;
65 nsRefPtr<nsIURI> uri;
66
67 nsCString spec(aSpec);
68
69 if (aBaseURI && !spec.IsEmpty() && spec[0] == '#') {
70 // Looks like a reference instead of a fully-specified URI.
71 // --> initialize |uri| as a clone of |aBaseURI|, with ref appended.
72 rv = aBaseURI->Clone(getter_AddRefs(uri));
73 if (NS_FAILED(rv))
74 return rv;
75 rv = uri->SetRef(spec);
76 } else {
77 // Otherwise, we'll assume |spec| is a fully-specified data URI
78 nsAutoCString contentType, contentCharset, dataBuffer, hashRef;
79 bool base64;
80 rv = ParseURI(spec, contentType, contentCharset, base64, dataBuffer, hashRef);
81 if (NS_FAILED(rv))
82 return rv;
83
84 // Strip whitespace unless this is text, where whitespace is important
85 // Don't strip escaped whitespace though (bug 391951)
86 if (base64 || (strncmp(contentType.get(),"text/",5) != 0 &&
87 contentType.Find("xml") == kNotFound)) {
88 // it's ascii encoded binary, don't let any spaces in
89 spec.StripWhitespace();
90 }
91
92 uri = do_CreateInstance(kSimpleURICID, &rv);
93 if (NS_FAILED(rv))
94 return rv;
95 rv = uri->SetSpec(spec);
96 }
97
98 if (NS_FAILED(rv))
99 return rv;
100
101 uri.forget(result);
102 return rv;
103 }
104
105 NS_IMETHODIMP
106 nsDataHandler::NewChannel(nsIURI* uri, nsIChannel* *result) {
107 NS_ENSURE_ARG_POINTER(uri);
108 nsDataChannel* channel = new nsDataChannel(uri);
109 if (!channel)
110 return NS_ERROR_OUT_OF_MEMORY;
111 NS_ADDREF(channel);
112
113 nsresult rv = channel->Init();
114 if (NS_FAILED(rv)) {
115 NS_RELEASE(channel);
116 return rv;
117 }
118
119 *result = channel;
120 return NS_OK;
121 }
122
123 NS_IMETHODIMP
124 nsDataHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) {
125 // don't override anything.
126 *_retval = false;
127 return NS_OK;
128 }
129
130 #define BASE64_EXTENSION ";base64"
131
132 nsresult
133 nsDataHandler::ParseURI(nsCString& spec,
134 nsCString& contentType,
135 nsCString& contentCharset,
136 bool& isBase64,
137 nsCString& dataBuffer,
138 nsCString& hashRef) {
139 isBase64 = false;
140
141 // move past "data:"
142 char *buffer = (char *) PL_strcasestr(spec.BeginWriting(), "data:");
143 if (!buffer) {
144 // malformed uri
145 return NS_ERROR_MALFORMED_URI;
146 }
147 buffer += 5;
148
149 // First, find the start of the data
150 char *comma = strchr(buffer, ',');
151 if (!comma)
152 return NS_ERROR_MALFORMED_URI;
153
154 *comma = '\0';
155
156 // determine if the data is base64 encoded.
157 char *base64 = PL_strcasestr(buffer, BASE64_EXTENSION);
158 if (base64) {
159 char *beyond = base64 + strlen(BASE64_EXTENSION);
160 // per the RFC 2397 grammar, "base64" MUST be followed by a comma
161 // previously substituted by '\0', but we also allow it in between
162 // parameters so a subsequent ";" is ok as well (this deals with
163 // *broken* data URIs, see bug 781693 for an example)
164 if (*beyond == '\0' || *beyond == ';') {
165 isBase64 = true;
166 *base64 = '\0';
167 }
168 }
169
170 if (comma == buffer) {
171 // nothing but data
172 contentType.AssignLiteral("text/plain");
173 contentCharset.AssignLiteral("US-ASCII");
174 } else {
175 // everything else is content type
176 char *semiColon = (char *) strchr(buffer, ';');
177 if (semiColon)
178 *semiColon = '\0';
179
180 if (semiColon == buffer || base64 == buffer) {
181 // there is no content type, but there are other parameters
182 contentType.AssignLiteral("text/plain");
183 } else {
184 contentType = buffer;
185 ToLowerCase(contentType);
186 }
187
188 if (semiColon) {
189 char *charset = PL_strcasestr(semiColon + 1, "charset=");
190 if (charset)
191 contentCharset = charset + sizeof("charset=") - 1;
192
193 *semiColon = ';';
194 }
195 }
196
197 *comma = ',';
198 if (isBase64)
199 *base64 = ';';
200
201 contentType.StripWhitespace();
202 contentCharset.StripWhitespace();
203
204 // Split encoded data from terminal "#ref" (if present)
205 char *data = comma + 1;
206 char *hash = strchr(data, '#');
207 if (!hash) {
208 dataBuffer.Assign(data);
209 hashRef.Truncate();
210 } else {
211 dataBuffer.Assign(data, hash - data);
212 hashRef.Assign(hash);
213 }
214
215 return NS_OK;
216 }

mercurial