Back to index

lightning-sunbird  0.9+nobinonly
nsLocalFileWin.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; 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 Communicator client code, released
00016  * March 31, 1998.
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-1999
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Doug Turner <dougt@netscape.com>
00025  *   Dean Tessman <dean_tessman@hotmail.com>
00026  *   Brodie Thiesfield <brofield@jellycan.com>
00027  *   Jungshik Shin <jshin@i18nl10n.com>
00028  *
00029  * Alternatively, the contents of this file may be used under the terms of
00030  * either of the GNU General Public License Version 2 or later (the "GPL"),
00031  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00032  * in which case the provisions of the GPL or the LGPL are applicable instead
00033  * of those above. If you wish to allow use of your version of this file only
00034  * under the terms of either the GPL or the LGPL, and not to allow others to
00035  * use your version of this file under the terms of the MPL, indicate your
00036  * decision by deleting the provisions above and replace them with the notice
00037  * and other provisions required by the GPL or the LGPL. If you do not delete
00038  * the provisions above, a recipient may use your version of this file under
00039  * the terms of any one of the MPL, the GPL or the LGPL.
00040  *
00041  * ***** END LICENSE BLOCK ***** */
00042 
00043 
00044 #include "nsCOMPtr.h"
00045 #include "nsMemory.h"
00046 
00047 #include "nsLocalFile.h"
00048 #include "nsIDirectoryEnumerator.h"
00049 #include "nsNativeCharsetUtils.h"
00050 #include "nsWinAPIs.h"
00051 
00052 #include "nsISimpleEnumerator.h"
00053 #include "nsIComponentManager.h"
00054 #include "prtypes.h"
00055 #include "prio.h"
00056 #include "private/pprio.h"  // To get PR_ImportFile
00057 #include "prprf.h"
00058 #include "prmem.h"
00059 
00060 #include "nsXPIDLString.h"
00061 #include "nsReadableUtils.h"
00062 
00063 #include <direct.h>
00064 #include <windows.h>
00065 
00066 #include "shellapi.h"
00067 #include "shlguid.h"
00068 
00069 #include  <io.h>
00070 #include  <stdio.h>
00071 #include  <stdlib.h>
00072 #include  <mbstring.h>
00073 
00074 #include "nsXPIDLString.h"
00075 #include "prproces.h"
00076 #include "nsITimelineService.h"
00077 
00078 #include "nsAutoLock.h"
00079 #include "SpecialSystemDirectory.h"
00080 
00081 // _mbsstr isn't declared in w32api headers but it's there in the libs
00082 #ifdef __MINGW32__
00083 extern "C" {
00084 unsigned char *_mbsstr( const unsigned char *str,
00085                         const unsigned char *substr );
00086 }
00087 #endif
00088 
00089 class nsDriveEnumerator : public nsISimpleEnumerator
00090 {
00091 public:
00092     nsDriveEnumerator();
00093     virtual ~nsDriveEnumerator();
00094     NS_DECL_ISUPPORTS
00095     NS_DECL_NSISIMPLEENUMERATOR
00096     nsresult Init();
00097 private:
00098     /* mDrives and mLetter share data
00099      * Init sets them.
00100      * HasMoreElements reads mLetter.
00101      * GetNext advances mLetter.
00102      */
00103     nsCString mDrives;
00104     const char *mLetter;
00105 };
00106 
00107 //----------------------------------------------------------------------------
00108 // short cut resolver
00109 //----------------------------------------------------------------------------
00110 #ifndef WINCE
00111 class ShortcutResolver
00112 {
00113 public:
00114     ShortcutResolver();
00115     // nonvirtual since we're not subclassed
00116     ~ShortcutResolver();
00117 
00118     nsresult Init();
00119     nsresult Resolve(const WCHAR* in, WCHAR* out);
00120 
00121 private:
00122     PRLock*       mLock;
00123     IPersistFile* mPersistFile;
00124     // Win 95 and 98 don't have IShellLinkW
00125     union 
00126     {
00127         IShellLinkW*  W;
00128         IShellLinkA*  A;
00129     } mShellLink; 
00130 };
00131 
00132 ShortcutResolver::ShortcutResolver()
00133 {
00134     mLock = nsnull;
00135     mPersistFile = nsnull;
00136     mShellLink.W   = nsnull;
00137 }
00138 
00139 ShortcutResolver::~ShortcutResolver()
00140 {
00141     if (mLock)
00142         PR_DestroyLock(mLock);
00143 
00144     // Release the pointer to the IPersistFile interface.
00145     if (mPersistFile)
00146         mPersistFile->Release();
00147 
00148     // Release the pointer to the IShellLink interface.
00149     if (NS_UseUnicode())
00150     {
00151         if (mShellLink.W)
00152             mShellLink.W->Release();
00153     }
00154     else
00155     {
00156         if (mShellLink.A)
00157             mShellLink.A->Release();
00158     }
00159 
00160     CoUninitialize();
00161 }
00162 
00163 nsresult
00164 ShortcutResolver::Init()
00165 {
00166     CoInitialize(NULL);  // FIX: we should probably move somewhere higher up during startup
00167 
00168     mLock = PR_NewLock();
00169     if (!mLock)
00170         return NS_ERROR_FAILURE;
00171 
00172     HRESULT hres; 
00173     if (NS_UseUnicode())
00174     {
00175         hres = CoCreateInstance(CLSID_ShellLink,
00176                                 NULL,
00177                                 CLSCTX_INPROC_SERVER,
00178                                 IID_IShellLinkW,
00179                                 (void**)&(mShellLink.W));
00180         if (SUCCEEDED(hres))
00181         {
00182             // Get a pointer to the IPersistFile interface.
00183             hres = mShellLink.W->QueryInterface(IID_IPersistFile,
00184                                                 (void**)&mPersistFile);
00185         }
00186     }
00187     else 
00188     {
00189         hres = CoCreateInstance(CLSID_ShellLink,
00190                                 NULL,
00191                                 CLSCTX_INPROC_SERVER,
00192                                 IID_IShellLinkA,
00193                                 (void**)&(mShellLink.A));
00194         if (SUCCEEDED(hres))
00195         {
00196             // Get a pointer to the IPersistFile interface.
00197             hres = mShellLink.A->QueryInterface(IID_IPersistFile,
00198                                                 (void**)&mPersistFile);
00199         }
00200     }
00201 
00202     if (mPersistFile == nsnull || mShellLink.W == nsnull)
00203         return NS_ERROR_FAILURE;
00204 
00205     return NS_OK;
00206 }
00207 
00208 // |out| must be an allocated buffer of size MAX_PATH
00209 nsresult
00210 ShortcutResolver::Resolve(const WCHAR* in, WCHAR* out)
00211 {
00212     nsAutoLock lock(mLock);
00213 
00214     // see if we can Load the path.
00215     HRESULT hres = mPersistFile->Load(in, STGM_READ);
00216 
00217     if (FAILED(hres))
00218         return NS_ERROR_FAILURE;
00219 
00220     // Resolve the link.
00221     hres = mShellLink.W->Resolve(nsnull, SLR_NO_UI );
00222 
00223     if (FAILED(hres))
00224         return NS_ERROR_FAILURE;
00225 
00226     // Get the path to the link target.
00227     if (NS_UseUnicode()) 
00228         hres = mShellLink.W->GetPath( out, MAX_PATH, NULL, SLGP_UNCPRIORITY );
00229     else {
00230         char tmp[MAX_PATH];
00231         hres = mShellLink.A->GetPath( tmp, MAX_PATH, NULL, SLGP_UNCPRIORITY );
00232         if (SUCCEEDED(hres))
00233             NS_ConvertAtoW(tmp, MAX_PATH, out);
00234     }
00235     if (FAILED(hres))
00236         return NS_ERROR_FAILURE;
00237     return NS_OK;
00238 }
00239 
00240 static ShortcutResolver * gResolver = nsnull;
00241 
00242 static nsresult NS_CreateShortcutResolver()
00243 {
00244     gResolver = new ShortcutResolver();
00245     if (!gResolver)
00246         return NS_ERROR_OUT_OF_MEMORY;
00247 
00248     return gResolver->Init();
00249 }
00250 
00251 static void NS_DestroyShortcutResolver()
00252 {
00253     delete gResolver;
00254     gResolver = nsnull;
00255 }
00256 #endif
00257 
00258 
00259 //-----------------------------------------------------------------------------
00260 // static helper functions
00261 //-----------------------------------------------------------------------------
00262 
00263 // certainly not all the error that can be
00264 // encountered, but many of them common ones
00265 static nsresult ConvertWinError(DWORD winErr)
00266 {
00267     nsresult rv;
00268 
00269     switch (winErr)
00270     {
00271         case ERROR_FILE_NOT_FOUND:
00272         case ERROR_PATH_NOT_FOUND:
00273         case ERROR_INVALID_DRIVE:
00274             rv = NS_ERROR_FILE_NOT_FOUND;
00275             break;
00276         case ERROR_ACCESS_DENIED:
00277         case ERROR_NOT_SAME_DEVICE:
00278             rv = NS_ERROR_FILE_ACCESS_DENIED;
00279             break;
00280         case ERROR_NOT_ENOUGH_MEMORY:
00281         case ERROR_INVALID_BLOCK:
00282         case ERROR_INVALID_HANDLE:
00283         case ERROR_ARENA_TRASHED:
00284             rv = NS_ERROR_OUT_OF_MEMORY;
00285             break;
00286         case ERROR_CURRENT_DIRECTORY:
00287             rv = NS_ERROR_FILE_DIR_NOT_EMPTY;
00288             break;
00289         case ERROR_WRITE_PROTECT:
00290             rv = NS_ERROR_FILE_READ_ONLY;
00291             break;
00292         case ERROR_HANDLE_DISK_FULL:
00293             rv = NS_ERROR_FILE_TOO_BIG;
00294             break;
00295         case ERROR_FILE_EXISTS:
00296         case ERROR_ALREADY_EXISTS:
00297         case ERROR_CANNOT_MAKE:
00298             rv = NS_ERROR_FILE_ALREADY_EXISTS;
00299             break;
00300         case ERROR_FILENAME_EXCED_RANGE:
00301             rv = NS_ERROR_FILE_NAME_TOO_LONG;
00302             break;
00303         case 0:
00304             rv = NS_OK;
00305             break;
00306         default:
00307             rv = NS_ERROR_FAILURE;
00308             break;
00309     }
00310     return rv;
00311 }
00312 
00313 // definition of INVALID_SET_FILE_POINTER from VC.NET header files
00314 // it doesn't appear to be defined by VC6
00315 #ifndef INVALID_SET_FILE_POINTER
00316 # define INVALID_SET_FILE_POINTER ((DWORD)-1)
00317 #endif
00318 // same goes for INVALID_FILE_ATTRIBUTES
00319 #ifndef INVALID_FILE_ATTRIBUTES
00320 # define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
00321 #endif
00322 
00323 // as suggested in the MSDN documentation on SetFilePointer
00324 static __int64 
00325 MyFileSeek64(HANDLE aHandle, __int64 aDistance, DWORD aMoveMethod)
00326 {
00327     LARGE_INTEGER li;
00328 
00329     li.QuadPart = aDistance;
00330     li.LowPart = SetFilePointer(aHandle, li.LowPart, &li.HighPart, aMoveMethod);
00331     if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
00332     {
00333         li.QuadPart = -1;
00334     }
00335 
00336     return li.QuadPart;
00337 }
00338 
00339 static PRBool
00340 IsShortcutPath(const nsAString &path)
00341 {
00342     // Under Windows, the shortcuts are just files with a ".lnk" extension. 
00343     // Note also that we don't resolve links in the middle of paths.
00344     // i.e. "c:\foo.lnk\bar.txt" is invalid.
00345     NS_ABORT_IF_FALSE(!path.IsEmpty(), "don't pass an empty string");
00346     PRInt32 len = path.Length();
00347     return (StringTail(path, 4).LowerCaseEqualsASCII(".lnk"));
00348 }
00349 
00350 //-----------------------------------------------------------------------------
00351 // We need the following three definitions to make |OpenFile| convert a file 
00352 // handle to an NSPR file descriptor correctly when |O_APPEND| flag is
00353 // specified. It is defined in a private header of NSPR (primpl.h) we can't
00354 // include. As a temporary workaround until we decide how to extend
00355 // |PR_ImportFile|, we define it here. Currently, |_PR_HAVE_PEEK_BUFFER|
00356 // and |PR_STRICT_ADDR_LEN| are not defined for the 'w95'-dependent portion
00357 // of NSPR so that fields of |PRFilePrivate| #ifdef'd by them are not copied.
00358 // Similarly, |_MDFileDesc| is taken from nsprpub/pr/include/md/_win95.h.
00359 // In an unlikely case we switch to 'NT'-dependent NSPR AND this temporary 
00360 // workaround last beyond the switch, |PRFilePrivate| and |_MDFileDesc| 
00361 // need to be changed to match the definitions for WinNT.
00362 //-----------------------------------------------------------------------------
00363 typedef enum {
00364     _PR_TRI_TRUE = 1,
00365     _PR_TRI_FALSE = 0,
00366     _PR_TRI_UNKNOWN = -1
00367 } _PRTriStateBool;
00368 
00369 struct _MDFileDesc {
00370     PRInt32 osfd;
00371 };
00372 
00373 struct PRFilePrivate {
00374     PRInt32 state;
00375     PRBool nonblocking;
00376     _PRTriStateBool inheritable;
00377     PRFileDesc *next;
00378     PRIntn lockCount;   /*   0: not locked
00379                          *  -1: a native lockfile call is in progress
00380                          * > 0: # times the file is locked */
00381     PRBool  appendMode; 
00382     _MDFileDesc md;
00383 };
00384 
00385 //-----------------------------------------------------------------------------
00386 // Six static methods defined below (OpenFile,  FileTimeToPRTime, GetFileInfo,
00387 // OpenDir, CloseDir, ReadDir) should go away once the corresponding 
00388 // UTF-16 APIs are implemented on all the supported platforms (or at least 
00389 // Windows 9x/ME) in NSPR. Currently, they're only implemented on 
00390 // Windows NT4 or later. (bug 330665)
00391 //-----------------------------------------------------------------------------
00392 
00393 // copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} : 
00394 // PR_Open and _PR_MD_OPEN
00395 static nsresult
00396 OpenFile(const nsAFlatString &name, PRIntn osflags, PRIntn mode,
00397          PRFileDesc **fd)
00398 {
00399     
00400     // XXX : 'mode' is not translated !!!
00401     PRInt32 access = 0;
00402     PRInt32 flags = 0;
00403     PRInt32 flag6 = 0;
00404 
00405     if (osflags & PR_SYNC) flag6 = FILE_FLAG_WRITE_THROUGH;
00406  
00407     if (osflags & PR_RDONLY || osflags & PR_RDWR)
00408         access |= GENERIC_READ;
00409     if (osflags & PR_WRONLY || osflags & PR_RDWR)
00410         access |= GENERIC_WRITE;
00411 
00412     if ( osflags & PR_CREATE_FILE && osflags & PR_EXCL )
00413         flags = CREATE_NEW;
00414     else if (osflags & PR_CREATE_FILE) {
00415         if (osflags & PR_TRUNCATE)
00416             flags = CREATE_ALWAYS;
00417         else
00418             flags = OPEN_ALWAYS;
00419     } else {
00420         if (osflags & PR_TRUNCATE)
00421             flags = TRUNCATE_EXISTING;
00422         else
00423             flags = OPEN_EXISTING;
00424     }
00425 
00426     HANDLE file = nsWinAPIs::mCreateFile(name.get(), access,
00427                                          FILE_SHARE_READ|FILE_SHARE_WRITE,
00428                                          NULL, flags, flag6, NULL);
00429 
00430     if (file == INVALID_HANDLE_VALUE) { 
00431         *fd = nsnull;
00432         return ConvertWinError(GetLastError());
00433     }
00434 
00435     *fd = PR_ImportFile((PRInt32) file); 
00436     if (*fd) {
00437         // On Windows, _PR_HAVE_O_APPEND is not defined so that we have to
00438         // add it manually. (see |PR_Open| in nsprpub/pr/src/io/prfile.c)
00439         (*fd)->secret->appendMode = (PR_APPEND & osflags) ? PR_TRUE : PR_FALSE;
00440         return NS_OK;
00441     }
00442 
00443     nsresult rv = NS_ErrorAccordingToNSPR();
00444 
00445     CloseHandle(file);
00446 
00447     return rv;
00448 }
00449 
00450 // copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} :
00451 // PR_FileTimeToPRTime and _PR_FileTimeToPRTime
00452 static
00453 void FileTimeToPRTime(const FILETIME *filetime, PRTime *prtm)
00454 {
00455 #ifdef __GNUC__
00456     const PRTime _pr_filetime_offset = 116444736000000000LL;
00457 #else
00458     const PRTime _pr_filetime_offset = 116444736000000000i64;
00459 #endif
00460 
00461     PR_ASSERT(sizeof(FILETIME) == sizeof(PRTime));
00462     ::CopyMemory(prtm, filetime, sizeof(PRTime));
00463 #ifdef __GNUC__
00464     *prtm = (*prtm - _pr_filetime_offset) / 10LL;
00465 #else
00466     *prtm = (*prtm - _pr_filetime_offset) / 10i64;
00467 #endif
00468 }
00469 
00470 // copied from nsprpub/pr/src/{io/prfile.c | md/windows/w95io.c} with some
00471 // changes : PR_GetFileInfo64, _PR_MD_GETFILEINFO64
00472 static nsresult
00473 GetFileInfo(const nsAFlatString &name, PRFileInfo64 *info)
00474 {
00475     WIN32_FILE_ATTRIBUTE_DATA fileData;
00476 
00477     if (name.IsEmpty() || name.FindCharInSet(L"?*") != kNotFound)
00478         return NS_ERROR_INVALID_ARG;
00479 
00480     // GetFileAttributesExW/A were confirmed to work correctly
00481     // with a filename ending with a slash and a root directory except on Win95
00482     // without MS IE 4 or later (see |mGetFileAttributesEx| in nsWinAPIs.cpp.)
00483     // Therefore, we don't need to add a fallback as done in 
00484     // _PR_MD_GETFILEINFO64 (w95io.c)
00485     if (!nsWinAPIs::mGetFileAttributesEx(name.get(), GetFileExInfoStandard,
00486                                          &fileData))
00487         return ConvertWinError(GetLastError());
00488 
00489     if (fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
00490         info->type = PR_FILE_DIRECTORY;
00491     } else {
00492         info->type = PR_FILE_FILE;
00493     }
00494 
00495     info->size = fileData.nFileSizeHigh;
00496     info->size = (info->size << 32) + fileData.nFileSizeLow;
00497 
00498     FileTimeToPRTime(&fileData.ftLastWriteTime, &info->modifyTime);
00499 
00500     if (0 == fileData.ftCreationTime.dwLowDateTime &&
00501             0 == fileData.ftCreationTime.dwHighDateTime) {
00502         info->creationTime = info->modifyTime;
00503     } else {
00504         FileTimeToPRTime(&fileData.ftCreationTime, &info->creationTime);
00505     }
00506 
00507     return NS_OK;
00508 }
00509 
00510 struct nsDir
00511 {
00512     HANDLE   handle; 
00513     union
00514     {
00515         WIN32_FIND_DATAW dataW;
00516         WIN32_FIND_DATAA dataA;
00517     } entry;
00518     PRBool   firstEntry;
00519 };
00520 
00521 static nsresult
00522 OpenDir(const nsAFlatString &name, nsDir * *dir)
00523 {
00524     NS_ENSURE_ARG_POINTER(dir);
00525 
00526     *dir = nsnull;
00527     if (name.Length() + 3 >= MAX_PATH)
00528         return NS_ERROR_FILE_NAME_TOO_LONG;
00529 
00530     nsDir *d  = PR_NEW(nsDir);
00531     if (!d)
00532         return NS_ERROR_OUT_OF_MEMORY;
00533 
00534     nsAutoString filename(name);
00535 
00536      //If 'name' ends in a slash or backslash, do not append
00537      //another backslash.
00538     if (filename.Last() == L'/' || filename.Last() == L'\\')
00539         filename.AppendASCII("*");
00540     else 
00541         filename.AppendASCII("\\*");
00542 
00543     filename.ReplaceChar(L'/', L'\\');
00544 
00545     if (NS_UseUnicode()) 
00546         d->handle = ::FindFirstFileW(filename.get(), &(d->entry.dataW) );
00547     else
00548     {
00549         nsCAutoString filenameA;
00550         NS_CopyUnicodeToNative(filename, filenameA);
00551         d->handle = ::FindFirstFileA(filenameA.get(), &(d->entry.dataA) );
00552     }
00553     if ( d->handle == INVALID_HANDLE_VALUE )
00554     {
00555         PR_Free(d);
00556         return ConvertWinError(GetLastError());
00557     }
00558     d->firstEntry = PR_TRUE;
00559 
00560     *dir = d;
00561     return NS_OK;
00562 }
00563 
00564 static nsresult
00565 ReadDir(nsDir *dir, PRDirFlags flags, nsString& name)
00566 {
00567     name.Truncate();
00568     NS_ENSURE_ARG(dir);
00569 
00570     while (1) {
00571         BOOL rv;
00572         if (dir->firstEntry)
00573         {
00574             dir->firstEntry = PR_FALSE;
00575             rv = 1;
00576         } else
00577             if (NS_UseUnicode())
00578                 rv = ::FindNextFileW(dir->handle, &(dir->entry.dataW));
00579             else 
00580                 rv = ::FindNextFileA(dir->handle, &(dir->entry.dataA));
00581 
00582         if (rv == 0)
00583             break;
00584 
00585         const PRUnichar *fileName;
00586         nsString tmp;
00587         if (NS_UseUnicode()) 
00588             fileName = (dir)->entry.dataW.cFileName;
00589         else 
00590         {
00591             NS_CopyNativeToUnicode(nsDependentCString((dir)->
00592                                                       entry.dataA.cFileName),
00593                                    tmp);
00594             fileName = tmp.get();
00595         }
00596 
00597         if ((flags & PR_SKIP_DOT) &&
00598             (fileName[0] == L'.') && (fileName[1] == L'\0'))
00599             continue;
00600         if ((flags & PR_SKIP_DOT_DOT) &&
00601             (fileName[0] == L'.') && (fileName[1] == L'.') &&
00602             (fileName[2] == L'\0'))
00603             continue;
00604 
00605         // Perhaps, we can just get away with using entry.dataW.dwFileAttributes
00606         // because dwFileAttributes is the first field of both WIN32_FIND_DATAW
00607         // and WIN32_FIND_DATAA, but let's be on the safe side here.
00608         DWORD attrib = NS_UseUnicode() ? dir->entry.dataW.dwFileAttributes :
00609                                          dir->entry.dataA.dwFileAttributes;
00610         if ((flags & PR_SKIP_HIDDEN) && (attrib & FILE_ATTRIBUTE_HIDDEN))
00611             continue;
00612 
00613         if (fileName == tmp.get())
00614             name = tmp;
00615         else 
00616             name = fileName;
00617         return NS_OK;
00618     }
00619 
00620     DWORD err = GetLastError();
00621     return err == ERROR_NO_MORE_FILES ? NS_OK : ConvertWinError(err);
00622 }
00623 
00624 static nsresult
00625 CloseDir(nsDir *d)
00626 {
00627     NS_ENSURE_ARG(d);
00628 
00629     BOOL isOk = FindClose(d->handle);
00630     PR_DELETE(d);
00631     return isOk ? NS_OK : ConvertWinError(GetLastError());
00632 }
00633 
00634 //-----------------------------------------------------------------------------
00635 // nsDirEnumerator
00636 //-----------------------------------------------------------------------------
00637 
00638 class nsDirEnumerator : public nsISimpleEnumerator,
00639                         public nsIDirectoryEnumerator
00640 {
00641     public:
00642 
00643         NS_DECL_ISUPPORTS
00644 
00645         nsDirEnumerator() : mDir(nsnull)
00646         {
00647         }
00648 
00649         nsresult Init(nsILocalFile* parent)
00650         {
00651             nsAutoString filepath;
00652             parent->GetTarget(filepath);
00653 
00654             if (filepath.IsEmpty())
00655             {
00656                 parent->GetPath(filepath);
00657             }
00658 
00659             if (filepath.IsEmpty())
00660             {
00661                 return NS_ERROR_UNEXPECTED;
00662             }
00663 
00664             nsresult rv = OpenDir(filepath, &mDir);
00665             if (NS_FAILED(rv))
00666                 return rv;
00667 
00668             mParent = parent;
00669             return NS_OK;
00670         }
00671 
00672         NS_IMETHOD HasMoreElements(PRBool *result)
00673         {
00674             nsresult rv;
00675             if (mNext == nsnull && mDir)
00676             {
00677                 nsString name;
00678                 rv = ReadDir(mDir, PR_SKIP_BOTH, name);
00679                 if (NS_FAILED(rv))
00680                     return rv;
00681                 if (name.IsEmpty()) 
00682                 {
00683                     // end of dir entries
00684                     if (NS_FAILED(CloseDir(mDir)))
00685                         return NS_ERROR_FAILURE;
00686 
00687                     mDir = nsnull;
00688 
00689                     *result = PR_FALSE;
00690                     return NS_OK;
00691                 }
00692 
00693                 nsCOMPtr<nsIFile> file;
00694                 rv = mParent->Clone(getter_AddRefs(file));
00695                 if (NS_FAILED(rv))
00696                     return rv;
00697 
00698                 rv = file->Append(name);
00699                 if (NS_FAILED(rv))
00700                     return rv;
00701 
00702                 // make sure the thing exists.  If it does, try the next one.
00703                 PRBool exists;
00704                 rv = file->Exists(&exists);
00705                 if (NS_FAILED(rv) || !exists)
00706                 {
00707                     return HasMoreElements(result);
00708                 }
00709 
00710                 mNext = do_QueryInterface(file);
00711             }
00712             *result = mNext != nsnull;
00713             if (!*result) 
00714                 Close();
00715             return NS_OK;
00716         }
00717 
00718         NS_IMETHOD GetNext(nsISupports **result)
00719         {
00720             nsresult rv;
00721             PRBool hasMore;
00722             rv = HasMoreElements(&hasMore);
00723             if (NS_FAILED(rv)) return rv;
00724 
00725             *result = mNext;        // might return nsnull
00726             NS_IF_ADDREF(*result);
00727 
00728             mNext = nsnull;
00729             return NS_OK;
00730         }
00731 
00732         NS_IMETHOD GetNextFile(nsIFile **result)
00733         {
00734             *result = nsnull;
00735             PRBool hasMore = PR_FALSE;
00736             nsresult rv = HasMoreElements(&hasMore);
00737             if (NS_FAILED(rv) || !hasMore)
00738                 return rv;
00739             *result = mNext;
00740             NS_IF_ADDREF(*result);
00741             mNext = nsnull;
00742             return NS_OK;
00743         }
00744 
00745         NS_IMETHOD Close()
00746         {
00747             if (mDir)
00748             {
00749                 nsresult rv = CloseDir(mDir);
00750                 NS_ASSERTION(NS_SUCCEEDED(rv), "close failed");
00751                 if (NS_FAILED(rv))
00752                     return NS_ERROR_FAILURE;
00753                 mDir = nsnull;
00754             }
00755             return NS_OK;
00756         }
00757 
00758         // dtor can be non-virtual since there are no subclasses, but must be
00759         // public to use the class on the stack.
00760         ~nsDirEnumerator()
00761         {
00762             Close();
00763         }
00764 
00765     protected:
00766         nsDir*                  mDir;
00767         nsCOMPtr<nsILocalFile>  mParent;
00768         nsCOMPtr<nsILocalFile>  mNext;
00769 };
00770 
00771 NS_IMPL_ISUPPORTS2(nsDirEnumerator, nsISimpleEnumerator, nsIDirectoryEnumerator)
00772 
00773 
00774 //-----------------------------------------------------------------------------
00775 // nsLocalFile <public>
00776 //-----------------------------------------------------------------------------
00777 
00778 nsLocalFile::nsLocalFile()
00779   : mDirty(PR_TRUE)
00780   , mFollowSymlinks(PR_FALSE)
00781 {
00782 }
00783 
00784 NS_METHOD
00785 nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
00786 {
00787     NS_ENSURE_ARG_POINTER(aInstancePtr);
00788     NS_ENSURE_NO_AGGREGATION(outer);
00789 
00790     nsLocalFile* inst = new nsLocalFile();
00791     if (inst == NULL)
00792         return NS_ERROR_OUT_OF_MEMORY;
00793 
00794     nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
00795     if (NS_FAILED(rv))
00796     {
00797         delete inst;
00798         return rv;
00799     }
00800     return NS_OK;
00801 }
00802 
00803 
00804 //-----------------------------------------------------------------------------
00805 // nsLocalFile::nsISupports
00806 //-----------------------------------------------------------------------------
00807 
00808 NS_IMPL_THREADSAFE_ISUPPORTS3(nsLocalFile, nsILocalFile, nsIFile, nsILocalFileWin)
00809 
00810 
00811 //-----------------------------------------------------------------------------
00812 // nsLocalFile <private>
00813 //-----------------------------------------------------------------------------
00814 
00815 nsLocalFile::nsLocalFile(const nsLocalFile& other)
00816   : mDirty(PR_TRUE)
00817   , mFollowSymlinks(other.mFollowSymlinks)
00818   , mWorkingPath(other.mWorkingPath)
00819 {
00820 }
00821 
00822 // Resolve the shortcut file from mWorkingPath and write the path 
00823 // it points to into mResolvedPath.
00824 nsresult
00825 nsLocalFile::ResolveShortcut()
00826 {
00827 #ifndef WINCE
00828     // we can't do anything without the resolver
00829     if (!gResolver)
00830         return NS_ERROR_FAILURE;
00831 
00832     mResolvedPath.SetLength(MAX_PATH);
00833     if (mResolvedPath.Length() != MAX_PATH)
00834         return NS_ERROR_OUT_OF_MEMORY;
00835 
00836     PRUnichar *resolvedPath = mResolvedPath.BeginWriting();
00837 
00838     // resolve this shortcut
00839     nsresult rv = gResolver->Resolve(mWorkingPath.get(), resolvedPath);
00840 
00841     size_t len = NS_FAILED(rv) ? 0 : wcslen(resolvedPath);
00842     mResolvedPath.SetLength(len);
00843 
00844     return rv;
00845 #else
00846     return NS_OK;
00847 #endif
00848 }
00849 
00850 // Resolve any shortcuts and stat the resolved path. After a successful return
00851 // the path is guaranteed valid and the members of mFileInfo64 can be used.
00852 nsresult
00853 nsLocalFile::ResolveAndStat()
00854 {
00855     // if we aren't dirty then we are already done
00856     if (!mDirty)
00857         return NS_OK;
00858 
00859     // we can't resolve/stat anything that isn't a valid NSPR addressable path
00860     if (mWorkingPath.IsEmpty())
00861         return NS_ERROR_FILE_INVALID_PATH;
00862 
00863     // this is usually correct
00864     mResolvedPath.Assign(mWorkingPath);
00865 
00866     // slutty hack designed to work around bug 134796 until it is fixed
00867     nsAutoString nsprPath(mWorkingPath.get());
00868     if (mWorkingPath.Length() == 2 && mWorkingPath.CharAt(1) == L':') 
00869         nsprPath.AppendASCII("\\");
00870 
00871     // first we will see if the working path exists. If it doesn't then
00872     // there is nothing more that can be done
00873     if (NS_FAILED(GetFileInfo(nsprPath, &mFileInfo64)))
00874         return NS_ERROR_FILE_NOT_FOUND;
00875 
00876     // if this isn't a shortcut file or we aren't following symlinks then we're done 
00877     if (!mFollowSymlinks 
00878         || mFileInfo64.type != PR_FILE_FILE 
00879         || !IsShortcutPath(mWorkingPath))
00880     {
00881         mDirty = PR_FALSE;
00882         return NS_OK;
00883     }
00884 
00885     // we need to resolve this shortcut to what it points to, this will
00886     // set mResolvedPath. Even if it fails we need to have the resolved
00887     // path equal to working path for those functions that always use
00888     // the resolved path.
00889     nsresult rv = ResolveShortcut();
00890     if (NS_FAILED(rv))
00891     {
00892         mResolvedPath.Assign(mWorkingPath);
00893         return rv;
00894     }
00895 
00896     // get the details of the resolved path
00897     if (NS_FAILED(GetFileInfo(mResolvedPath, &mFileInfo64)))
00898         return NS_ERROR_FILE_NOT_FOUND;
00899 
00900     mDirty = PR_FALSE;
00901     return NS_OK;
00902 }
00903 
00904 
00905 //-----------------------------------------------------------------------------
00906 // nsLocalFile::nsIFile,nsILocalFile
00907 //-----------------------------------------------------------------------------
00908 
00909 NS_IMETHODIMP
00910 nsLocalFile::Clone(nsIFile **file)
00911 {
00912     // Just copy-construct ourselves
00913     *file = new nsLocalFile(*this);
00914     if (!*file)
00915       return NS_ERROR_OUT_OF_MEMORY;
00916 
00917     NS_ADDREF(*file);
00918     
00919     return NS_OK;
00920 }
00921 
00922 NS_IMETHODIMP
00923 nsLocalFile::InitWithFile(nsILocalFile *aFile)
00924 {
00925     NS_ENSURE_ARG(aFile);
00926     
00927     nsAutoString path;
00928     aFile->GetPath(path);
00929     if (path.IsEmpty())
00930         return NS_ERROR_INVALID_ARG;
00931     return InitWithPath(path); 
00932 }
00933 
00934 NS_IMETHODIMP
00935 nsLocalFile::InitWithPath(const nsAString &filePath)
00936 {
00937     MakeDirty();
00938 
00939     nsAString::const_iterator begin, end;
00940     filePath.BeginReading(begin);
00941     filePath.EndReading(end);
00942 
00943     // input string must not be empty
00944     if (begin == end)
00945         return NS_ERROR_FAILURE;
00946 
00947     PRUnichar firstChar = *begin;
00948     PRUnichar secondChar = *(++begin);
00949 
00950     // just do a sanity check.  if it has any forward slashes, it is not a Native path
00951     // on windows.  Also, it must have a colon at after the first char.
00952 
00953     PRUnichar *path = nsnull;
00954     PRInt32 pathLen = 0;
00955 
00956     if ( ( (secondChar == L':') && !FindCharInReadable(L'/', begin, end) ) ||  // normal path
00957 #ifdef WINCE
00958          ( (firstChar == L'\\') )   // wince absolute path or network path
00959 #else
00960          ( (firstChar == L'\\') && (secondChar == L'\\') )   // network path
00961 #endif
00962          )
00963     {
00964         // This is a native path
00965         path = ToNewUnicode(filePath);
00966         pathLen = filePath.Length();
00967     }
00968 
00969     if (path == nsnull) {
00970         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
00971     }
00972 
00973     // kill any trailing '\'
00974     PRInt32 len = pathLen - 1;
00975     if (path[len] == L'\\')
00976     {
00977         path[len] = L'\0';
00978         pathLen = len;
00979     }
00980 
00981     mWorkingPath.Adopt(path, pathLen);
00982     return NS_OK;
00983 
00984 }
00985 
00986 NS_IMETHODIMP
00987 nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
00988 {
00989     nsresult rv = ResolveAndStat();
00990     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
00991         return rv;
00992 
00993     return OpenFile(mResolvedPath, flags, mode, _retval);
00994 }
00995 
00996 
00997 NS_IMETHODIMP
00998 nsLocalFile::OpenANSIFileDesc(const char *mode, FILE * *_retval)
00999 {
01000     nsresult rv = ResolveAndStat();
01001     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
01002         return rv;
01003 
01004     *_retval = nsWinAPIs::mFopen(mResolvedPath.get(), 
01005                                  NS_ConvertASCIItoUTF16(mode).get());
01006     if (*_retval)
01007         return NS_OK;
01008 
01009     return NS_ERROR_FAILURE;
01010 }
01011 
01012 
01013 
01014 NS_IMETHODIMP
01015 nsLocalFile::Create(PRUint32 type, PRUint32 attributes)
01016 {
01017     if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
01018         return NS_ERROR_FILE_UNKNOWN_TYPE;
01019 
01020     nsresult rv = ResolveAndStat();
01021     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
01022         return rv;
01023 
01024     // create directories to target
01025     //
01026     // A given local file can be either one of these forms:
01027     //
01028     //   - normal:    X:\some\path\on\this\drive
01029     //                       ^--- start here
01030     //
01031     //   - UNC path:  \\machine\volume\some\path\on\this\drive
01032     //                                     ^--- start here
01033     //
01034     // Skip the first 'X:\' for the first form, and skip the first full
01035     // '\\machine\volume\' segment for the second form.
01036 
01037     PRUnichar* path = mResolvedPath.BeginWriting();
01038 
01039     if (path[0] == L'\\' && path[1] == L'\\')
01040     {
01041 #ifdef WINCE
01042         ++path;
01043 #else
01044         // dealing with a UNC path here; skip past '\\machine\'
01045         path = wcschr(path + 2, L'\\');
01046         if (!path)
01047             return NS_ERROR_FILE_INVALID_PATH;
01048         ++path;
01049 #endif
01050     }
01051 
01052     // search for first slash after the drive (or volume) name
01053     PRUnichar* slash = wcschr(path, L'\\');
01054 
01055     if (slash)
01056     {
01057         // skip the first '\\'
01058         ++slash;
01059         slash = wcschr(slash, L'\\');
01060 
01061         while (slash)
01062         {
01063             *slash = L'\0';
01064 
01065             if (!nsWinAPIs::mCreateDirectory(mResolvedPath.get(), NULL)) {
01066                 rv = ConvertWinError(GetLastError());
01067                 // perhaps the base path already exists, or perhaps we don't have
01068                 // permissions to create the directory.  NOTE: access denied could
01069                 // occur on a parent directory even though it exists.
01070                 if (rv != NS_ERROR_FILE_ALREADY_EXISTS &&
01071                     rv != NS_ERROR_FILE_ACCESS_DENIED)
01072                     return rv;
01073             }
01074             *slash = L'\\';
01075             ++slash;
01076             slash = wcschr(slash, L'\\');
01077         }
01078     }
01079 
01080     if (type == NORMAL_FILE_TYPE)
01081     {
01082         PRFileDesc* file;
01083         rv = OpenFile(mResolvedPath,
01084                       PR_RDONLY | PR_CREATE_FILE | PR_APPEND | PR_EXCL, attributes,
01085                       &file);
01086         if (file)
01087             PR_Close(file);
01088         return rv;
01089     }
01090 
01091     if (type == DIRECTORY_TYPE)
01092     {
01093         if (!nsWinAPIs::mCreateDirectory(mResolvedPath.get(), NULL))
01094             return ConvertWinError(GetLastError());
01095         else
01096             return NS_OK;
01097     }
01098 
01099     return NS_ERROR_FILE_UNKNOWN_TYPE;
01100 }
01101 
01102 
01103 NS_IMETHODIMP
01104 nsLocalFile::Append(const nsAString &node)
01105 {
01106     // append this path, multiple components are not permitted
01107     return AppendInternal(PromiseFlatString(node), PR_FALSE);
01108 }
01109 
01110 NS_IMETHODIMP
01111 nsLocalFile::AppendRelativePath(const nsAString &node)
01112 {
01113     // append this path, multiple components are permitted
01114     return AppendInternal(PromiseFlatString(node), PR_TRUE);
01115 }
01116 
01117 
01118 nsresult
01119 nsLocalFile::AppendInternal(const nsAFlatString &node, PRBool multipleComponents)
01120 {
01121     if (node.IsEmpty())
01122         return NS_OK;
01123 
01124     // check the relative path for validity
01125     if (node.First() == L'\\'                                   // can't start with an '\'
01126         || node.FindChar(L'/') != kNotFound                     // can't contain /
01127         || node.EqualsASCII(".."))                              // can't be ..
01128         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
01129 
01130 #ifndef WINCE  // who cares?
01131     if (multipleComponents)
01132     {
01133         // can't contain .. as a path component. Ensure that the valid components
01134         // "foo..foo", "..foo", and "foo.." are not falsely detected,
01135         // but the invalid paths "..\", "foo\..", "foo\..\foo", 
01136         // "..\foo", etc are.
01137         NS_NAMED_LITERAL_STRING(doubleDot, "\\.."); 
01138         nsAString::const_iterator start, end, offset;
01139         node.BeginReading(start);
01140         node.EndReading(end);
01141         offset = end; 
01142         while (FindInReadable(doubleDot, start, offset))
01143         {
01144             if (offset == end || *offset == L'\\')
01145                 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
01146             start = offset;
01147             offset = end;
01148         }
01149         
01150         // catches the remaining cases of prefixes 
01151         if (StringBeginsWith(node, NS_LITERAL_STRING("..\\")))
01152             return NS_ERROR_FILE_UNRECOGNIZED_PATH;
01153     }
01154     // single components can't contain '\'
01155     else if (node.FindChar(L'\\') != kNotFound)
01156         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
01157 #endif
01158 
01159     MakeDirty();
01160     
01161     mWorkingPath.Append(NS_LITERAL_STRING("\\") + node);
01162     
01163     return NS_OK;
01164 }
01165 
01166 #define TOUPPER(u) (((u) >= L'a' && (u) <= L'z') ? \
01167                     (u) - (L'a' - L'A') : (u))
01168 
01169 NS_IMETHODIMP
01170 nsLocalFile::Normalize()
01171 {
01172 #ifndef WINCE
01173     // XXX See bug 187957 comment 18 for possible problems with this implementation.
01174     
01175     if (mWorkingPath.IsEmpty())
01176         return NS_OK;
01177 
01178     nsAutoString path(mWorkingPath);
01179 
01180     // find the index of the root backslash for the path. Everything before 
01181     // this is considered fully normalized and cannot be ascended beyond 
01182     // using ".."  For a local drive this is the first slash (e.g. "c:\").
01183     // For a UNC path it is the slash following the share name 
01184     // (e.g. "\\server\share\").
01185     PRInt32 rootIdx = 2;        // default to local drive
01186     if (path.First() == L'\\')   // if a share then calculate the rootIdx
01187     {
01188         rootIdx = path.FindChar(L'\\', 2);   // skip \\ in front of the server
01189         if (rootIdx == kNotFound)
01190             return NS_OK;                   // already normalized
01191         rootIdx = path.FindChar(L'\\', rootIdx+1);
01192         if (rootIdx == kNotFound)
01193             return NS_OK;                   // already normalized
01194     }
01195     else if (path.CharAt(rootIdx) != L'\\')
01196     {
01197         // The path has been specified relative to the current working directory 
01198         // for that drive. To normalize it, the current working directory for 
01199         // that drive needs to be inserted before the supplied relative path
01200         // which will provide an absolute path (and the rootIdx will still be 2).
01201         WCHAR cwd[MAX_PATH];
01202         WCHAR * pcwd = cwd;
01203         int drive = TOUPPER(path.First()) - 'A' + 1;
01204         if (!nsWinAPIs::mGetDCwd(drive, pcwd, MAX_PATH))
01205             pcwd = nsWinAPIs::mGetDCwd(drive, 0, 0);
01206         if (!pcwd)
01207             return NS_ERROR_OUT_OF_MEMORY;
01208 
01209         nsAutoString currentDir(pcwd);
01210         if (pcwd != cwd)
01211             free(pcwd);
01212 
01213         if (currentDir.Last() == '\\')
01214             path.Replace(0, 2, currentDir);
01215         else
01216             path.Replace(0, 2, currentDir + NS_LITERAL_STRING("\\"));
01217     }
01218     NS_POSTCONDITION(0 < rootIdx && rootIdx < (PRInt32)path.Length(), "rootIdx is invalid");
01219     NS_POSTCONDITION(path.CharAt(rootIdx) == '\\', "rootIdx is invalid");
01220 
01221     // if there is nothing following the root path then it is already normalized
01222     if (rootIdx + 1 == (PRInt32)path.Length())
01223         return NS_OK;
01224 
01225     // assign the root
01226     const PRUnichar * pathBuffer = path.get();  // simplify access to the buffer
01227     mWorkingPath.SetCapacity(path.Length()); // it won't ever grow longer
01228     mWorkingPath.Assign(pathBuffer, rootIdx);
01229 
01230     // Normalize the path components. The actions taken are:
01231     //
01232     //  "\\"    condense to single backslash
01233     //  "."     remove from path
01234     //  ".."    up a directory
01235     //  "..."   remove from path (any number of dots > 2)
01236     //
01237     // The last form is something that Windows 95 and 98 supported and 
01238     // is a shortcut for changing up multiple directories. Windows XP
01239     // and ilk ignore it in a path, as is done here.
01240     PRInt32 len, begin, end = rootIdx;
01241     while (end < (PRInt32)path.Length())
01242     {
01243         // find the current segment (text between the backslashes) to 
01244         // be examined, this will set the following variables:
01245         //  begin == index of first char in segment
01246         //  end   == index 1 char after last char in segment
01247         //  len   == length of segment 
01248         begin = end + 1;
01249         end = path.FindChar('\\', begin);
01250         if (end == kNotFound)
01251             end = path.Length();
01252         len = end - begin;
01253         
01254         // ignore double backslashes
01255         if (len == 0)
01256             continue;
01257         
01258         // len != 0, and interesting paths always begin with a dot
01259         if (pathBuffer[begin] == '.')
01260         {
01261             // ignore single dots
01262             if (len == 1)
01263                 continue;   
01264 
01265             // handle multiple dots
01266             if (len >= 2 && pathBuffer[begin+1] == L'.')
01267             {
01268                 // back up a path component on double dot
01269                 if (len == 2)
01270                 {
01271                     PRInt32 prev = mWorkingPath.RFindChar('\\');
01272                     if (prev >= rootIdx)
01273                         mWorkingPath.Truncate(prev);
01274                     continue;
01275                 }
01276 
01277                 // length is > 2 and the first two characters are dots. 
01278                 // if the rest of the string is dots, then ignore it.
01279                 int idx = len - 1;
01280                 for (; idx >= 2; --idx) 
01281                 {
01282                     if (pathBuffer[begin+idx] != L'.')
01283                         break;
01284                 }
01285 
01286                 // this is true if the loop above didn't break
01287                 // and all characters in this segment are dots.
01288                 if (idx < 2) 
01289                     continue;
01290             }
01291         }
01292 
01293         // add the current component to the path, including the preceding backslash
01294         mWorkingPath.Append(pathBuffer + begin - 1, len + 1);
01295     }
01296 
01297     // kill trailing dots and spaces.
01298     PRInt32 filePathLen = mWorkingPath.Length() - 1;
01299     while(filePathLen > 0 && (mWorkingPath[filePathLen] == L' ' ||
01300           mWorkingPath[filePathLen] == L'.'))
01301     {
01302         mWorkingPath.Truncate(filePathLen--);
01303     } 
01304 
01305     MakeDirty();
01306 #else // WINCE
01307     // WINCE FIX
01308 #endif 
01309     return NS_OK;
01310 }
01311 
01312 NS_IMETHODIMP
01313 nsLocalFile::GetLeafName(nsAString &aLeafName)
01314 {
01315     aLeafName.Truncate();
01316 
01317     if(mWorkingPath.IsEmpty())
01318         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
01319 
01320     PRInt32 offset = mWorkingPath.RFindChar(L'\\');
01321 
01322     // if the working path is just a node without any lashes.
01323     if (offset == kNotFound)
01324         aLeafName = mWorkingPath;
01325     else
01326         aLeafName = Substring(mWorkingPath, offset + 1);
01327 
01328     return NS_OK;
01329 }
01330 
01331 NS_IMETHODIMP
01332 nsLocalFile::SetLeafName(const nsAString &aLeafName)
01333 {
01334     MakeDirty();
01335 
01336     if(mWorkingPath.IsEmpty())
01337         return NS_ERROR_FILE_UNRECOGNIZED_PATH;
01338 
01339     // cannot use nsCString::RFindChar() due to 0x5c problem
01340     PRInt32 offset = mWorkingPath.RFindChar(L'\\');
01341     if (offset)
01342     {
01343         mWorkingPath.Truncate(offset+1);
01344     }
01345     mWorkingPath.Append(aLeafName);
01346 
01347     return NS_OK;
01348 }
01349 
01350 
01351 NS_IMETHODIMP
01352 nsLocalFile::GetPath(nsAString &_retval)
01353 {
01354     _retval = mWorkingPath;
01355     return NS_OK;
01356 }
01357 
01358 typedef struct {
01359     WORD wLanguage;
01360     WORD wCodePage;
01361 } LANGANDCODEPAGE;
01362 
01363 NS_IMETHODIMP
01364 nsLocalFile::GetVersionInfoField(const char* aField, nsAString& _retval)
01365 {
01366     nsresult rv = ResolveAndStat();
01367     if (NS_FAILED(rv))
01368         return rv;
01369 
01370     rv = NS_ERROR_FAILURE;
01371 
01372     // Cast away const-ness here because WinAPI functions don't understand it, 
01373     // the path is used for [in] parameters only however so it's safe. 
01374     WCHAR *path = NS_CONST_CAST(WCHAR*, mFollowSymlinks ? mResolvedPath.get() 
01375                                                         : mWorkingPath.get());
01376 
01377     // Per http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/resources/versioninformation/versioninformationreference/versioninformationfunctions/getfileversioninfosize.asp
01378     // if the "short" version of this file name is > 125 characters, 
01379     // GetFileVersionInfoSize will not work (for Win9x compatibility)
01380     WCHAR shortPath[126];
01381     nsWinAPIs::mGetShortPathName(path, shortPath, sizeof(shortPath));
01382 
01383     DWORD dummy;
01384     DWORD size = nsWinAPIs::mGetFileVersionInfoSize(shortPath, &dummy);
01385     if (!size)
01386         return rv;
01387 
01388     void* ver = calloc(size, 1);
01389     if (!ver)
01390         return NS_ERROR_OUT_OF_MEMORY;
01391 
01392     if (nsWinAPIs::mGetFileVersionInfo(path, 0, size, ver)) 
01393     {
01394         LANGANDCODEPAGE* translate = nsnull;
01395         UINT pageCount;
01396         BOOL queryResult = ::VerQueryValue(ver, "\\VarFileInfo\\Translation", 
01397                                             (void**)&translate, &pageCount);
01398         if (queryResult && translate) 
01399         {
01400             for (PRInt32 i = 0; i < 2; ++i) 
01401             { 
01402                 // XXX : need to make sure that we can get away with 
01403                 // 'VerQueryValueA' here.
01404                 char subBlock[MAX_PATH];
01405                 PR_snprintf(subBlock, sizeof(subBlock), 
01406                             "\\StringFileInfo\\%04x%04x\\%s", 
01407                             (i == 0 ? translate[0].wLanguage 
01408                                     : ::GetUserDefaultLangID()),
01409                             translate[0].wCodePage, aField);
01410 
01411                 LPVOID value = nsnull;
01412                 UINT size;
01413                 // XXX : we should use 'W' API on Win 2k or later to cope 
01414                 // with presumably rare cases where the default system 
01415                 // codepage is  different from the codepage of a string 
01416                 // returned by |VerQueryValueA| (bug 344630)
01417                 queryResult = ::VerQueryValueA(ver, subBlock, &value, &size);
01418                 if (queryResult && value)
01419                 {
01420                     NS_CopyNativeToUnicode(
01421                             nsDependentCString((const char*) value), _retval);
01422                     if (!_retval.IsEmpty()) 
01423                     {
01424                         rv = NS_OK;
01425                         break;
01426                     }
01427                 }
01428             }
01429         }
01430     }
01431     free(ver);
01432     
01433     return rv;
01434 }
01435 
01436 nsresult
01437 nsLocalFile::CopySingleFile(nsIFile *sourceFile, nsIFile *destParent,
01438                             const nsAString &newName, 
01439                             PRBool followSymlinks, PRBool move)
01440 {
01441     nsresult rv;
01442     nsAutoString filePath;
01443 
01444     // get the path that we are going to copy to.
01445     // Since windows does not know how to auto
01446     // resolve shortcuts, we must work with the
01447     // target.
01448     nsAutoString destPath;
01449     destParent->GetTarget(destPath);
01450 
01451     destPath.AppendASCII("\\");
01452 
01453     if (newName.IsEmpty())
01454     {
01455         nsAutoString aFileName;
01456         sourceFile->GetLeafName(aFileName);
01457         destPath.Append(aFileName);
01458     }
01459     else
01460     {
01461         destPath.Append(newName);
01462     }
01463 
01464 
01465     if (followSymlinks)
01466     {
01467         rv = sourceFile->GetTarget(filePath);
01468         if (filePath.IsEmpty())
01469             rv = sourceFile->GetPath(filePath);
01470     }
01471     else
01472     {
01473         rv = sourceFile->GetPath(filePath);
01474     }
01475 
01476     if (NS_FAILED(rv))
01477         return rv;
01478 
01479     int copyOK;
01480 
01481     if (!move)
01482         copyOK = nsWinAPIs::mCopyFile(filePath.get(), destPath.get(), PR_TRUE);
01483     else
01484     {
01485         // What we have to do is check to see if the destPath exists.  If it
01486         // does, we have to move it out of the say so that MoveFile will
01487         // succeed.  However, we don't want to just remove it since MoveFile
01488         // can fail leaving us without a file.
01489 
01490         nsAutoString backup;
01491         PRFileInfo64 fileInfo64;
01492         if (NS_SUCCEEDED(GetFileInfo(destPath, &fileInfo64)))
01493         {
01494 
01495             // the file exists.  Check to make sure it is not a directory,
01496             // then move it out of the way.
01497             if (fileInfo64.type == PR_FILE_FILE)
01498             {
01499                 backup.Append(destPath);
01500                 backup.Append(L".moztmp");
01501 
01502                 // we are about to remove the .moztmp file,
01503                 // so attempt to make sure the file is writable
01504                 // (meaning:  the "read only" attribute is not set)
01505                 // _wchmod can silently fail (return -1) if 
01506                 // the file doesn't exist but that's ok, because 
01507                 // _wremove() will also silently fail if the file
01508                 // doesn't exist.
01509                 (void)nsWinAPIs::mChmod(backup.get(), _S_IREAD | _S_IWRITE);
01510  
01511                 // remove any existing backup file that we may already have.
01512                 // maybe we should be doing some kind of unique naming here,
01513                 // but why bother.
01514                 (void)nsWinAPIs::mRemove(backup.get());
01515 
01516                 // move destination file to backup file
01517                 copyOK = nsWinAPIs::mMoveFile(destPath.get(), backup.get());
01518                 if (!copyOK)
01519                 {
01520                     // I guess we can't do the backup copy, so return.
01521                     rv = ConvertWinError(GetLastError());
01522                     return rv;
01523                 }
01524             }
01525         }
01526         // move source file to destination file
01527         copyOK = nsWinAPIs::mMoveFile(filePath.get(), destPath.get());
01528 
01529         if (!backup.IsEmpty())
01530         {
01531             if (copyOK)
01532             {
01533                 // remove the backup copy.
01534                 nsWinAPIs::mRemove(backup.get());
01535             }
01536             else
01537             {
01538                 // restore backup
01539                 int backupOk = nsWinAPIs::mMoveFile(backup.get(),
01540                                                     destPath.get());
01541                 NS_ASSERTION(backupOk, "move backup failed");
01542             }
01543         }
01544     }
01545     if (!copyOK)  // CopyFile and MoveFile returns non-zero if succeeds (backward if you ask me).
01546         rv = ConvertWinError(GetLastError());
01547 
01548     return rv;
01549 }
01550 
01551 nsresult
01552 nsLocalFile::CopyMove(nsIFile *aParentDir, const nsAString &newName, PRBool followSymlinks, PRBool move)
01553 {
01554     nsCOMPtr<nsIFile> newParentDir = aParentDir;
01555     // check to see if this exists, otherwise return an error.
01556     // we will check this by resolving.  If the user wants us
01557     // to follow links, then we are talking about the target,
01558     // hence we can use the |followSymlinks| parameter.
01559     nsresult rv  = ResolveAndStat();
01560     if (NS_FAILED(rv))
01561         return rv;
01562 
01563     if (!newParentDir)
01564     {
01565         // no parent was specified.  We must rename.
01566 
01567         if (newName.IsEmpty())
01568             return NS_ERROR_INVALID_ARG;
01569 
01570         rv = GetParent(getter_AddRefs(newParentDir));
01571         if (NS_FAILED(rv))
01572             return rv;
01573     }
01574 
01575     if (!newParentDir)
01576         return NS_ERROR_FILE_DESTINATION_NOT_DIR;
01577 
01578     // make sure it exists and is a directory.  Create it if not there.
01579     PRBool exists;
01580     newParentDir->Exists(&exists);
01581     if (!exists)
01582     {
01583         rv = newParentDir->Create(DIRECTORY_TYPE, 0644);  // TODO, what permissions should we use
01584         if (NS_FAILED(rv))
01585             return rv;
01586     }
01587     else
01588     {
01589         PRBool isDir;
01590         newParentDir->IsDirectory(&isDir);
01591         if (isDir == PR_FALSE)
01592         {
01593             if (followSymlinks)
01594             {
01595                 PRBool isLink;
01596                 newParentDir->IsSymlink(&isLink);
01597                 if (isLink)
01598                 {
01599                     nsAutoString target;
01600                     newParentDir->GetTarget(target);
01601 
01602                     nsCOMPtr<nsILocalFile> realDest = new nsLocalFile();
01603                     if (realDest == nsnull)
01604                         return NS_ERROR_OUT_OF_MEMORY;
01605 
01606                     rv = realDest->InitWithPath(target);
01607 
01608                     if (NS_FAILED(rv))
01609                         return rv;
01610 
01611                     return CopyMove(realDest, newName, followSymlinks, move);
01612                 }
01613             }
01614             else
01615             {
01616                 return NS_ERROR_FILE_DESTINATION_NOT_DIR;
01617             }
01618         }
01619     }
01620 
01621     // Try different ways to move/copy files/directories
01622     PRBool done = PR_FALSE;
01623     PRBool isDir;
01624     IsDirectory(&isDir);
01625     PRBool isSymlink;
01626     IsSymlink(&isSymlink);
01627 
01628     // Try to move the file or directory, or try to copy a single file (or non-followed symlink)
01629     if (move || !isDir || (isSymlink && !followSymlinks))
01630     {
01631         // Copy/Move single file, or move a directory
01632         rv = CopySingleFile(this, newParentDir, newName, followSymlinks, move);
01633         done = NS_SUCCEEDED(rv);
01634         // If we are moving a directory and that fails, fallback on directory
01635         // enumeration.  See bug 231300 for details.
01636         if (!done && !(move && isDir))
01637             return rv;  
01638     }
01639     
01640     // Not able to copy or move directly, so enumerate it
01641     if (!done)
01642     {
01643         // create a new target destination in the new parentDir;
01644         nsCOMPtr<nsIFile> target;
01645         rv = newParentDir->Clone(getter_AddRefs(target));
01646 
01647         if (NS_FAILED(rv))
01648             return rv;
01649 
01650         nsAutoString allocatedNewName;
01651         if (newName.IsEmpty())
01652         {
01653             PRBool isLink;
01654             IsSymlink(&isLink);
01655             if (isLink)
01656             {
01657                 nsAutoString temp;
01658                 GetTarget(temp);
01659                 PRInt32 offset = temp.RFindChar(L'\\'); 
01660                 if (offset == kNotFound)
01661                     allocatedNewName = temp;
01662                 else 
01663                     allocatedNewName = Substring(temp, offset + 1);
01664             }
01665             else
01666             {
01667                 GetLeafName(allocatedNewName);// this should be the leaf name of the
01668             }
01669         }
01670         else
01671         {
01672             allocatedNewName = newName;
01673         }
01674 
01675         rv = target->Append(allocatedNewName);
01676         if (NS_FAILED(rv))
01677             return rv;
01678 
01679         allocatedNewName.Truncate();
01680 
01681         // check if the destination directory already exists
01682         target->Exists(&exists);
01683         if (!exists)
01684         {
01685             // if the destination directory cannot be created, return an error
01686             rv = target->Create(DIRECTORY_TYPE, 0644);  // TODO, what permissions should we use
01687             if (NS_FAILED(rv))
01688                 return rv;
01689         }
01690         else
01691         {
01692             // check if the destination directory is writable and empty
01693             PRBool isWritable;
01694 
01695             target->IsWritable(&isWritable);
01696             if (!isWritable)
01697                 return NS_ERROR_FILE_ACCESS_DENIED;
01698 
01699             nsCOMPtr<nsISimpleEnumerator> targetIterator;
01700             rv = target->GetDirectoryEntries(getter_AddRefs(targetIterator));
01701 
01702             PRBool more;
01703             targetIterator->HasMoreElements(&more);
01704             // return error if target directory is not empty
01705             if (more)
01706                 return NS_ERROR_FILE_DIR_NOT_EMPTY;
01707         }
01708 
01709         nsDirEnumerator dirEnum;
01710 
01711         rv = dirEnum.Init(this);
01712         if (NS_FAILED(rv)) {
01713             NS_WARNING("dirEnum initialization failed");
01714             return rv;
01715         }
01716 
01717         PRBool more;
01718         while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
01719         {
01720             nsCOMPtr<nsISupports> item;
01721             nsCOMPtr<nsIFile> file;
01722             dirEnum.GetNext(getter_AddRefs(item));
01723             file = do_QueryInterface(item);
01724             if (file)
01725             {
01726                 PRBool isDir, isLink;
01727 
01728                 file->IsDirectory(&isDir);
01729                 file->IsSymlink(&isLink);
01730 
01731                 if (move)
01732                 {
01733                     if (followSymlinks)
01734                         return NS_ERROR_FAILURE;
01735 
01736                     rv = file->MoveTo(target, EmptyString());
01737                     NS_ENSURE_SUCCESS(rv,rv);
01738                 }
01739                 else
01740                 {
01741                     if (followSymlinks)
01742                         rv = file->CopyToFollowingLinks(target, EmptyString());
01743                     else
01744                         rv = file->CopyTo(target, EmptyString());
01745                     NS_ENSURE_SUCCESS(rv,rv);
01746                 }
01747             }
01748         }
01749         // we've finished moving all the children of this directory
01750         // in the new directory.  so now delete the directory
01751         // note, we don't need to do a recursive delete.
01752         // MoveTo() is recursive.  At this point,
01753         // we've already moved the children of the current folder
01754         // to the new location.  nothing should be left in the folder.
01755         if (move)
01756         {
01757           rv = Remove(PR_FALSE /* recursive */);
01758           NS_ENSURE_SUCCESS(rv,rv);
01759         }
01760     }
01761 
01762 
01763     // If we moved, we want to adjust this.
01764     if (move)
01765     {
01766         MakeDirty();
01767 
01768         nsAutoString newParentPath;
01769         newParentDir->GetPath(newParentPath);
01770 
01771         if (newParentPath.IsEmpty())
01772             return NS_ERROR_FAILURE;
01773 
01774         if (newName.IsEmpty())
01775         {
01776             nsAutoString aFileName;
01777             GetLeafName(aFileName);
01778 
01779             InitWithPath(newParentPath);
01780             Append(aFileName);
01781         }
01782         else
01783         {
01784             InitWithPath(newParentPath);
01785             Append(newName);
01786         }
01787     }
01788 
01789     return NS_OK;
01790 }
01791 
01792 NS_IMETHODIMP
01793 nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
01794 {
01795     return CopyMove(newParentDir, newName, PR_FALSE, PR_FALSE);
01796 }
01797 
01798 NS_IMETHODIMP
01799 nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
01800 {
01801     return CopyMove(newParentDir, newName, PR_TRUE, PR_FALSE);
01802 }
01803 
01804 NS_IMETHODIMP
01805 nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
01806 {
01807     return CopyMove(newParentDir, newName, PR_FALSE, PR_TRUE);
01808 }
01809 
01810 
01811 NS_IMETHODIMP
01812 nsLocalFile::Load(PRLibrary * *_retval)
01813 {
01814     PRBool isFile;
01815     nsresult rv = IsFile(&isFile);
01816 
01817     if (NS_FAILED(rv))
01818         return rv;
01819 
01820     if (! isFile)
01821         return NS_ERROR_FILE_IS_DIRECTORY;
01822 
01823     NS_TIMELINE_START_TIMER("PR_LoadLibraryWithFlags");
01824     PRLibSpec libSpec;
01825     libSpec.value.pathname_u = mResolvedPath.get();
01826     libSpec.type = PR_LibSpec_PathnameU;
01827     *_retval =  PR_LoadLibraryWithFlags(libSpec, 0);
01828     NS_TIMELINE_STOP_TIMER("PR_LoadLibraryWithFlags");
01829     NS_TIMELINE_MARK_TIMER1("PR_LoadLibraryWithFlags",
01830                             NS_ConvertUTF16toUTF8(mResolvedPath).get());
01831 
01832     if (*_retval)
01833         return NS_OK;
01834     return NS_ERROR_NULL_POINTER;
01835 }
01836 
01837 NS_IMETHODIMP
01838 nsLocalFile::Remove(PRBool recursive)
01839 {
01840     // NOTE:
01841     //
01842     // if the working path points to a shortcut, then we will only
01843     // delete the shortcut itself.  even if the shortcut points to
01844     // a directory, we will not recurse into that directory or 
01845     // delete that directory itself.  likewise, if the shortcut
01846     // points to a normal file, we will not delete the real file.
01847     // this is done to be consistent with the other platforms that
01848     // behave this way.  we do this even if the followLinks attribute
01849     // is set to true.  this helps protect against misuse that could
01850     // lead to security bugs (e.g., bug 210588).
01851     //
01852     // Since shortcut files are no longer permitted to be used as unix-like
01853     // symlinks interspersed in the path (e.g. "c:/file.lnk/foo/bar.txt")
01854     // this processing is a lot simpler. Even if the shortcut file is 
01855     // pointing to a directory, only the mWorkingPath value is used and so
01856     // only the shortcut file will be deleted.
01857 
01858     PRBool isDir, isLink;
01859     nsresult rv;
01860     
01861     isDir = PR_FALSE;
01862     rv = IsSymlink(&isLink);
01863     if (NS_FAILED(rv))
01864         return rv;
01865 
01866     // only check to see if we have a directory if it isn't a link
01867     if (!isLink)
01868     {
01869         rv = IsDirectory(&isDir);
01870         if (NS_FAILED(rv))
01871             return rv;
01872     }
01873 
01874     if (isDir)
01875     {
01876         if (recursive)
01877         {
01878             nsDirEnumerator dirEnum;
01879 
01880             rv = dirEnum.Init(this);
01881             if (NS_FAILED(rv))
01882                 return rv;
01883 
01884             PRBool more;
01885             while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
01886             {
01887                 nsCOMPtr<nsISupports> item;
01888                 dirEnum.GetNext(getter_AddRefs(item));
01889                 nsCOMPtr<nsIFile> file = do_QueryInterface(item);
01890                 if (file)
01891                     file->Remove(recursive);
01892             }
01893         }
01894         rv = nsWinAPIs::mRmdir(mWorkingPath.get());
01895     }
01896     else
01897     {
01898         rv = nsWinAPIs::mRemove(mWorkingPath.get());
01899     }
01900 
01901     // fixup error code if necessary...
01902     if (rv == (nsresult)-1)
01903         rv = NSRESULT_FOR_ERRNO();
01904     
01905     MakeDirty();
01906     return rv;
01907 }
01908 
01909 NS_IMETHODIMP
01910 nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime)
01911 {
01912     NS_ENSURE_ARG(aLastModifiedTime);
01913  
01914     // get the modified time of the target as determined by mFollowSymlinks
01915     // If PR_TRUE, then this will be for the target of the shortcut file, 
01916     // otherwise it will be for the shortcut file itself (i.e. the same 
01917     // results as GetLastModifiedTimeOfLink)
01918 
01919     nsresult rv = ResolveAndStat();
01920     if (NS_FAILED(rv))
01921         return rv;
01922 
01923     // microseconds -> milliseconds
01924     PRInt64 usecPerMsec;
01925     LL_I2L(usecPerMsec, PR_USEC_PER_MSEC);
01926     LL_DIV(*aLastModifiedTime, mFileInfo64.modifyTime, usecPerMsec);
01927     return NS_OK;
01928 }
01929 
01930 
01931 NS_IMETHODIMP
01932 nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTime)
01933 {
01934     NS_ENSURE_ARG(aLastModifiedTime);
01935  
01936     // The caller is assumed to have already called IsSymlink 
01937     // and to have found that this file is a link. 
01938 
01939     PRFileInfo64 info;
01940     nsresult rv = GetFileInfo(mWorkingPath, &info);
01941     if (NS_FAILED(rv)) 
01942         return rv;
01943 
01944     // microseconds -> milliseconds
01945     PRInt64 usecPerMsec;
01946     LL_I2L(usecPerMsec, PR_USEC_PER_MSEC);
01947     LL_DIV(*aLastModifiedTime, info.modifyTime, usecPerMsec);
01948     return NS_OK;
01949 }
01950 
01951 
01952 NS_IMETHODIMP
01953 nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime)
01954 {
01955     nsresult rv = ResolveAndStat();
01956     if (NS_FAILED(rv))
01957         return rv;
01958 
01959     // set the modified time of the target as determined by mFollowSymlinks
01960     // If PR_TRUE, then this will be for the target of the shortcut file, 
01961     // otherwise it will be for the shortcut file itself (i.e. the same 
01962     // results as SetLastModifiedTimeOfLink)
01963 
01964     rv = SetModDate(aLastModifiedTime, mResolvedPath.get());
01965     if (NS_SUCCEEDED(rv))
01966         MakeDirty();
01967 
01968     return rv;
01969 }
01970 
01971 
01972 NS_IMETHODIMP
01973 nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTime)
01974 {
01975     // The caller is assumed to have already called IsSymlink 
01976     // and to have found that this file is a link. 
01977 
01978     nsresult rv = SetModDate(aLastModifiedTime, mWorkingPath.get());
01979     if (NS_SUCCEEDED(rv))
01980         MakeDirty();
01981 
01982     return rv;
01983 }
01984 
01985 nsresult
01986 nsLocalFile::SetModDate(PRInt64 aLastModifiedTime, const PRUnichar *filePath)
01987 {
01988     HANDLE file = nsWinAPIs::mCreateFile(filePath, // pointer to name of the file
01989                               GENERIC_WRITE,     // access (write) mode
01990                               0,                 // share mode
01991                               NULL,              // pointer to security attributes
01992                               OPEN_EXISTING,     // how to create
01993                               0,                 // file attributes
01994                               NULL);
01995 
01996     if (file == INVALID_HANDLE_VALUE)
01997     {
01998         return ConvertWinError(GetLastError());
01999     }
02000 
02001     FILETIME lft, ft;
02002     SYSTEMTIME st;
02003     PRExplodedTime pret;
02004 
02005     // PR_ExplodeTime expects usecs...
02006     PR_ExplodeTime(aLastModifiedTime * PR_USEC_PER_MSEC, PR_LocalTimeParameters, &pret);
02007     st.wYear            = pret.tm_year;
02008     st.wMonth           = pret.tm_month + 1; // Convert start offset -- Win32: Jan=1; NSPR: Jan=0
02009     st.wDayOfWeek       = pret.tm_wday;
02010     st.wDay             = pret.tm_mday;
02011     st.wHour            = pret.tm_hour;
02012     st.wMinute          = pret.tm_min;
02013     st.wSecond          = pret.tm_sec;
02014     st.wMilliseconds    = pret.tm_usec/1000;
02015 
02016     nsresult rv = NS_OK;
02017     // is at least one of theese fails...
02018     if (!(SystemTimeToFileTime(&st, &lft) != 0 &&
02019           LocalFileTimeToFileTime(&lft, &ft) != 0 &&
02020           SetFileTime(file, NULL, &ft, &ft) != 0))
02021     {
02022       rv = ConvertWinError(GetLastError());
02023     }
02024 
02025     CloseHandle(file);
02026     return rv;
02027 }
02028 
02029 NS_IMETHODIMP
02030 nsLocalFile::GetPermissions(PRUint32 *aPermissions)
02031 {
02032     NS_ENSURE_ARG(aPermissions);
02033 
02034     // get the permissions of the target as determined by mFollowSymlinks
02035     // If PR_TRUE, then this will be for the target of the shortcut file, 
02036     // otherwise it will be for the shortcut file itself (i.e. the same 
02037     // results as GetPermissionsOfLink)
02038     nsresult rv = ResolveAndStat();
02039     if (NS_FAILED(rv))
02040         return rv;
02041 
02042     PRBool isWritable, isExecutable;
02043     IsWritable(&isWritable);
02044     IsExecutable(&isExecutable);
02045 
02046     *aPermissions = PR_IRUSR|PR_IRGRP|PR_IROTH;         // all read
02047     if (isWritable)
02048         *aPermissions |= PR_IWUSR|PR_IWGRP|PR_IWOTH;    // all write
02049     if (isExecutable)
02050         *aPermissions |= PR_IXUSR|PR_IXGRP|PR_IXOTH;    // all execute
02051 
02052     return NS_OK;
02053 }
02054 
02055 NS_IMETHODIMP
02056 nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissions)
02057 {
02058     NS_ENSURE_ARG(aPermissions);
02059 
02060     // The caller is assumed to have already called IsSymlink 
02061     // and to have found that this file is a link. It is not 
02062     // possible for a link file to be executable.
02063 
02064     DWORD word = nsWinAPIs::mGetFileAttributes(mWorkingPath.get());
02065     if (word == INVALID_FILE_ATTRIBUTES)
02066         return NS_ERROR_FILE_INVALID_PATH;
02067 
02068     PRBool isWritable = !(word & FILE_ATTRIBUTE_READONLY);
02069     *aPermissions = PR_IRUSR|PR_IRGRP|PR_IROTH;         // all read
02070     if (isWritable)
02071         *aPermissions |= PR_IWUSR|PR_IWGRP|PR_IWOTH;    // all write
02072 
02073     return NS_OK;
02074 }
02075 
02076 
02077 NS_IMETHODIMP
02078 nsLocalFile::SetPermissions(PRUint32 aPermissions)
02079 {
02080     // set the permissions of the target as determined by mFollowSymlinks
02081     // If PR_TRUE, then this will be for the target of the shortcut file, 
02082     // otherwise it will be for the shortcut file itself (i.e. the same 
02083     // results as SetPermissionsOfLink)
02084     nsresult rv = ResolveAndStat();
02085     if (NS_FAILED(rv))
02086         return rv;
02087 
02088     // windows only knows about the following permissions
02089     int mode = 0;
02090     if (aPermissions & (PR_IRUSR|PR_IRGRP|PR_IROTH))    // any read
02091         mode |= _S_IREAD;
02092     if (aPermissions & (PR_IWUSR|PR_IWGRP|PR_IWOTH))    // any write
02093         mode |= _S_IWRITE;
02094 
02095     if (nsWinAPIs::mChmod(mResolvedPath.get(), mode) == -1)
02096         return NS_ERROR_FAILURE;
02097 
02098     return NS_OK;
02099 }
02100 
02101 NS_IMETHODIMP
02102 nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
02103 {
02104     // The caller is assumed to have already called IsSymlink 
02105     // and to have found that this file is a link. 
02106 
02107     // windows only knows about the following permissions
02108     int mode = 0;
02109     if (aPermissions & (PR_IRUSR|PR_IRGRP|PR_IROTH))    // any read
02110         mode |= _S_IREAD;
02111     if (aPermissions & (PR_IWUSR|PR_IWGRP|PR_IWOTH))    // any write
02112         mode |= _S_IWRITE;
02113 
02114     if (nsWinAPIs::mChmod(mWorkingPath.get(), mode) == -1)
02115         return NS_ERROR_FAILURE;
02116 
02117     return NS_OK;
02118 }
02119 
02120 
02121 NS_IMETHODIMP
02122 nsLocalFile::GetFileSize(PRInt64 *aFileSize)
02123 {
02124     NS_ENSURE_ARG(aFileSize);
02125 
02126     nsresult rv = ResolveAndStat();
02127     if (NS_FAILED(rv))
02128         return rv;
02129 
02130     *aFileSize = mFileInfo64.size;
02131     return NS_OK;
02132 }
02133 
02134 
02135 NS_IMETHODIMP
02136 nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
02137 {
02138     NS_ENSURE_ARG(aFileSize);
02139 
02140     // The caller is assumed to have already called IsSymlink 
02141     // and to have found that this file is a link. 
02142 
02143     PRFileInfo64 info;
02144     if (NS_FAILED(GetFileInfo(mWorkingPath, &info)))
02145         return NS_ERROR_FILE_INVALID_PATH;
02146 
02147     *aFileSize = info.size;
02148     return NS_OK;
02149 }
02150 
02151 NS_IMETHODIMP
02152 nsLocalFile::SetFileSize(PRInt64 aFileSize)
02153 {
02154     nsresult rv = ResolveAndStat();
02155     if (NS_FAILED(rv))
02156         return rv;
02157 
02158     HANDLE hFile = nsWinAPIs::mCreateFile(mResolvedPath.get(), // pointer to name of the file
02159                                GENERIC_WRITE,            // access (write) mode
02160                                FILE_SHARE_READ,          // share mode
02161                                NULL,                     // pointer to security attributes
02162                                OPEN_EXISTING,            // how to create
02163                                FILE_ATTRIBUTE_NORMAL,    // file attributes
02164                                NULL);
02165     if (hFile == INVALID_HANDLE_VALUE)
02166     {
02167         return ConvertWinError(GetLastError());
02168     }
02169 
02170     // seek the file pointer to the new, desired end of file
02171     // and then truncate the file at that position
02172     rv = NS_ERROR_FAILURE;
02173     aFileSize = MyFileSeek64(hFile, aFileSize, FILE_BEGIN);
02174     if (aFileSize != -1 && SetEndOfFile(hFile))
02175     {
02176         MakeDirty();
02177         rv = NS_OK;
02178     }
02179 
02180     CloseHandle(hFile);
02181     return rv;
02182 }
02183 
02184 NS_IMETHODIMP
02185 nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
02186 {
02187 #ifndef WINCE
02188     NS_ENSURE_ARG(aDiskSpaceAvailable);
02189 
02190     ResolveAndStat();
02191 
02192     ULARGE_INTEGER liFreeBytesAvailableToCaller, liTotalNumberOfBytes;
02193     if (nsWinAPIs::mGetDiskFreeSpaceEx &&
02194         nsWinAPIs::mGetDiskFreeSpaceEx(mResolvedPath.get(), 
02195                                        &liFreeBytesAvailableToCaller, 
02196                                        &liTotalNumberOfBytes, 
02197                                        NULL))
02198     {
02199         *aDiskSpaceAvailable = liFreeBytesAvailableToCaller.QuadPart;
02200         return NS_OK;
02201     }
02202 
02203     // use the old method of getting available disk space
02204     WCHAR aDrive[_MAX_DRIVE + 2];
02205     _wsplitpath( mResolvedPath.get(), aDrive, NULL, NULL, NULL);
02206     wcscat(aDrive, L"\\");
02207 
02208     DWORD dwSecPerClus, dwBytesPerSec, dwFreeClus, dwTotalClus;
02209 
02210     if (nsWinAPIs::mGetDiskFreeSpace(aDrive, &dwSecPerClus, &dwBytesPerSec,
02211                                      &dwFreeClus, &dwTotalClus))
02212     {
02213         __int64 bytes = dwFreeClus;
02214         bytes *= dwSecPerClus;
02215         bytes *= dwBytesPerSec;
02216 
02217         *aDiskSpaceAvailable = bytes;
02218         return NS_OK;
02219     }
02220 
02221 #endif
02222     // WINCE FIX
02223     *aDiskSpaceAvailable = 0;
02224     return NS_OK;
02225 }
02226 
02227 NS_IMETHODIMP
02228 nsLocalFile::GetParent(nsIFile * *aParent)
02229 {
02230     NS_ENSURE_ARG_POINTER(aParent);
02231 
02232     nsAutoString parentPath(mWorkingPath);
02233 
02234     PRInt32 offset = parentPath.RFindChar(PRUnichar('\\'));
02235     // adding this offset check that was removed in bug 241708 fixes mail
02236     // directories that aren't relative to/underneath the profile dir.
02237     // e.g., on a different drive. Before you remove them, please make
02238     // sure local mail directories that aren't underneath the profile dir work.
02239     if (offset == kNotFound)
02240       return NS_ERROR_FILE_UNRECOGNIZED_PATH;
02241 
02242     if (offset == 1 && parentPath[0] == L'\\') {
02243         *aParent = nsnull;
02244         return NS_OK;
02245     }
02246     if (offset > 0)
02247         parentPath.Truncate(offset);
02248     else
02249         parentPath.AssignLiteral("\\\\.");
02250 
02251     nsCOMPtr<nsILocalFile> localFile;
02252     nsresult rv = NS_NewLocalFile(parentPath, mFollowSymlinks, getter_AddRefs(localFile));
02253 
02254     if(NS_SUCCEEDED(rv) && localFile)
02255     {
02256         return CallQueryInterface(localFile, aParent);
02257     }
02258     return rv;
02259 }
02260 
02261 NS_IMETHODIMP
02262 nsLocalFile::Exists(PRBool *_retval)
02263 {
02264     NS_ENSURE_ARG(_retval);
02265     *_retval = PR_FALSE;
02266 
02267     MakeDirty();
02268     nsresult rv = ResolveAndStat();
02269     *_retval = NS_SUCCEEDED(rv);
02270 
02271     return NS_OK;
02272 }
02273 
02274 NS_IMETHODIMP
02275 nsLocalFile::IsWritable(PRBool *aIsWritable)
02276 {
02277     //TODO: extend to support NTFS file permissions
02278 
02279     // The read-only attribute on a FAT directory only means that it can't 
02280     // be deleted. It is still possible to modify the contents of the directory.
02281     nsresult rv = IsDirectory(aIsWritable);
02282     if (NS_FAILED(rv))
02283         return rv;
02284     if (*aIsWritable)
02285         return NS_OK;
02286 
02287     // writable if the file doesn't have the readonly attribute
02288     rv = HasFileAttribute(FILE_ATTRIBUTE_READONLY, aIsWritable);
02289     if (NS_FAILED(rv))
02290         return rv;
02291     *aIsWritable = !*aIsWritable;
02292 
02293     return NS_OK;
02294 }
02295 
02296 NS_IMETHODIMP
02297 nsLocalFile::IsReadable(PRBool *_retval)
02298 {
02299     NS_ENSURE_ARG(_retval);
02300     *_retval = PR_FALSE;
02301 
02302     nsresult rv = ResolveAndStat();
02303     if (NS_FAILED(rv))
02304         return rv;
02305 
02306     *_retval = PR_TRUE;
02307     return NS_OK;
02308 }
02309 
02310 
02311 NS_IMETHODIMP
02312 nsLocalFile::IsExecutable(PRBool *_retval)
02313 {
02314     NS_ENSURE_ARG(_retval);
02315     *_retval = PR_FALSE;
02316     
02317     nsresult rv;
02318 
02319     // only files can be executables
02320     PRBool isFile;
02321     rv = IsFile(&isFile);
02322     if (NS_FAILED(rv))
02323         return rv;
02324     if (!isFile)
02325         return NS_OK;
02326 
02327     //TODO: shouldn't we be checking mFollowSymlinks here?
02328     PRBool symLink;
02329     rv = IsSymlink(&symLink);
02330     if (NS_FAILED(rv))
02331         return rv;
02332 
02333     nsAutoString path;
02334     if (symLink)
02335         GetTarget(path);
02336     else
02337         GetPath(path);
02338 
02339     // kill trailing dots and spaces.
02340     PRInt32 filePathLen = path.Length() - 1;
02341     while(filePathLen > 0 && (path[filePathLen] == L' ' || path[filePathLen] == L'.'))
02342     {
02343         path.Truncate(filePathLen--);
02344     } 
02345 
02346     // Get extension.
02347     PRInt32 dotIdx = path.RFindChar(PRUnichar('.'));
02348     if ( dotIdx != kNotFound ) {
02349         // Convert extension to lower case.
02350         PRUnichar *p = path.BeginWriting();
02351         for( p+= dotIdx + 1; *p; p++ )
02352             *p +=  (*p >= L'A' && *p <= L'Z') ? 'a' - 'A' : 0; 
02353         
02354         // Search for any of the set of executable extensions.
02355         const char * const executableExts[] = {
02356             "ad",
02357             "ade",         // access project extension
02358             "adp",
02359             "app",         // executable application
02360             "application", // from bug 348763
02361             "asp",
02362             "bas",
02363             "bat",
02364             "chm",
02365             "cmd",
02366             "com",
02367             "cpl",
02368             "crt",
02369             "exe",
02370             "fxp",         // FoxPro compiled app
02371             "hlp",
02372             "hta",
02373             "inf",
02374             "ins",
02375             "isp",
02376             "js",
02377             "jse",
02378             "lnk",
02379             "mad",         // Access Module Shortcut
02380             "maf",         // Access
02381             "mag",         // Access Diagram Shortcut
02382             "mam",         // Access Macro Shortcut
02383             "maq",         // Access Query Shortcut
02384             "mar",         // Access Report Shortcut
02385             "mas",         // Access Stored Procedure
02386             "mat",         // Access Table Shortcut
02387             "mau",         // Media Attachment Unit
02388             "mav",         // Access View Shortcut
02389             "maw",         // Access Data Access Page
02390             "mda",         // Access Add-in, MDA Access 2 Workgroup
02391             "mdb",
02392             "mde",
02393             "mdt",         // Access Add-in Data
02394             "mdw",         // Access Workgroup Information
02395             "mdz",         // Access Wizard Template
02396             "msc",
02397             "msh",         // Microsoft Shell
02398             "mshxml",      // Microsoft Shell
02399             "msi",
02400             "msp",
02401             "mst",
02402             "ops",         // Office Profile Settings
02403             "pcd",
02404             "pif",
02405             "plg",         // Developer Studio Build Log
02406             "prf",         // windows system file
02407             "prg",
02408             "pst",
02409             "reg",
02410             "scf",         // Windows explorer command
02411             "scr",
02412             "sct",
02413             "shb",
02414             "shs",
02415             "url",
02416             "vb",
02417             "vbe",
02418             "vbs",
02419             "vsd",
02420             "vsmacros",    // Visual Studio .NET Binary-based Macro Project
02421             "vss",
02422             "vst",
02423             "vsw",
02424             "ws",
02425             "wsc",
02426             "wsf",
02427             "wsh",
02428             0 };
02429         for ( int i = 0; executableExts[i]; i++ ) {
02430             if ( Substring(path, dotIdx + 1).EqualsASCII(executableExts[i])) {
02431                 // Found a match.  Set result and quit.
02432                 *_retval = PR_TRUE;
02433                 break;
02434             }
02435         }
02436     }
02437 
02438     return NS_OK;
02439 }
02440 
02441 
02442 NS_IMETHODIMP
02443 nsLocalFile::IsDirectory(PRBool *_retval)
02444 {
02445     NS_ENSURE_ARG(_retval);
02446 
02447     nsresult rv = ResolveAndStat();
02448     if (NS_FAILED(rv))
02449         return rv;
02450 
02451     *_retval = (mFileInfo64.type == PR_FILE_DIRECTORY); 
02452     return NS_OK;
02453 }
02454 
02455 NS_IMETHODIMP
02456 nsLocalFile::IsFile(PRBool *_retval)
02457 {
02458     NS_ENSURE_ARG(_retval);
02459 
02460     nsresult rv = ResolveAndStat();
02461     if (NS_FAILED(rv))
02462         return rv;
02463 
02464     *_retval = (mFileInfo64.type == PR_FILE_FILE); 
02465     return NS_OK;
02466 }
02467 
02468 NS_IMETHODIMP
02469 nsLocalFile::IsHidden(PRBool *_retval)
02470 {
02471     return HasFileAttribute(FILE_ATTRIBUTE_HIDDEN, _retval);
02472 }
02473 
02474 nsresult
02475 nsLocalFile::HasFileAttribute(DWORD fileAttrib, PRBool *_retval)
02476 {
02477     NS_ENSURE_ARG(_retval);
02478 
02479     nsresult rv = ResolveAndStat();
02480     if (NS_FAILED(rv))
02481         return rv;
02482 
02483     // get the file attributes for the correct item depending on following symlinks
02484     const PRUnichar *filePath = mFollowSymlinks ? 
02485                                 mResolvedPath.get() : mWorkingPath.get();
02486     DWORD word = nsWinAPIs::mGetFileAttributes(filePath);
02487 
02488     *_retval = ((word & fileAttrib) != 0);
02489     return NS_OK;
02490 }
02491 
02492 NS_IMETHODIMP
02493 nsLocalFile::IsSymlink(PRBool *_retval)
02494 {
02495     NS_ENSURE_ARG(_retval);
02496 
02497     // unless it is a valid shortcut path it's not a symlink
02498     if (!IsShortcutPath(mWorkingPath))
02499     {
02500         *_retval = PR_FALSE;
02501         return NS_OK;
02502     }
02503 
02504     // we need to know if this is a file or directory
02505     nsresult rv = ResolveAndStat();
02506     if (NS_FAILED(rv))
02507         return rv;
02508 
02509     // it's only a shortcut if it is a file
02510     *_retval = (mFileInfo64.type == PR_FILE_FILE);
02511     return NS_OK;
02512 }
02513 
02514 NS_IMETHODIMP
02515 nsLocalFile::IsSpecial(PRBool *_retval)
02516 {
02517     return HasFileAttribute(FILE_ATTRIBUTE_SYSTEM, _retval);
02518 }
02519 
02520 NS_IMETHODIMP
02521 nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
02522 {
02523     NS_ENSURE_ARG(inFile);
02524     NS_ENSURE_ARG(_retval);
02525 
02526     nsAutoString inFilePath;
02527     inFile->GetPath(inFilePath);
02528 
02529     // Normalize both paths to the short form, failing back to a string
02530     // comparison of the long form.
02531       
02532     WCHAR thisshort[MAX_PATH];
02533     WCHAR thatshort[MAX_PATH];
02534 
02535     DWORD thisr = nsWinAPIs::mGetShortPathName(mWorkingPath.get(), thisshort,
02536                                                sizeof(thisshort));
02537     DWORD thatr = nsWinAPIs::mGetShortPathName(inFilePath.get(), thatshort,
02538                                                sizeof(thatshort));
02539 
02540     if (thisr && thatr && thisr < sizeof(thisshort) && thatr < sizeof(thatshort)) {
02541         *_retval = (_wcsicmp(thisshort, thatshort) == 0);
02542     }
02543     else {
02544         *_retval = (_wcsicmp(inFilePath.get(),  mWorkingPath.get()) == 0);
02545     }
02546 
02547     return NS_OK;
02548 }
02549 
02550 
02551 NS_IMETHODIMP
02552 nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
02553 {
02554     *_retval = PR_FALSE;
02555 
02556     nsAutoString myFilePath;
02557     if (NS_FAILED(GetTarget(myFilePath)))
02558         GetPath(myFilePath);
02559 
02560     PRUint32 myFilePathLen = myFilePath.Length();
02561 
02562     nsAutoString inFilePath;
02563     if (NS_FAILED(inFile->GetTarget(inFilePath)))
02564         inFile->GetPath(inFilePath);
02565 
02566     // make sure that the |inFile|'s path has a trailing separator.
02567     if (inFilePath.Length() >= myFilePathLen && inFilePath[myFilePathLen] == L'\\')
02568     {
02569         // Ok : Win9x
02570         if (_wcsnicmp(myFilePath.get(), inFilePath.get(), myFilePathLen) == 0)
02571         {
02572             *_retval = PR_TRUE;
02573         }
02574 
02575     }
02576 
02577     return NS_OK;
02578 }
02579 
02580 
02581 NS_IMETHODIMP
02582 nsLocalFile::GetTarget(nsAString &_retval)
02583 {
02584     _retval.Truncate();
02585 #if STRICT_FAKE_SYMLINKS
02586     PRBool symLink;
02587 
02588     nsresult rv = IsSymlink(&symLink);
02589     if (NS_FAILED(rv))
02590         return rv;
02591 
02592     if (!symLink)
02593     {
02594         return NS_ERROR_FILE_INVALID_PATH;
02595     }
02596 #endif
02597     ResolveAndStat();
02598 
02599     _retval = mResolvedPath;
02600     return NS_OK;
02601 }
02602 
02603 
02604 /* attribute PRBool followLinks; */
02605 NS_IMETHODIMP
02606 nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
02607 {
02608     *aFollowLinks = mFollowSymlinks;
02609     return NS_OK;
02610 }
02611 NS_IMETHODIMP
02612 nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
02613 {
02614     MakeDirty();
02615     mFollowSymlinks = aFollowLinks;
02616     return NS_OK;
02617 }
02618 
02619 
02620 NS_IMETHODIMP
02621 nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator * *entries)
02622 {
02623     nsresult rv;
02624 
02625     *entries = nsnull;
02626     if (mWorkingPath.EqualsLiteral("\\\\.")) {
02627         nsDriveEnumerator *drives = new nsDriveEnumerator;
02628         if (!drives)
02629             return NS_ERROR_OUT_OF_MEMORY;
02630         NS_ADDREF(drives);
02631         rv = drives->Init();
02632         if (NS_FAILED(rv)) {
02633             NS_RELEASE(drives);
02634             return rv;
02635         }
02636         *entries = drives;
02637         return NS_OK;
02638     }
02639 
02640     PRBool isDir;
02641     rv = IsDirectory(&isDir);
02642     if (NS_FAILED(rv))
02643         return rv;
02644     if (!isDir)
02645         return NS_ERROR_FILE_NOT_DIRECTORY;
02646 
02647     nsDirEnumerator* dirEnum = new nsDirEnumerator();
02648     if (dirEnum == nsnull)
02649         return NS_ERROR_OUT_OF_MEMORY;
02650     NS_ADDREF(dirEnum);
02651     rv = dirEnum->Init(this);
02652     if (NS_FAILED(rv))
02653     {
02654         NS_RELEASE(dirEnum);
02655         return rv;
02656     }
02657 
02658     *entries = dirEnum;
02659     return NS_OK;
02660 }
02661 
02662 NS_IMETHODIMP
02663 nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
02664 {
02665     CopyUTF16toUTF8(mWorkingPath, aPersistentDescriptor);
02666     return NS_OK;
02667 }   
02668     
02669 NS_IMETHODIMP
02670 nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
02671 {
02672     if (IsUTF8(aPersistentDescriptor))
02673         return InitWithPath(NS_ConvertUTF8toUTF16(aPersistentDescriptor));
02674     else
02675         return InitWithNativePath(aPersistentDescriptor);
02676 }   
02677 
02678 NS_IMETHODIMP
02679 nsLocalFile::Reveal()
02680 {
02681     // make sure mResolvedPath is set
02682     nsresult rv = ResolveAndStat();
02683     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
02684         return rv;
02685 
02686     // use the full path to explorer for security
02687     nsCOMPtr<nsILocalFile> winDir;
02688     rv = GetSpecialSystemDirectory(Win_WindowsDirectory, getter_AddRefs(winDir));
02689     NS_ENSURE_SUCCESS(rv, rv);
02690     nsAutoString explorerPath;
02691     rv = winDir->GetPath(explorerPath);  
02692     NS_ENSURE_SUCCESS(rv, rv);
02693     explorerPath.Append(L"\\explorer.exe");
02694 
02695     // Always open a new window for files because Win2K doesn't appear to select
02696     // the file if a window showing that folder was already open. If the resolved 
02697     // path is a directory then instead of opening the parent and selecting it, 
02698     // we open the directory itself.
02699     nsAutoString explorerParams;
02700     if (mFileInfo64.type != PR_FILE_DIRECTORY) // valid because we ResolveAndStat above
02701         explorerParams.Append(L"/n,/select,");
02702     explorerParams.Append(L'\"');
02703     explorerParams.Append(mResolvedPath);
02704     explorerParams.Append(L'\"');
02705 
02706     if (nsWinAPIs::mShellExecute(NULL, L"open", explorerPath.get(),
02707                                  explorerParams.get(),
02708                                  NULL, SW_SHOWNORMAL) <= (HINSTANCE) 32)
02709         return NS_ERROR_FAILURE;
02710  
02711     return NS_OK;
02712 }
02713 
02714 
02715 NS_IMETHODIMP
02716 nsLocalFile::Launch()
02717 {
02718     const nsString &path = mWorkingPath;
02719 
02720     // use the app registry name to launch a shell execute....
02721     LONG r = (LONG) nsWinAPIs::mShellExecute( NULL, NULL, path.get(),
02722                                               NULL, NULL, SW_SHOWNORMAL);
02723 
02724     // if the file has no association, we launch windows' "what do you want to do" dialog
02725     if (r == SE_ERR_NOASSOC) {
02726         nsAutoString shellArg;
02727         shellArg.Assign(NS_LITERAL_STRING("shell32.dll,OpenAs_RunDLL ") + path);
02728         r = (LONG) nsWinAPIs::mShellExecute(NULL, NULL, L"RUNDLL32.EXE",
02729                                             shellArg.get(),
02730                                             NULL, SW_SHOWNORMAL);
02731     }
02732     if (r < 32) {
02733         switch (r) {
02734           case 0:
02735           case SE_ERR_OOM:
02736             return NS_ERROR_OUT_OF_MEMORY;
02737           case ERROR_FILE_NOT_FOUND:
02738             return NS_ERROR_FILE_NOT_FOUND;
02739           case ERROR_PATH_NOT_FOUND:
02740             return NS_ERROR_FILE_UNRECOGNIZED_PATH;
02741           case ERROR_BAD_FORMAT:
02742             return NS_ERROR_FILE_CORRUPTED;
02743           case SE_ERR_ACCESSDENIED:
02744             return NS_ERROR_FILE_ACCESS_DENIED;
02745           case SE_ERR_ASSOCINCOMPLETE:
02746           case SE_ERR_NOASSOC:
02747             return NS_ERROR_UNEXPECTED;
02748           case SE_ERR_DDEBUSY:
02749           case SE_ERR_DDEFAIL:
02750           case SE_ERR_DDETIMEOUT:
02751             return NS_ERROR_NOT_AVAILABLE;
02752           case SE_ERR_DLLNOTFOUND:
02753             return NS_ERROR_FAILURE;
02754           case SE_ERR_SHARE:
02755             return NS_ERROR_FILE_IS_LOCKED;
02756           default:
02757             return NS_ERROR_FILE_EXECUTION_FAILED;
02758         }
02759     }
02760     return NS_OK;
02761 }
02762 
02763 
02764 nsresult
02765 NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
02766 {
02767     nsLocalFile* file = new nsLocalFile();
02768     if (file == nsnull)
02769         return NS_ERROR_OUT_OF_MEMORY;
02770     NS_ADDREF(file);
02771 
02772     file->SetFollowLinks(followLinks);
02773 
02774     if (!path.IsEmpty()) {
02775         nsresult rv = file->InitWithPath(path);
02776         if (NS_FAILED(rv)) {
02777             NS_RELEASE(file);
02778             return rv;
02779         }
02780     }
02781 
02782     *result = file;
02783     return NS_OK;
02784 }
02785 
02786 //-----------------------------------------------------------------------------
02787 // Native (lossy) interface
02788 //-----------------------------------------------------------------------------
02789   
02790 NS_IMETHODIMP
02791 nsLocalFile::InitWithNativePath(const nsACString &filePath)
02792 {
02793    nsAutoString tmp;
02794    nsresult rv = NS_CopyNativeToUnicode(filePath, tmp);
02795    if (NS_SUCCEEDED(rv))
02796        return InitWithPath(tmp);
02797 
02798    return rv;
02799 }
02800 
02801 NS_IMETHODIMP
02802 nsLocalFile::AppendNative(const nsACString &node)
02803 {
02804     nsAutoString tmp;
02805     nsresult rv = NS_CopyNativeToUnicode(node, tmp);
02806     if (NS_SUCCEEDED(rv))
02807         return Append(tmp);
02808 
02809     return rv;
02810 }
02811 
02812 NS_IMETHODIMP
02813 nsLocalFile::AppendRelativeNativePath(const nsACString &node)
02814 {
02815     nsAutoString tmp;
02816     nsresult rv = NS_CopyNativeToUnicode(node, tmp);
02817     if (NS_SUCCEEDED(rv))
02818         return AppendRelativePath(tmp);
02819     return rv;
02820 }
02821 
02822 
02823 NS_IMETHODIMP
02824 nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
02825 {
02826     //NS_WARNING("This API is lossy. Use GetLeafName !");
02827     nsAutoString tmp;
02828     nsresult rv = GetLeafName(tmp);
02829     if (NS_SUCCEEDED(rv))
02830         rv = NS_CopyUnicodeToNative(tmp, aLeafName);
02831 
02832     return rv;
02833 }
02834 
02835 NS_IMETHODIMP
02836 nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
02837 {
02838     nsAutoString tmp;
02839     nsresult rv = NS_CopyNativeToUnicode(aLeafName, tmp);
02840     if (NS_SUCCEEDED(rv))
02841         return SetLeafName(tmp);
02842 
02843     return rv;
02844 }
02845 
02846 
02847 NS_IMETHODIMP
02848 nsLocalFile::GetNativePath(nsACString &_retval)
02849 {
02850     //NS_WARNING("This API is lossy. Use GetPath !");
02851     nsAutoString tmp;
02852     nsresult rv = GetPath(tmp);
02853     if (NS_SUCCEEDED(rv))
02854         rv = NS_CopyUnicodeToNative(tmp, _retval);
02855 
02856     return rv;
02857 }
02858 
02859 
02860 NS_IMETHODIMP
02861 nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString &newName)
02862 {
02863     if (newName.IsEmpty())
02864         return CopyTo(newParentDir, EmptyString());
02865 
02866     nsAutoString tmp;
02867     nsresult rv = NS_CopyNativeToUnicode(newName, tmp);
02868     if (NS_SUCCEEDED(rv))
02869         return CopyTo(newParentDir, tmp);
02870 
02871     return rv;
02872 }
02873 
02874 NS_IMETHODIMP
02875 nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString &newName)
02876 {
02877     if (newName.IsEmpty())
02878         return CopyToFollowingLinks(newParentDir, EmptyString());
02879 
02880     nsAutoString tmp;
02881     nsresult rv = NS_CopyNativeToUnicode(newName, tmp);
02882     if (NS_SUCCEEDED(rv))
02883         return CopyToFollowingLinks(newParentDir, tmp);
02884 
02885     return rv;
02886 }
02887 
02888 NS_IMETHODIMP
02889 nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString &newName)
02890 {
02891     if (newName.IsEmpty())
02892         return MoveTo(newParentDir, EmptyString());
02893 
02894     nsAutoString tmp;
02895     nsresult rv = NS_CopyNativeToUnicode(newName, tmp);
02896     if (NS_SUCCEEDED(rv))
02897         return MoveTo(newParentDir, tmp);
02898 
02899     return rv;
02900 }
02901 
02902 NS_IMETHODIMP
02903 nsLocalFile::GetNativeTarget(nsACString &_retval)
02904 {
02905     NS_WARNING("This API is lossy. Use GetTarget !");
02906     nsAutoString tmp;
02907     nsresult rv = GetTarget(tmp);
02908     if (NS_SUCCEEDED(rv))
02909         rv = NS_CopyUnicodeToNative(tmp, _retval);
02910 
02911     return rv;
02912 }
02913 
02914 nsresult
02915 NS_NewNativeLocalFile(const nsACString &path, PRBool followLinks, nsILocalFile* *result)
02916 {
02917     nsAutoString buf;
02918     nsresult rv = NS_CopyNativeToUnicode(path, buf);
02919     if (NS_FAILED(rv)) {
02920         *result = nsnull;
02921         return rv;
02922     }
02923     return NS_NewLocalFile(buf, followLinks, result);
02924 }
02925 
02926 //-----------------------------------------------------------------------------
02927 // nsLocalFile <static members>
02928 //-----------------------------------------------------------------------------
02929 
02930 void
02931 nsLocalFile::GlobalInit()
02932 {
02933 #ifndef WINCE
02934     nsresult rv = NS_CreateShortcutResolver();
02935     NS_ASSERTION(NS_SUCCEEDED(rv), "Shortcut resolver could not be created");
02936 #endif
02937 }
02938 
02939 void
02940 nsLocalFile::GlobalShutdown()
02941 {
02942 #ifndef WINCE
02943     NS_DestroyShortcutResolver();
02944 #endif
02945 }
02946 
02947 NS_IMPL_ISUPPORTS1(nsDriveEnumerator, nsISimpleEnumerator)
02948 
02949 nsDriveEnumerator::nsDriveEnumerator()
02950  : mLetter(0)
02951 {
02952 }
02953 
02954 nsDriveEnumerator::~nsDriveEnumerator()
02955 {
02956 }
02957 
02958 nsresult nsDriveEnumerator::Init()
02959 {
02960 #ifdef WINCE
02961     return NS_OK;
02962 #else
02963     /* If the length passed to GetLogicalDriveStrings is smaller
02964      * than the length of the string it would return, it returns
02965      * the length required for the string. */
02966     DWORD length = GetLogicalDriveStrings(0, 0);
02967     /* The string is null terminated */
02968     if (!EnsureStringLength(mDrives, length+1))
02969         return NS_ERROR_OUT_OF_MEMORY;
02970     if (!GetLogicalDriveStrings(length, mDrives.BeginWriting()))
02971         return NS_ERROR_FAILURE;
02972     mLetter = mDrives.get();
02973     return NS_OK;
02974 #endif
02975 }
02976 
02977 NS_IMETHODIMP nsDriveEnumerator::HasMoreElements(PRBool *aHasMore)
02978 {
02979 #ifdef WINCE
02980     *aHasMore = FALSE;
02981 #else
02982     *aHasMore = *mLetter != '\0';
02983 #endif
02984     return NS_OK;
02985 }
02986 
02987 NS_IMETHODIMP nsDriveEnumerator::GetNext(nsISupports **aNext)
02988 {
02989 #ifdef WINCE
02990     nsILocalFile *file;
02991     nsresult rv = NS_NewLocalFile(NS_LITERAL_STRING("\\"), PR_FALSE, &file);
02992     *aNext = file;
02993 #else
02994     /* GetLogicalDrives stored in mLetter is a concatenation
02995      * of null terminated strings, followed by a null terminator. */
02996     if (!*mLetter) {
02997         *aNext = nsnull;
02998         return NS_OK;
02999     }
03000     NS_ConvertASCIItoUTF16 drive(mLetter);
03001     mLetter += drive.Length() + 1;
03002     nsILocalFile *file;
03003     nsresult rv = 
03004         NS_NewLocalFile(drive, PR_FALSE, &file);
03005 
03006     *aNext = file;
03007 #endif
03008     return rv;
03009 }
03010