VirtualBox

source: vbox/trunk/src/libs/xpcom18a4/xpcom/io/nsLocalFileWin.cpp@ 78394

Last change on this file since 78394 was 62374, checked in by vboxsync, 8 years ago

xpcom: add missing breaks to ConvertWinError, ConvertOS2Error. This
code is not used, but keep static analyzers happy.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 67.3 KB
Line 
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is Mozilla Communicator client code, released
16 * March 31, 1998.
17 *
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998-1999
21 * the Initial Developer. All Rights Reserved.
22 *
23 * Contributor(s):
24 * Doug Turner <[email protected]>
25 * Dean Tessman <[email protected]>
26 * Brodie Thiesfield <[email protected]>
27 *
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
39 *
40 * ***** END LICENSE BLOCK ***** */
41
42
43#include "nsCOMPtr.h"
44#include "nsMemory.h"
45
46#include "nsLocalFile.h"
47#include "nsNativeCharsetUtils.h"
48
49#include "nsISimpleEnumerator.h"
50#include "nsIComponentManager.h"
51#include "prtypes.h"
52#include "prio.h"
53
54#include "nsXPIDLString.h"
55#include "nsReadableUtils.h"
56
57#include <direct.h>
58#include <windows.h>
59
60#include "shellapi.h"
61#include "shlguid.h"
62
63#include <io.h>
64#include <stdio.h>
65#include <stdlib.h>
66#include <mbstring.h>
67
68#include "nsXPIDLString.h"
69#include "prproces.h"
70#include "nsITimelineService.h"
71
72#include "nsAutoLock.h"
73#include "SpecialSystemDirectory.h"
74
75// _mbsstr isn't declared in w32api headers but it's there in the libs
76#ifdef __MINGW32__
77extern "C" {
78unsigned char *_mbsstr( const unsigned char *str,
79 const unsigned char *substr );
80}
81#endif
82
83class nsDriveEnumerator : public nsISimpleEnumerator
84{
85public:
86 nsDriveEnumerator();
87 virtual ~nsDriveEnumerator();
88 NS_DECL_ISUPPORTS
89 NS_DECL_NSISIMPLEENUMERATOR
90 nsresult Init();
91private:
92 /* mDrives and mLetter share data
93 * Init sets them.
94 * HasMoreElements reads mLetter.
95 * GetNext advances mLetter.
96 */
97 nsCString mDrives;
98 const char *mLetter;
99};
100
101//----------------------------------------------------------------------------
102// short cut resolver
103//----------------------------------------------------------------------------
104
105class ShortcutResolver
106{
107public:
108 ShortcutResolver();
109 // nonvirtual since we're not subclassed
110 ~ShortcutResolver();
111
112 nsresult Init();
113 nsresult Resolve(const WCHAR* in, char* out);
114
115private:
116 PRLock* mLock;
117 IPersistFile* mPersistFile;
118 IShellLink* mShellLink;
119};
120
121ShortcutResolver::ShortcutResolver()
122{
123 mLock = nsnull;
124 mPersistFile = nsnull;
125 mShellLink = nsnull;
126}
127
128ShortcutResolver::~ShortcutResolver()
129{
130 if (mLock)
131 PR_DestroyLock(mLock);
132
133 // Release the pointer to the IPersistFile interface.
134 if (mPersistFile)
135 mPersistFile->Release();
136
137 // Release the pointer to the IShellLink interface.
138 if(mShellLink)
139 mShellLink->Release();
140
141 CoUninitialize();
142}
143
144nsresult
145ShortcutResolver::Init()
146{
147 CoInitialize(NULL); // FIX: we should probably move somewhere higher up during startup
148
149 mLock = PR_NewLock();
150 if (!mLock)
151 return NS_ERROR_FAILURE;
152
153 HRESULT hres = CoCreateInstance(CLSID_ShellLink,
154 NULL,
155 CLSCTX_INPROC_SERVER,
156 IID_IShellLink,
157 (void**)&mShellLink);
158 if (SUCCEEDED(hres))
159 {
160 // Get a pointer to the IPersistFile interface.
161 hres = mShellLink->QueryInterface(IID_IPersistFile, (void**)&mPersistFile);
162 }
163
164 if (mPersistFile == nsnull || mShellLink == nsnull)
165 return NS_ERROR_FAILURE;
166
167 return NS_OK;
168}
169
170// |out| must be an allocated buffer of size MAX_PATH
171nsresult
172ShortcutResolver::Resolve(const WCHAR* in, char* out)
173{
174 nsAutoLock lock(mLock);
175
176 // see if we can Load the path.
177 HRESULT hres = mPersistFile->Load(in, STGM_READ);
178
179 if (FAILED(hres))
180 return NS_ERROR_FAILURE;
181
182 // Resolve the link.
183 hres = mShellLink->Resolve(nsnull, SLR_NO_UI );
184
185 if (FAILED(hres))
186 return NS_ERROR_FAILURE;
187
188 WIN32_FIND_DATA wfd;
189 // Get the path to the link target.
190 hres = mShellLink->GetPath( out, MAX_PATH, &wfd, SLGP_UNCPRIORITY );
191 if (FAILED(hres))
192 return NS_ERROR_FAILURE;
193 return NS_OK;
194}
195
196static ShortcutResolver * gResolver = nsnull;
197
198static nsresult NS_CreateShortcutResolver()
199{
200 gResolver = new ShortcutResolver();
201 if (!gResolver)
202 return NS_ERROR_OUT_OF_MEMORY;
203
204 return gResolver->Init();
205}
206
207static void NS_DestroyShortcutResolver()
208{
209 delete gResolver;
210 gResolver = nsnull;
211}
212
213
214//-----------------------------------------------------------------------------
215// static helper functions
216//-----------------------------------------------------------------------------
217
218// certainly not all the error that can be
219// encountered, but many of them common ones
220static nsresult ConvertWinError(DWORD winErr)
221{
222 nsresult rv;
223
224 switch (winErr)
225 {
226 case ERROR_FILE_NOT_FOUND:
227 case ERROR_PATH_NOT_FOUND:
228 case ERROR_INVALID_DRIVE:
229 rv = NS_ERROR_FILE_NOT_FOUND;
230 break;
231 case ERROR_ACCESS_DENIED:
232 case ERROR_NOT_SAME_DEVICE:
233 rv = NS_ERROR_FILE_ACCESS_DENIED;
234 break;
235 case ERROR_NOT_ENOUGH_MEMORY:
236 case ERROR_INVALID_BLOCK:
237 case ERROR_INVALID_HANDLE:
238 case ERROR_ARENA_TRASHED:
239 rv = NS_ERROR_OUT_OF_MEMORY;
240 break;
241 case ERROR_CURRENT_DIRECTORY:
242 rv = NS_ERROR_FILE_DIR_NOT_EMPTY;
243 break;
244 case ERROR_WRITE_PROTECT:
245 rv = NS_ERROR_FILE_READ_ONLY;
246 break;
247 case ERROR_HANDLE_DISK_FULL:
248 rv = NS_ERROR_FILE_TOO_BIG;
249 break;
250 case ERROR_FILE_EXISTS:
251 case ERROR_ALREADY_EXISTS:
252 case ERROR_CANNOT_MAKE:
253 rv = NS_ERROR_FILE_ALREADY_EXISTS;
254 break;
255 case 0:
256 rv = NS_OK;
257 break;
258 default:
259 rv = NS_ERROR_FAILURE;
260 break;
261 }
262 return rv;
263}
264
265// definition of INVALID_SET_FILE_POINTER from VC.NET header files
266// it doesn't appear to be defined by VC6
267#ifndef INVALID_SET_FILE_POINTER
268# define INVALID_SET_FILE_POINTER ((DWORD)-1)
269#endif
270// same goes for INVALID_FILE_ATTRIBUTES
271#ifndef INVALID_FILE_ATTRIBUTES
272# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
273#endif
274
275// as suggested in the MSDN documentation on SetFilePointer
276static __int64
277MyFileSeek64(HANDLE aHandle, __int64 aDistance, DWORD aMoveMethod)
278{
279 LARGE_INTEGER li;
280
281 li.QuadPart = aDistance;
282 li.LowPart = SetFilePointer(aHandle, li.LowPart, &li.HighPart, aMoveMethod);
283 if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
284 {
285 li.QuadPart = -1;
286 }
287
288 return li.QuadPart;
289}
290
291static PRBool
292IsShortcutPath(const char *path)
293{
294 // Under Windows, the shortcuts are just files with a ".lnk" extension.
295 // Note also that we don't resolve links in the middle of paths.
296 // i.e. "c:\foo.lnk\bar.txt" is invalid.
297 NS_ABORT_IF_FALSE(path, "don't pass nulls");
298 const char * ext = (const char *) _mbsrchr((const unsigned char *)path, '.');
299 if (!ext || 0 != stricmp(ext + 1, "lnk"))
300 return PR_FALSE;
301 return PR_TRUE;
302}
303
304//-----------------------------------------------------------------------------
305// nsDirEnumerator
306//-----------------------------------------------------------------------------
307
308class nsDirEnumerator : public nsISimpleEnumerator
309{
310 public:
311
312 NS_DECL_ISUPPORTS
313
314 nsDirEnumerator() : mDir(nsnull)
315 {
316 }
317
318 nsresult Init(nsILocalFile* parent)
319 {
320 nsCAutoString filepath;
321 parent->GetNativeTarget(filepath);
322
323 if (filepath.IsEmpty())
324 {
325 parent->GetNativePath(filepath);
326 }
327
328 if (filepath.IsEmpty())
329 {
330 return NS_ERROR_UNEXPECTED;
331 }
332
333 mDir = PR_OpenDir(filepath.get());
334 if (mDir == nsnull) // not a directory?
335 return NS_ERROR_FAILURE;
336
337 mParent = parent;
338 return NS_OK;
339 }
340
341 NS_IMETHOD HasMoreElements(PRBool *result)
342 {
343 nsresult rv;
344 if (mNext == nsnull && mDir)
345 {
346 PRDirEntry* entry = PR_ReadDir(mDir, PR_SKIP_BOTH);
347 if (entry == nsnull)
348 {
349 // end of dir entries
350
351 PRStatus status = PR_CloseDir(mDir);
352 if (status != PR_SUCCESS)
353 return NS_ERROR_FAILURE;
354 mDir = nsnull;
355
356 *result = PR_FALSE;
357 return NS_OK;
358 }
359
360 nsCOMPtr<nsIFile> file;
361 rv = mParent->Clone(getter_AddRefs(file));
362 if (NS_FAILED(rv))
363 return rv;
364
365 rv = file->AppendNative(nsDependentCString(entry->name));
366 if (NS_FAILED(rv))
367 return rv;
368
369 // make sure the thing exists. If it does, try the next one.
370 PRBool exists;
371 rv = file->Exists(&exists);
372 if (NS_FAILED(rv) || !exists)
373 {
374 return HasMoreElements(result);
375 }
376
377 mNext = do_QueryInterface(file);
378 }
379 *result = mNext != nsnull;
380 return NS_OK;
381 }
382
383 NS_IMETHOD GetNext(nsISupports **result)
384 {
385 nsresult rv;
386 PRBool hasMore;
387 rv = HasMoreElements(&hasMore);
388 if (NS_FAILED(rv)) return rv;
389
390 *result = mNext; // might return nsnull
391 NS_IF_ADDREF(*result);
392
393 mNext = nsnull;
394 return NS_OK;
395 }
396
397 // dtor can be non-virtual since there are no subclasses, but must be
398 // public to use the class on the stack.
399 ~nsDirEnumerator()
400 {
401 if (mDir)
402 {
403 PRStatus status = PR_CloseDir(mDir);
404 NS_ASSERTION(status == PR_SUCCESS, "close failed");
405 }
406 }
407
408 protected:
409 PRDir* mDir;
410 nsCOMPtr<nsILocalFile> mParent;
411 nsCOMPtr<nsILocalFile> mNext;
412};
413
414NS_IMPL_ISUPPORTS1(nsDirEnumerator, nsISimpleEnumerator)
415
416
417//-----------------------------------------------------------------------------
418// nsLocalFile <public>
419//-----------------------------------------------------------------------------
420
421nsLocalFile::nsLocalFile()
422 : mFollowSymlinks(PR_FALSE)
423{
424 MakeDirty();
425 memset(&mFileInfo64, 0, sizeof(mFileInfo64));
426}
427
428NS_METHOD
429nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
430{
431 NS_ENSURE_ARG_POINTER(aInstancePtr);
432 NS_ENSURE_NO_AGGREGATION(outer);
433
434 nsLocalFile* inst = new nsLocalFile();
435 if (inst == NULL)
436 return NS_ERROR_OUT_OF_MEMORY;
437
438 nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
439 if (NS_FAILED(rv))
440 {
441 delete inst;
442 return rv;
443 }
444 return NS_OK;
445}
446
447
448//-----------------------------------------------------------------------------
449// nsLocalFile::nsISupports
450//-----------------------------------------------------------------------------
451
452NS_IMPL_THREADSAFE_ISUPPORTS2(nsLocalFile, nsILocalFile, nsIFile)
453
454
455//-----------------------------------------------------------------------------
456// nsLocalFile <private>
457//-----------------------------------------------------------------------------
458
459nsLocalFile::nsLocalFile(const nsLocalFile& other)
460 : mDirty(other.mDirty)
461 , mFollowSymlinks(other.mFollowSymlinks)
462 , mWorkingPath(other.mWorkingPath)
463 , mResolvedPath(other.mResolvedPath)
464 , mFileInfo64(other.mFileInfo64)
465{
466}
467
468// Resolve the shortcut file from mWorkingPath and write the path
469// it points to into mResolvedPath.
470nsresult
471nsLocalFile::ResolveShortcut()
472{
473 // we can't do anything without the resolver
474 if (!gResolver)
475 return NS_ERROR_FAILURE;
476
477 // allocate the memory for the result of the resolution
478 nsAutoString ucsBuf;
479 NS_CopyNativeToUnicode(mWorkingPath, ucsBuf);
480
481 mResolvedPath.SetLength(MAX_PATH);
482 char *resolvedPath = mResolvedPath.BeginWriting();
483
484 // resolve this shortcut
485 nsresult rv = gResolver->Resolve(ucsBuf.get(), resolvedPath);
486
487 size_t len = NS_FAILED(rv) ? 0 : strlen(resolvedPath);
488 mResolvedPath.SetLength(len);
489
490 return rv;
491}
492
493// Resolve any shortcuts and stat the resolved path. After a successful return
494// the path is guaranteed valid and the members of mFileInfo64 can be used.
495nsresult
496nsLocalFile::ResolveAndStat()
497{
498 // if we aren't dirty then we are already done
499 if (!mDirty)
500 return NS_OK;
501
502 // we can't resolve/stat anything that isn't a valid NSPR addressable path
503 if (mWorkingPath.IsEmpty())
504 return NS_ERROR_FILE_INVALID_PATH;
505
506 // this is usually correct
507 mResolvedPath.Assign(mWorkingPath);
508
509 // slutty hack designed to work around bug 134796 until it is fixed
510 char temp[4];
511 const char *nsprPath = mWorkingPath.get();
512 if (mWorkingPath.Length() == 2 && mWorkingPath.CharAt(1) == ':')
513 {
514 temp[0] = mWorkingPath[0];
515 temp[1] = mWorkingPath[1];
516 temp[2] = '\\';
517 temp[3] = '\0';
518 nsprPath = temp;
519 }
520
521 // first we will see if the working path exists. If it doesn't then
522 // there is nothing more that can be done
523 PRStatus status = PR_GetFileInfo64(nsprPath, &mFileInfo64);
524 if (status != PR_SUCCESS)
525 return NS_ERROR_FILE_NOT_FOUND;
526
527 // if this isn't a shortcut file or we aren't following symlinks then we're done
528 if (!mFollowSymlinks
529 || mFileInfo64.type != PR_FILE_FILE
530 || !IsShortcutPath(mWorkingPath.get()))
531 {
532 mDirty = PR_FALSE;
533 return NS_OK;
534 }
535
536 // we need to resolve this shortcut to what it points to, this will
537 // set mResolvedPath. Even if it fails we need to have the resolved
538 // path equal to working path for those functions that always use
539 // the resolved path.
540 nsresult rv = ResolveShortcut();
541 if (NS_FAILED(rv))
542 {
543 mResolvedPath.Assign(mWorkingPath);
544 return rv;
545 }
546
547 // get the details of the resolved path
548 status = PR_GetFileInfo64(mResolvedPath.get(), &mFileInfo64);
549 if (status != PR_SUCCESS)
550 return NS_ERROR_FILE_NOT_FOUND;
551
552 mDirty = PR_FALSE;
553 return NS_OK;
554}
555
556
557//-----------------------------------------------------------------------------
558// nsLocalFile::nsIFile,nsILocalFile
559//-----------------------------------------------------------------------------
560
561NS_IMETHODIMP
562nsLocalFile::Clone(nsIFile **file)
563{
564 // Just copy-construct ourselves
565 *file = new nsLocalFile(*this);
566 if (!*file)
567 return NS_ERROR_OUT_OF_MEMORY;
568
569 NS_ADDREF(*file);
570
571 return NS_OK;
572}
573
574NS_IMETHODIMP
575nsLocalFile::InitWithNativePath(const nsACString &filePath)
576{
577 MakeDirty();
578
579 nsACString::const_iterator begin, end;
580 filePath.BeginReading(begin);
581 filePath.EndReading(end);
582
583 // input string must not be empty
584 if (begin == end)
585 return NS_ERROR_FAILURE;
586
587 char firstChar = *begin;
588 char secondChar = *(++begin);
589
590 // just do a sanity check. if it has any forward slashes, it is not a Native path
591 // on windows. Also, it must have a colon at after the first char.
592
593 char *path = nsnull;
594 PRInt32 pathLen = 0;
595
596 if ( ( (secondChar == ':') && !FindCharInReadable('/', begin, end) ) || // normal path
597 ( (firstChar == '\\') && (secondChar == '\\') ) ) // network path
598 {
599 // This is a native path
600 path = ToNewCString(filePath);
601 pathLen = filePath.Length();
602 }
603
604 if (path == nsnull)
605 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
606
607 // kill any trailing '\' provided it isn't the second char of DBCS
608 PRInt32 len = pathLen - 1;
609 if (path[len] == '\\' &&
610 (!::IsDBCSLeadByte(path[len-1]) ||
611 _mbsrchr((const unsigned char *)path, '\\') == (const unsigned char *)path+len))
612 {
613 path[len] = '\0';
614 pathLen = len;
615 }
616
617 mWorkingPath.Adopt(path, pathLen);
618 return NS_OK;
619}
620
621NS_IMETHODIMP
622nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
623{
624 nsresult rv = ResolveAndStat();
625 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
626 return rv;
627
628 *_retval = PR_Open(mResolvedPath.get(), flags, mode);
629 if (*_retval)
630 return NS_OK;
631
632 return NS_ErrorAccordingToNSPR();
633}
634
635
636NS_IMETHODIMP
637nsLocalFile::OpenANSIFileDesc(const char *mode, FILE * *_retval)
638{
639 nsresult rv = ResolveAndStat();
640 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
641 return rv;
642
643 *_retval = fopen(mResolvedPath.get(), mode);
644 if (*_retval)
645 return NS_OK;
646
647 return NS_ERROR_FAILURE;
648}
649
650
651
652NS_IMETHODIMP
653nsLocalFile::Create(PRUint32 type, PRUint32 attributes)
654{
655 if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
656 return NS_ERROR_FILE_UNKNOWN_TYPE;
657
658 nsresult rv = ResolveAndStat();
659 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
660 return rv;
661
662 // create directories to target
663 //
664 // A given local file can be either one of these forms:
665 //
666 // - normal: X:\some\path\on\this\drive
667 // ^--- start here
668 //
669 // - UNC path: \\machine\volume\some\path\on\this\drive
670 // ^--- start here
671 //
672 // Skip the first 'X:\' for the first form, and skip the first full
673 // '\\machine\volume\' segment for the second form.
674
675 const unsigned char* path = (const unsigned char*) mResolvedPath.get();
676 if (path[0] == '\\' && path[1] == '\\')
677 {
678 // dealing with a UNC path here; skip past '\\machine\'
679 path = _mbschr(path + 2, '\\');
680 if (!path)
681 return NS_ERROR_FILE_INVALID_PATH;
682 ++path;
683 }
684
685 // search for first slash after the drive (or volume) name
686 unsigned char* slash = _mbschr(path, '\\');
687
688 if (slash)
689 {
690 // skip the first '\\'
691 ++slash;
692 slash = _mbschr(slash, '\\');
693
694 while (slash)
695 {
696 *slash = '\0';
697
698 if (!CreateDirectoryA(mResolvedPath.get(), NULL)) {
699 rv = ConvertWinError(GetLastError());
700 // perhaps the base path already exists, or perhaps we don't have
701 // permissions to create the directory. NOTE: access denied could
702 // occur on a parent directory even though it exists.
703 if (rv != NS_ERROR_FILE_ALREADY_EXISTS &&
704 rv != NS_ERROR_FILE_ACCESS_DENIED)
705 return rv;
706 }
707 *slash = '\\';
708 ++slash;
709 slash = _mbschr(slash, '\\');
710 }
711 }
712
713 if (type == NORMAL_FILE_TYPE)
714 {
715 PRFileDesc* file = PR_Open(mResolvedPath.get(), PR_RDONLY | PR_CREATE_FILE | PR_APPEND | PR_EXCL, attributes);
716 if (!file) return NS_ERROR_FILE_ALREADY_EXISTS;
717
718 PR_Close(file);
719 return NS_OK;
720 }
721
722 if (type == DIRECTORY_TYPE)
723 {
724 if (!CreateDirectoryA(mResolvedPath.get(), NULL))
725 return ConvertWinError(GetLastError());
726 else
727 return NS_OK;
728 }
729
730 return NS_ERROR_FILE_UNKNOWN_TYPE;
731}
732
733NS_IMETHODIMP
734nsLocalFile::AppendNative(const nsACString &node)
735{
736 // append this path, multiple components are not permitted
737 return AppendNativeInternal(PromiseFlatCString(node), PR_FALSE);
738}
739
740NS_IMETHODIMP
741nsLocalFile::AppendRelativeNativePath(const nsACString &node)
742{
743 // append this path, multiple components are permitted
744 return AppendNativeInternal(PromiseFlatCString(node), PR_TRUE);
745}
746
747nsresult
748nsLocalFile::AppendNativeInternal(const nsAFlatCString &node, PRBool multipleComponents)
749{
750 if (node.IsEmpty())
751 return NS_OK;
752
753 // check the relative path for validity
754 const unsigned char * nodePath = (const unsigned char *) node.get();
755 if (*nodePath == '\\' // can't start with an '\'
756 || _mbschr(nodePath, '/') // can't contain /
757 || node.Equals("..")) // can't be ..
758 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
759
760 if (multipleComponents)
761 {
762 // can't contain .. as a path component. Ensure that the valid components
763 // "foo..foo", "..foo", and "foo.." are not falsely detected, but the invalid
764 // paths "..\", "foo\..", "foo\..\foo", "..\foo", etc are.
765 unsigned char * doubleDot = _mbsstr(nodePath, (unsigned char *)"\\..");
766 while (doubleDot)
767 {
768 doubleDot += 3;
769 if (*doubleDot == '\0' || *doubleDot == '\\')
770 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
771 doubleDot = _mbsstr(doubleDot, (unsigned char *)"\\..");
772 }
773 if (0 == _mbsncmp(nodePath, (unsigned char *)"..\\", 3)) // catches the remaining cases of prefixes
774 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
775 }
776 else if (_mbschr(nodePath, '\\')) // single components can't contain '\'
777 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
778
779 MakeDirty();
780
781 mWorkingPath.Append(NS_LITERAL_CSTRING("\\") + node);
782
783 return NS_OK;
784}
785
786NS_IMETHODIMP
787nsLocalFile::Normalize()
788{
789 // XXX See bug 187957 comment 18 for possible problems with this implementation.
790
791 if (mWorkingPath.IsEmpty())
792 return NS_OK;
793
794 // work in unicode for ease
795 nsAutoString path;
796 NS_CopyNativeToUnicode(mWorkingPath, path);
797
798 // find the index of the root backslash for the path. Everything before
799 // this is considered fully normalized and cannot be ascended beyond
800 // using ".." For a local drive this is the first slash (e.g. "c:\").
801 // For a UNC path it is the slash following the share name
802 // (e.g. "\\server\share\").
803 PRInt32 rootIdx = 2; // default to local drive
804 if (path.First() == '\\') // if a share then calculate the rootIdx
805 {
806 rootIdx = path.FindChar('\\', 2); // skip \\ in front of the server
807 if (rootIdx == kNotFound)
808 return NS_OK; // already normalized
809 rootIdx = path.FindChar('\\', rootIdx+1);
810 if (rootIdx == kNotFound)
811 return NS_OK; // already normalized
812 }
813 else if (path.CharAt(rootIdx) != '\\')
814 {
815 // The path has been specified relative to the current working directory
816 // for that drive. To normalize it, the current working directory for
817 // that drive needs to be inserted before the supplied relative path
818 // which will provide an absolute path (and the rootIdx will still be 2).
819 char cwd[MAX_PATH];
820 char * pcwd = cwd;
821 int drive = toupper(path.First()) - 'A' + 1;
822 if (!_getdcwd(drive, pcwd, MAX_PATH))
823 pcwd = _getdcwd(drive, 0, 0);
824 if (!pcwd)
825 return NS_ERROR_OUT_OF_MEMORY;
826
827 nsAutoString currentDir;
828 NS_CopyNativeToUnicode(nsDependentCString(pcwd), currentDir);
829 if (pcwd != cwd)
830 free(pcwd);
831
832 if (currentDir.Last() == '\\')
833 path.Replace(0, 2, currentDir);
834 else
835 path.Replace(0, 2, currentDir + NS_LITERAL_STRING("\\"));
836 }
837 NS_POSTCONDITION(0 < rootIdx && rootIdx < (PRInt32)path.Length(), "rootIdx is invalid");
838 NS_POSTCONDITION(path.CharAt(rootIdx) == '\\', "rootIdx is invalid");
839
840 // if there is nothing following the root path then it is already normalized
841 if (rootIdx + 1 == (PRInt32)path.Length())
842 return NS_OK;
843
844 // assign the root
845 nsAutoString normal;
846 const PRUnichar * pathBuffer = path.get(); // simplify access to the buffer
847 normal.SetCapacity(path.Length()); // it won't ever grow longer
848 normal.Assign(pathBuffer, rootIdx);
849
850 // Normalize the path components. The actions taken are:
851 //
852 // "\\" condense to single backslash
853 // "." remove from path
854 // ".." up a directory
855 // "..." remove from path (any number of dots > 2)
856 //
857 // The last form is something that Windows 95 and 98 supported and
858 // is a shortcut for changing up multiple directories. Windows XP
859 // and ilk ignore it in a path, as is done here.
860 PRInt32 len, begin, end = rootIdx;
861 while (end < (PRInt32)path.Length())
862 {
863 // find the current segment (text between the backslashes) to
864 // be examined, this will set the following variables:
865 // begin == index of first char in segment
866 // end == index 1 char after last char in segment
867 // len == length of segment
868 begin = end + 1;
869 end = path.FindChar('\\', begin);
870 if (end == kNotFound)
871 end = path.Length();
872 len = end - begin;
873
874 // ignore double backslashes
875 if (len == 0)
876 continue;
877
878 // len != 0, and interesting paths always begin with a dot
879 if (pathBuffer[begin] == '.')
880 {
881 // ignore single dots
882 if (len == 1)
883 continue;
884
885 // handle multiple dots
886 if (len >= 2 && pathBuffer[begin+1] == '.')
887 {
888 // back up a path component on double dot
889 if (len == 2)
890 {
891 PRInt32 prev = normal.RFindChar('\\');
892 if (prev >= rootIdx)
893 normal.Truncate(prev);
894 continue;
895 }
896
897 // length is > 2 and the first two characters are dots.
898 // if the rest of the string is dots, then ignore it.
899 int idx = len - 1;
900 for (; idx >= 2; --idx)
901 {
902 if (pathBuffer[begin+idx] != '.')
903 break;
904 }
905
906 // this is true if the loop above didn't break
907 // and all characters in this segment are dots.
908 if (idx < 2)
909 continue;
910 }
911 }
912
913 // add the current component to the path, including the preceding backslash
914 normal.Append(pathBuffer + begin - 1, len + 1);
915 }
916
917 NS_CopyUnicodeToNative(normal, mWorkingPath);
918 MakeDirty();
919
920 return NS_OK;
921}
922
923NS_IMETHODIMP
924nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
925{
926 aLeafName.Truncate();
927
928 const char* temp = mWorkingPath.get();
929 if(temp == nsnull)
930 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
931
932 const char* leaf = (const char*) _mbsrchr((const unsigned char*) temp, '\\');
933
934 // if the working path is just a node without any lashes.
935 if (leaf == nsnull)
936 leaf = temp;
937 else
938 leaf++;
939
940 aLeafName.Assign(leaf);
941 return NS_OK;
942}
943
944NS_IMETHODIMP
945nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
946{
947 MakeDirty();
948
949 const unsigned char* temp = (const unsigned char*) mWorkingPath.get();
950 if(temp == nsnull)
951 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
952
953 // cannot use nsCString::RFindChar() due to 0x5c problem
954 PRInt32 offset = (PRInt32) (_mbsrchr(temp, '\\') - temp);
955 if (offset)
956 {
957 mWorkingPath.Truncate(offset+1);
958 }
959 mWorkingPath.Append(aLeafName);
960
961 return NS_OK;
962}
963
964
965NS_IMETHODIMP
966nsLocalFile::GetNativePath(nsACString &_retval)
967{
968 _retval = mWorkingPath;
969 return NS_OK;
970}
971
972nsresult
973nsLocalFile::CopySingleFile(nsIFile *sourceFile, nsIFile *destParent, const nsACString &newName,
974 PRBool followSymlinks, PRBool move)
975{
976 nsresult rv;
977 nsCAutoString filePath;
978
979 // get the path that we are going to copy to.
980 // Since windows does not know how to auto
981 // resolve shortcuts, we must work with the
982 // target.
983 nsCAutoString destPath;
984 destParent->GetNativeTarget(destPath);
985
986 destPath.Append("\\");
987
988 if (newName.IsEmpty())
989 {
990 nsCAutoString aFileName;
991 sourceFile->GetNativeLeafName(aFileName);
992 destPath.Append(aFileName);
993 }
994 else
995 {
996 destPath.Append(newName);
997 }
998
999
1000 if (followSymlinks)
1001 {
1002 rv = sourceFile->GetNativeTarget(filePath);
1003 if (filePath.IsEmpty())
1004 rv = sourceFile->GetNativePath(filePath);
1005 }
1006 else
1007 {
1008 rv = sourceFile->GetNativePath(filePath);
1009 }
1010
1011 if (NS_FAILED(rv))
1012 return rv;
1013
1014 int copyOK;
1015
1016 if (!move)
1017 copyOK = CopyFile(filePath.get(), destPath.get(), PR_TRUE);
1018 else
1019 {
1020 // What we have to do is check to see if the destPath exists. If it
1021 // does, we have to move it out of the say so that MoveFile will
1022 // succeed. However, we don't want to just remove it since MoveFile
1023 // can fail leaving us without a file.
1024
1025 nsCAutoString backup;
1026 PRFileInfo64 fileInfo64;
1027 PRStatus status = PR_GetFileInfo64(destPath.get(), &fileInfo64);
1028 if (status == PR_SUCCESS)
1029 {
1030
1031 // the file exists. Check to make sure it is not a directory,
1032 // then move it out of the way.
1033 if (fileInfo64.type == PR_FILE_FILE)
1034 {
1035 backup.Append(destPath);
1036 backup.Append(".moztmp");
1037
1038 // remove any existing backup file that we may already have.
1039 // maybe we should be doing some kind of unique naming here,
1040 // but why bother.
1041 remove(backup.get());
1042
1043 // move destination file to backup file
1044 copyOK = MoveFile(destPath.get(), backup.get());
1045 if (!copyOK)
1046 {
1047 // I guess we can't do the backup copy, so return.
1048 rv = ConvertWinError(GetLastError());
1049 return rv;
1050 }
1051 }
1052 }
1053 // move source file to destination file
1054 copyOK = MoveFile(filePath.get(), destPath.get());
1055
1056 if (!backup.IsEmpty())
1057 {
1058 if (copyOK)
1059 {
1060 // remove the backup copy.
1061 remove(backup.get());
1062 }
1063 else
1064 {
1065 // restore backup
1066 int backupOk = MoveFile(backup.get(), destPath.get());
1067 NS_ASSERTION(backupOk, "move backup failed");
1068 }
1069 }
1070 }
1071 if (!copyOK) // CopyFile and MoveFile returns non-zero if succeeds (backward if you ask me).
1072 rv = ConvertWinError(GetLastError());
1073
1074 return rv;
1075}
1076
1077
1078nsresult
1079nsLocalFile::CopyMove(nsIFile *aParentDir, const nsACString &newName, PRBool followSymlinks, PRBool move)
1080{
1081 nsCOMPtr<nsIFile> newParentDir = aParentDir;
1082 // check to see if this exists, otherwise return an error.
1083 // we will check this by resolving. If the user wants us
1084 // to follow links, then we are talking about the target,
1085 // hence we can use the |followSymlinks| parameter.
1086 nsresult rv = ResolveAndStat();
1087 if (NS_FAILED(rv))
1088 return rv;
1089
1090 if (!newParentDir)
1091 {
1092 // no parent was specified. We must rename.
1093
1094 if (newName.IsEmpty())
1095 return NS_ERROR_INVALID_ARG;
1096
1097 rv = GetParent(getter_AddRefs(newParentDir));
1098 if (NS_FAILED(rv))
1099 return rv;
1100 }
1101
1102 if (!newParentDir)
1103 return NS_ERROR_FILE_DESTINATION_NOT_DIR;
1104
1105 // make sure it exists and is a directory. Create it if not there.
1106 PRBool exists;
1107 newParentDir->Exists(&exists);
1108 if (!exists)
1109 {
1110 rv = newParentDir->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
1111 if (NS_FAILED(rv))
1112 return rv;
1113 }
1114 else
1115 {
1116 PRBool isDir;
1117 newParentDir->IsDirectory(&isDir);
1118 if (isDir == PR_FALSE)
1119 {
1120 if (followSymlinks)
1121 {
1122 PRBool isLink;
1123 newParentDir->IsSymlink(&isLink);
1124 if (isLink)
1125 {
1126 nsCAutoString target;
1127 newParentDir->GetNativeTarget(target);
1128
1129 nsCOMPtr<nsILocalFile> realDest = new nsLocalFile();
1130 if (realDest == nsnull)
1131 return NS_ERROR_OUT_OF_MEMORY;
1132
1133 rv = realDest->InitWithNativePath(target);
1134
1135 if (NS_FAILED(rv))
1136 return rv;
1137
1138 return CopyMove(realDest, newName, followSymlinks, move);
1139 }
1140 }
1141 else
1142 {
1143 return NS_ERROR_FILE_DESTINATION_NOT_DIR;
1144 }
1145 }
1146 }
1147
1148 // check to see if we are a directory, if so enumerate it.
1149
1150 PRBool isDir;
1151 IsDirectory(&isDir);
1152 PRBool isSymlink;
1153 IsSymlink(&isSymlink);
1154
1155 if (!isDir || (isSymlink && !followSymlinks))
1156 {
1157 rv = CopySingleFile(this, newParentDir, newName, followSymlinks, move);
1158 if (NS_FAILED(rv))
1159 return rv;
1160 }
1161 else
1162 {
1163 // create a new target destination in the new parentDir;
1164 nsCOMPtr<nsIFile> target;
1165 rv = newParentDir->Clone(getter_AddRefs(target));
1166
1167 if (NS_FAILED(rv))
1168 return rv;
1169
1170 nsCAutoString allocatedNewName;
1171 if (newName.IsEmpty())
1172 {
1173 PRBool isLink;
1174 IsSymlink(&isLink);
1175 if (isLink)
1176 {
1177 nsCAutoString temp;
1178 GetNativeTarget(temp);
1179 const char* leaf = (const char*) _mbsrchr((const unsigned char*) temp.get(), '\\');
1180 if (leaf[0] == '\\')
1181 leaf++;
1182 allocatedNewName = leaf;
1183 }
1184 else
1185 {
1186 GetNativeLeafName(allocatedNewName);// this should be the leaf name of the
1187 }
1188 }
1189 else
1190 {
1191 allocatedNewName = newName;
1192 }
1193
1194 rv = target->AppendNative(allocatedNewName);
1195 if (NS_FAILED(rv))
1196 return rv;
1197
1198 allocatedNewName.Truncate();
1199
1200 // check if the destination directory already exists
1201 target->Exists(&exists);
1202 if (!exists)
1203 {
1204 // if the destination directory cannot be created, return an error
1205 rv = target->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
1206 if (NS_FAILED(rv))
1207 return rv;
1208 }
1209 else
1210 {
1211 // check if the destination directory is writable and empty
1212 PRBool isWritable;
1213
1214 target->IsWritable(&isWritable);
1215 if (!isWritable)
1216 return NS_ERROR_FILE_ACCESS_DENIED;
1217
1218 nsCOMPtr<nsISimpleEnumerator> targetIterator;
1219 rv = target->GetDirectoryEntries(getter_AddRefs(targetIterator));
1220
1221 PRBool more;
1222 targetIterator->HasMoreElements(&more);
1223 // return error if target directory is not empty
1224 if (more)
1225 return NS_ERROR_FILE_DIR_NOT_EMPTY;
1226 }
1227
1228 nsDirEnumerator dirEnum;
1229
1230 rv = dirEnum.Init(this);
1231 if (NS_FAILED(rv)) {
1232 NS_WARNING("dirEnum initalization failed");
1233 return rv;
1234 }
1235
1236 PRBool more;
1237 while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
1238 {
1239 nsCOMPtr<nsISupports> item;
1240 nsCOMPtr<nsIFile> file;
1241 dirEnum.GetNext(getter_AddRefs(item));
1242 file = do_QueryInterface(item);
1243 if (file)
1244 {
1245 PRBool isDir, isLink;
1246
1247 file->IsDirectory(&isDir);
1248 file->IsSymlink(&isLink);
1249
1250 if (move)
1251 {
1252 if (followSymlinks)
1253 return NS_ERROR_FAILURE;
1254
1255 rv = file->MoveToNative(target, nsCString());
1256 NS_ENSURE_SUCCESS(rv,rv);
1257 }
1258 else
1259 {
1260 if (followSymlinks)
1261 rv = file->CopyToFollowingLinksNative(target, nsCString());
1262 else
1263 rv = file->CopyToNative(target, nsCString());
1264 NS_ENSURE_SUCCESS(rv,rv);
1265 }
1266 }
1267 }
1268 // we've finished moving all the children of this directory
1269 // in the new directory. so now delete the directory
1270 // note, we don't need to do a recursive delete.
1271 // MoveTo() is recursive. At this point,
1272 // we've already moved the children of the current folder
1273 // to the new location. nothing should be left in the folder.
1274 if (move)
1275 {
1276 rv = Remove(PR_FALSE /* recursive */);
1277 NS_ENSURE_SUCCESS(rv,rv);
1278 }
1279 }
1280
1281
1282 // If we moved, we want to adjust this.
1283 if (move)
1284 {
1285 MakeDirty();
1286
1287 nsCAutoString newParentPath;
1288 newParentDir->GetNativePath(newParentPath);
1289
1290 if (newParentPath.IsEmpty())
1291 return NS_ERROR_FAILURE;
1292
1293 if (newName.IsEmpty())
1294 {
1295 nsCAutoString aFileName;
1296 GetNativeLeafName(aFileName);
1297
1298 InitWithNativePath(newParentPath);
1299 AppendNative(aFileName);
1300 }
1301 else
1302 {
1303 InitWithNativePath(newParentPath);
1304 AppendNative(newName);
1305 }
1306 }
1307
1308 return NS_OK;
1309}
1310
1311NS_IMETHODIMP
1312nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString &newName)
1313{
1314 return CopyMove(newParentDir, newName, PR_FALSE, PR_FALSE);
1315}
1316
1317NS_IMETHODIMP
1318nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString &newName)
1319{
1320 return CopyMove(newParentDir, newName, PR_TRUE, PR_FALSE);
1321}
1322
1323NS_IMETHODIMP
1324nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString &newName)
1325{
1326 return CopyMove(newParentDir, newName, PR_FALSE, PR_TRUE);
1327}
1328
1329NS_IMETHODIMP
1330nsLocalFile::Load(PRLibrary * *_retval)
1331{
1332 PRBool isFile;
1333 nsresult rv = IsFile(&isFile);
1334
1335 if (NS_FAILED(rv))
1336 return rv;
1337
1338 if (! isFile)
1339 return NS_ERROR_FILE_IS_DIRECTORY;
1340
1341 NS_TIMELINE_START_TIMER("PR_LoadLibrary");
1342 *_retval = PR_LoadLibrary(mResolvedPath.get());
1343 NS_TIMELINE_STOP_TIMER("PR_LoadLibrary");
1344 NS_TIMELINE_MARK_TIMER1("PR_LoadLibrary", mResolvedPath.get());
1345
1346 if (*_retval)
1347 return NS_OK;
1348
1349 return NS_ERROR_NULL_POINTER;
1350}
1351
1352NS_IMETHODIMP
1353nsLocalFile::Remove(PRBool recursive)
1354{
1355 // NOTE:
1356 //
1357 // if the working path points to a shortcut, then we will only
1358 // delete the shortcut itself. even if the shortcut points to
1359 // a directory, we will not recurse into that directory or
1360 // delete that directory itself. likewise, if the shortcut
1361 // points to a normal file, we will not delete the real file.
1362 // this is done to be consistent with the other platforms that
1363 // behave this way. we do this even if the followLinks attribute
1364 // is set to true. this helps protect against misuse that could
1365 // lead to security bugs (e.g., bug 210588).
1366 //
1367 // Since shortcut files are no longer permitted to be used as unix-like
1368 // symlinks interspersed in the path (e.g. "c:/file.lnk/foo/bar.txt")
1369 // this processing is a lot simpler. Even if the shortcut file is
1370 // pointing to a directory, only the mWorkingPath value is used and so
1371 // only the shortcut file will be deleted.
1372
1373 PRBool isDir, isLink;
1374 nsresult rv;
1375
1376 isDir = PR_FALSE;
1377 rv = IsSymlink(&isLink);
1378 if (NS_FAILED(rv))
1379 return rv;
1380
1381 // only check to see if we have a directory if it isn't a link
1382 if (!isLink)
1383 {
1384 rv = IsDirectory(&isDir);
1385 if (NS_FAILED(rv))
1386 return rv;
1387 }
1388
1389 if (isDir)
1390 {
1391 if (recursive)
1392 {
1393 nsDirEnumerator dirEnum;
1394
1395 rv = dirEnum.Init(this);
1396 if (NS_FAILED(rv))
1397 return rv;
1398
1399 PRBool more;
1400 while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
1401 {
1402 nsCOMPtr<nsISupports> item;
1403 dirEnum.GetNext(getter_AddRefs(item));
1404 nsCOMPtr<nsIFile> file = do_QueryInterface(item);
1405 if (file)
1406 file->Remove(recursive);
1407 }
1408 }
1409 rv = rmdir(mWorkingPath.get());
1410 }
1411 else
1412 {
1413 rv = remove(mWorkingPath.get());
1414 }
1415
1416 // fixup error code if necessary...
1417 if (rv == (nsresult)-1)
1418 rv = NSRESULT_FOR_ERRNO();
1419
1420 MakeDirty();
1421 return rv;
1422}
1423
1424NS_IMETHODIMP
1425nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime)
1426{
1427 NS_ENSURE_ARG(aLastModifiedTime);
1428
1429 // get the modified time of the target as determined by mFollowSymlinks
1430 // If PR_TRUE, then this will be for the target of the shortcut file,
1431 // otherwise it will be for the shortcut file itself (i.e. the same
1432 // results as GetLastModifiedTimeOfLink)
1433
1434 nsresult rv = ResolveAndStat();
1435 if (NS_FAILED(rv))
1436 return rv;
1437
1438 // microseconds -> milliseconds
1439 PRInt64 usecPerMsec;
1440 LL_I2L(usecPerMsec, PR_USEC_PER_MSEC);
1441 LL_DIV(*aLastModifiedTime, mFileInfo64.modifyTime, usecPerMsec);
1442 return NS_OK;
1443}
1444
1445
1446NS_IMETHODIMP
1447nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTime)
1448{
1449 NS_ENSURE_ARG(aLastModifiedTime);
1450
1451 // The caller is assumed to have already called IsSymlink
1452 // and to have found that this file is a link.
1453
1454 PRFileInfo64 info;
1455 if (PR_GetFileInfo64(mWorkingPath.get(), &info) != PR_SUCCESS)
1456 return NSRESULT_FOR_ERRNO();
1457
1458 // microseconds -> milliseconds
1459 PRInt64 usecPerMsec;
1460 LL_I2L(usecPerMsec, PR_USEC_PER_MSEC);
1461 LL_DIV(*aLastModifiedTime, info.modifyTime, usecPerMsec);
1462 return NS_OK;
1463}
1464
1465
1466NS_IMETHODIMP
1467nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime)
1468{
1469 nsresult rv = ResolveAndStat();
1470 if (NS_FAILED(rv))
1471 return rv;
1472
1473 // set the modified time of the target as determined by mFollowSymlinks
1474 // If PR_TRUE, then this will be for the target of the shortcut file,
1475 // otherwise it will be for the shortcut file itself (i.e. the same
1476 // results as SetLastModifiedTimeOfLink)
1477
1478 rv = SetModDate(aLastModifiedTime, mResolvedPath.get());
1479 if (NS_SUCCEEDED(rv))
1480 MakeDirty();
1481
1482 return rv;
1483}
1484
1485
1486NS_IMETHODIMP
1487nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTime)
1488{
1489 // The caller is assumed to have already called IsSymlink
1490 // and to have found that this file is a link.
1491
1492 nsresult rv = SetModDate(aLastModifiedTime, mWorkingPath.get());
1493 if (NS_SUCCEEDED(rv))
1494 MakeDirty();
1495
1496 return rv;
1497}
1498
1499nsresult
1500nsLocalFile::SetModDate(PRInt64 aLastModifiedTime, const char *filePath)
1501{
1502 HANDLE file = CreateFile(filePath, // pointer to name of the file
1503 GENERIC_WRITE, // access (write) mode
1504 0, // share mode
1505 NULL, // pointer to security attributes
1506 OPEN_EXISTING, // how to create
1507 0, // file attributes
1508 NULL);
1509 if (file == INVALID_HANDLE_VALUE)
1510 {
1511 return ConvertWinError(GetLastError());
1512 }
1513
1514 FILETIME lft, ft;
1515 SYSTEMTIME st;
1516 PRExplodedTime pret;
1517
1518 // PR_ExplodeTime expects usecs...
1519 PR_ExplodeTime(aLastModifiedTime * PR_USEC_PER_MSEC, PR_LocalTimeParameters, &pret);
1520 st.wYear = pret.tm_year;
1521 st.wMonth = pret.tm_month + 1; // Convert start offset -- Win32: Jan=1; NSPR: Jan=0
1522 st.wDayOfWeek = pret.tm_wday;
1523 st.wDay = pret.tm_mday;
1524 st.wHour = pret.tm_hour;
1525 st.wMinute = pret.tm_min;
1526 st.wSecond = pret.tm_sec;
1527 st.wMilliseconds = pret.tm_usec/1000;
1528
1529 nsresult rv = NS_OK;
1530 if ( 0 != SystemTimeToFileTime(&st, &lft)
1531 || 0 != LocalFileTimeToFileTime(&lft, &ft)
1532 || 0 != SetFileTime(file, NULL, &ft, &ft) )
1533 {
1534 rv = ConvertWinError(GetLastError());
1535 }
1536
1537 CloseHandle(file);
1538 return rv;
1539}
1540
1541NS_IMETHODIMP
1542nsLocalFile::GetPermissions(PRUint32 *aPermissions)
1543{
1544 NS_ENSURE_ARG(aPermissions);
1545
1546 // get the permissions of the target as determined by mFollowSymlinks
1547 // If PR_TRUE, then this will be for the target of the shortcut file,
1548 // otherwise it will be for the shortcut file itself (i.e. the same
1549 // results as GetPermissionsOfLink)
1550 nsresult rv = ResolveAndStat();
1551 if (NS_FAILED(rv))
1552 return rv;
1553
1554 PRBool isWritable, isExecutable;
1555 IsWritable(&isWritable);
1556 IsExecutable(&isExecutable);
1557
1558 *aPermissions = PR_IRUSR|PR_IRGRP|PR_IROTH; // all read
1559 if (isWritable)
1560 *aPermissions |= PR_IWUSR|PR_IWGRP|PR_IWOTH; // all write
1561 if (isExecutable)
1562 *aPermissions |= PR_IXUSR|PR_IXGRP|PR_IXOTH; // all execute
1563
1564 return NS_OK;
1565}
1566
1567NS_IMETHODIMP
1568nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissions)
1569{
1570 NS_ENSURE_ARG(aPermissions);
1571
1572 // The caller is assumed to have already called IsSymlink
1573 // and to have found that this file is a link. It is not
1574 // possible for a link file to be executable.
1575
1576 DWORD word = GetFileAttributes(mWorkingPath.get());
1577 if (word == INVALID_FILE_ATTRIBUTES)
1578 return NS_ERROR_FILE_INVALID_PATH;
1579
1580 PRBool isWritable = !(word & FILE_ATTRIBUTE_READONLY);
1581 *aPermissions = PR_IRUSR|PR_IRGRP|PR_IROTH; // all read
1582 if (isWritable)
1583 *aPermissions |= PR_IWUSR|PR_IWGRP|PR_IWOTH; // all write
1584
1585 return NS_OK;
1586}
1587
1588
1589NS_IMETHODIMP
1590nsLocalFile::SetPermissions(PRUint32 aPermissions)
1591{
1592 // set the permissions of the target as determined by mFollowSymlinks
1593 // If PR_TRUE, then this will be for the target of the shortcut file,
1594 // otherwise it will be for the shortcut file itself (i.e. the same
1595 // results as SetPermissionsOfLink)
1596 nsresult rv = ResolveAndStat();
1597 if (NS_FAILED(rv))
1598 return rv;
1599
1600 // windows only knows about the following permissions
1601 int mode = 0;
1602 if (aPermissions & (PR_IRUSR|PR_IRGRP|PR_IROTH)) // any read
1603 mode |= _S_IREAD;
1604 if (aPermissions & (PR_IWUSR|PR_IWGRP|PR_IWOTH)) // any write
1605 mode |= _S_IWRITE;
1606
1607 if (chmod(mResolvedPath.get(), mode) == -1)
1608 return NS_ERROR_FAILURE;
1609
1610 return NS_OK;
1611}
1612
1613NS_IMETHODIMP
1614nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
1615{
1616 // The caller is assumed to have already called IsSymlink
1617 // and to have found that this file is a link.
1618
1619 // windows only knows about the following permissions
1620 int mode = 0;
1621 if (aPermissions & (PR_IRUSR|PR_IRGRP|PR_IROTH)) // any read
1622 mode |= _S_IREAD;
1623 if (aPermissions & (PR_IWUSR|PR_IWGRP|PR_IWOTH)) // any write
1624 mode |= _S_IWRITE;
1625
1626 if (chmod(mWorkingPath.get(), mode) == -1)
1627 return NS_ERROR_FAILURE;
1628
1629 return NS_OK;
1630}
1631
1632
1633NS_IMETHODIMP
1634nsLocalFile::GetFileSize(PRInt64 *aFileSize)
1635{
1636 NS_ENSURE_ARG(aFileSize);
1637
1638 nsresult rv = ResolveAndStat();
1639 if (NS_FAILED(rv))
1640 return rv;
1641
1642 *aFileSize = mFileInfo64.size;
1643 return NS_OK;
1644}
1645
1646
1647NS_IMETHODIMP
1648nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
1649{
1650 NS_ENSURE_ARG(aFileSize);
1651
1652 // The caller is assumed to have already called IsSymlink
1653 // and to have found that this file is a link.
1654
1655 PRFileInfo64 info;
1656 if (!PR_GetFileInfo64(mWorkingPath.get(), &info))
1657 return NS_ERROR_FILE_INVALID_PATH;
1658
1659 *aFileSize = info.size;
1660 return NS_OK;
1661}
1662
1663NS_IMETHODIMP
1664nsLocalFile::SetFileSize(PRInt64 aFileSize)
1665{
1666 nsresult rv = ResolveAndStat();
1667 if (NS_FAILED(rv))
1668 return rv;
1669
1670 HANDLE hFile = CreateFile(mResolvedPath.get(), // pointer to name of the file
1671 GENERIC_WRITE, // access (write) mode
1672 FILE_SHARE_READ, // share mode
1673 NULL, // pointer to security attributes
1674 OPEN_EXISTING, // how to create
1675 FILE_ATTRIBUTE_NORMAL, // file attributes
1676 NULL);
1677 if (hFile == INVALID_HANDLE_VALUE)
1678 {
1679 return ConvertWinError(GetLastError());
1680 }
1681
1682 // seek the file pointer to the new, desired end of file
1683 // and then truncate the file at that position
1684 rv = NS_ERROR_FAILURE;
1685 aFileSize = MyFileSeek64(hFile, aFileSize, FILE_BEGIN);
1686 if (aFileSize != -1 && SetEndOfFile(hFile))
1687 {
1688 MakeDirty();
1689 rv = NS_OK;
1690 }
1691
1692 CloseHandle(hFile);
1693 return rv;
1694}
1695
1696typedef BOOL (WINAPI *fpGetDiskFreeSpaceExA)(LPCTSTR lpDirectoryName,
1697 PULARGE_INTEGER lpFreeBytesAvailableToCaller,
1698 PULARGE_INTEGER lpTotalNumberOfBytes,
1699 PULARGE_INTEGER lpTotalNumberOfFreeBytes);
1700
1701NS_IMETHODIMP
1702nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
1703{
1704 NS_ENSURE_ARG(aDiskSpaceAvailable);
1705
1706 ResolveAndStat();
1707
1708 // Attempt to check disk space using the GetDiskFreeSpaceExA function.
1709 // --- FROM MSDN ---
1710 // The GetDiskFreeSpaceEx function is available beginning with Windows 95 OEM Service
1711 // Release 2 (OSR2). To determine whether GetDiskFreeSpaceEx is available, call
1712 // GetModuleHandle to get the handle to Kernel32.dll. Then you can call GetProcAddress.
1713 // It is not necessary to call LoadLibrary on Kernel32.dll because it is already loaded
1714 // into every process address space.
1715 fpGetDiskFreeSpaceExA pGetDiskFreeSpaceExA = (fpGetDiskFreeSpaceExA)
1716 GetProcAddress(GetModuleHandle("kernel32.dll"), "GetDiskFreeSpaceExA");
1717 if (pGetDiskFreeSpaceExA)
1718 {
1719 ULARGE_INTEGER liFreeBytesAvailableToCaller, liTotalNumberOfBytes;
1720 if (pGetDiskFreeSpaceExA(mResolvedPath.get(), &liFreeBytesAvailableToCaller,
1721 &liTotalNumberOfBytes, NULL))
1722 {
1723 *aDiskSpaceAvailable = liFreeBytesAvailableToCaller.QuadPart;
1724 return NS_OK;
1725 }
1726 }
1727
1728 // use the old method of getting available disk space
1729 char aDrive[_MAX_DRIVE + 2];
1730 _splitpath( mResolvedPath.get(), aDrive, NULL, NULL, NULL);
1731 strcat(aDrive, "\\");
1732
1733 DWORD dwSecPerClus, dwBytesPerSec, dwFreeClus, dwTotalClus;
1734 if (GetDiskFreeSpace(aDrive, &dwSecPerClus, &dwBytesPerSec, &dwFreeClus, &dwTotalClus))
1735 {
1736 __int64 bytes = dwFreeClus;
1737 bytes *= dwSecPerClus;
1738 bytes *= dwBytesPerSec;
1739
1740 *aDiskSpaceAvailable = bytes;
1741 return NS_OK;
1742 }
1743
1744 return NS_ERROR_FAILURE;
1745}
1746
1747NS_IMETHODIMP
1748nsLocalFile::GetParent(nsIFile * *aParent)
1749{
1750 NS_ENSURE_ARG_POINTER(aParent);
1751
1752 nsCAutoString parentPath(mWorkingPath);
1753
1754 // cannot use nsCString::RFindChar() due to 0x5c problem
1755 PRInt32 offset = (PRInt32) (_mbsrchr((const unsigned char *) parentPath.get(), '\\')
1756 - (const unsigned char *) parentPath.get());
1757 // adding this offset check that was removed in bug 241708 fixes mail
1758 // directories that aren't relative to/underneath the profile dir.
1759 // e.g., on a different drive. Before you remove them, please make
1760 // sure local mail directories that aren't underneath the profile dir work.
1761 if (offset < 0)
1762 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
1763
1764 if (offset == 1 && parentPath[0] == '\\') {
1765 aParent = nsnull;
1766 return NS_OK;
1767 }
1768 if (offset > 0)
1769 parentPath.Truncate(offset);
1770 else
1771 parentPath.AssignLiteral("\\\\.");
1772
1773 nsCOMPtr<nsILocalFile> localFile;
1774 nsresult rv = NS_NewNativeLocalFile(parentPath, mFollowSymlinks, getter_AddRefs(localFile));
1775
1776 if(NS_SUCCEEDED(rv) && localFile)
1777 {
1778 return CallQueryInterface(localFile, aParent);
1779 }
1780 return rv;
1781}
1782
1783NS_IMETHODIMP
1784nsLocalFile::Exists(PRBool *_retval)
1785{
1786 NS_ENSURE_ARG(_retval);
1787 *_retval = PR_FALSE;
1788
1789 MakeDirty();
1790 nsresult rv = ResolveAndStat();
1791 *_retval = NS_SUCCEEDED(rv);
1792
1793 return NS_OK;
1794}
1795
1796NS_IMETHODIMP
1797nsLocalFile::IsWritable(PRBool *aIsWritable)
1798{
1799 //TODO: extend to support NTFS file permissions
1800
1801 // The read-only attribute on a FAT directory only means that it can't
1802 // be deleted. It is still possible to modify the contents of the directory.
1803 nsresult rv = IsDirectory(aIsWritable);
1804 if (NS_FAILED(rv))
1805 return rv;
1806 if (*aIsWritable)
1807 return NS_OK;
1808
1809 // writable if the file doesn't have the readonly attribute
1810 rv = HasFileAttribute(FILE_ATTRIBUTE_READONLY, aIsWritable);
1811 if (NS_FAILED(rv))
1812 return rv;
1813 *aIsWritable = !*aIsWritable;
1814
1815 return NS_OK;
1816}
1817
1818NS_IMETHODIMP
1819nsLocalFile::IsReadable(PRBool *_retval)
1820{
1821 NS_ENSURE_ARG(_retval);
1822 *_retval = PR_FALSE;
1823
1824 nsresult rv = ResolveAndStat();
1825 if (NS_FAILED(rv))
1826 return rv;
1827
1828 *_retval = PR_TRUE;
1829 return NS_OK;
1830}
1831
1832
1833NS_IMETHODIMP
1834nsLocalFile::IsExecutable(PRBool *_retval)
1835{
1836 NS_ENSURE_ARG(_retval);
1837 *_retval = PR_FALSE;
1838
1839 nsresult rv;
1840
1841 // only files can be executables
1842 PRBool isFile;
1843 rv = IsFile(&isFile);
1844 if (NS_FAILED(rv))
1845 return rv;
1846 if (!isFile)
1847 return NS_OK;
1848
1849 //TODO: shouldn't we be checking mFollowSymlinks here?
1850 PRBool symLink;
1851 rv = IsSymlink(&symLink);
1852 if (NS_FAILED(rv))
1853 return rv;
1854
1855 nsCAutoString path;
1856 if (symLink)
1857 GetNativeTarget(path);
1858 else
1859 GetNativePath(path);
1860
1861 // Get extension.
1862 char * ext = (char *) _mbsrchr((unsigned char *)path.BeginWriting(), '.');
1863 if ( ext ) {
1864 // Convert extension to lower case.
1865 for( unsigned char *p = (unsigned char *)ext; *p; p++ )
1866 *p = _mbctolower( *p );
1867
1868 // Search for any of the set of executable extensions.
1869 const char * const executableExts[] = {
1870 ".ad",
1871 ".adp",
1872 ".asp",
1873 ".bas",
1874 ".bat",
1875 ".chm",
1876 ".cmd",
1877 ".com",
1878 ".cpl",
1879 ".crt",
1880 ".exe",
1881 ".hlp",
1882 ".hta",
1883 ".inf",
1884 ".ins",
1885 ".isp",
1886 ".js",
1887 ".jse",
1888 ".lnk",
1889 ".mdb",
1890 ".mde",
1891 ".msc",
1892 ".msi",
1893 ".msp",
1894 ".mst",
1895 ".pcd",
1896 ".pif",
1897 ".reg",
1898 ".scr",
1899 ".sct",
1900 ".shb",
1901 ".shs",
1902 ".url",
1903 ".vb",
1904 ".vbe",
1905 ".vbs",
1906 ".vsd",
1907 ".vss",
1908 ".vst",
1909 ".vsw",
1910 ".ws",
1911 ".wsc",
1912 ".wsf",
1913 ".wsh",
1914 0 };
1915 for ( int i = 0; executableExts[i]; i++ ) {
1916 if ( ::strcmp( executableExts[i], ext ) == 0 ) {
1917 // Found a match. Set result and quit.
1918 *_retval = PR_TRUE;
1919 break;
1920 }
1921 }
1922 }
1923
1924 return NS_OK;
1925}
1926
1927
1928NS_IMETHODIMP
1929nsLocalFile::IsDirectory(PRBool *_retval)
1930{
1931 NS_ENSURE_ARG(_retval);
1932
1933 nsresult rv = ResolveAndStat();
1934 if (NS_FAILED(rv))
1935 return rv;
1936
1937 *_retval = (mFileInfo64.type == PR_FILE_DIRECTORY);
1938 return NS_OK;
1939}
1940
1941NS_IMETHODIMP
1942nsLocalFile::IsFile(PRBool *_retval)
1943{
1944 NS_ENSURE_ARG(_retval);
1945
1946 nsresult rv = ResolveAndStat();
1947 if (NS_FAILED(rv))
1948 return rv;
1949
1950 *_retval = (mFileInfo64.type == PR_FILE_FILE);
1951 return NS_OK;
1952}
1953
1954NS_IMETHODIMP
1955nsLocalFile::IsHidden(PRBool *_retval)
1956{
1957 return HasFileAttribute(FILE_ATTRIBUTE_HIDDEN, _retval);
1958}
1959
1960nsresult
1961nsLocalFile::HasFileAttribute(DWORD fileAttrib, PRBool *_retval)
1962{
1963 NS_ENSURE_ARG(_retval);
1964
1965 nsresult rv = ResolveAndStat();
1966 if (NS_FAILED(rv))
1967 return rv;
1968
1969 // get the file attributes for the correct item depending on following symlinks
1970 const char *filePath = mFollowSymlinks ? mResolvedPath.get() : mWorkingPath.get();
1971 DWORD word = GetFileAttributes(filePath);
1972
1973 *_retval = ((word & fileAttrib) != 0);
1974 return NS_OK;
1975}
1976
1977NS_IMETHODIMP
1978nsLocalFile::IsSymlink(PRBool *_retval)
1979{
1980 NS_ENSURE_ARG(_retval);
1981
1982 // unless it is a valid shortcut path it's not a symlink
1983 if (!IsShortcutPath(mWorkingPath.get()))
1984 {
1985 *_retval = PR_FALSE;
1986 return NS_OK;
1987 }
1988
1989 // we need to know if this is a file or directory
1990 nsresult rv = ResolveAndStat();
1991 if (NS_FAILED(rv))
1992 return rv;
1993
1994 // it's only a shortcut if it is a file
1995 *_retval = (mFileInfo64.type == PR_FILE_FILE);
1996 return NS_OK;
1997}
1998
1999NS_IMETHODIMP
2000nsLocalFile::IsSpecial(PRBool *_retval)
2001{
2002 return HasFileAttribute(FILE_ATTRIBUTE_SYSTEM, _retval);
2003}
2004
2005NS_IMETHODIMP
2006nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
2007{
2008 NS_ENSURE_ARG(inFile);
2009 NS_ENSURE_ARG(_retval);
2010
2011 nsCAutoString inFilePath;
2012 inFile->GetNativePath(inFilePath);
2013
2014 *_retval = inFilePath.Equals(mWorkingPath);
2015 return NS_OK;
2016}
2017
2018NS_IMETHODIMP
2019nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
2020{
2021 *_retval = PR_FALSE;
2022
2023 nsCAutoString myFilePath;
2024 if ( NS_FAILED(GetNativeTarget(myFilePath)))
2025 GetNativePath(myFilePath);
2026
2027 PRInt32 myFilePathLen = myFilePath.Length();
2028
2029 nsCAutoString inFilePath;
2030 if ( NS_FAILED(inFile->GetNativeTarget(inFilePath)))
2031 inFile->GetNativePath(inFilePath);
2032
2033 if ( strnicmp( myFilePath.get(), inFilePath.get(), myFilePathLen) == 0)
2034 {
2035 // now make sure that the |inFile|'s path has a trailing
2036 // separator.
2037
2038 if (inFilePath[myFilePathLen] == '\\')
2039 {
2040 *_retval = PR_TRUE;
2041 }
2042
2043 }
2044
2045 return NS_OK;
2046}
2047
2048
2049
2050NS_IMETHODIMP
2051nsLocalFile::GetNativeTarget(nsACString &_retval)
2052{
2053 _retval.Truncate();
2054#if STRICT_FAKE_SYMLINKS
2055 PRBool symLink;
2056
2057 nsresult rv = IsSymlink(&symLink);
2058 if (NS_FAILED(rv))
2059 return rv;
2060
2061 if (!symLink)
2062 {
2063 return NS_ERROR_FILE_INVALID_PATH;
2064 }
2065#endif
2066 ResolveAndStat();
2067
2068 _retval = mResolvedPath;
2069 return NS_OK;
2070}
2071
2072
2073/* attribute PRBool followLinks; */
2074NS_IMETHODIMP
2075nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
2076{
2077 *aFollowLinks = mFollowSymlinks;
2078 return NS_OK;
2079}
2080NS_IMETHODIMP
2081nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
2082{
2083 MakeDirty();
2084 mFollowSymlinks = aFollowLinks;
2085 return NS_OK;
2086}
2087
2088
2089NS_IMETHODIMP
2090nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator * *entries)
2091{
2092 nsresult rv;
2093
2094 *entries = nsnull;
2095 if (mWorkingPath.EqualsLiteral("\\\\.")) {
2096 nsDriveEnumerator *drives = new nsDriveEnumerator;
2097 if (!drives)
2098 return NS_ERROR_OUT_OF_MEMORY;
2099 NS_ADDREF(drives);
2100 rv = drives->Init();
2101 if (NS_FAILED(rv)) {
2102 NS_RELEASE(drives);
2103 return rv;
2104 }
2105 *entries = drives;
2106 return NS_OK;
2107 }
2108
2109 PRBool isDir;
2110 rv = IsDirectory(&isDir);
2111 if (NS_FAILED(rv))
2112 return rv;
2113 if (!isDir)
2114 return NS_ERROR_FILE_NOT_DIRECTORY;
2115
2116 nsDirEnumerator* dirEnum = new nsDirEnumerator();
2117 if (dirEnum == nsnull)
2118 return NS_ERROR_OUT_OF_MEMORY;
2119 NS_ADDREF(dirEnum);
2120 rv = dirEnum->Init(this);
2121 if (NS_FAILED(rv))
2122 {
2123 NS_RELEASE(dirEnum);
2124 return rv;
2125 }
2126
2127 *entries = dirEnum;
2128 return NS_OK;
2129}
2130
2131NS_IMETHODIMP
2132nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
2133{
2134 return GetNativePath(aPersistentDescriptor);
2135}
2136
2137NS_IMETHODIMP
2138nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
2139{
2140 return InitWithNativePath(aPersistentDescriptor);
2141}
2142
2143NS_IMETHODIMP
2144nsLocalFile::Reveal()
2145{
2146 // make sure mResolvedPath is set
2147 nsresult rv = ResolveAndStat();
2148 if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
2149 return rv;
2150
2151 // use the full path to explorer for security
2152 nsCOMPtr<nsILocalFile> winDir;
2153 rv = GetSpecialSystemDirectory(Win_WindowsDirectory, getter_AddRefs(winDir));
2154 NS_ENSURE_SUCCESS(rv, rv);
2155 nsCAutoString explorerPath;
2156 rv = winDir->GetNativePath(explorerPath);
2157 NS_ENSURE_SUCCESS(rv, rv);
2158 explorerPath.Append("\\explorer.exe");
2159
2160 // Always open a new window for files because Win2K doesn't appear to select
2161 // the file if a window showing that folder was already open. If the resolved
2162 // path is a directory then instead of opening the parent and selecting it,
2163 // we open the directory itself.
2164 nsCAutoString explorerParams;
2165 if (mFileInfo64.type != PR_FILE_DIRECTORY) // valid because we ResolveAndStat above
2166 explorerParams.Append("/n,/select,");
2167 explorerParams.Append('\"');
2168 explorerParams.Append(mResolvedPath);
2169 explorerParams.Append('\"');
2170
2171 if (::ShellExecute(NULL, "open", explorerPath.get(), explorerParams.get(),
2172 NULL, SW_SHOWNORMAL) <= (HINSTANCE) 32)
2173 return NS_ERROR_FAILURE;
2174
2175 return NS_OK;
2176}
2177
2178
2179NS_IMETHODIMP
2180nsLocalFile::Launch()
2181{
2182 const nsCString &path = mWorkingPath;
2183
2184 // use the app registry name to launch a shell execute....
2185 LONG r = (LONG) ::ShellExecute( NULL, NULL, path.get(), NULL, NULL, SW_SHOWNORMAL);
2186
2187 // if the file has no association, we launch windows' "what do you want to do" dialog
2188 if (r == SE_ERR_NOASSOC) {
2189 nsCAutoString shellArg;
2190 shellArg.Assign(NS_LITERAL_CSTRING("shell32.dll,OpenAs_RunDLL ") + path);
2191 r = (LONG) ::ShellExecute(NULL, NULL, "RUNDLL32.EXE", shellArg.get(),
2192 NULL, SW_SHOWNORMAL);
2193 }
2194 if (r < 32) {
2195 switch (r) {
2196 case 0:
2197 case SE_ERR_OOM:
2198 return NS_ERROR_OUT_OF_MEMORY;
2199 case ERROR_FILE_NOT_FOUND:
2200 return NS_ERROR_FILE_NOT_FOUND;
2201 case ERROR_PATH_NOT_FOUND:
2202 return NS_ERROR_FILE_UNRECOGNIZED_PATH;
2203 case ERROR_BAD_FORMAT:
2204 return NS_ERROR_FILE_CORRUPTED;
2205 case SE_ERR_ACCESSDENIED:
2206 return NS_ERROR_FILE_ACCESS_DENIED;
2207 case SE_ERR_ASSOCINCOMPLETE:
2208 case SE_ERR_NOASSOC:
2209 return NS_ERROR_UNEXPECTED;
2210 case SE_ERR_DDEBUSY:
2211 case SE_ERR_DDEFAIL:
2212 case SE_ERR_DDETIMEOUT:
2213 return NS_ERROR_NOT_AVAILABLE;
2214 case SE_ERR_DLLNOTFOUND:
2215 return NS_ERROR_FAILURE;
2216 case SE_ERR_SHARE:
2217 return NS_ERROR_FILE_IS_LOCKED;
2218 default:
2219 return NS_ERROR_FILE_EXECUTION_FAILED;
2220 }
2221 }
2222 return NS_OK;
2223}
2224
2225nsresult
2226NS_NewNativeLocalFile(const nsACString &path, PRBool followLinks, nsILocalFile* *result)
2227{
2228 nsLocalFile* file = new nsLocalFile();
2229 if (file == nsnull)
2230 return NS_ERROR_OUT_OF_MEMORY;
2231 NS_ADDREF(file);
2232
2233 file->SetFollowLinks(followLinks);
2234
2235 if (!path.IsEmpty()) {
2236 nsresult rv = file->InitWithNativePath(path);
2237 if (NS_FAILED(rv)) {
2238 NS_RELEASE(file);
2239 return rv;
2240 }
2241 }
2242
2243 *result = file;
2244 return NS_OK;
2245}
2246
2247//-----------------------------------------------------------------------------
2248// UCS2 interface
2249//-----------------------------------------------------------------------------
2250
2251nsresult
2252nsLocalFile::InitWithPath(const nsAString &filePath)
2253{
2254 nsCAutoString tmp;
2255 nsresult rv = NS_CopyUnicodeToNative(filePath, tmp);
2256 if (NS_SUCCEEDED(rv))
2257 return InitWithNativePath(tmp);
2258
2259 return rv;
2260}
2261
2262nsresult
2263nsLocalFile::Append(const nsAString &node)
2264{
2265 nsCAutoString tmp;
2266 nsresult rv = NS_CopyUnicodeToNative(node, tmp);
2267 if (NS_SUCCEEDED(rv))
2268 return AppendNative(tmp);
2269
2270 return rv;
2271}
2272
2273nsresult
2274nsLocalFile::AppendRelativePath(const nsAString &node)
2275{
2276 nsCAutoString tmp;
2277 nsresult rv = NS_CopyUnicodeToNative(node, tmp);
2278 if (NS_SUCCEEDED(rv))
2279 return AppendRelativeNativePath(tmp);
2280 return rv;
2281}
2282
2283nsresult
2284nsLocalFile::GetLeafName(nsAString &aLeafName)
2285{
2286 nsCAutoString tmp;
2287 nsresult rv = GetNativeLeafName(tmp);
2288 if (NS_SUCCEEDED(rv))
2289 rv = NS_CopyNativeToUnicode(tmp, aLeafName);
2290
2291 return rv;
2292}
2293
2294nsresult
2295nsLocalFile::SetLeafName(const nsAString &aLeafName)
2296{
2297 nsCAutoString tmp;
2298 nsresult rv = NS_CopyUnicodeToNative(aLeafName, tmp);
2299 if (NS_SUCCEEDED(rv))
2300 return SetNativeLeafName(tmp);
2301
2302 return rv;
2303}
2304
2305nsresult
2306nsLocalFile::GetPath(nsAString &_retval)
2307{
2308 return NS_CopyNativeToUnicode(mWorkingPath, _retval);
2309}
2310
2311nsresult
2312nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
2313{
2314 if (newName.IsEmpty())
2315 return CopyToNative(newParentDir, nsCString());
2316
2317 nsCAutoString tmp;
2318 nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
2319 if (NS_SUCCEEDED(rv))
2320 return CopyToNative(newParentDir, tmp);
2321
2322 return rv;
2323}
2324
2325nsresult
2326nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
2327{
2328 if (newName.IsEmpty())
2329 return CopyToFollowingLinksNative(newParentDir, nsCString());
2330
2331 nsCAutoString tmp;
2332 nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
2333 if (NS_SUCCEEDED(rv))
2334 return CopyToFollowingLinksNative(newParentDir, tmp);
2335
2336 return rv;
2337}
2338
2339nsresult
2340nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
2341{
2342 if (newName.IsEmpty())
2343 return MoveToNative(newParentDir, nsCString());
2344
2345 nsCAutoString tmp;
2346 nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
2347 if (NS_SUCCEEDED(rv))
2348 return MoveToNative(newParentDir, tmp);
2349
2350 return rv;
2351}
2352
2353nsresult
2354nsLocalFile::GetTarget(nsAString &_retval)
2355{
2356 nsCAutoString tmp;
2357 nsresult rv = GetNativeTarget(tmp);
2358 if (NS_SUCCEEDED(rv))
2359 rv = NS_CopyNativeToUnicode(tmp, _retval);
2360
2361 return rv;
2362}
2363
2364nsresult
2365NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
2366{
2367 nsCAutoString buf;
2368 nsresult rv = NS_CopyUnicodeToNative(path, buf);
2369 if (NS_FAILED(rv)) {
2370 *result = nsnull;
2371 return rv;
2372 }
2373 return NS_NewNativeLocalFile(buf, followLinks, result);
2374}
2375
2376//-----------------------------------------------------------------------------
2377// nsLocalFile <static members>
2378//-----------------------------------------------------------------------------
2379
2380void
2381nsLocalFile::GlobalInit()
2382{
2383 nsresult rv = NS_CreateShortcutResolver();
2384 NS_ASSERTION(NS_SUCCEEDED(rv), "Shortcut resolver could not be created");
2385}
2386
2387void
2388nsLocalFile::GlobalShutdown()
2389{
2390 NS_DestroyShortcutResolver();
2391}
2392
2393NS_IMPL_ISUPPORTS1(nsDriveEnumerator, nsISimpleEnumerator)
2394
2395nsDriveEnumerator::nsDriveEnumerator()
2396 : mLetter(0)
2397{
2398}
2399
2400nsDriveEnumerator::~nsDriveEnumerator()
2401{
2402}
2403
2404nsresult nsDriveEnumerator::Init()
2405{
2406 /* If the length passed to GetLogicalDriveStrings is smaller
2407 * than the length of the string it would return, it returns
2408 * the length required for the string. */
2409 DWORD length = GetLogicalDriveStrings(0, 0);
2410 /* The string is null terminated */
2411 mDrives.SetLength(length+1);
2412 if (!GetLogicalDriveStrings(length, mDrives.BeginWriting()))
2413 return NS_ERROR_FAILURE;
2414 mLetter = mDrives.get();
2415 return NS_OK;
2416}
2417
2418NS_IMETHODIMP nsDriveEnumerator::HasMoreElements(PRBool *aHasMore)
2419{
2420 *aHasMore = *mLetter != '\0';
2421 return NS_OK;
2422}
2423
2424NS_IMETHODIMP nsDriveEnumerator::GetNext(nsISupports **aNext)
2425{
2426 /* GetLogicalDrives stored in mLetter is a concatenation
2427 * of null terminated strings, followed by a null terminator. */
2428 if (!*mLetter) {
2429 *aNext = nsnull;
2430 return NS_OK;
2431 }
2432 const char *drive = mLetter;
2433 mLetter += strlen(drive) + 1;
2434 nsILocalFile *file;
2435 nsresult rv =
2436 NS_NewNativeLocalFile(nsDependentCString(drive), PR_FALSE, &file);
2437
2438 *aNext = file;
2439 return rv;
2440}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette