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