VirtualBox

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

Last change on this file since 4837 was 1, checked in by vboxsync, 54 years ago

import

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

© 2023 Oracle
ContactPrivacy policyTerms of Use