1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3
* The contents of this file are subject to the Netscape Public
4
* License Version 1.1 (the "License"); you may not use this file
5
* except in compliance with the License. You may obtain a copy of
6
* the License at http://www.mozilla.org/NPL/
8
* Software distributed under the License is distributed on an "AS
9
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
10
* implied. See the License for the specific language governing
11
* rights and limitations under the License.
13
* The Original Code is Mozilla Communicator client code,
14
* released March 31, 1998.
16
* The Initial Developer of the Original Code is Netscape Communications
17
* Corporation. Portions created by Netscape are
18
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
22
* Doug Turner <dougt@netscape.com>
29
#include "nsLocalFile.h"
30
#include "nsNativeCharsetUtils.h"
32
#include "nsISimpleEnumerator.h"
33
#include "nsIComponentManager.h"
37
#include "nsXPIDLString.h"
38
#include "nsReadableUtils.h"
51
#include "nsXPIDLString.h"
53
#include "nsITimelineService.h"
55
#include "nsAutoLock.h"
57
// _mbsstr isn't declared in w32api headers but it's there in the libs
60
unsigned char *_mbsstr( const unsigned char *str,
61
const unsigned char *substr );
65
//----------------------------------------------------------------------------
67
//----------------------------------------------------------------------------
69
class ShortcutResolver
73
// nonvirtual since we're not subclassed
77
nsresult Resolve(const WCHAR* in, char* out);
81
IPersistFile* mPersistFile;
82
IShellLink* mShellLink;
85
ShortcutResolver::ShortcutResolver()
88
mPersistFile = nsnull;
92
ShortcutResolver::~ShortcutResolver()
95
PR_DestroyLock(mLock);
97
// Release the pointer to the IPersistFile interface.
99
mPersistFile->Release();
101
// Release the pointer to the IShellLink interface.
103
mShellLink->Release();
109
ShortcutResolver::Init()
111
CoInitialize(NULL); // FIX: we should probably move somewhere higher up during startup
113
mLock = PR_NewLock();
115
return NS_ERROR_FAILURE;
117
HRESULT hres = CoCreateInstance(CLSID_ShellLink,
119
CLSCTX_INPROC_SERVER,
121
(void**)&mShellLink);
124
// Get a pointer to the IPersistFile interface.
125
hres = mShellLink->QueryInterface(IID_IPersistFile, (void**)&mPersistFile);
128
if (mPersistFile == nsnull || mShellLink == nsnull)
129
return NS_ERROR_FAILURE;
134
// |out| must be an allocated buffer of size MAX_PATH
136
ShortcutResolver::Resolve(const WCHAR* in, char* out)
138
nsAutoLock lock(mLock);
140
// see if we can Load the path.
141
HRESULT hres = mPersistFile->Load(in, STGM_READ);
144
return NS_ERROR_FAILURE;
147
hres = mShellLink->Resolve(nsnull, SLR_NO_UI );
150
return NS_ERROR_FAILURE;
153
// Get the path to the link target.
154
hres = mShellLink->GetPath( out, MAX_PATH, &wfd, SLGP_UNCPRIORITY );
156
return NS_ERROR_FAILURE;
160
static ShortcutResolver * gResolver = nsnull;
162
static nsresult NS_CreateShortcutResolver()
164
gResolver = new ShortcutResolver();
166
return NS_ERROR_OUT_OF_MEMORY;
168
return gResolver->Init();
171
static void NS_DestroyShortcutResolver()
178
//-----------------------------------------------------------------------------
179
// static helper functions
180
//-----------------------------------------------------------------------------
182
// certainly not all the error that can be
183
// encountered, but many of them common ones
184
static nsresult ConvertWinError(DWORD winErr)
190
case ERROR_FILE_NOT_FOUND:
191
case ERROR_PATH_NOT_FOUND:
192
case ERROR_INVALID_DRIVE:
193
rv = NS_ERROR_FILE_NOT_FOUND;
195
case ERROR_ACCESS_DENIED:
196
case ERROR_NOT_SAME_DEVICE:
197
rv = NS_ERROR_FILE_ACCESS_DENIED;
199
case ERROR_NOT_ENOUGH_MEMORY:
200
case ERROR_INVALID_BLOCK:
201
case ERROR_INVALID_HANDLE:
202
case ERROR_ARENA_TRASHED:
203
rv = NS_ERROR_OUT_OF_MEMORY;
205
case ERROR_CURRENT_DIRECTORY:
206
rv = NS_ERROR_FILE_DIR_NOT_EMPTY;
208
case ERROR_WRITE_PROTECT:
209
rv = NS_ERROR_FILE_READ_ONLY;
211
case ERROR_HANDLE_DISK_FULL:
212
rv = NS_ERROR_FILE_TOO_BIG;
214
case ERROR_FILE_EXISTS:
215
case ERROR_ALREADY_EXISTS:
216
case ERROR_CANNOT_MAKE:
217
rv = NS_ERROR_FILE_ALREADY_EXISTS;
222
rv = NS_ERROR_FAILURE;
229
myLL_II2L(PRInt32 hi, PRInt32 lo, PRInt64 *result)
231
PRInt64 a64, b64; // probably could have been done with
232
// only one PRInt64, but these are macros,
235
// put hi in the low bits of a64.
237
// now shift it to the upperbit and place it the result in result
238
LL_SHL(b64, a64, 32);
239
// now put the low bits on by adding them to the result.
240
LL_ADD(*result, b64, lo);
245
myLL_L2II(PRInt64 result, PRInt32 *hi, PRInt32 *lo )
247
PRInt64 a64, b64; // probably could have been done with
248
// only one PRInt64, but these are macros,
251
// shift the hi word to the low word, then push it into a long.
252
LL_SHR(a64, result, 32);
255
// shift the low word to the hi word first, then shift it back.
256
LL_SHL(b64, result, 32);
257
LL_SHR(a64, b64, 32);
262
IsShortcut(const char* workingPath, int filePathLen)
264
// XXX this is badly broken!!
265
// XXX consider "C:\FOO.LNK"
266
// XXX consider "C:\foo.lnkx\bar.lnk"
268
// check to see if it is shortcut, i.e., it has ".lnk" in it
269
unsigned char* dest = _mbsstr((unsigned char*)workingPath,
270
(unsigned char*)".lnk");
274
// find index of ".lnk"
275
int result = (int)(dest - (unsigned char*)workingPath);
277
// if ".lnk" is not at the leaf of this path, we need to make sure the
278
// next char after ".lnk" is a '\\'. e.g. "c:\\foo.lnk\\a.html" is valid,
279
// whereas "c:\\foo.lnkx" is not.
280
if (result + 4 < filePathLen)
282
if (workingPath[result + 4] != '\\')
288
//-----------------------------------------------------------------------------
290
//-----------------------------------------------------------------------------
292
class nsDirEnumerator : public nsISimpleEnumerator
298
nsDirEnumerator() : mDir(nsnull)
302
nsresult Init(nsILocalFile* parent)
304
nsCAutoString filepath;
305
parent->GetNativeTarget(filepath);
307
if (filepath.IsEmpty())
309
parent->GetNativePath(filepath);
312
if (filepath.IsEmpty())
314
return NS_ERROR_UNEXPECTED;
317
mDir = PR_OpenDir(filepath.get());
318
if (mDir == nsnull) // not a directory?
319
return NS_ERROR_FAILURE;
325
NS_IMETHOD HasMoreElements(PRBool *result)
328
if (mNext == nsnull && mDir)
330
PRDirEntry* entry = PR_ReadDir(mDir, PR_SKIP_BOTH);
333
// end of dir entries
335
PRStatus status = PR_CloseDir(mDir);
336
if (status != PR_SUCCESS)
337
return NS_ERROR_FAILURE;
344
nsCOMPtr<nsIFile> file;
345
rv = mParent->Clone(getter_AddRefs(file));
349
rv = file->AppendNative(nsDependentCString(entry->name));
353
// make sure the thing exists. If it does, try the next one.
355
rv = file->Exists(&exists);
356
if (NS_FAILED(rv) || !exists)
358
return HasMoreElements(result);
361
mNext = do_QueryInterface(file);
363
*result = mNext != nsnull;
367
NS_IMETHOD GetNext(nsISupports **result)
371
rv = HasMoreElements(&hasMore);
372
if (NS_FAILED(rv)) return rv;
374
*result = mNext; // might return nsnull
375
NS_IF_ADDREF(*result);
381
// dtor can be non-virtual since there are no subclasses, but must be
382
// public to use the class on the stack.
387
PRStatus status = PR_CloseDir(mDir);
388
NS_ASSERTION(status == PR_SUCCESS, "close failed");
394
nsCOMPtr<nsILocalFile> mParent;
395
nsCOMPtr<nsILocalFile> mNext;
398
NS_IMPL_ISUPPORTS1(nsDirEnumerator, nsISimpleEnumerator)
401
//-----------------------------------------------------------------------------
402
// nsLocalFile <public>
403
//-----------------------------------------------------------------------------
405
nsLocalFile::nsLocalFile()
407
mLastResolution = PR_FALSE;
408
mFollowSymlinks = PR_FALSE;
413
nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
415
NS_ENSURE_ARG_POINTER(aInstancePtr);
416
NS_ENSURE_NO_AGGREGATION(outer);
418
nsLocalFile* inst = new nsLocalFile();
420
return NS_ERROR_OUT_OF_MEMORY;
422
nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
432
//-----------------------------------------------------------------------------
433
// nsLocalFile::nsISupports
434
//-----------------------------------------------------------------------------
436
NS_IMPL_THREADSAFE_ISUPPORTS2(nsLocalFile, nsILocalFile, nsIFile)
439
//-----------------------------------------------------------------------------
440
// nsLocalFile <private>
441
//-----------------------------------------------------------------------------
443
nsLocalFile::nsLocalFile(const nsLocalFile& other)
444
: mDirty(other.mDirty)
445
, mLastResolution(other.mLastResolution)
446
, mFollowSymlinks(other.mFollowSymlinks)
447
, mWorkingPath(other.mWorkingPath)
448
, mResolvedPath(other.mResolvedPath)
449
, mFileInfo64(other.mFileInfo64)
454
// this function will walk the native path of |this| resolving any symbolic
455
// links found. The new resulting path will be placed into mResolvedPath.
457
nsLocalFile::ResolvePath(const char* workingPath, PRBool resolveTerminal, char** resolvedPath)
461
PRBool isDir = PR_FALSE;
463
// check to see if it is shortcut, i.e., it has ".lnk" in it
464
int filePathLen = strlen(workingPath);
465
PRBool isShortcut = IsShortcut(workingPath, filePathLen);
467
return NS_ERROR_FILE_INVALID_PATH;
470
printf("localfile - resolving symlink\n");
473
// Get the native path for |this|
474
// allocate extra bytes incase we need to append '\\' and '\0' to the
475
// workingPath, if it is just a drive letter and a colon
476
char *filePath = (char *) nsMemory::Alloc(filePathLen+2);
479
return NS_ERROR_NULL_POINTER;
481
memcpy(filePath, workingPath, filePathLen + 1);
483
// We are going to walk the native file path
484
// and stop at each slash. For each partial
485
// path (the string to the left of the slash)
486
// we will check to see if it is a shortcut.
487
// if it is, we will resolve it and continue
488
// with that resolved path.
490
// Get the first slash.
491
unsigned char* slash = _mbschr((unsigned char*) filePath, '\\');
495
if (nsCRT::IsAsciiAlpha(filePath[0]) && filePath[1] == ':' && filePath[2] == '\0')
497
// we have a drive letter and a colon (eg 'c:'
498
// this is resolve already
499
filePath[filePathLen] = '\\';
500
filePath[filePathLen+1] = '\0';
502
*resolvedPath = filePath;
507
nsMemory::Free(filePath);
508
return NS_ERROR_FILE_INVALID_PATH;
513
// We really cant have just a drive letter as
514
// a shortcut, so we will skip the first '\\'
515
slash = _mbschr(++slash, '\\');
517
while (slash || resolveTerminal)
519
// Change the slash into a null so that
520
// we can use the partial path. It is is
521
// null, we know it is the terminal node.
526
else if (resolveTerminal)
528
// this is our last time in this loop.
529
// set loop condition to false
530
resolveTerminal = PR_FALSE;
534
// something is wrong. we should not have
535
// both slash being null and resolveTerminal
537
nsMemory::Free(filePath);
538
return NS_ERROR_NULL_POINTER;
541
// check to see the file is a shortcut by the magic .lnk extension.
542
size_t offset = strlen(filePath) - 4;
543
if ((offset > 0) && (strnicmp( (filePath + offset), ".lnk", 4) == 0))
546
NS_CopyNativeToUnicode(nsDependentCString(filePath), ucsBuf);
547
char *temp = (char*) nsMemory::Alloc( MAX_PATH );
550
nsMemory::Free(filePath);
551
return NS_ERROR_NULL_POINTER;
555
rv = gResolver->Resolve(ucsBuf.get(), temp);
557
rv = NS_ERROR_FAILURE;
559
if (NS_SUCCEEDED(rv))
563
// addend a slash on it since it does not come out of GetPath()
564
// with one only if it is a directory. If it is not a directory
565
// and there is more to append, than we have a problem.
568
int statrv = stat(temp, &st);
570
if (0 == statrv && (_S_IFDIR & st.st_mode))
572
// For root directory slash is already appended
573
// XXX not multibyte safe
574
if (temp[strlen(temp) - 1] != '\\')
582
// save where we left off.
583
char *carot= (temp + strlen(temp) -1 );
585
// append all the stuff that we have not done.
586
_mbscat((unsigned char*)temp, ++slash);
588
slash = (unsigned char*)carot;
591
nsMemory::Free(filePath);
597
// could not resolve shortcut. Return error;
598
nsMemory::Free(filePath);
599
return NS_ERROR_FILE_INVALID_PATH;
606
slash = _mbschr(slash, '\\');
610
// kill any trailing separator
611
char* temp = filePath;
612
int len = strlen(temp) - 1;
613
if((temp[len] == '\\') && (!isDir))
616
*resolvedPath = filePath;
621
nsLocalFile::ResolveAndStat(PRBool resolveTerminal)
623
if (!mDirty && mLastResolution == resolveTerminal)
627
mLastResolution = resolveTerminal;
628
mResolvedPath.Assign(mWorkingPath); //until we know better.
630
// First we will see if the workingPath exists. If it does, then we
631
// can simply use that as the resolved path. This simplification can
632
// be done on windows cause its symlinks (shortcuts) use the .lnk
636
const char* workingFilePath = mWorkingPath.get();
637
const char* nsprPath = workingFilePath;
639
if (mWorkingPath.Length() == 2 && mWorkingPath.CharAt(1) == ':') {
640
temp[0] = workingFilePath[0];
641
temp[1] = workingFilePath[1];
647
PRStatus status = PR_GetFileInfo64(nsprPath, &mFileInfo64);
648
if ( status == PR_SUCCESS )
650
if (!resolveTerminal)
656
// check to see that this is shortcut, i.e., the leaf is ".lnk"
657
// if the length < 4, then it's not a link.
659
int pathLen = strlen(workingFilePath);
660
const char* leaf = workingFilePath + pathLen - 4;
662
// if we found the file and we are not following symlinks, then return success.
664
if (!mFollowSymlinks || pathLen < 4 || (stricmp(leaf, ".lnk") != 0))
671
if (!mFollowSymlinks)
672
return NS_ERROR_FILE_NOT_FOUND; // if we are not resolving, we just give up here.
676
// okay, something is wrong with the working path. We will try to resolve it.
680
result = ResolvePath(workingFilePath, resolveTerminal, &resolvePath);
681
if (NS_FAILED(result))
682
return NS_ERROR_FILE_NOT_FOUND;
684
mResolvedPath.Assign(resolvePath);
685
nsMemory::Free(resolvePath);
687
status = PR_GetFileInfo64(mResolvedPath.get(), &mFileInfo64);
689
if ( status == PR_SUCCESS )
692
result = NS_ERROR_FILE_NOT_FOUND;
698
//-----------------------------------------------------------------------------
699
// nsLocalFile::nsIFile,nsILocalFile
700
//-----------------------------------------------------------------------------
703
nsLocalFile::Clone(nsIFile **file)
705
// Just copy-construct ourselves
706
*file = new nsLocalFile(*this);
708
return NS_ERROR_OUT_OF_MEMORY;
716
nsLocalFile::InitWithNativePath(const nsACString &filePath)
720
nsACString::const_iterator begin, end;
721
filePath.BeginReading(begin);
722
filePath.EndReading(end);
724
// input string must not be empty
726
return NS_ERROR_FAILURE;
728
char firstChar = *begin;
729
char secondChar = *(++begin);
731
// just do a sanity check. if it has any forward slashes, it is not a Native path
732
// on windows. Also, it must have a colon at after the first char.
737
if ( ( (secondChar == ':') && !FindCharInReadable('/', begin, end) ) || // normal path
738
( (firstChar == '\\') && (secondChar == '\\') ) ) // network path
740
// This is a native path
741
path = ToNewCString(filePath);
742
pathLen = filePath.Length();
746
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
748
// kill any trailing '\' provided it isn't the second char of DBCS
749
PRInt32 len = pathLen - 1;
750
if (path[len] == '\\' && (!::IsDBCSLeadByte(path[len-1]) ||
751
_mbsrchr((const unsigned char *)path, '\\') == (const unsigned char *)path+len))
757
mWorkingPath.Adopt(path, pathLen);
762
nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
764
// check to see if it is shortcut, i.e., it has ".lnk" in it
765
PRBool isShortcut = IsShortcut(mWorkingPath.get(), mWorkingPath.Length());
767
if (!isShortcut && mDirty)
769
// we will optimize here. If we are not a shortcut and we are opening
770
// a file and we are still dirty, assume that the working path is vaild
771
// and try to open it. The working path will be different from its
772
// resolved path for a shortcut file.
773
// If it does work, get the stat info via the file descriptor
774
mResolvedPath.Assign(mWorkingPath);
775
*_retval = PR_Open(mResolvedPath.get(), flags, mode);
778
PRStatus status = PR_GetOpenFileInfo64(*_retval, &mFileInfo64);
779
if (status == PR_SUCCESS)
782
mLastResolution = PR_TRUE;
785
NS_ERROR("FileInfo64 invalid while PR_Open succeeded.");
790
nsresult rv = ResolveAndStat(PR_TRUE);
791
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
794
*_retval = PR_Open(mResolvedPath.get(), flags, mode);
799
return NS_ErrorAccordingToNSPR();
804
nsLocalFile::OpenANSIFileDesc(const char *mode, FILE * *_retval)
806
nsresult rv = ResolveAndStat(PR_TRUE);
807
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
810
*_retval = fopen(mResolvedPath.get(), mode);
815
return NS_ERROR_FAILURE;
821
nsLocalFile::Create(PRUint32 type, PRUint32 attributes)
823
if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
824
return NS_ERROR_FILE_UNKNOWN_TYPE;
826
nsresult rv = ResolveAndStat(PR_FALSE);
827
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
830
// create directories to target
832
// A given local file can be either one of these forms:
834
// - normal: X:\some\path\on\this\drive
837
// - UNC path: \\machine\volume\some\path\on\this\drive
840
// Skip the first 'X:\' for the first form, and skip the first full
841
// '\\machine\volume\' segment for the second form.
843
const unsigned char* path = (const unsigned char*) mResolvedPath.get();
844
if (path[0] == '\\' && path[1] == '\\')
846
// dealing with a UNC path here; skip past '\\machine\'
847
path = _mbschr(path + 2, '\\');
849
return NS_ERROR_FILE_INVALID_PATH;
853
// search for first slash after the drive (or volume) name
854
unsigned char* slash = _mbschr(path, '\\');
858
// skip the first '\\'
860
slash = _mbschr(slash, '\\');
866
if (!CreateDirectoryA(mResolvedPath.get(), NULL)) {
867
rv = ConvertWinError(GetLastError());
868
// perhaps the base path already exists, or perhaps we don't have
869
// permissions to create the directory. NOTE: access denied could
870
// occur on a parent directory even though it exists.
871
if (rv != NS_ERROR_FILE_ALREADY_EXISTS &&
872
rv != NS_ERROR_FILE_ACCESS_DENIED)
877
slash = _mbschr(slash, '\\');
881
if (type == NORMAL_FILE_TYPE)
883
PRFileDesc* file = PR_Open(mResolvedPath.get(), PR_RDONLY | PR_CREATE_FILE | PR_APPEND | PR_EXCL, attributes);
884
if (!file) return NS_ERROR_FILE_ALREADY_EXISTS;
890
if (type == DIRECTORY_TYPE)
892
if (!CreateDirectoryA(mResolvedPath.get(), NULL))
893
return ConvertWinError(GetLastError());
898
return NS_ERROR_FILE_UNKNOWN_TYPE;
902
nsLocalFile::AppendNative(const nsACString &node)
907
// Append only one component. Check for subdirs.
908
// XXX can we avoid the PromiseFlatCString call?
909
if (_mbschr((const unsigned char*) PromiseFlatCString(node).get(), '\\') != nsnull)
910
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
912
return AppendRelativeNativePath(node);
916
nsLocalFile::AppendRelativeNativePath(const nsACString &node)
918
// Cannot start with a / or have .. or have / anywhere
919
nsACString::const_iterator begin, end;
920
node.BeginReading(begin);
921
node.EndReading(end);
922
if (node.IsEmpty() || FindCharInReadable('/', begin, end))
924
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
927
mWorkingPath.Append(NS_LITERAL_CSTRING("\\") + node);
932
nsLocalFile::Normalize()
938
nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
940
aLeafName.Truncate();
942
const char* temp = mWorkingPath.get();
944
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
946
const char* leaf = (const char*) _mbsrchr((const unsigned char*) temp, '\\');
948
// if the working path is just a node without any lashes.
954
aLeafName.Assign(leaf);
959
nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
963
const unsigned char* temp = (const unsigned char*) mWorkingPath.get();
965
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
967
// cannot use nsCString::RFindChar() due to 0x5c problem
968
PRInt32 offset = (PRInt32) (_mbsrchr(temp, '\\') - temp);
971
mWorkingPath.Truncate(offset+1);
973
mWorkingPath.Append(aLeafName);
980
nsLocalFile::GetNativePath(nsACString &_retval)
982
_retval = mWorkingPath;
987
nsLocalFile::CopySingleFile(nsIFile *sourceFile, nsIFile *destParent, const nsACString &newName, PRBool followSymlinks, PRBool move)
990
nsCAutoString filePath;
992
// get the path that we are going to copy to.
993
// Since windows does not know how to auto
994
// resolve shortcust, we must work with the
996
nsCAutoString destPath;
997
destParent->GetNativeTarget(destPath);
999
destPath.Append("\\");
1001
if (newName.IsEmpty())
1003
nsCAutoString aFileName;
1004
sourceFile->GetNativeLeafName(aFileName);
1005
destPath.Append(aFileName);
1009
destPath.Append(newName);
1015
rv = sourceFile->GetNativeTarget(filePath);
1016
if (filePath.IsEmpty())
1017
rv = sourceFile->GetNativePath(filePath);
1021
rv = sourceFile->GetNativePath(filePath);
1030
copyOK = CopyFile(filePath.get(), destPath.get(), PR_TRUE);
1033
// What we have to do is check to see if the destPath exists. If it
1034
// does, we have to move it out of the say so that MoveFile will
1035
// succeed. However, we don't want to just remove it since MoveFile
1036
// can fail leaving us without a file.
1038
nsCAutoString backup;
1039
PRFileInfo64 fileInfo64;
1040
PRStatus status = PR_GetFileInfo64(destPath.get(), &fileInfo64);
1041
if (status == PR_SUCCESS)
1044
// the file exists. Check to make sure it is not a directory,
1045
// then move it out of the way.
1046
if (fileInfo64.type == PR_FILE_FILE)
1048
backup.Append(destPath);
1049
backup.Append(".moztmp");
1051
// remove any existing backup file that we may already have.
1052
// maybe we should be doing some kind of unique naming here,
1054
remove(backup.get());
1056
// move destination file to backup file
1057
copyOK = MoveFile(destPath.get(), backup.get());
1060
// I guess we can't do the backup copy, so return.
1061
rv = ConvertWinError(GetLastError());
1066
// move source file to destination file
1067
copyOK = MoveFile(filePath.get(), destPath.get());
1069
if (!backup.IsEmpty())
1073
// remove the backup copy.
1074
remove(backup.get());
1079
int backupOk = MoveFile(backup.get(), destPath.get());
1080
NS_ASSERTION(backupOk, "move backup failed");
1084
if (!copyOK) // CopyFile and MoveFile returns non-zero if succeeds (backward if you ask me).
1085
rv = ConvertWinError(GetLastError());
1092
nsLocalFile::CopyMove(nsIFile *aParentDir, const nsACString &newName, PRBool followSymlinks, PRBool move)
1094
nsCOMPtr<nsIFile> newParentDir = aParentDir;
1095
// check to see if this exists, otherwise return an error.
1096
// we will check this by resolving. If the user wants us
1097
// to follow links, then we are talking about the target,
1098
// hence we can use the |followSymlinks| parameter.
1099
nsresult rv = ResolveAndStat(followSymlinks);
1105
// no parent was specified. We must rename.
1107
if (newName.IsEmpty())
1108
return NS_ERROR_INVALID_ARG;
1110
rv = GetParent(getter_AddRefs(newParentDir));
1116
return NS_ERROR_FILE_DESTINATION_NOT_DIR;
1118
// make sure it exists and is a directory. Create it if not there.
1120
newParentDir->Exists(&exists);
1123
rv = newParentDir->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
1130
newParentDir->IsDirectory(&isDir);
1131
if (isDir == PR_FALSE)
1136
newParentDir->IsSymlink(&isLink);
1139
nsCAutoString target;
1140
newParentDir->GetNativeTarget(target);
1142
nsCOMPtr<nsILocalFile> realDest = new nsLocalFile();
1143
if (realDest == nsnull)
1144
return NS_ERROR_OUT_OF_MEMORY;
1146
rv = realDest->InitWithNativePath(target);
1151
return CopyMove(realDest, newName, followSymlinks, move);
1156
return NS_ERROR_FILE_DESTINATION_NOT_DIR;
1161
// check to see if we are a directory, if so enumerate it.
1164
IsDirectory(&isDir);
1166
IsSymlink(&isSymlink);
1168
if (!isDir || (isSymlink && !followSymlinks))
1170
rv = CopySingleFile(this, newParentDir, newName, followSymlinks, move);
1176
// create a new target destination in the new parentDir;
1177
nsCOMPtr<nsIFile> target;
1178
rv = newParentDir->Clone(getter_AddRefs(target));
1183
nsCAutoString allocatedNewName;
1184
if (newName.IsEmpty())
1191
GetNativeTarget(temp);
1192
const char* leaf = (const char*) _mbsrchr((const unsigned char*) temp.get(), '\\');
1193
if (leaf[0] == '\\')
1195
allocatedNewName = leaf;
1199
GetNativeLeafName(allocatedNewName);// this should be the leaf name of the
1204
allocatedNewName = newName;
1207
rv = target->AppendNative(allocatedNewName);
1211
allocatedNewName.Truncate();
1213
// check if the destination directory already exists
1214
target->Exists(&exists);
1217
// if the destination directory cannot be created, return an error
1218
rv = target->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
1224
// check if the destination directory is writable and empty
1227
target->IsWritable(&isWritable);
1229
return NS_ERROR_FILE_ACCESS_DENIED;
1231
nsCOMPtr<nsISimpleEnumerator> targetIterator;
1232
rv = target->GetDirectoryEntries(getter_AddRefs(targetIterator));
1235
targetIterator->HasMoreElements(&more);
1236
// return error if target directory is not empty
1238
return NS_ERROR_FILE_DIR_NOT_EMPTY;
1241
nsDirEnumerator dirEnum;
1243
rv = dirEnum.Init(this);
1244
if (NS_FAILED(rv)) {
1245
NS_WARNING("dirEnum initalization failed");
1250
while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
1252
nsCOMPtr<nsISupports> item;
1253
nsCOMPtr<nsIFile> file;
1254
dirEnum.GetNext(getter_AddRefs(item));
1255
file = do_QueryInterface(item);
1258
PRBool isDir, isLink;
1260
file->IsDirectory(&isDir);
1261
file->IsSymlink(&isLink);
1266
return NS_ERROR_FAILURE;
1268
rv = file->MoveToNative(target, nsCString());
1269
NS_ENSURE_SUCCESS(rv,rv);
1274
rv = file->CopyToFollowingLinksNative(target, nsCString());
1276
rv = file->CopyToNative(target, nsCString());
1277
NS_ENSURE_SUCCESS(rv,rv);
1281
// we've finished moving all the children of this directory
1282
// in the new directory. so now delete the directory
1283
// note, we don't need to do a recursive delete.
1284
// MoveTo() is recursive. At this point,
1285
// we've already moved the children of the current folder
1286
// to the new location. nothing should be left in the folder.
1289
rv = Remove(PR_FALSE /* recursive */);
1290
NS_ENSURE_SUCCESS(rv,rv);
1295
// If we moved, we want to adjust this.
1300
nsCAutoString newParentPath;
1301
newParentDir->GetNativePath(newParentPath);
1303
if (newParentPath.IsEmpty())
1304
return NS_ERROR_FAILURE;
1306
if (newName.IsEmpty())
1308
nsCAutoString aFileName;
1309
GetNativeLeafName(aFileName);
1311
InitWithNativePath(newParentPath);
1312
AppendNative(aFileName);
1316
InitWithNativePath(newParentPath);
1317
AppendNative(newName);
1325
nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString &newName)
1327
return CopyMove(newParentDir, newName, PR_FALSE, PR_FALSE);
1331
nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString &newName)
1333
return CopyMove(newParentDir, newName, PR_TRUE, PR_FALSE);
1337
nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString &newName)
1339
return CopyMove(newParentDir, newName, PR_FALSE, PR_TRUE);
1343
nsLocalFile::Load(PRLibrary * *_retval)
1346
nsresult rv = IsFile(&isFile);
1352
return NS_ERROR_FILE_IS_DIRECTORY;
1354
NS_TIMELINE_START_TIMER("PR_LoadLibrary");
1355
*_retval = PR_LoadLibrary(mResolvedPath.get());
1356
NS_TIMELINE_STOP_TIMER("PR_LoadLibrary");
1357
NS_TIMELINE_MARK_TIMER1("PR_LoadLibrary", mResolvedPath.get());
1362
return NS_ERROR_NULL_POINTER;
1366
nsLocalFile::Remove(PRBool recursive)
1369
PRBool isDir, isLink;
1371
const char *filePath;
1375
// if the working path points to a shortcut, then we will only
1376
// delete the shortcut itself. even if the shortcut points to
1377
// a directory, we will not recurse into that directory or
1378
// delete that directory itself. likewise, if the shortcut
1379
// points to a normal file, we will not delete the real file.
1380
// this is done to be consistent with the other platforms that
1381
// behave this way. we do this even if the followLinks attribute
1382
// is set to true. this helps protect against misuse that could
1383
// lead to security bugs (e.g., bug 210588).
1385
// in the case of non-terminal shortcuts, those are all followed
1386
// unconditionally. this is done because 1) we only delete
1387
// terminal nodes and possibly their children, and 2) the remove
1388
// and rmdir CRT calls don't know how to handle shortcuts.
1394
// resolve non-terminal nodes only.
1395
rv = ResolvePath(mWorkingPath.get(), PR_FALSE, getter_Copies(buf));
1398
filePath = buf.get();
1402
rv = IsDirectory(&isDir);
1405
// in this case, it doesn't matter that terminal nodes were
1406
// resolved, so we can safely leverage mResolvedPath.
1407
filePath = mResolvedPath.get();
1414
nsDirEnumerator dirEnum;
1416
rv = dirEnum.Init(this);
1421
while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
1423
nsCOMPtr<nsISupports> item;
1424
dirEnum.GetNext(getter_AddRefs(item));
1425
nsCOMPtr<nsIFile> file = do_QueryInterface(item);
1427
file->Remove(recursive);
1430
rv = rmdir(filePath);
1434
rv = remove(filePath);
1437
// fixup error code if necessary...
1439
rv = NSRESULT_FOR_ERRNO();
1446
nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime)
1448
NS_ENSURE_ARG(aLastModifiedTime);
1450
*aLastModifiedTime = 0;
1452
nsresult rv = ResolveAndStat(PR_TRUE);
1457
// microseconds -> milliseconds
1458
*aLastModifiedTime = mFileInfo64.modifyTime / PR_USEC_PER_MSEC;
1464
nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTime)
1466
NS_ENSURE_ARG(aLastModifiedTime);
1468
*aLastModifiedTime = 0;
1470
nsresult rv = ResolveAndStat(PR_FALSE);
1475
// microseconds -> milliseconds
1476
*aLastModifiedTime = mFileInfo64.modifyTime / PR_USEC_PER_MSEC;
1483
nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime)
1485
return nsLocalFile::SetModDate(aLastModifiedTime, PR_TRUE);
1490
nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTime)
1492
return nsLocalFile::SetModDate(aLastModifiedTime, PR_FALSE);
1496
nsLocalFile::SetModDate(PRInt64 aLastModifiedTime, PRBool resolveTerminal)
1498
nsresult rv = ResolveAndStat(resolveTerminal);
1503
const char *filePath = mResolvedPath.get();
1505
HANDLE file = CreateFile( filePath, // pointer to name of the file
1506
GENERIC_WRITE, // access (write) mode
1508
NULL, // pointer to security attributes
1509
OPEN_EXISTING, // how to create
1510
0, // file attributes
1517
return ConvertWinError(GetLastError());
1522
PRExplodedTime pret;
1524
// PR_ExplodeTime expects usecs...
1525
PR_ExplodeTime(aLastModifiedTime * PR_USEC_PER_MSEC, PR_LocalTimeParameters, &pret);
1526
st.wYear = pret.tm_year;
1527
st.wMonth = pret.tm_month + 1; // Convert start offset -- Win32: Jan=1; NSPR: Jan=0
1528
st.wDayOfWeek = pret.tm_wday;
1529
st.wDay = pret.tm_mday;
1530
st.wHour = pret.tm_hour;
1531
st.wMinute = pret.tm_min;
1532
st.wSecond = pret.tm_sec;
1533
st.wMilliseconds = pret.tm_usec/1000;
1535
if ( 0 == SystemTimeToFileTime(&st, &lft) )
1537
rv = ConvertWinError(GetLastError());
1539
else if ( 0 == LocalFileTimeToFileTime(&lft, &ft) )
1541
rv = ConvertWinError(GetLastError());
1543
else if ( 0 == SetFileTime(file, NULL, &ft, &ft) )
1545
// could not set time
1546
rv = ConvertWinError(GetLastError());
1549
CloseHandle( file );
1554
nsLocalFile::GetPermissions(PRUint32 *aPermissions)
1556
nsresult rv = ResolveAndStat(PR_TRUE);
1561
const char *filePath = mResolvedPath.get();
1568
nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
1570
return NS_ERROR_NOT_IMPLEMENTED;
1575
nsLocalFile::SetPermissions(PRUint32 aPermissions)
1577
nsresult rv = ResolveAndStat(PR_TRUE);
1582
const char *filePath = mResolvedPath.get();
1583
if( chmod(filePath, aPermissions) == -1 )
1584
return NS_ERROR_FAILURE;
1590
nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
1592
nsresult rv = ResolveAndStat(PR_FALSE);
1597
const char *filePath = mResolvedPath.get();
1598
if( chmod(filePath, aPermissions) == -1 )
1599
return NS_ERROR_FAILURE;
1606
nsLocalFile::GetFileSize(PRInt64 *aFileSize)
1608
NS_ENSURE_ARG(aFileSize);
1612
nsresult rv = ResolveAndStat(PR_TRUE);
1618
*aFileSize = mFileInfo64.size;
1624
nsLocalFile::SetFileSize(PRInt64 aFileSize)
1630
nsresult rv = ResolveAndStat(PR_TRUE);
1635
const char *filePath = mResolvedPath.get();
1638
// Leave it to Microsoft to open an existing file with a function
1639
// named "CreateFile".
1640
hFile = CreateFile(filePath,
1645
FILE_ATTRIBUTE_NORMAL,
1648
if (hFile == INVALID_HANDLE_VALUE)
1651
return NS_ERROR_FAILURE;
1654
// Seek to new, desired end of file
1656
myLL_L2II(aFileSize, &hi, &lo );
1658
status = SetFilePointer(hFile, lo, NULL, FILE_BEGIN);
1659
if (status == 0xffffffff)
1662
// Truncate file at current cursor position
1663
if (!SetEndOfFile(hFile))
1666
if (!CloseHandle(hFile))
1667
return NS_ERROR_FAILURE;
1675
return NS_ERROR_FAILURE;
1679
nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
1681
NS_ENSURE_ARG(aFileSize);
1685
nsresult rv = ResolveAndStat(PR_FALSE);
1690
*aFileSize = mFileInfo64.size;
1695
nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
1697
NS_ENSURE_ARG(aDiskSpaceAvailable);
1699
ResolveAndStat(PR_FALSE);
1703
LL_I2L(int64 , LONG_MAX);
1706
DWORD dwSecPerClus, dwBytesPerSec, dwFreeClus, dwTotalClus;
1707
ULARGE_INTEGER liFreeBytesAvailableToCaller, liTotalNumberOfBytes, liTotalNumberOfFreeBytes;
1710
BOOL (WINAPI* getDiskFreeSpaceExA)(LPCTSTR lpDirectoryName,
1711
PULARGE_INTEGER lpFreeBytesAvailableToCaller,
1712
PULARGE_INTEGER lpTotalNumberOfBytes,
1713
PULARGE_INTEGER lpTotalNumberOfFreeBytes) = NULL;
1715
HINSTANCE hInst = LoadLibrary("KERNEL32.DLL");
1716
NS_ASSERTION(hInst != NULL, "COULD NOT LOAD KERNEL32.DLL");
1719
getDiskFreeSpaceExA = (BOOL (WINAPI*)(LPCTSTR lpDirectoryName,
1720
PULARGE_INTEGER lpFreeBytesAvailableToCaller,
1721
PULARGE_INTEGER lpTotalNumberOfBytes,
1722
PULARGE_INTEGER lpTotalNumberOfFreeBytes))
1723
GetProcAddress(hInst, "GetDiskFreeSpaceExA");
1727
if (getDiskFreeSpaceExA && (*getDiskFreeSpaceExA)(mResolvedPath.get(),
1728
&liFreeBytesAvailableToCaller,
1729
&liTotalNumberOfBytes,
1730
&liTotalNumberOfFreeBytes))
1732
nBytes = (double)(signed __int64)liFreeBytesAvailableToCaller.QuadPart;
1735
char aDrive[_MAX_DRIVE + 2];
1736
_splitpath( mResolvedPath.get(), aDrive, NULL, NULL, NULL);
1737
strcat(aDrive, "\\");
1739
if ( GetDiskFreeSpace(aDrive, &dwSecPerClus, &dwBytesPerSec, &dwFreeClus, &dwTotalClus))
1741
nBytes = (double)dwFreeClus*(double)dwSecPerClus*(double) dwBytesPerSec;
1744
LL_D2L(*aDiskSpaceAvailable, nBytes);
1751
nsLocalFile::GetParent(nsIFile * *aParent)
1753
NS_ENSURE_ARG_POINTER(aParent);
1755
nsCAutoString parentPath(mWorkingPath);
1757
// cannot use nsCString::RFindChar() due to 0x5c problem
1758
PRInt32 offset = (PRInt32) (_mbsrchr((const unsigned char *) parentPath.get(), '\\')
1759
- (const unsigned char *) parentPath.get());
1761
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
1763
parentPath.Truncate(offset);
1765
nsCOMPtr<nsILocalFile> localFile;
1766
nsresult rv = NS_NewNativeLocalFile(parentPath, mFollowSymlinks, getter_AddRefs(localFile));
1768
if(NS_SUCCEEDED(rv) && localFile)
1770
return CallQueryInterface(localFile, aParent);
1776
nsLocalFile::Exists(PRBool *_retval)
1778
NS_ENSURE_ARG(_retval);
1781
nsresult rv = ResolveAndStat( PR_TRUE );
1783
if (NS_SUCCEEDED(rv))
1786
*_retval = PR_FALSE;
1792
nsLocalFile::IsWritable(PRBool *_retval)
1794
NS_ENSURE_ARG(_retval);
1795
*_retval = PR_FALSE;
1797
nsresult rv = ResolveAndStat(PR_TRUE);
1802
const char *workingFilePath = mWorkingPath.get();
1803
DWORD word = GetFileAttributes(workingFilePath);
1805
*_retval = !((word & FILE_ATTRIBUTE_READONLY) != 0);
1811
nsLocalFile::IsReadable(PRBool *_retval)
1813
NS_ENSURE_ARG(_retval);
1814
*_retval = PR_FALSE;
1816
nsresult rv = ResolveAndStat( PR_TRUE );
1826
nsLocalFile::IsExecutable(PRBool *_retval)
1828
NS_ENSURE_ARG(_retval);
1829
*_retval = PR_FALSE;
1832
nsresult rv = ResolveAndStat( PR_TRUE );
1839
rv = IsSymlink(&symLink);
1844
GetNativeTarget(path);
1846
GetNativePath(path);
1849
char* ext = ::strrchr( path.BeginWriting(), '.' );
1851
// Convert extension to lower case.
1852
for( char *p = ext; *p; p++ ) {
1853
if ( ::isupper( *p ) ) {
1854
*p = ::tolower( *p );
1857
// Search for any of the set of executable extensions.
1858
const char * const executableExts[] = {
1904
for ( int i = 0; executableExts[i]; i++ ) {
1905
if ( ::strcmp( executableExts[i], ext ) == 0 ) {
1906
// Found a match. Set result and quit.
1918
nsLocalFile::IsDirectory(PRBool *_retval)
1920
NS_PRECONDITION(_retval, "null pointer");
1922
nsresult rv = ResolveAndStat(PR_TRUE);
1923
if (NS_FAILED(rv)) {
1924
*_retval = PR_FALSE;
1928
*_retval = (mFileInfo64.type == PR_FILE_DIRECTORY);
1933
nsLocalFile::IsFile(PRBool *_retval)
1935
NS_ENSURE_ARG(_retval);
1936
*_retval = PR_FALSE;
1938
nsresult rv = ResolveAndStat(PR_TRUE);
1943
*_retval = (mFileInfo64.type == PR_FILE_FILE);
1948
nsLocalFile::IsHidden(PRBool *_retval)
1950
NS_ENSURE_ARG(_retval);
1951
*_retval = PR_FALSE;
1953
nsresult rv = ResolveAndStat(PR_TRUE);
1959
if (mFollowSymlinks)
1961
const char *resolvedFilePath = mResolvedPath.get();
1962
word = GetFileAttributes(resolvedFilePath);
1966
const char *workingFilePath = mWorkingPath.get();
1967
word = GetFileAttributes(workingFilePath);
1970
*_retval = ((word & FILE_ATTRIBUTE_HIDDEN) != 0);
1976
nsLocalFile::IsSymlink(PRBool *_retval)
1978
NS_PRECONDITION(_retval, "null pointer");
1980
PRUint32 len = mWorkingPath.Length();
1982
*_retval = PR_FALSE;
1984
const char* leaf = mWorkingPath.get() + len - 4;
1985
*_retval = (strnicmp(leaf, ".lnk", 4) == 0);
1991
nsLocalFile::IsSpecial(PRBool *_retval)
1993
NS_ENSURE_ARG(_retval);
1994
*_retval = PR_FALSE;
1996
nsresult rv = ResolveAndStat(PR_TRUE);
2001
const char *workingFilePath = mWorkingPath.get();
2002
DWORD word = GetFileAttributes(workingFilePath);
2004
*_retval = ((word & FILE_ATTRIBUTE_SYSTEM) != 0);
2010
nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
2012
NS_ENSURE_ARG(inFile);
2013
NS_ENSURE_ARG(_retval);
2015
nsCAutoString inFilePath;
2016
inFile->GetNativePath(inFilePath);
2018
*_retval = inFilePath.Equals(mWorkingPath);
2023
nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
2025
*_retval = PR_FALSE;
2027
nsCAutoString myFilePath;
2028
if ( NS_FAILED(GetNativeTarget(myFilePath)))
2029
GetNativePath(myFilePath);
2031
PRInt32 myFilePathLen = myFilePath.Length();
2033
nsCAutoString inFilePath;
2034
if ( NS_FAILED(inFile->GetNativeTarget(inFilePath)))
2035
inFile->GetNativePath(inFilePath);
2037
if ( strnicmp( myFilePath.get(), inFilePath.get(), myFilePathLen) == 0)
2039
// now make sure that the |inFile|'s path has a trailing
2042
if (inFilePath[myFilePathLen] == '\\')
2055
nsLocalFile::GetNativeTarget(nsACString &_retval)
2058
#if STRICT_FAKE_SYMLINKS
2061
nsresult rv = IsSymlink(&symLink);
2067
return NS_ERROR_FILE_INVALID_PATH;
2070
ResolveAndStat(PR_TRUE);
2072
_retval = mResolvedPath;
2077
/* attribute PRBool followLinks; */
2079
nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
2081
*aFollowLinks = mFollowSymlinks;
2085
nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
2088
mFollowSymlinks = aFollowLinks;
2094
nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator * *entries)
2101
rv = IsDirectory(&isDir);
2105
return NS_ERROR_FILE_NOT_DIRECTORY;
2107
nsDirEnumerator* dirEnum = new nsDirEnumerator();
2108
if (dirEnum == nsnull)
2109
return NS_ERROR_OUT_OF_MEMORY;
2111
rv = dirEnum->Init(this);
2114
NS_RELEASE(dirEnum);
2123
nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
2125
return GetNativePath(aPersistentDescriptor);
2129
nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
2131
return InitWithNativePath(aPersistentDescriptor);
2135
nsLocalFile::Reveal()
2137
nsresult rv = NS_OK;
2138
PRBool isDirectory = PR_FALSE;
2140
nsAutoString unicodePath;
2142
IsDirectory(&isDirectory);
2145
GetNativePath(path);
2149
nsCOMPtr<nsIFile> parent;
2150
GetParent(getter_AddRefs(parent));
2153
parent->GetNativePath(path);
2154
parent->GetPath(unicodePath);
2158
// Remember the current fg window.
2159
HWND origWin, fgWin;
2160
origWin = fgWin = ::GetForegroundWindow();
2162
// use the app registry name to launch a shell execute....
2163
LONG r = (LONG) ::ShellExecute( NULL, "open", path.get(), NULL, NULL, SW_SHOWNORMAL);
2165
return NS_ERROR_FAILURE;
2167
// If this is a directory, then we don't need to select a file.
2171
// Resources we may need to free when done.
2172
IShellFolder *desktopFolder = 0;
2173
IMalloc *shellMalloc = 0;
2174
IShellFolder *folder = 0;
2175
LPITEMIDLIST folder_pidl = 0;
2176
LPITEMIDLIST file_pidl = 0;
2177
LPITEMIDLIST win95_file_pidl = 0;
2178
HMODULE shell32 = 0;
2180
// We break out of this do/while non-loop at any point where we have to give up.
2182
// Wait for the window to open. We wait a maximum of 2 seconds.
2183
// If we get the wrong window, that will be dealt with below.
2184
for (int iter = 10; iter; iter--)
2186
fgWin = ::GetForegroundWindow();
2187
if (fgWin != origWin)
2191
// If we failed to locate the new window, give up.
2192
if (origWin == fgWin)
2195
// Now we have the explorer window. We need to send it the "select item"
2196
// message (which isn't trivial, so buckly your seat belt)...
2198
// We need the explorer's process id.
2200
::GetWindowThreadProcessId(fgWin, &pid);
2202
// Get desktop folder.
2203
HRESULT rc = ::SHGetDesktopFolder(&desktopFolder);
2207
// Get IMalloc interface to use for shell pidls.
2208
rc = ::SHGetMalloc(&shellMalloc);
2212
// Convert folder path to pidl. This requires the Unicode path name.
2213
// It returns a pidl that must be freed via shellMalloc->Free.
2215
rc = desktopFolder->ParseDisplayName( 0,
2217
(LPOLESTR)unicodePath.get(),
2224
// Now get IShellFolder interface for the folder we opened.
2225
rc = desktopFolder->BindToObject( folder_pidl,
2232
// Now get file name pidl from that folder.
2233
nsAutoString unicodeLeaf;
2234
if (NS_FAILED(GetLeafName(unicodeLeaf)))
2236
rc = folder->ParseDisplayName( 0,
2238
(LPOLESTR)unicodeLeaf.get(),
2245
// We need the module handle for shell32.dll.
2246
shell32 = ::GetModuleHandle("shell32.dll");
2250
// Allocate shared memory copy of pidl. This uses the undocumented "SHAllocShared"
2251
// function. Note that it is freed automatically after the ::SendMessage so we
2252
// don't have to free it.
2253
static HANDLE(WINAPI*SHAllocShared)(LPVOID,ULONG,DWORD) = (HANDLE(WINAPI*)(LPVOID,ULONG,DWORD))::GetProcAddress(shell32, (LPCTSTR)520);
2254
HANDLE pidlHandle = 0;
2257
// We need the size of the pidl, which we get via another undocumented
2258
// API: "ILGetSize".
2259
UINT (WINAPI*ILGetSize)(LPCITEMIDLIST) = (UINT(WINAPI*)(LPCITEMIDLIST))::GetProcAddress(shell32, (LPCTSTR)152);
2262
pidlHandle = SHAllocShared((void*)(ITEMIDLIST*)file_pidl,
2263
ILGetSize(file_pidl),
2270
// On Win95, there is no SHAllocShared. Instead, we clone the file's pidl in
2271
// the shell's global heap (via ILGlobalClone) and pass that.
2272
LPITEMIDLIST(WINAPI*ILGlobalClone)(LPCITEMIDLIST) = (LPITEMIDLIST(WINAPI*)(LPCITEMIDLIST))::GetProcAddress(shell32, (LPCTSTR)20);
2275
win95_file_pidl = ILGlobalClone(file_pidl);
2276
if (!win95_file_pidl)
2278
// Arrange so that this pidl is passed on the ::SendMessage.
2279
pidlHandle = win95_file_pidl;
2282
// Send message to select this file.
2283
::SendMessage(fgWin,
2285
SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE,
2286
(LPARAM)pidlHandle );
2287
} while ( PR_FALSE );
2289
// Clean up (freeing stuff as needed, in reverse order).
2290
if (win95_file_pidl)
2292
// We need to free this using ILGlobalFree, another undocumented API.
2293
static void (WINAPI*ILGlobalFree)(LPCITEMIDLIST) = (void(WINAPI*)(LPCITEMIDLIST))::GetProcAddress(shell32,(LPCTSTR)156);
2295
ILGlobalFree(win95_file_pidl);
2298
shellMalloc->Free(file_pidl);
2300
shellMalloc->Free(folder_pidl);
2304
shellMalloc->Release();
2306
desktopFolder->Release();
2313
nsLocalFile::Launch()
2315
nsresult rv = NS_OK;
2316
const nsCString &path = mWorkingPath;
2318
// use the app registry name to launch a shell execute....
2319
LONG r = (LONG) ::ShellExecute( NULL, NULL, path.get(), NULL, NULL, SW_SHOWNORMAL);
2321
// if the file has no association, we launch windows' "what do you want to do" dialog
2322
if (r == SE_ERR_NOASSOC) {
2323
nsCAutoString shellArg;
2324
shellArg.Assign(NS_LITERAL_CSTRING("shell32.dll,OpenAs_RunDLL ") + path);
2325
r = (LONG) ::ShellExecute(NULL, NULL, "RUNDLL32.EXE", shellArg.get(),
2326
NULL, SW_SHOWNORMAL);
2332
return NS_ERROR_OUT_OF_MEMORY;
2333
case ERROR_FILE_NOT_FOUND:
2334
return NS_ERROR_FILE_NOT_FOUND;
2335
case ERROR_PATH_NOT_FOUND:
2336
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
2337
case ERROR_BAD_FORMAT:
2338
return NS_ERROR_FILE_CORRUPTED;
2339
case SE_ERR_ACCESSDENIED:
2340
return NS_ERROR_FILE_ACCESS_DENIED;
2341
case SE_ERR_ASSOCINCOMPLETE:
2342
case SE_ERR_NOASSOC:
2343
return NS_ERROR_UNEXPECTED;
2344
case SE_ERR_DDEBUSY:
2345
case SE_ERR_DDEFAIL:
2346
case SE_ERR_DDETIMEOUT:
2347
return NS_ERROR_NOT_AVAILABLE;
2348
case SE_ERR_DLLNOTFOUND:
2349
return NS_ERROR_FAILURE;
2351
return NS_ERROR_FILE_IS_LOCKED;
2353
return NS_ERROR_FILE_EXECUTION_FAILED;
2360
NS_NewNativeLocalFile(const nsACString &path, PRBool followLinks, nsILocalFile* *result)
2362
nsLocalFile* file = new nsLocalFile();
2364
return NS_ERROR_OUT_OF_MEMORY;
2367
file->SetFollowLinks(followLinks);
2369
if (!path.IsEmpty()) {
2370
nsresult rv = file->InitWithNativePath(path);
2371
if (NS_FAILED(rv)) {
2381
//-----------------------------------------------------------------------------
2383
//-----------------------------------------------------------------------------
2386
nsLocalFile::InitWithPath(const nsAString &filePath)
2389
nsresult rv = NS_CopyUnicodeToNative(filePath, tmp);
2390
if (NS_SUCCEEDED(rv))
2391
return InitWithNativePath(tmp);
2397
nsLocalFile::Append(const nsAString &node)
2400
nsresult rv = NS_CopyUnicodeToNative(node, tmp);
2401
if (NS_SUCCEEDED(rv))
2402
return AppendNative(tmp);
2408
nsLocalFile::AppendRelativePath(const nsAString &node)
2411
nsresult rv = NS_CopyUnicodeToNative(node, tmp);
2412
if (NS_SUCCEEDED(rv))
2413
return AppendRelativeNativePath(tmp);
2418
nsLocalFile::GetLeafName(nsAString &aLeafName)
2421
nsresult rv = GetNativeLeafName(tmp);
2422
if (NS_SUCCEEDED(rv))
2423
rv = NS_CopyNativeToUnicode(tmp, aLeafName);
2429
nsLocalFile::SetLeafName(const nsAString &aLeafName)
2432
nsresult rv = NS_CopyUnicodeToNative(aLeafName, tmp);
2433
if (NS_SUCCEEDED(rv))
2434
return SetNativeLeafName(tmp);
2440
nsLocalFile::GetPath(nsAString &_retval)
2442
return NS_CopyNativeToUnicode(mWorkingPath, _retval);
2446
nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
2448
if (newName.IsEmpty())
2449
return CopyToNative(newParentDir, nsCString());
2452
nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
2453
if (NS_SUCCEEDED(rv))
2454
return CopyToNative(newParentDir, tmp);
2460
nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
2462
if (newName.IsEmpty())
2463
return CopyToFollowingLinksNative(newParentDir, nsCString());
2466
nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
2467
if (NS_SUCCEEDED(rv))
2468
return CopyToFollowingLinksNative(newParentDir, tmp);
2474
nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
2476
if (newName.IsEmpty())
2477
return MoveToNative(newParentDir, nsCString());
2480
nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
2481
if (NS_SUCCEEDED(rv))
2482
return MoveToNative(newParentDir, tmp);
2488
nsLocalFile::GetTarget(nsAString &_retval)
2491
nsresult rv = GetNativeTarget(tmp);
2492
if (NS_SUCCEEDED(rv))
2493
rv = NS_CopyNativeToUnicode(tmp, _retval);
2499
NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
2502
nsresult rv = NS_CopyUnicodeToNative(path, buf);
2503
if (NS_FAILED(rv)) {
2507
return NS_NewNativeLocalFile(buf, followLinks, result);
2510
//-----------------------------------------------------------------------------
2511
// nsLocalFile <static members>
2512
//-----------------------------------------------------------------------------
2515
nsLocalFile::GlobalInit()
2517
nsresult rv = NS_CreateShortcutResolver();
2518
NS_ASSERTION(NS_SUCCEEDED(rv), "Shortcut resolver could not be created");
2522
nsLocalFile::GlobalShutdown()
2524
NS_DestroyShortcutResolver();