1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Darin Fisher <darin@netscape.com>
24 : * Benjamin Smedberg <bsmedberg@covad.net>
25 : * Daniel Veditz <dveditz@cruzio.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "mozilla/chrome/RegistryMessageUtils.h"
42 :
43 : #include "nsResProtocolHandler.h"
44 : #include "nsIURL.h"
45 : #include "nsIIOService.h"
46 : #include "nsIServiceManager.h"
47 : #include "nsILocalFile.h"
48 : #include "prenv.h"
49 : #include "prmem.h"
50 : #include "prprf.h"
51 : #include "nsXPIDLString.h"
52 : #include "nsIFile.h"
53 : #include "nsDirectoryServiceDefs.h"
54 : #include "nsNetUtil.h"
55 : #include "nsURLHelper.h"
56 : #include "nsEscape.h"
57 :
58 : #include "mozilla/Omnijar.h"
59 :
60 : static NS_DEFINE_CID(kResURLCID, NS_RESURL_CID);
61 :
62 : static nsResProtocolHandler *gResHandler = nsnull;
63 :
64 : #if defined(PR_LOGGING)
65 : //
66 : // Log module for Resource Protocol logging...
67 : //
68 : // To enable logging (see prlog.h for full details):
69 : //
70 : // set NSPR_LOG_MODULES=nsResProtocol:5
71 : // set NSPR_LOG_FILE=log.txt
72 : //
73 : // this enables PR_LOG_ALWAYS level information and places all output in
74 : // the file log.txt
75 : //
76 : static PRLogModuleInfo *gResLog;
77 : #endif
78 :
79 : #define kAPP NS_LITERAL_CSTRING("app")
80 : #define kGRE NS_LITERAL_CSTRING("gre")
81 :
82 : //----------------------------------------------------------------------------
83 : // nsResURL : overrides nsStandardURL::GetFile to provide nsIFile resolution
84 : //----------------------------------------------------------------------------
85 :
86 : nsresult
87 9594 : nsResURL::EnsureFile()
88 : {
89 : nsresult rv;
90 :
91 9594 : NS_ENSURE_TRUE(gResHandler, NS_ERROR_NOT_AVAILABLE);
92 :
93 19188 : nsCAutoString spec;
94 9594 : rv = gResHandler->ResolveURI(this, spec);
95 9594 : if (NS_FAILED(rv))
96 0 : return rv;
97 :
98 19188 : nsCAutoString scheme;
99 9594 : rv = net_ExtractURLScheme(spec, nsnull, nsnull, &scheme);
100 9594 : if (NS_FAILED(rv))
101 0 : return rv;
102 :
103 : // Bug 585869:
104 : // In most cases, the scheme is jar if it's not file.
105 : // Regardless, net_GetFileFromURLSpec should be avoided
106 : // when the scheme isn't file.
107 9594 : if (!scheme.Equals(NS_LITERAL_CSTRING("file")))
108 0 : return NS_ERROR_NO_INTERFACE;
109 :
110 9594 : rv = net_GetFileFromURLSpec(spec, getter_AddRefs(mFile));
111 : #ifdef DEBUG_bsmedberg
112 : if (NS_SUCCEEDED(rv)) {
113 : bool exists = true;
114 : mFile->Exists(&exists);
115 : if (!exists) {
116 : printf("resource %s doesn't exist!\n", spec.get());
117 : }
118 : }
119 : #endif
120 :
121 9594 : return rv;
122 : }
123 :
124 : /* virtual */ nsStandardURL*
125 48 : nsResURL::StartClone()
126 : {
127 48 : nsResURL *clone = new nsResURL();
128 48 : return clone;
129 : }
130 :
131 : NS_IMETHODIMP
132 0 : nsResURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
133 : {
134 0 : *aClassIDNoAlloc = kResURLCID;
135 0 : return NS_OK;
136 : }
137 :
138 : //----------------------------------------------------------------------------
139 : // nsResProtocolHandler <public>
140 : //----------------------------------------------------------------------------
141 :
142 1365 : nsResProtocolHandler::nsResProtocolHandler()
143 : {
144 : #if defined(PR_LOGGING)
145 1365 : gResLog = PR_NewLogModule("nsResProtocol");
146 : #endif
147 :
148 1365 : NS_ASSERTION(!gResHandler, "res handler already created!");
149 1365 : gResHandler = this;
150 1365 : }
151 :
152 4095 : nsResProtocolHandler::~nsResProtocolHandler()
153 : {
154 1365 : gResHandler = nsnull;
155 5460 : }
156 :
157 : nsresult
158 1365 : nsResProtocolHandler::Init()
159 : {
160 1365 : if (!mSubstitutions.Init(32))
161 0 : return NS_ERROR_UNEXPECTED;
162 :
163 : nsresult rv;
164 :
165 1365 : mIOService = do_GetIOService(&rv);
166 1365 : NS_ENSURE_SUCCESS(rv, rv);
167 :
168 2730 : nsCAutoString appURI, greURI;
169 1365 : rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, appURI);
170 1365 : NS_ENSURE_SUCCESS(rv, rv);
171 1365 : rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, greURI);
172 1365 : NS_ENSURE_SUCCESS(rv, rv);
173 :
174 : //
175 : // make resource:/// point to the application directory or omnijar
176 : //
177 2730 : nsCOMPtr<nsIURI> uri;
178 1365 : rv = NS_NewURI(getter_AddRefs(uri), appURI.Length() ? appURI : greURI);
179 1365 : NS_ENSURE_SUCCESS(rv, rv);
180 :
181 1365 : rv = SetSubstitution(EmptyCString(), uri);
182 1365 : NS_ENSURE_SUCCESS(rv, rv);
183 :
184 : //
185 : // make resource://app/ point to the application directory or omnijar
186 : //
187 1365 : rv = SetSubstitution(kAPP, uri);
188 1365 : NS_ENSURE_SUCCESS(rv, rv);
189 :
190 : //
191 : // make resource://gre/ point to the GRE directory
192 : //
193 1365 : if (appURI.Length()) { // We already have greURI in uri if appURI.Length() is 0.
194 0 : rv = NS_NewURI(getter_AddRefs(uri), greURI);
195 0 : NS_ENSURE_SUCCESS(rv, rv);
196 : }
197 :
198 1365 : rv = SetSubstitution(kGRE, uri);
199 1365 : NS_ENSURE_SUCCESS(rv, rv);
200 :
201 : //XXXbsmedberg Neil wants a resource://pchrome/ for the profile chrome dir...
202 : // but once I finish multiple chrome registration I'm not sure that it is needed
203 :
204 : // XXX dveditz: resource://pchrome/ defeats profile directory salting
205 : // if web content can load it. Tread carefully.
206 :
207 1365 : return rv;
208 : }
209 :
210 : static PLDHashOperator
211 0 : EnumerateSubstitution(const nsACString& aKey,
212 : nsIURI* aURI,
213 : void* aArg)
214 : {
215 : nsTArray<ResourceMapping>* resources =
216 0 : static_cast<nsTArray<ResourceMapping>*>(aArg);
217 0 : SerializedURI uri;
218 0 : if (aURI) {
219 0 : aURI->GetSpec(uri.spec);
220 0 : aURI->GetOriginCharset(uri.charset);
221 : }
222 :
223 : ResourceMapping resource = {
224 : nsCString(aKey), uri
225 0 : };
226 0 : resources->AppendElement(resource);
227 0 : return (PLDHashOperator)PL_DHASH_NEXT;
228 : }
229 :
230 : void
231 0 : nsResProtocolHandler::CollectSubstitutions(InfallibleTArray<ResourceMapping>& aResources)
232 : {
233 0 : mSubstitutions.EnumerateRead(&EnumerateSubstitution, &aResources);
234 0 : }
235 :
236 : //----------------------------------------------------------------------------
237 : // nsResProtocolHandler::nsISupports
238 : //----------------------------------------------------------------------------
239 :
240 795371 : NS_IMPL_THREADSAFE_ISUPPORTS3(nsResProtocolHandler,
241 : nsIResProtocolHandler,
242 : nsIProtocolHandler,
243 : nsISupportsWeakReference)
244 :
245 : //----------------------------------------------------------------------------
246 : // nsResProtocolHandler::nsIProtocolHandler
247 : //----------------------------------------------------------------------------
248 :
249 : NS_IMETHODIMP
250 0 : nsResProtocolHandler::GetScheme(nsACString &result)
251 : {
252 0 : result.AssignLiteral("resource");
253 0 : return NS_OK;
254 : }
255 :
256 : NS_IMETHODIMP
257 0 : nsResProtocolHandler::GetDefaultPort(PRInt32 *result)
258 : {
259 0 : *result = -1; // no port for res: URLs
260 0 : return NS_OK;
261 : }
262 :
263 : NS_IMETHODIMP
264 50213 : nsResProtocolHandler::GetProtocolFlags(PRUint32 *result)
265 : {
266 : // XXXbz Is this really true for all resource: URIs? Could we
267 : // somehow give different flags to some of them?
268 50213 : *result = URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE;
269 50213 : return NS_OK;
270 : }
271 :
272 : NS_IMETHODIMP
273 50609 : nsResProtocolHandler::NewURI(const nsACString &aSpec,
274 : const char *aCharset,
275 : nsIURI *aBaseURI,
276 : nsIURI **result)
277 : {
278 : nsresult rv;
279 :
280 50609 : nsResURL *resURL = new nsResURL();
281 50609 : if (!resURL)
282 0 : return NS_ERROR_OUT_OF_MEMORY;
283 50609 : NS_ADDREF(resURL);
284 :
285 : // unescape any %2f and %2e to make sure nsStandardURL coalesces them.
286 : // Later net_GetFileFromURLSpec() will do a full unescape and we want to
287 : // treat them the same way the file system will. (bugs 380994, 394075)
288 101218 : nsCAutoString spec;
289 50609 : const char *src = aSpec.BeginReading();
290 50609 : const char *end = aSpec.EndReading();
291 50609 : const char *last = src;
292 :
293 50609 : spec.SetCapacity(aSpec.Length()+1);
294 1954956 : for ( ; src < end; ++src) {
295 1904347 : if (*src == '%' && (src < end-2) && *(src+1) == '2') {
296 11 : char ch = '\0';
297 11 : if (*(src+2) == 'f' || *(src+2) == 'F')
298 7 : ch = '/';
299 4 : else if (*(src+2) == 'e' || *(src+2) == 'E')
300 4 : ch = '.';
301 :
302 11 : if (ch) {
303 11 : if (last < src)
304 7 : spec.Append(last, src-last);
305 11 : spec.Append(ch);
306 11 : src += 2;
307 11 : last = src+1; // src will be incremented by the loop
308 : }
309 : }
310 : }
311 50609 : if (last < src)
312 50608 : spec.Append(last, src-last);
313 :
314 50609 : rv = resURL->Init(nsIStandardURL::URLTYPE_STANDARD, -1, spec, aCharset, aBaseURI);
315 50609 : if (NS_SUCCEEDED(rv))
316 50609 : rv = CallQueryInterface(resURL, result);
317 50609 : NS_RELEASE(resURL);
318 50609 : return rv;
319 : }
320 :
321 : NS_IMETHODIMP
322 47467 : nsResProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
323 : {
324 47467 : NS_ENSURE_ARG_POINTER(uri);
325 : nsresult rv;
326 94934 : nsCAutoString spec;
327 :
328 47467 : rv = ResolveURI(uri, spec);
329 47467 : if (NS_FAILED(rv)) return rv;
330 :
331 47460 : rv = mIOService->NewChannel(spec, nsnull, nsnull, result);
332 47460 : if (NS_FAILED(rv)) return rv;
333 :
334 47460 : return (*result)->SetOriginalURI(uri);
335 : }
336 :
337 : NS_IMETHODIMP
338 0 : nsResProtocolHandler::AllowPort(PRInt32 port, const char *scheme, bool *_retval)
339 : {
340 : // don't override anything.
341 0 : *_retval = false;
342 0 : return NS_OK;
343 : }
344 :
345 : //----------------------------------------------------------------------------
346 : // nsResProtocolHandler::nsIResProtocolHandler
347 : //----------------------------------------------------------------------------
348 :
349 : NS_IMETHODIMP
350 9790 : nsResProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *baseURI)
351 : {
352 9790 : if (!baseURI) {
353 0 : mSubstitutions.Remove(root);
354 0 : return NS_OK;
355 : }
356 :
357 : // If baseURI isn't a resource URI, we can set the substitution immediately.
358 19580 : nsCAutoString scheme;
359 9790 : nsresult rv = baseURI->GetScheme(scheme);
360 9790 : NS_ENSURE_SUCCESS(rv, rv);
361 9790 : if (!scheme.Equals(NS_LITERAL_CSTRING("resource"))) {
362 6830 : return mSubstitutions.Put(root, baseURI) ? NS_OK : NS_ERROR_UNEXPECTED;
363 : }
364 :
365 : // baseURI is a resource URI, let's resolve it first.
366 5920 : nsCAutoString newBase;
367 2960 : rv = ResolveURI(baseURI, newBase);
368 2960 : NS_ENSURE_SUCCESS(rv, rv);
369 :
370 5920 : nsCOMPtr<nsIURI> newBaseURI;
371 2960 : rv = mIOService->NewURI(newBase, nsnull, nsnull,
372 2960 : getter_AddRefs(newBaseURI));
373 2960 : NS_ENSURE_SUCCESS(rv, rv);
374 :
375 2960 : return mSubstitutions.Put(root, newBaseURI) ? NS_OK : NS_ERROR_UNEXPECTED;
376 : }
377 :
378 : NS_IMETHODIMP
379 69694 : nsResProtocolHandler::GetSubstitution(const nsACString& root, nsIURI **result)
380 : {
381 69694 : NS_ENSURE_ARG_POINTER(result);
382 :
383 69694 : if (mSubstitutions.Get(root, result))
384 69691 : return NS_OK;
385 :
386 : // try invoking the directory service for "resource:root"
387 :
388 6 : nsCAutoString key;
389 3 : key.AssignLiteral("resource:");
390 3 : key.Append(root);
391 :
392 6 : nsCOMPtr<nsIFile> file;
393 3 : nsresult rv = NS_GetSpecialDirectory(key.get(), getter_AddRefs(file));
394 3 : if (NS_FAILED(rv))
395 3 : return NS_ERROR_NOT_AVAILABLE;
396 :
397 0 : rv = mIOService->NewFileURI(file, result);
398 0 : if (NS_FAILED(rv))
399 0 : return NS_ERROR_NOT_AVAILABLE;
400 :
401 0 : return NS_OK;
402 : }
403 :
404 : NS_IMETHODIMP
405 4732 : nsResProtocolHandler::HasSubstitution(const nsACString& root, bool *result)
406 : {
407 4732 : NS_ENSURE_ARG_POINTER(result);
408 :
409 4732 : *result = mSubstitutions.Get(root, nsnull);
410 4732 : return NS_OK;
411 : }
412 :
413 : NS_IMETHODIMP
414 69699 : nsResProtocolHandler::ResolveURI(nsIURI *uri, nsACString &result)
415 : {
416 : nsresult rv;
417 :
418 139398 : nsCAutoString host;
419 139398 : nsCAutoString path;
420 :
421 69699 : rv = uri->GetAsciiHost(host);
422 69699 : if (NS_FAILED(rv)) return rv;
423 :
424 69699 : rv = uri->GetPath(path);
425 69699 : if (NS_FAILED(rv)) return rv;
426 :
427 : // Unescape the path so we can perform some checks on it.
428 139398 : nsCAutoString unescapedPath(path);
429 69699 : NS_UnescapeURL(unescapedPath);
430 :
431 : // Don't misinterpret the filepath as an absolute URI.
432 69699 : if (unescapedPath.FindChar(':') != -1)
433 2 : return NS_ERROR_MALFORMED_URI;
434 :
435 69697 : if (unescapedPath.FindChar('\\') != -1)
436 4 : return NS_ERROR_MALFORMED_URI;
437 :
438 69693 : const char *p = path.get() + 1; // path always starts with a slash
439 69693 : NS_ASSERTION(*(p-1) == '/', "Path did not begin with a slash!");
440 :
441 69693 : if (*p == '/')
442 1 : return NS_ERROR_MALFORMED_URI;
443 :
444 139384 : nsCOMPtr<nsIURI> baseURI;
445 69692 : rv = GetSubstitution(host, getter_AddRefs(baseURI));
446 69692 : if (NS_FAILED(rv)) return rv;
447 :
448 69689 : rv = baseURI->Resolve(nsDependentCString(p, path.Length()-1), result);
449 :
450 : #if defined(PR_LOGGING)
451 69689 : if (PR_LOG_TEST(gResLog, PR_LOG_DEBUG)) {
452 0 : nsCAutoString spec;
453 0 : uri->GetAsciiSpec(spec);
454 0 : PR_LOG(gResLog, PR_LOG_DEBUG,
455 : ("%s\n -> %s\n", spec.get(), PromiseFlatCString(result).get()));
456 : }
457 : #endif
458 69689 : return rv;
459 : }
|