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 9608 : nsResURL::EnsureFile()
88 : {
89 : nsresult rv;
90 :
91 9608 : NS_ENSURE_TRUE(gResHandler, NS_ERROR_NOT_AVAILABLE);
92 :
93 19216 : nsCAutoString spec;
94 9608 : rv = gResHandler->ResolveURI(this, spec);
95 9608 : if (NS_FAILED(rv))
96 0 : return rv;
97 :
98 19216 : nsCAutoString scheme;
99 9608 : rv = net_ExtractURLScheme(spec, nsnull, nsnull, &scheme);
100 9608 : 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 9608 : if (!scheme.Equals(NS_LITERAL_CSTRING("file")))
108 0 : return NS_ERROR_NO_INTERFACE;
109 :
110 9608 : 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 9608 : 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 1419 : nsResProtocolHandler::nsResProtocolHandler()
143 : {
144 : #if defined(PR_LOGGING)
145 1419 : gResLog = PR_NewLogModule("nsResProtocol");
146 : #endif
147 :
148 1419 : NS_ASSERTION(!gResHandler, "res handler already created!");
149 1419 : gResHandler = this;
150 1419 : }
151 :
152 4257 : nsResProtocolHandler::~nsResProtocolHandler()
153 : {
154 1419 : gResHandler = nsnull;
155 5676 : }
156 :
157 : nsresult
158 1419 : nsResProtocolHandler::Init()
159 : {
160 1419 : if (!mSubstitutions.Init(32))
161 0 : return NS_ERROR_UNEXPECTED;
162 :
163 : nsresult rv;
164 :
165 1419 : mIOService = do_GetIOService(&rv);
166 1419 : NS_ENSURE_SUCCESS(rv, rv);
167 :
168 2838 : nsCAutoString appURI, greURI;
169 1419 : rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, appURI);
170 1419 : NS_ENSURE_SUCCESS(rv, rv);
171 1419 : rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, greURI);
172 1419 : NS_ENSURE_SUCCESS(rv, rv);
173 :
174 : //
175 : // make resource:/// point to the application directory or omnijar
176 : //
177 2838 : nsCOMPtr<nsIURI> uri;
178 1419 : rv = NS_NewURI(getter_AddRefs(uri), appURI.Length() ? appURI : greURI);
179 1419 : NS_ENSURE_SUCCESS(rv, rv);
180 :
181 1419 : rv = SetSubstitution(EmptyCString(), uri);
182 1419 : NS_ENSURE_SUCCESS(rv, rv);
183 :
184 : //
185 : // make resource://app/ point to the application directory or omnijar
186 : //
187 1419 : rv = SetSubstitution(kAPP, uri);
188 1419 : NS_ENSURE_SUCCESS(rv, rv);
189 :
190 : //
191 : // make resource://gre/ point to the GRE directory
192 : //
193 1419 : 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 1419 : rv = SetSubstitution(kGRE, uri);
199 1419 : 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 1419 : 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 799829 : 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 50347 : 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 50347 : *result = URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE;
269 50347 : return NS_OK;
270 : }
271 :
272 : NS_IMETHODIMP
273 50741 : nsResProtocolHandler::NewURI(const nsACString &aSpec,
274 : const char *aCharset,
275 : nsIURI *aBaseURI,
276 : nsIURI **result)
277 : {
278 : nsresult rv;
279 :
280 50741 : nsResURL *resURL = new nsResURL();
281 50741 : if (!resURL)
282 0 : return NS_ERROR_OUT_OF_MEMORY;
283 50741 : 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 101482 : nsCAutoString spec;
289 50741 : const char *src = aSpec.BeginReading();
290 50741 : const char *end = aSpec.EndReading();
291 50741 : const char *last = src;
292 :
293 50741 : spec.SetCapacity(aSpec.Length()+1);
294 1959757 : for ( ; src < end; ++src) {
295 1909016 : 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 50741 : if (last < src)
312 50740 : spec.Append(last, src-last);
313 :
314 50741 : rv = resURL->Init(nsIStandardURL::URLTYPE_STANDARD, -1, spec, aCharset, aBaseURI);
315 50741 : if (NS_SUCCEEDED(rv))
316 50741 : rv = CallQueryInterface(resURL, result);
317 50741 : NS_RELEASE(resURL);
318 50741 : return rv;
319 : }
320 :
321 : NS_IMETHODIMP
322 47491 : nsResProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
323 : {
324 47491 : NS_ENSURE_ARG_POINTER(uri);
325 : nsresult rv;
326 94982 : nsCAutoString spec;
327 :
328 47491 : rv = ResolveURI(uri, spec);
329 47491 : if (NS_FAILED(rv)) return rv;
330 :
331 47484 : rv = mIOService->NewChannel(spec, nsnull, nsnull, result);
332 47484 : if (NS_FAILED(rv)) return rv;
333 :
334 47484 : nsLoadFlags loadFlags = 0;
335 47484 : (*result)->GetLoadFlags(&loadFlags);
336 47484 : (*result)->SetLoadFlags(loadFlags & ~nsIChannel::LOAD_REPLACE);
337 47484 : return (*result)->SetOriginalURI(uri);
338 : }
339 :
340 : NS_IMETHODIMP
341 0 : nsResProtocolHandler::AllowPort(PRInt32 port, const char *scheme, bool *_retval)
342 : {
343 : // don't override anything.
344 0 : *_retval = false;
345 0 : return NS_OK;
346 : }
347 :
348 : //----------------------------------------------------------------------------
349 : // nsResProtocolHandler::nsIResProtocolHandler
350 : //----------------------------------------------------------------------------
351 :
352 : NS_IMETHODIMP
353 10135 : nsResProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *baseURI)
354 : {
355 10135 : if (!baseURI) {
356 0 : mSubstitutions.Remove(root);
357 0 : return NS_OK;
358 : }
359 :
360 : // If baseURI isn't a resource URI, we can set the substitution immediately.
361 20270 : nsCAutoString scheme;
362 10135 : nsresult rv = baseURI->GetScheme(scheme);
363 10135 : NS_ENSURE_SUCCESS(rv, rv);
364 10135 : if (!scheme.Equals(NS_LITERAL_CSTRING("resource"))) {
365 7067 : return mSubstitutions.Put(root, baseURI) ? NS_OK : NS_ERROR_UNEXPECTED;
366 : }
367 :
368 : // baseURI is a resource URI, let's resolve it first.
369 6136 : nsCAutoString newBase;
370 3068 : rv = ResolveURI(baseURI, newBase);
371 3068 : NS_ENSURE_SUCCESS(rv, rv);
372 :
373 6136 : nsCOMPtr<nsIURI> newBaseURI;
374 3068 : rv = mIOService->NewURI(newBase, nsnull, nsnull,
375 3068 : getter_AddRefs(newBaseURI));
376 3068 : NS_ENSURE_SUCCESS(rv, rv);
377 :
378 3068 : return mSubstitutions.Put(root, newBaseURI) ? NS_OK : NS_ERROR_UNEXPECTED;
379 : }
380 :
381 : NS_IMETHODIMP
382 69854 : nsResProtocolHandler::GetSubstitution(const nsACString& root, nsIURI **result)
383 : {
384 69854 : NS_ENSURE_ARG_POINTER(result);
385 :
386 69854 : if (mSubstitutions.Get(root, result))
387 69851 : return NS_OK;
388 :
389 : // try invoking the directory service for "resource:root"
390 :
391 6 : nsCAutoString key;
392 3 : key.AssignLiteral("resource:");
393 3 : key.Append(root);
394 :
395 6 : nsCOMPtr<nsIFile> file;
396 3 : nsresult rv = NS_GetSpecialDirectory(key.get(), getter_AddRefs(file));
397 3 : if (NS_FAILED(rv))
398 3 : return NS_ERROR_NOT_AVAILABLE;
399 :
400 0 : rv = mIOService->NewFileURI(file, result);
401 0 : if (NS_FAILED(rv))
402 0 : return NS_ERROR_NOT_AVAILABLE;
403 :
404 0 : return NS_OK;
405 : }
406 :
407 : NS_IMETHODIMP
408 4894 : nsResProtocolHandler::HasSubstitution(const nsACString& root, bool *result)
409 : {
410 4894 : NS_ENSURE_ARG_POINTER(result);
411 :
412 4894 : *result = mSubstitutions.Get(root, nsnull);
413 4894 : return NS_OK;
414 : }
415 :
416 : NS_IMETHODIMP
417 69859 : nsResProtocolHandler::ResolveURI(nsIURI *uri, nsACString &result)
418 : {
419 : nsresult rv;
420 :
421 139718 : nsCAutoString host;
422 139718 : nsCAutoString path;
423 :
424 69859 : rv = uri->GetAsciiHost(host);
425 69859 : if (NS_FAILED(rv)) return rv;
426 :
427 69859 : rv = uri->GetPath(path);
428 69859 : if (NS_FAILED(rv)) return rv;
429 :
430 : // Unescape the path so we can perform some checks on it.
431 139718 : nsCAutoString unescapedPath(path);
432 69859 : NS_UnescapeURL(unescapedPath);
433 :
434 : // Don't misinterpret the filepath as an absolute URI.
435 69859 : if (unescapedPath.FindChar(':') != -1)
436 2 : return NS_ERROR_MALFORMED_URI;
437 :
438 69857 : if (unescapedPath.FindChar('\\') != -1)
439 4 : return NS_ERROR_MALFORMED_URI;
440 :
441 69853 : const char *p = path.get() + 1; // path always starts with a slash
442 69853 : NS_ASSERTION(*(p-1) == '/', "Path did not begin with a slash!");
443 :
444 69853 : if (*p == '/')
445 1 : return NS_ERROR_MALFORMED_URI;
446 :
447 139704 : nsCOMPtr<nsIURI> baseURI;
448 69852 : rv = GetSubstitution(host, getter_AddRefs(baseURI));
449 69852 : if (NS_FAILED(rv)) return rv;
450 :
451 69849 : rv = baseURI->Resolve(nsDependentCString(p, path.Length()-1), result);
452 :
453 : #if defined(PR_LOGGING)
454 69849 : if (PR_LOG_TEST(gResLog, PR_LOG_DEBUG)) {
455 0 : nsCAutoString spec;
456 0 : uri->GetAsciiSpec(spec);
457 0 : PR_LOG(gResLog, PR_LOG_DEBUG,
458 : ("%s\n -> %s\n", spec.get(), PromiseFlatCString(result).get()));
459 : }
460 : #endif
461 69849 : return rv;
462 : }
|