Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mozilla/chrome/RegistryMessageUtils.h"
8 : #include "mozilla/dom/ContentParent.h"
9 : #include "mozilla/Unused.h"
10 :
11 : #include "SubstitutingProtocolHandler.h"
12 : #include "nsIChannel.h"
13 : #include "nsIIOService.h"
14 : #include "nsIFile.h"
15 : #include "nsNetCID.h"
16 : #include "nsNetUtil.h"
17 : #include "nsURLHelper.h"
18 : #include "nsEscape.h"
19 :
20 : using mozilla::dom::ContentParent;
21 :
22 : namespace mozilla {
23 : namespace net {
24 :
25 : // Log module for Substituting Protocol logging. We keep the pre-existing module
26 : // name of "nsResProtocol" to avoid disruption.
27 : static LazyLogModule gResLog("nsResProtocol");
28 :
29 : static NS_DEFINE_CID(kSubstitutingURLCID, NS_SUBSTITUTINGURL_CID);
30 :
31 : //---------------------------------------------------------------------------------
32 : // SubstitutingURL : overrides nsStandardURL::GetFile to provide nsIFile resolution
33 : //---------------------------------------------------------------------------------
34 :
35 : nsresult
36 200 : SubstitutingURL::EnsureFile()
37 : {
38 400 : nsAutoCString ourScheme;
39 200 : nsresult rv = GetScheme(ourScheme);
40 200 : NS_ENSURE_SUCCESS(rv, rv);
41 :
42 : // Get the handler associated with this scheme. It would be nice to just
43 : // pass this in when constructing SubstitutingURLs, but we need a generic
44 : // factory constructor.
45 400 : nsCOMPtr<nsIIOService> io = do_GetIOService(&rv);
46 400 : nsCOMPtr<nsIProtocolHandler> handler;
47 200 : rv = io->GetProtocolHandler(ourScheme.get(), getter_AddRefs(handler));
48 200 : NS_ENSURE_SUCCESS(rv, rv);
49 400 : nsCOMPtr<nsISubstitutingProtocolHandler> substHandler = do_QueryInterface(handler);
50 200 : MOZ_ASSERT(substHandler);
51 :
52 400 : nsAutoCString spec;
53 200 : rv = substHandler->ResolveURI(this, spec);
54 200 : if (NS_FAILED(rv))
55 0 : return rv;
56 :
57 400 : nsAutoCString scheme;
58 200 : rv = net_ExtractURLScheme(spec, scheme);
59 200 : if (NS_FAILED(rv))
60 0 : return rv;
61 :
62 : // Bug 585869:
63 : // In most cases, the scheme is jar if it's not file.
64 : // Regardless, net_GetFileFromURLSpec should be avoided
65 : // when the scheme isn't file.
66 200 : if (!scheme.EqualsLiteral("file"))
67 0 : return NS_ERROR_NO_INTERFACE;
68 :
69 200 : return net_GetFileFromURLSpec(spec, getter_AddRefs(mFile));
70 : }
71 :
72 : /* virtual */ nsStandardURL*
73 14 : SubstitutingURL::StartClone()
74 : {
75 14 : SubstitutingURL *clone = new SubstitutingURL();
76 14 : return clone;
77 : }
78 :
79 : NS_IMETHODIMP
80 0 : SubstitutingURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
81 : {
82 0 : *aClassIDNoAlloc = kSubstitutingURLCID;
83 0 : return NS_OK;
84 : }
85 :
86 3 : SubstitutingProtocolHandler::SubstitutingProtocolHandler(const char* aScheme, uint32_t aFlags,
87 3 : bool aEnforceFileOrJar)
88 : : mScheme(aScheme)
89 : , mSubstitutions(16)
90 3 : , mEnforceFileOrJar(aEnforceFileOrJar)
91 : {
92 3 : mFlags.emplace(aFlags);
93 3 : ConstructInternal();
94 3 : }
95 :
96 0 : SubstitutingProtocolHandler::SubstitutingProtocolHandler(const char* aScheme)
97 : : mScheme(aScheme)
98 : , mSubstitutions(16)
99 0 : , mEnforceFileOrJar(true)
100 : {
101 0 : ConstructInternal();
102 0 : }
103 :
104 : void
105 3 : SubstitutingProtocolHandler::ConstructInternal()
106 : {
107 : nsresult rv;
108 3 : mIOService = do_GetIOService(&rv);
109 3 : MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mIOService);
110 3 : }
111 :
112 : //
113 : // IPC marshalling.
114 : //
115 :
116 : nsresult
117 2 : SubstitutingProtocolHandler::CollectSubstitutions(InfallibleTArray<SubstitutionMapping>& aMappings)
118 : {
119 24 : for (auto iter = mSubstitutions.ConstIter(); !iter.Done(); iter.Next()) {
120 44 : nsCOMPtr<nsIURI> uri = iter.Data();
121 44 : SerializedURI serialized;
122 22 : if (uri) {
123 22 : nsresult rv = uri->GetSpec(serialized.spec);
124 22 : NS_ENSURE_SUCCESS(rv, rv);
125 22 : uri->GetOriginCharset(serialized.charset);
126 : }
127 44 : SubstitutionMapping substitution = { mScheme, nsCString(iter.Key()), serialized };
128 22 : aMappings.AppendElement(substitution);
129 : }
130 :
131 2 : return NS_OK;
132 : }
133 :
134 : nsresult
135 33 : SubstitutingProtocolHandler::SendSubstitution(const nsACString& aRoot, nsIURI* aBaseURI)
136 : {
137 33 : if (GeckoProcessType_Content == XRE_GetProcessType()) {
138 22 : return NS_OK;
139 : }
140 :
141 22 : nsTArray<ContentParent*> parents;
142 11 : ContentParent::GetAll(parents);
143 11 : if (!parents.Length()) {
144 11 : return NS_OK;
145 : }
146 :
147 0 : SubstitutionMapping mapping;
148 0 : mapping.scheme = mScheme;
149 0 : mapping.path = aRoot;
150 0 : if (aBaseURI) {
151 0 : nsresult rv = aBaseURI->GetSpec(mapping.resolvedURI.spec);
152 0 : NS_ENSURE_SUCCESS(rv, rv);
153 0 : aBaseURI->GetOriginCharset(mapping.resolvedURI.charset);
154 : }
155 :
156 0 : for (uint32_t i = 0; i < parents.Length(); i++) {
157 0 : Unused << parents[i]->SendRegisterChromeItem(mapping);
158 : }
159 :
160 0 : return NS_OK;
161 : }
162 :
163 : //----------------------------------------------------------------------------
164 : // nsIProtocolHandler
165 : //----------------------------------------------------------------------------
166 :
167 : nsresult
168 0 : SubstitutingProtocolHandler::GetScheme(nsACString &result)
169 : {
170 0 : result = mScheme;
171 0 : return NS_OK;
172 : }
173 :
174 : nsresult
175 0 : SubstitutingProtocolHandler::GetDefaultPort(int32_t *result)
176 : {
177 0 : *result = -1;
178 0 : return NS_OK;
179 : }
180 :
181 : nsresult
182 1116 : SubstitutingProtocolHandler::GetProtocolFlags(uint32_t *result)
183 : {
184 1116 : if (mFlags.isNothing()) {
185 0 : NS_WARNING("Trying to get protocol flags the wrong way - use nsIProtocolHandlerWithDynamicFlags instead");
186 0 : return NS_ERROR_NOT_AVAILABLE;
187 : }
188 :
189 1116 : *result = mFlags.ref();
190 1116 : return NS_OK;
191 : }
192 :
193 : nsresult
194 1062 : SubstitutingProtocolHandler::NewURI(const nsACString &aSpec,
195 : const char *aCharset,
196 : nsIURI *aBaseURI,
197 : nsIURI **result)
198 : {
199 : nsresult rv;
200 :
201 2124 : RefPtr<SubstitutingURL> url = new SubstitutingURL();
202 1062 : if (!url)
203 0 : return NS_ERROR_OUT_OF_MEMORY;
204 :
205 : // unescape any %2f and %2e to make sure nsStandardURL coalesces them.
206 : // Later net_GetFileFromURLSpec() will do a full unescape and we want to
207 : // treat them the same way the file system will. (bugs 380994, 394075)
208 2124 : nsAutoCString spec;
209 1062 : const char *src = aSpec.BeginReading();
210 1062 : const char *end = aSpec.EndReading();
211 1062 : const char *last = src;
212 :
213 1062 : spec.SetCapacity(aSpec.Length()+1);
214 82268 : for ( ; src < end; ++src) {
215 40603 : if (*src == '%' && (src < end-2) && *(src+1) == '2') {
216 0 : char ch = '\0';
217 0 : if (*(src+2) == 'f' || *(src+2) == 'F') {
218 0 : ch = '/';
219 0 : } else if (*(src+2) == 'e' || *(src+2) == 'E') {
220 0 : ch = '.';
221 : }
222 :
223 0 : if (ch) {
224 0 : if (last < src) {
225 0 : spec.Append(last, src-last);
226 : }
227 0 : spec.Append(ch);
228 0 : src += 2;
229 0 : last = src+1; // src will be incremented by the loop
230 : }
231 : }
232 : }
233 1062 : if (last < src)
234 1062 : spec.Append(last, src-last);
235 :
236 1062 : rv = url->Init(nsIStandardURL::URLTYPE_STANDARD, -1, spec, aCharset, aBaseURI);
237 1062 : if (NS_SUCCEEDED(rv)) {
238 1062 : url.forget(result);
239 : }
240 1062 : return rv;
241 : }
242 :
243 : nsresult
244 1031 : SubstitutingProtocolHandler::NewChannel2(nsIURI* uri,
245 : nsILoadInfo* aLoadInfo,
246 : nsIChannel** result)
247 : {
248 1031 : NS_ENSURE_ARG_POINTER(uri);
249 1031 : NS_ENSURE_ARG_POINTER(aLoadInfo);
250 :
251 2062 : nsAutoCString spec;
252 1031 : nsresult rv = ResolveURI(uri, spec);
253 1031 : NS_ENSURE_SUCCESS(rv, rv);
254 :
255 2062 : nsCOMPtr<nsIURI> newURI;
256 1031 : rv = NS_NewURI(getter_AddRefs(newURI), spec);
257 1031 : NS_ENSURE_SUCCESS(rv, rv);
258 :
259 : // We don't want to allow the inner protocol handler to modify the result
260 : // principal URI since we want either |uri| or anything pre-set by upper
261 : // layers to prevail.
262 2062 : nsCOMPtr<nsIURI> savedResultPrincipalURI;
263 1031 : rv = aLoadInfo->GetResultPrincipalURI(getter_AddRefs(savedResultPrincipalURI));
264 1031 : NS_ENSURE_SUCCESS(rv, rv);
265 :
266 1031 : rv = NS_NewChannelInternal(result, newURI, aLoadInfo);
267 1031 : NS_ENSURE_SUCCESS(rv, rv);
268 :
269 1031 : rv = aLoadInfo->SetResultPrincipalURI(savedResultPrincipalURI);
270 1031 : NS_ENSURE_SUCCESS(rv, rv);
271 1031 : rv = (*result)->SetOriginalURI(uri);
272 1031 : NS_ENSURE_SUCCESS(rv, rv);
273 :
274 1031 : return SubstituteChannel(uri, aLoadInfo, result);
275 : }
276 :
277 : nsresult
278 0 : SubstitutingProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
279 : {
280 0 : return NewChannel2(uri, nullptr, result);
281 : }
282 :
283 : nsresult
284 0 : SubstitutingProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
285 : {
286 : // don't override anything.
287 0 : *_retval = false;
288 0 : return NS_OK;
289 : }
290 :
291 : //----------------------------------------------------------------------------
292 : // nsISubstitutingProtocolHandler
293 : //----------------------------------------------------------------------------
294 :
295 : nsresult
296 33 : SubstitutingProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *baseURI)
297 : {
298 33 : if (!baseURI) {
299 0 : mSubstitutions.Remove(root);
300 0 : NotifyObservers(root, baseURI);
301 0 : return SendSubstitution(root, baseURI);
302 : }
303 :
304 : // If baseURI isn't a same-scheme URI, we can set the substitution immediately.
305 66 : nsAutoCString scheme;
306 33 : nsresult rv = baseURI->GetScheme(scheme);
307 33 : NS_ENSURE_SUCCESS(rv, rv);
308 33 : if (!scheme.Equals(mScheme)) {
309 60 : if (mEnforceFileOrJar && !scheme.EqualsLiteral("file") && !scheme.EqualsLiteral("jar")
310 30 : && !scheme.EqualsLiteral("app")) {
311 0 : NS_WARNING("Refusing to create substituting URI to non-file:// target");
312 0 : return NS_ERROR_INVALID_ARG;
313 : }
314 :
315 30 : mSubstitutions.Put(root, baseURI);
316 30 : NotifyObservers(root, baseURI);
317 30 : return SendSubstitution(root, baseURI);
318 : }
319 :
320 : // baseURI is a same-type substituting URI, let's resolve it first.
321 6 : nsAutoCString newBase;
322 3 : rv = ResolveURI(baseURI, newBase);
323 3 : NS_ENSURE_SUCCESS(rv, rv);
324 :
325 6 : nsCOMPtr<nsIURI> newBaseURI;
326 3 : rv = mIOService->NewURI(newBase, nullptr, nullptr, getter_AddRefs(newBaseURI));
327 3 : NS_ENSURE_SUCCESS(rv, rv);
328 :
329 3 : mSubstitutions.Put(root, newBaseURI);
330 3 : NotifyObservers(root, baseURI);
331 3 : return SendSubstitution(root, newBaseURI);
332 : }
333 :
334 : nsresult
335 169 : SubstitutingProtocolHandler::GetSubstitution(const nsACString& root, nsIURI **result)
336 : {
337 169 : NS_ENSURE_ARG_POINTER(result);
338 :
339 169 : if (mSubstitutions.Get(root, result))
340 169 : return NS_OK;
341 :
342 0 : return GetSubstitutionInternal(root, result);
343 : }
344 :
345 : nsresult
346 0 : SubstitutingProtocolHandler::HasSubstitution(const nsACString& root, bool *result)
347 : {
348 0 : NS_ENSURE_ARG_POINTER(result);
349 0 : *result = HasSubstitution(root);
350 0 : return NS_OK;
351 : }
352 :
353 : nsresult
354 1611 : SubstitutingProtocolHandler::ResolveURI(nsIURI *uri, nsACString &result)
355 : {
356 : nsresult rv;
357 :
358 3222 : nsAutoCString host;
359 3222 : nsAutoCString path;
360 3222 : nsAutoCString pathname;
361 :
362 3222 : nsCOMPtr<nsIURL> url = do_QueryInterface(uri);
363 1611 : if (!url) {
364 0 : return NS_ERROR_MALFORMED_URI;
365 : }
366 :
367 1611 : rv = uri->GetAsciiHost(host);
368 1611 : if (NS_FAILED(rv)) return rv;
369 :
370 1611 : rv = uri->GetPath(path);
371 1611 : if (NS_FAILED(rv)) return rv;
372 :
373 1611 : rv = url->GetFilePath(pathname);
374 1611 : if (NS_FAILED(rv)) return rv;
375 :
376 1611 : if (ResolveSpecialCases(host, path, pathname, result)) {
377 1442 : return NS_OK;
378 : }
379 :
380 338 : nsCOMPtr<nsIURI> baseURI;
381 169 : rv = GetSubstitution(host, getter_AddRefs(baseURI));
382 169 : if (NS_FAILED(rv)) return rv;
383 :
384 : // Unescape the path so we can perform some checks on it.
385 169 : NS_UnescapeURL(pathname);
386 169 : if (pathname.FindChar('\\') != -1) {
387 0 : return NS_ERROR_MALFORMED_URI;
388 : }
389 :
390 : // Some code relies on an empty path resolving to a file rather than a
391 : // directory.
392 169 : NS_ASSERTION(path.CharAt(0) == '/', "Path must begin with '/'");
393 169 : if (path.Length() == 1) {
394 0 : rv = baseURI->GetSpec(result);
395 : } else {
396 : // Make sure we always resolve the path as file-relative to our target URI.
397 169 : path.InsertLiteral(".", 0);
398 :
399 169 : rv = baseURI->Resolve(path, result);
400 : }
401 :
402 169 : if (NS_WARN_IF(NS_FAILED(rv))) {
403 0 : return rv;
404 : }
405 :
406 169 : if (MOZ_LOG_TEST(gResLog, LogLevel::Debug)) {
407 0 : nsAutoCString spec;
408 0 : uri->GetAsciiSpec(spec);
409 0 : MOZ_LOG(gResLog, LogLevel::Debug, ("%s\n -> %s\n", spec.get(), PromiseFlatCString(result).get()));
410 : }
411 169 : return rv;
412 : }
413 :
414 : nsresult
415 0 : SubstitutingProtocolHandler::AddObserver(nsISubstitutionObserver* aObserver)
416 : {
417 0 : NS_ENSURE_ARG(aObserver);
418 0 : if (mObservers.Contains(aObserver)) {
419 0 : return NS_ERROR_DUPLICATE_HANDLE;
420 : }
421 :
422 0 : mObservers.AppendElement(aObserver);
423 0 : return NS_OK;
424 : }
425 :
426 : nsresult
427 0 : SubstitutingProtocolHandler::RemoveObserver(nsISubstitutionObserver* aObserver)
428 : {
429 0 : NS_ENSURE_ARG(aObserver);
430 0 : if (!mObservers.Contains(aObserver)) {
431 0 : return NS_ERROR_INVALID_ARG;
432 : }
433 :
434 0 : mObservers.RemoveElement(aObserver);
435 0 : return NS_OK;
436 : }
437 :
438 : void
439 33 : SubstitutingProtocolHandler::NotifyObservers(const nsACString& aRoot,
440 : nsIURI* aBaseURI)
441 : {
442 33 : for (size_t i = 0; i < mObservers.Length(); ++i) {
443 0 : mObservers[i]->OnSetSubstitution(aRoot, aBaseURI);
444 : }
445 33 : }
446 :
447 : } // namespace net
448 : } // namespace mozilla
|