Back to index

lightning-sunbird  0.9+nobinonly
nsResProtocolHandler.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Darin Fisher <darin@netscape.com>
00024  *   Benjamin Smedberg <bsmedberg@covad.net>
00025  *   Daniel Veditz <dveditz@cruzio.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsResProtocolHandler.h"
00042 #include "nsAutoLock.h"
00043 #include "nsIURL.h"
00044 #include "nsIIOService.h"
00045 #include "nsIServiceManager.h"
00046 #include "nsILocalFile.h"
00047 #include "prenv.h"
00048 #include "prmem.h"
00049 #include "prprf.h"
00050 #include "nsXPIDLString.h"
00051 #include "nsIFile.h"
00052 #include "nsDirectoryServiceDefs.h"
00053 #include "nsNetUtil.h"
00054 #include "nsURLHelper.h"
00055 #include "nsEscape.h"
00056 
00057 static NS_DEFINE_CID(kResURLCID, NS_RESURL_CID);
00058 
00059 static nsResProtocolHandler *gResHandler = nsnull;
00060 
00061 #if defined(PR_LOGGING)
00062 //
00063 // Log module for Resource Protocol logging...
00064 //
00065 // To enable logging (see prlog.h for full details):
00066 //
00067 //    set NSPR_LOG_MODULES=nsResProtocol:5
00068 //    set NSPR_LOG_FILE=log.txt
00069 //
00070 // this enables PR_LOG_ALWAYS level information and places all output in
00071 // the file log.txt
00072 //
00073 static PRLogModuleInfo *gResLog;
00074 #endif
00075 #define LOG(args) PR_LOG(gResLog, PR_LOG_DEBUG, args)
00076 
00077 //----------------------------------------------------------------------------
00078 // nsResURL : overrides nsStandardURL::GetFile to provide nsIFile resolution
00079 //----------------------------------------------------------------------------
00080 
00081 nsresult
00082 nsResURL::EnsureFile()
00083 {
00084     nsresult rv;
00085 
00086     NS_ENSURE_TRUE(gResHandler, NS_ERROR_NOT_AVAILABLE);
00087 
00088     nsCAutoString spec;
00089     rv = gResHandler->ResolveURI(this, spec);
00090     if (NS_FAILED(rv)) return rv;
00091 
00092     rv = net_GetFileFromURLSpec(spec, getter_AddRefs(mFile));
00093 #ifdef DEBUG_bsmedberg
00094     if (NS_SUCCEEDED(rv)) {
00095         PRBool exists = PR_TRUE;
00096         mFile->Exists(&exists);
00097         if (!exists) {
00098             printf("resource %s doesn't exist!\n", spec.get());
00099         }
00100     }
00101 #endif
00102 
00103     return rv;
00104 }
00105 
00106 /* virtual */ nsStandardURL*
00107 nsResURL::StartClone()
00108 {
00109     nsResURL *clone;
00110     NS_NEWXPCOM(clone, nsResURL);
00111     return clone;
00112 }
00113 
00114 NS_IMETHODIMP 
00115 nsResURL::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
00116 {
00117     *aClassIDNoAlloc = kResURLCID;
00118     return NS_OK;
00119 }
00120 
00121 //----------------------------------------------------------------------------
00122 // nsResProtocolHandler <public>
00123 //----------------------------------------------------------------------------
00124 
00125 nsResProtocolHandler::nsResProtocolHandler()
00126 {
00127 #if defined(PR_LOGGING)
00128     gResLog = PR_NewLogModule("nsResProtocol");
00129 #endif
00130 
00131     NS_ASSERTION(!gResHandler, "res handler already created!");
00132     gResHandler = this;
00133 }
00134 
00135 nsResProtocolHandler::~nsResProtocolHandler()
00136 {
00137     gResHandler = nsnull;
00138 }
00139 
00140 nsresult
00141 nsResProtocolHandler::AddSpecialDir(const char* aSpecialDir, const nsACString& aSubstitution)
00142 {
00143     nsCOMPtr<nsIFile> file;
00144     nsresult rv = NS_GetSpecialDirectory(aSpecialDir, getter_AddRefs(file));
00145     NS_ENSURE_SUCCESS(rv, rv);
00146 
00147     nsCOMPtr<nsIURI> uri;
00148     rv = mIOService->NewFileURI(file, getter_AddRefs(uri));
00149     NS_ENSURE_SUCCESS(rv, rv);
00150 
00151     return SetSubstitution(aSubstitution, uri);
00152 }
00153 
00154 nsresult
00155 nsResProtocolHandler::Init()
00156 {
00157     if (!mSubstitutions.Init(32))
00158         return NS_ERROR_UNEXPECTED;
00159 
00160     nsresult rv;
00161 
00162     mIOService = do_GetIOService(&rv);
00163     NS_ENSURE_SUCCESS(rv, rv);
00164 
00165     //
00166     // make resource:/// point to the application directory
00167     //
00168     rv = AddSpecialDir(NS_OS_CURRENT_PROCESS_DIR, EmptyCString());
00169     NS_ENSURE_SUCCESS(rv, rv);
00170 
00171     //
00172     // make resource://gre/ point to the GRE directory
00173     //
00174     rv = AddSpecialDir(NS_GRE_DIR, NS_LITERAL_CSTRING("gre"));
00175     NS_ENSURE_SUCCESS(rv, rv);
00176 
00177     //XXXbsmedberg Neil wants a resource://pchrome/ for the profile chrome dir...
00178     // but once I finish multiple chrome registration I'm not sure that it is needed
00179 
00180     // XXX dveditz: resource://pchrome/ defeats profile directory salting
00181     // if web content can load it. Tread carefully.
00182 
00183     return rv;
00184 }
00185 
00186 //----------------------------------------------------------------------------
00187 // nsResProtocolHandler::nsISupports
00188 //----------------------------------------------------------------------------
00189 
00190 NS_IMPL_THREADSAFE_ISUPPORTS3(nsResProtocolHandler,
00191                               nsIResProtocolHandler,
00192                               nsIProtocolHandler,
00193                               nsISupportsWeakReference)
00194 
00195 //----------------------------------------------------------------------------
00196 // nsResProtocolHandler::nsIProtocolHandler
00197 //----------------------------------------------------------------------------
00198 
00199 NS_IMETHODIMP
00200 nsResProtocolHandler::GetScheme(nsACString &result)
00201 {
00202     result.AssignLiteral("resource");
00203     return NS_OK;
00204 }
00205 
00206 NS_IMETHODIMP
00207 nsResProtocolHandler::GetDefaultPort(PRInt32 *result)
00208 {
00209     *result = -1;        // no port for res: URLs
00210     return NS_OK;
00211 }
00212 
00213 NS_IMETHODIMP
00214 nsResProtocolHandler::GetProtocolFlags(PRUint32 *result)
00215 {
00216     *result = URI_STD;
00217     return NS_OK;
00218 }
00219 
00220 NS_IMETHODIMP
00221 nsResProtocolHandler::NewURI(const nsACString &aSpec,
00222                              const char *aCharset,
00223                              nsIURI *aBaseURI,
00224                              nsIURI **result)
00225 {
00226     nsresult rv;
00227 
00228     nsResURL *resURL;
00229     NS_NEWXPCOM(resURL, nsResURL);
00230     if (!resURL)
00231         return NS_ERROR_OUT_OF_MEMORY;
00232     NS_ADDREF(resURL);
00233 
00234     // unescape any %2f and %2e to make sure nsStandardURL coalesces them.
00235     // Later net_GetFileFromURLSpec() will do a full unescape and we want to
00236     // treat them the same way the file system will. (bugs 380994, 394075)
00237     nsCAutoString spec;
00238     const char *src = aSpec.BeginReading();
00239     const char *end = aSpec.EndReading();
00240     const char *last = src;
00241 
00242     spec.SetCapacity(aSpec.Length()+1);
00243     for ( ; src < end; ++src) {
00244         if (*src == '%' && (src < end-2) && *(src+1) == '2') {
00245            char ch = '\0';
00246            if (*(src+2) == 'f' || *(src+2) == 'F')
00247              ch = '/';
00248            else if (*(src+2) == 'e' || *(src+2) == 'E')
00249              ch = '.';
00250 
00251            if (ch) {
00252              if (last < src)
00253                spec.Append(last, src-last);
00254              spec.Append(ch);
00255              src += 2;
00256              last = src+1; // src will be incremented by the loop
00257            }
00258         }
00259     }
00260     if (last < src)
00261       spec.Append(last, src-last);
00262 
00263     rv = resURL->Init(nsIStandardURL::URLTYPE_STANDARD, -1, spec, aCharset, aBaseURI);
00264     if (NS_SUCCEEDED(rv))
00265         rv = CallQueryInterface(resURL, result);
00266     NS_RELEASE(resURL);
00267     return rv;
00268 }
00269 
00270 NS_IMETHODIMP
00271 nsResProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result)
00272 {
00273     NS_ENSURE_ARG_POINTER(uri);
00274     nsresult rv;
00275     nsCAutoString spec;
00276 
00277     rv = ResolveURI(uri, spec);
00278     if (NS_FAILED(rv)) return rv;
00279 
00280     rv = mIOService->NewChannel(spec, nsnull, nsnull, result);
00281     if (NS_FAILED(rv)) return rv;
00282 
00283     return (*result)->SetOriginalURI(uri);
00284 }
00285 
00286 NS_IMETHODIMP 
00287 nsResProtocolHandler::AllowPort(PRInt32 port, const char *scheme, PRBool *_retval)
00288 {
00289     // don't override anything.  
00290     *_retval = PR_FALSE;
00291     return NS_OK;
00292 }
00293 
00294 //----------------------------------------------------------------------------
00295 // nsResProtocolHandler::nsIResProtocolHandler
00296 //----------------------------------------------------------------------------
00297 
00298 NS_IMETHODIMP
00299 nsResProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *baseURI)
00300 {
00301     if (!baseURI) {
00302         mSubstitutions.Remove(root);
00303         return NS_OK;
00304     }
00305 
00306     return mSubstitutions.Put(root, baseURI) ? NS_OK : NS_ERROR_UNEXPECTED;
00307 }
00308 
00309 NS_IMETHODIMP
00310 nsResProtocolHandler::GetSubstitution(const nsACString& root, nsIURI **result)
00311 {
00312     NS_ENSURE_ARG_POINTER(result);
00313 
00314     if (mSubstitutions.Get(root, result))
00315         return NS_OK;
00316 
00317     // try invoking the directory service for "resource:root"
00318 
00319     nsCAutoString key;
00320     key.AssignLiteral("resource:");
00321     key.Append(root);
00322 
00323     nsCOMPtr<nsIFile> file;
00324     nsresult rv = NS_GetSpecialDirectory(key.get(), getter_AddRefs(file));
00325     if (NS_FAILED(rv))
00326         return NS_ERROR_NOT_AVAILABLE;
00327         
00328     rv = mIOService->NewFileURI(file, result);
00329     if (NS_FAILED(rv))
00330         return NS_ERROR_NOT_AVAILABLE;
00331 
00332     return NS_OK;
00333 }
00334 
00335 NS_IMETHODIMP
00336 nsResProtocolHandler::HasSubstitution(const nsACString& root, PRBool *result)
00337 {
00338     NS_ENSURE_ARG_POINTER(result);
00339 
00340     *result = mSubstitutions.Get(root, nsnull);
00341     return NS_OK;
00342 }
00343 
00344 NS_IMETHODIMP
00345 nsResProtocolHandler::ResolveURI(nsIURI *uri, nsACString &result)
00346 {
00347     nsresult rv;
00348 
00349     nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
00350     if (!url)
00351         return NS_NOINTERFACE;
00352 
00353     nsCAutoString host;
00354     nsCAutoString path;
00355 
00356     rv = uri->GetAsciiHost(host);
00357     if (NS_FAILED(rv)) return rv;
00358 
00359     rv = uri->GetPath(path);
00360     if (NS_FAILED(rv)) return rv;
00361 
00362     nsCAutoString filepath;
00363     url->GetFilePath(filepath);
00364 
00365     // Don't misinterpret the filepath as an absolute URI.
00366     if (filepath.FindChar(':') != -1)
00367         return NS_ERROR_MALFORMED_URI;
00368 
00369     NS_UnescapeURL(filepath);
00370     if (filepath.FindChar('\\') != -1)
00371         return NS_ERROR_MALFORMED_URI;
00372 
00373     const char *p = path.get() + 1; // path always starts with a slash
00374     NS_ASSERTION(*(p-1) == '/', "Path did not begin with a slash!");
00375 
00376     if (*p == '/')
00377         return NS_ERROR_MALFORMED_URI;
00378 
00379     nsCOMPtr<nsIURI> baseURI;
00380     rv = GetSubstitution(host, getter_AddRefs(baseURI));
00381     if (NS_FAILED(rv)) return rv;
00382 
00383     rv = baseURI->Resolve(nsDependentCString(p, path.Length()-1), result);
00384 
00385 #if defined(PR_LOGGING)
00386     if (PR_LOG_TEST(gResLog, PR_LOG_DEBUG)) {
00387         nsCAutoString spec;
00388         uri->GetAsciiSpec(spec);
00389         LOG(("%s\n -> %s\n", spec.get(), PromiseFlatCString(result).get()));
00390     }
00391 #endif
00392     return rv;
00393 }