Back to index

lightning-sunbird  0.9+nobinonly
nsURLHelperOSX.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* vim:set ts=2 sw=2 et cindent: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Alec Flett <alecf@netscape.com>
00025  *   Darin Fisher <darin@netscape.com>
00026  *   Conrad Carlen <ccarlen@netscape.com>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either the GNU General Public License Version 2 or later (the "GPL"), or
00030  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 /* Unix-specific local file uri parsing */
00043 #include "nsURLHelper.h"
00044 #include "nsEscape.h"
00045 #include "nsILocalFile.h"
00046 #include "nsVoidArray.h"
00047 #include "nsReadableUtils.h"
00048 #include <Files.h>
00049 
00050 static PRBool pathBeginsWithVolName(const nsACString& path, nsACString& firstPathComponent)
00051 {
00052   // Return whether the 1st path component in path (escaped) is equal to the name
00053   // of a mounted volume. Return the 1st path component (unescaped) in any case.
00054   // This needs to be done as quickly as possible, so we cache a list of volume names.
00055   // XXX Register an event handler to detect drives being mounted/unmounted?
00056   
00057   static nsCStringArray gVolumeList; // We will leak this - one for the life of the app :-/
00058 
00059   // Cache a list of volume names
00060   if (!gVolumeList.Count()) {
00061     OSErr err;
00062     ItemCount volumeIndex = 1;
00063     
00064     do {
00065       HFSUniStr255 volName;
00066       FSRef rootDirectory;
00067       err = ::FSGetVolumeInfo(0, volumeIndex, NULL, kFSVolInfoNone, NULL, &volName, &rootDirectory);
00068       if (err == noErr) {
00069         NS_ConvertUTF16toUTF8 volNameStr(Substring((PRUnichar *)volName.unicode,
00070                                                    (PRUnichar *)volName.unicode + volName.length));
00071         gVolumeList.AppendCString(volNameStr);
00072         volumeIndex++;
00073       }
00074     } while (err == noErr);
00075   }
00076   
00077   // Extract the first component of the path
00078   nsACString::const_iterator start;
00079   path.BeginReading(start);
00080   start.advance(1); // path begins with '/'
00081   nsACString::const_iterator directory_end;
00082   path.EndReading(directory_end);
00083   nsACString::const_iterator component_end(start);
00084   FindCharInReadable('/', component_end, directory_end);
00085   
00086   nsCAutoString flatComponent((Substring(start, component_end)));
00087   NS_UnescapeURL(flatComponent);
00088   PRInt32 foundIndex = gVolumeList.IndexOf(flatComponent);
00089   firstPathComponent = flatComponent;
00090   return (foundIndex != -1);
00091 }
00092 
00093 static nsresult convertHFSPathtoPOSIX(const nsACString& hfsPath, nsACString& posixPath)
00094 {
00095   // Use CFURL to do the conversion. We don't want to do this by simply
00096   // using SwapSlashColon - we need the charset mapped from MacRoman
00097   // to UTF-8, and we need "/Volumes" (or whatever - Apple says this is subject to change)
00098   // prepended if the path is not on the boot drive.
00099 
00100   CFStringRef pathStrRef = CFStringCreateWithCString(NULL,
00101                               PromiseFlatCString(hfsPath).get(),
00102                               kCFStringEncodingMacRoman);
00103   if (!pathStrRef)
00104     return NS_ERROR_FAILURE;
00105 
00106   nsresult rv = NS_ERROR_FAILURE;
00107   CFURLRef urlRef = CFURLCreateWithFileSystemPath(NULL,
00108                               pathStrRef, kCFURLHFSPathStyle, true);
00109   if (urlRef) {
00110     UInt8 pathBuf[PATH_MAX];
00111     if (CFURLGetFileSystemRepresentation(urlRef, true, pathBuf, sizeof(pathBuf))) {
00112       posixPath = (char *)pathBuf;
00113       rv = NS_OK;
00114     }
00115   }
00116   CFRelease(pathStrRef);
00117   if (urlRef)
00118     CFRelease(urlRef);
00119   return rv;
00120 }
00121 
00122 static void SwapSlashColon(char *s)
00123 {
00124   while (*s) {
00125     if (*s == '/')
00126       *s = ':';
00127     else if (*s == ':')
00128       *s = '/';
00129     s++;
00130   }
00131 } 
00132 
00133 nsresult
00134 net_GetURLSpecFromFile(nsIFile *aFile, nsACString &result)
00135 {
00136   // NOTE: This used to be identical to the implementation in 
00137   // nsURLHelperUnix.cpp, but with bug 278161 fixed, it's now
00138   // different because unlike on Unix, the file system charset 
00139   // is always UTF-8  on OS X.
00140   
00141   nsresult rv;
00142   nsCAutoString ePath;
00143 
00144   // construct URL spec from native file path
00145   rv = aFile->GetNativePath(ePath);
00146   if (NS_FAILED(rv))
00147     return rv;
00148 
00149   nsCAutoString escPath;
00150   NS_NAMED_LITERAL_CSTRING(prefix, "file://");
00151       
00152   // Escape the path with the directory mask
00153   if (NS_EscapeURL(ePath.get(), ePath.Length(), esc_Directory+esc_Forced, escPath))
00154     escPath.Insert(prefix, 0);
00155   else
00156     escPath.Assign(prefix + ePath);
00157 
00158   // esc_Directory does not escape the semicolons, so if a filename 
00159   // contains semicolons we need to manually escape them.
00160   escPath.ReplaceSubstring(";", "%3b");
00161 
00162   // if this file references a directory, then we need to ensure that the
00163   // URL ends with a slash.  this is important since it affects the rules
00164   // for relative URL resolution when this URL is used as a base URL.
00165   // if the file does not exist, then we make no assumption about its type,
00166   // and simply leave the URL unmodified.
00167   if (escPath.Last() != '/') {
00168     PRBool dir;
00169     rv = aFile->IsDirectory(&dir);
00170     if (NS_SUCCEEDED(rv) && dir)
00171       escPath += '/';
00172   }
00173   
00174   result = escPath;
00175   return NS_OK;
00176 }
00177 
00178 nsresult
00179 net_GetFileFromURLSpec(const nsACString &aURL, nsIFile **result)
00180 {
00181   // NOTE: See also the implementation in nsURLHelperUnix.cpp
00182   // This matches it except for the HFS path handling and file
00183   // system charset conversion.
00184 
00185   nsresult rv;
00186 
00187   nsCOMPtr<nsILocalFile> localFile;
00188   rv = NS_NewNativeLocalFile(EmptyCString(), PR_TRUE, getter_AddRefs(localFile));
00189   if (NS_FAILED(rv))
00190     return rv;
00191   
00192   nsCAutoString directory, fileBaseName, fileExtension, path;
00193   PRBool bHFSPath = PR_FALSE;
00194 
00195   rv = net_ParseFileURL(aURL, directory, fileBaseName, fileExtension);
00196   if (NS_FAILED(rv))
00197     return rv;
00198 
00199   if (!directory.IsEmpty()) {
00200     NS_EscapeURL(directory, esc_Directory|esc_AlwaysCopy, path);
00201 
00202     // The canonical form of file URLs on OSX use POSIX paths:
00203     //   file:///path-name.
00204     // But, we still encounter file URLs that use HFS paths:
00205     //   file:///volume-name/path-name
00206     // Determine that here and normalize HFS paths to POSIX.
00207     nsCAutoString possibleVolName;
00208     if (pathBeginsWithVolName(directory, possibleVolName)) {        
00209       // Though we know it begins with a volume name, it could still
00210       // be a valid POSIX path if the boot drive is named "Mac HD"
00211       // and there is a directory "Mac HD" at its root. If such a
00212       // directory doesn't exist, we'll assume this is an HFS path.
00213       FSRef testRef;
00214       possibleVolName.Insert("/", 0);
00215       if (::FSPathMakeRef((UInt8*)possibleVolName.get(), &testRef, nsnull) != noErr)
00216         bHFSPath = PR_TRUE;
00217     }
00218 
00219     if (bHFSPath) {
00220       // "%2F"s need to become slashes, while all other slashes need to
00221       // become colons. If we start out by changing "%2F"s to colons, we
00222       // can reply on SwapSlashColon() to do what we need
00223       path.ReplaceSubstring("%2F", ":");
00224       path.Cut(0, 1); // directory begins with '/'
00225       SwapSlashColon((char *)path.get());
00226       // At this point, path is an HFS path made using the same
00227       // algorithm as nsURLHelperMac. We'll convert to POSIX below.
00228     }
00229   }
00230   if (!fileBaseName.IsEmpty())
00231     NS_EscapeURL(fileBaseName, esc_FileBaseName|esc_AlwaysCopy, path);
00232   if (!fileExtension.IsEmpty()) {
00233     path += '.';
00234     NS_EscapeURL(fileExtension, esc_FileExtension|esc_AlwaysCopy, path);
00235   }
00236 
00237   NS_UnescapeURL(path);
00238   if (path.Length() != strlen(path.get()))
00239     return NS_ERROR_FILE_INVALID_PATH;
00240 
00241   if (bHFSPath)
00242     convertHFSPathtoPOSIX(path, path);
00243 
00244   // assuming path is encoded in the native charset
00245   rv = localFile->InitWithNativePath(path);
00246   if (NS_FAILED(rv))
00247     return rv;
00248 
00249   NS_ADDREF(*result = localFile);
00250   return NS_OK;
00251 }