зеркало из https://github.com/mozilla/pjs.git
2465 строки
66 KiB
C++
2465 строки
66 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* The contents of this file are subject to the Netscape Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is Mozilla Communicator client code,
|
|
* released March 31, 1998.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape Communications
|
|
* Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Doug Turner <dougt@netscape.com>
|
|
*/
|
|
|
|
|
|
#include "nsCOMPtr.h"
|
|
#include "nsMemory.h"
|
|
|
|
#include "nsLocalFile.h"
|
|
#include "nsNativeCharsetUtils.h"
|
|
|
|
#include "nsISimpleEnumerator.h"
|
|
#include "nsIComponentManager.h"
|
|
#include "prtypes.h"
|
|
#include "prio.h"
|
|
|
|
#include "nsXPIDLString.h"
|
|
#include "nsReadableUtils.h"
|
|
|
|
#include <direct.h>
|
|
#include <windows.h>
|
|
|
|
#include "shellapi.h"
|
|
#include "shlguid.h"
|
|
|
|
#include <io.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <mbstring.h>
|
|
|
|
#include "nsXPIDLString.h"
|
|
#include "prproces.h"
|
|
#include "nsITimelineService.h"
|
|
|
|
#include "nsAutoLock.h"
|
|
|
|
// _mbsstr isn't declared in w32api headers but it's there in the libs
|
|
#ifdef __MINGW32__
|
|
extern "C" {
|
|
unsigned char *_mbsstr( const unsigned char *str,
|
|
const unsigned char *substr );
|
|
};
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------------
|
|
// short cut resolver
|
|
//----------------------------------------------------------------------------
|
|
|
|
class ShortcutResolver
|
|
{
|
|
public:
|
|
ShortcutResolver();
|
|
virtual ~ShortcutResolver();
|
|
|
|
nsresult Init();
|
|
nsresult Resolve(const WCHAR* in, char* out);
|
|
|
|
private:
|
|
PRLock* mLock;
|
|
IPersistFile* mPersistFile;
|
|
IShellLink* mShellLink;
|
|
};
|
|
|
|
ShortcutResolver::ShortcutResolver()
|
|
{
|
|
mLock = nsnull;
|
|
mPersistFile = nsnull;
|
|
mShellLink = nsnull;
|
|
}
|
|
|
|
ShortcutResolver::~ShortcutResolver()
|
|
{
|
|
if (mLock)
|
|
PR_DestroyLock(mLock);
|
|
|
|
// Release the pointer to the IPersistFile interface.
|
|
if (mPersistFile)
|
|
mPersistFile->Release();
|
|
|
|
// Release the pointer to the IShellLink interface.
|
|
if(mShellLink)
|
|
mShellLink->Release();
|
|
|
|
CoUninitialize();
|
|
}
|
|
|
|
nsresult
|
|
ShortcutResolver::Init()
|
|
{
|
|
CoInitialize(NULL); // FIX: we should probably move somewhere higher up during startup
|
|
|
|
mLock = PR_NewLock();
|
|
if (!mLock)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
HRESULT hres = CoCreateInstance(CLSID_ShellLink,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IShellLink,
|
|
(void**)&mShellLink);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// Get a pointer to the IPersistFile interface.
|
|
hres = mShellLink->QueryInterface(IID_IPersistFile, (void**)&mPersistFile);
|
|
}
|
|
|
|
if (mPersistFile == nsnull || mShellLink == nsnull)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// |out| must be an allocated buffer of size MAX_PATH
|
|
nsresult
|
|
ShortcutResolver::Resolve(const WCHAR* in, char* out)
|
|
{
|
|
nsAutoLock lock(mLock);
|
|
|
|
// see if we can Load the path.
|
|
HRESULT hres = mPersistFile->Load(in, STGM_READ);
|
|
|
|
if (FAILED(hres))
|
|
return NS_ERROR_FAILURE;
|
|
|
|
// Resolve the link.
|
|
hres = mShellLink->Resolve(nsnull, SLR_NO_UI );
|
|
|
|
if (FAILED(hres))
|
|
return NS_ERROR_FAILURE;
|
|
|
|
WIN32_FIND_DATA wfd;
|
|
// Get the path to the link target.
|
|
hres = mShellLink->GetPath( out, MAX_PATH, &wfd, SLGP_UNCPRIORITY );
|
|
if (FAILED(hres))
|
|
return NS_ERROR_FAILURE;
|
|
return NS_OK;
|
|
}
|
|
|
|
static ShortcutResolver * gResolver = nsnull;
|
|
|
|
static nsresult NS_CreateShortcutResolver()
|
|
{
|
|
gResolver = new ShortcutResolver();
|
|
if (!gResolver)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
return gResolver->Init();
|
|
}
|
|
|
|
static void NS_DestroyShortcutResolver()
|
|
{
|
|
delete gResolver;
|
|
gResolver = nsnull;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// static helper functions
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// certainly not all the error that can be
|
|
// encountered, but many of them common ones
|
|
static nsresult ConvertWinError(DWORD winErr)
|
|
{
|
|
nsresult rv;
|
|
|
|
switch (winErr)
|
|
{
|
|
case ERROR_FILE_NOT_FOUND:
|
|
case ERROR_PATH_NOT_FOUND:
|
|
case ERROR_INVALID_DRIVE:
|
|
rv = NS_ERROR_FILE_NOT_FOUND;
|
|
break;
|
|
case ERROR_ACCESS_DENIED:
|
|
case ERROR_NOT_SAME_DEVICE:
|
|
rv = NS_ERROR_FILE_ACCESS_DENIED;
|
|
break;
|
|
case ERROR_NOT_ENOUGH_MEMORY:
|
|
case ERROR_INVALID_BLOCK:
|
|
case ERROR_INVALID_HANDLE:
|
|
case ERROR_ARENA_TRASHED:
|
|
rv = NS_ERROR_OUT_OF_MEMORY;
|
|
break;
|
|
case ERROR_CURRENT_DIRECTORY:
|
|
rv = NS_ERROR_FILE_DIR_NOT_EMPTY;
|
|
break;
|
|
case ERROR_WRITE_PROTECT:
|
|
rv = NS_ERROR_FILE_READ_ONLY;
|
|
break;
|
|
case ERROR_HANDLE_DISK_FULL:
|
|
rv = NS_ERROR_FILE_TOO_BIG;
|
|
break;
|
|
case ERROR_FILE_EXISTS:
|
|
case ERROR_ALREADY_EXISTS:
|
|
case ERROR_CANNOT_MAKE:
|
|
rv = NS_ERROR_FILE_ALREADY_EXISTS;
|
|
break;
|
|
case 0:
|
|
rv = NS_OK;
|
|
default:
|
|
rv = NS_ERROR_FAILURE;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
static void
|
|
myLL_II2L(PRInt32 hi, PRInt32 lo, PRInt64 *result)
|
|
{
|
|
PRInt64 a64, b64; // probably could have been done with
|
|
// only one PRInt64, but these are macros,
|
|
// and I am a wimp.
|
|
|
|
// put hi in the low bits of a64.
|
|
LL_I2L(a64, hi);
|
|
// now shift it to the upperbit and place it the result in result
|
|
LL_SHL(b64, a64, 32);
|
|
// now put the low bits on by adding them to the result.
|
|
LL_ADD(*result, b64, lo);
|
|
}
|
|
|
|
|
|
static void
|
|
myLL_L2II(PRInt64 result, PRInt32 *hi, PRInt32 *lo )
|
|
{
|
|
PRInt64 a64, b64; // probably could have been done with
|
|
// only one PRInt64, but these are macros,
|
|
// and I am a wimp.
|
|
|
|
// shift the hi word to the low word, then push it into a long.
|
|
LL_SHR(a64, result, 32);
|
|
LL_L2I(*hi, a64);
|
|
|
|
// shift the low word to the hi word first, then shift it back.
|
|
LL_SHL(b64, result, 32);
|
|
LL_SHR(a64, b64, 32);
|
|
LL_L2I(*lo, a64);
|
|
}
|
|
|
|
static PRBool
|
|
IsShortcut(const char* workingPath, int filePathLen)
|
|
{
|
|
// XXX this is badly broken!!
|
|
// XXX consider "C:\FOO.LNK"
|
|
// XXX consider "C:\foo.lnkx\bar.lnk"
|
|
|
|
// check to see if it is shortcut, i.e., it has ".lnk" in it
|
|
unsigned char* dest = _mbsstr((unsigned char*)workingPath,
|
|
(unsigned char*)".lnk");
|
|
if (!dest)
|
|
return PR_FALSE;
|
|
|
|
// find index of ".lnk"
|
|
int result = (int)(dest - (unsigned char*)workingPath);
|
|
|
|
// if ".lnk" is not at the leaf of this path, we need to make sure the
|
|
// next char after ".lnk" is a '\\'. e.g. "c:\\foo.lnk\\a.html" is valid,
|
|
// whereas "c:\\foo.lnkx" is not.
|
|
if (result + 4 < filePathLen)
|
|
{
|
|
if (workingPath[result + 4] != '\\')
|
|
return PR_FALSE;
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// nsDirEnumerator
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class nsDirEnumerator : public nsISimpleEnumerator
|
|
{
|
|
public:
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
nsDirEnumerator() : mDir(nsnull)
|
|
{
|
|
}
|
|
|
|
nsresult Init(nsILocalFile* parent)
|
|
{
|
|
nsCAutoString filepath;
|
|
parent->GetNativeTarget(filepath);
|
|
|
|
if (filepath.IsEmpty())
|
|
{
|
|
parent->GetNativePath(filepath);
|
|
}
|
|
|
|
if (filepath.IsEmpty())
|
|
{
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
mDir = PR_OpenDir(filepath.get());
|
|
if (mDir == nsnull) // not a directory?
|
|
return NS_ERROR_FAILURE;
|
|
|
|
mParent = parent;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD HasMoreElements(PRBool *result)
|
|
{
|
|
nsresult rv;
|
|
if (mNext == nsnull && mDir)
|
|
{
|
|
PRDirEntry* entry = PR_ReadDir(mDir, PR_SKIP_BOTH);
|
|
if (entry == nsnull)
|
|
{
|
|
// end of dir entries
|
|
|
|
PRStatus status = PR_CloseDir(mDir);
|
|
if (status != PR_SUCCESS)
|
|
return NS_ERROR_FAILURE;
|
|
mDir = nsnull;
|
|
|
|
*result = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsIFile> file;
|
|
rv = mParent->Clone(getter_AddRefs(file));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
rv = file->AppendNative(nsDependentCString(entry->name));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
// make sure the thing exists. If it does, try the next one.
|
|
PRBool exists;
|
|
rv = file->Exists(&exists);
|
|
if (NS_FAILED(rv) || !exists)
|
|
{
|
|
return HasMoreElements(result);
|
|
}
|
|
|
|
mNext = do_QueryInterface(file);
|
|
}
|
|
*result = mNext != nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD GetNext(nsISupports **result)
|
|
{
|
|
nsresult rv;
|
|
PRBool hasMore;
|
|
rv = HasMoreElements(&hasMore);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
*result = mNext; // might return nsnull
|
|
NS_IF_ADDREF(*result);
|
|
|
|
mNext = nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
virtual ~nsDirEnumerator()
|
|
{
|
|
if (mDir)
|
|
{
|
|
PRStatus status = PR_CloseDir(mDir);
|
|
NS_ASSERTION(status == PR_SUCCESS, "close failed");
|
|
}
|
|
}
|
|
|
|
protected:
|
|
PRDir* mDir;
|
|
nsCOMPtr<nsILocalFile> mParent;
|
|
nsCOMPtr<nsILocalFile> mNext;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS1(nsDirEnumerator, nsISimpleEnumerator);
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// nsLocalFile <public>
|
|
//-----------------------------------------------------------------------------
|
|
|
|
nsLocalFile::nsLocalFile()
|
|
{
|
|
mLastResolution = PR_FALSE;
|
|
mFollowSymlinks = PR_FALSE;
|
|
MakeDirty();
|
|
}
|
|
|
|
nsLocalFile::~nsLocalFile()
|
|
{
|
|
}
|
|
|
|
NS_METHOD
|
|
nsLocalFile::nsLocalFileConstructor(nsISupports* outer, const nsIID& aIID, void* *aInstancePtr)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aInstancePtr);
|
|
NS_ENSURE_NO_AGGREGATION(outer);
|
|
|
|
nsLocalFile* inst = new nsLocalFile();
|
|
if (inst == NULL)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
nsresult rv = inst->QueryInterface(aIID, aInstancePtr);
|
|
if (NS_FAILED(rv))
|
|
{
|
|
delete inst;
|
|
return rv;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// nsLocalFile::nsISupports
|
|
//-----------------------------------------------------------------------------
|
|
|
|
NS_IMPL_THREADSAFE_ISUPPORTS2(nsLocalFile, nsILocalFile, nsIFile)
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// nsLocalFile <private>
|
|
//-----------------------------------------------------------------------------
|
|
|
|
nsLocalFile::nsLocalFile(const nsLocalFile& other)
|
|
: mDirty(other.mDirty)
|
|
, mLastResolution(other.mLastResolution)
|
|
, mFollowSymlinks(other.mFollowSymlinks)
|
|
, mWorkingPath(other.mWorkingPath)
|
|
, mResolvedPath(other.mResolvedPath)
|
|
, mFileInfo64(other.mFileInfo64)
|
|
{
|
|
}
|
|
|
|
// ResolvePath
|
|
// this function will walk the native path of |this| resolving any symbolic
|
|
// links found. The new resulting path will be placed into mResolvedPath.
|
|
nsresult
|
|
nsLocalFile::ResolvePath(const char* workingPath, PRBool resolveTerminal, char** resolvedPath)
|
|
{
|
|
|
|
nsresult rv = NS_OK;
|
|
PRBool isDir = PR_FALSE;
|
|
|
|
// check to see if it is shortcut, i.e., it has ".lnk" in it
|
|
int filePathLen = strlen(workingPath);
|
|
PRBool isShortcut = IsShortcut(workingPath, filePathLen);
|
|
if (!isShortcut)
|
|
return NS_ERROR_FILE_INVALID_PATH;
|
|
|
|
#ifdef DEBUG_dougt
|
|
printf("localfile - resolving symlink\n");
|
|
#endif
|
|
|
|
// Get the native path for |this|
|
|
// allocate extra bytes incase we need to append '\\' and '\0' to the
|
|
// workingPath, if it is just a drive letter and a colon
|
|
char *filePath = (char *) nsMemory::Alloc(filePathLen+2);
|
|
|
|
if (!filePath)
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
memcpy(filePath, workingPath, filePathLen + 1);
|
|
|
|
// We are going to walk the native file path
|
|
// and stop at each slash. For each partial
|
|
// path (the string to the left of the slash)
|
|
// we will check to see if it is a shortcut.
|
|
// if it is, we will resolve it and continue
|
|
// with that resolved path.
|
|
|
|
// Get the first slash.
|
|
unsigned char* slash = _mbschr((unsigned char*) filePath, '\\');
|
|
|
|
if (slash == nsnull)
|
|
{
|
|
if (nsCRT::IsAsciiAlpha(filePath[0]) && filePath[1] == ':' && filePath[2] == '\0')
|
|
{
|
|
// we have a drive letter and a colon (eg 'c:'
|
|
// this is resolve already
|
|
filePath[filePathLen] = '\\';
|
|
filePath[filePathLen+1] = '\0';
|
|
|
|
*resolvedPath = filePath;
|
|
return NS_OK;
|
|
}
|
|
else
|
|
{
|
|
nsMemory::Free(filePath);
|
|
return NS_ERROR_FILE_INVALID_PATH;
|
|
}
|
|
}
|
|
|
|
|
|
// We really cant have just a drive letter as
|
|
// a shortcut, so we will skip the first '\\'
|
|
slash = _mbschr(++slash, '\\');
|
|
|
|
while (slash || resolveTerminal)
|
|
{
|
|
// Change the slash into a null so that
|
|
// we can use the partial path. It is is
|
|
// null, we know it is the terminal node.
|
|
if (slash)
|
|
{
|
|
*slash = '\0';
|
|
}
|
|
else if (resolveTerminal)
|
|
{
|
|
// this is our last time in this loop.
|
|
// set loop condition to false
|
|
resolveTerminal = PR_FALSE;
|
|
}
|
|
else
|
|
{
|
|
// something is wrong. we should not have
|
|
// both slash being null and resolveTerminal
|
|
// not set!
|
|
nsMemory::Free(filePath);
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
// check to see the file is a shortcut by the magic .lnk extension.
|
|
size_t offset = strlen(filePath) - 4;
|
|
if ((offset > 0) && (strnicmp( (filePath + offset), ".lnk", 4) == 0))
|
|
{
|
|
nsAutoString ucsBuf;
|
|
NS_CopyNativeToUnicode(nsDependentCString(filePath), ucsBuf);
|
|
char *temp = (char*) nsMemory::Alloc( MAX_PATH );
|
|
if (temp == nsnull)
|
|
{
|
|
nsMemory::Free(filePath);
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
if (gResolver)
|
|
rv = gResolver->Resolve(ucsBuf.get(), temp);
|
|
else
|
|
rv = NS_ERROR_FAILURE;
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
{
|
|
// found a new path.
|
|
|
|
// addend a slash on it since it does not come out of GetPath()
|
|
// with one only if it is a directory. If it is not a directory
|
|
// and there is more to append, than we have a problem.
|
|
|
|
struct stat st;
|
|
int statrv = stat(temp, &st);
|
|
|
|
if (0 == statrv && (_S_IFDIR & st.st_mode))
|
|
{
|
|
strcat(temp, "\\");
|
|
isDir = PR_TRUE;
|
|
}
|
|
|
|
if (slash)
|
|
{
|
|
// save where we left off.
|
|
char *carot= (temp + strlen(temp) -1 );
|
|
|
|
// append all the stuff that we have not done.
|
|
_mbscat((unsigned char*)temp, ++slash);
|
|
|
|
slash = (unsigned char*)carot;
|
|
}
|
|
|
|
nsMemory::Free(filePath);
|
|
filePath = temp;
|
|
|
|
}
|
|
else
|
|
{
|
|
// could not resolve shortcut. Return error;
|
|
nsMemory::Free(filePath);
|
|
return NS_ERROR_FILE_INVALID_PATH;
|
|
}
|
|
}
|
|
if (slash)
|
|
{
|
|
*slash = '\\';
|
|
++slash;
|
|
slash = _mbschr(slash, '\\');
|
|
}
|
|
}
|
|
|
|
// kill any trailing separator
|
|
char* temp = filePath;
|
|
int len = strlen(temp) - 1;
|
|
if((temp[len] == '\\') && (!isDir))
|
|
temp[len] = '\0';
|
|
|
|
*resolvedPath = filePath;
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsLocalFile::ResolveAndStat(PRBool resolveTerminal)
|
|
{
|
|
if (!mDirty && mLastResolution == resolveTerminal)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
mLastResolution = resolveTerminal;
|
|
mResolvedPath.Assign(mWorkingPath); //until we know better.
|
|
|
|
// First we will see if the workingPath exists. If it does, then we
|
|
// can simply use that as the resolved path. This simplification can
|
|
// be done on windows cause its symlinks (shortcuts) use the .lnk
|
|
// file extension.
|
|
|
|
char temp[4];
|
|
const char* workingFilePath = mWorkingPath.get();
|
|
const char* nsprPath = workingFilePath;
|
|
|
|
if (mWorkingPath.Length() == 2 && mWorkingPath.CharAt(1) == ':') {
|
|
temp[0] = workingFilePath[0];
|
|
temp[1] = workingFilePath[1];
|
|
temp[2] = '\\';
|
|
temp[3] = '\0';
|
|
nsprPath = temp;
|
|
}
|
|
|
|
PRStatus status = PR_GetFileInfo64(nsprPath, &mFileInfo64);
|
|
if ( status == PR_SUCCESS )
|
|
{
|
|
if (!resolveTerminal)
|
|
{
|
|
mDirty = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
// check to see that this is shortcut, i.e., the leaf is ".lnk"
|
|
// if the length < 4, then it's not a link.
|
|
|
|
int pathLen = strlen(workingFilePath);
|
|
const char* leaf = workingFilePath + pathLen - 4;
|
|
|
|
// if we found the file and we are not following symlinks, then return success.
|
|
|
|
if (!mFollowSymlinks || pathLen < 4 || (stricmp(leaf, ".lnk") != 0))
|
|
{
|
|
mDirty = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
if (!mFollowSymlinks)
|
|
return NS_ERROR_FILE_NOT_FOUND; // if we are not resolving, we just give up here.
|
|
|
|
nsresult result;
|
|
|
|
// okay, something is wrong with the working path. We will try to resolve it.
|
|
|
|
char *resolvePath;
|
|
|
|
result = ResolvePath(workingFilePath, resolveTerminal, &resolvePath);
|
|
if (NS_FAILED(result))
|
|
return NS_ERROR_FILE_NOT_FOUND;
|
|
|
|
mResolvedPath.Assign(resolvePath);
|
|
nsMemory::Free(resolvePath);
|
|
|
|
status = PR_GetFileInfo64(mResolvedPath.get(), &mFileInfo64);
|
|
|
|
if ( status == PR_SUCCESS )
|
|
mDirty = PR_FALSE;
|
|
else
|
|
result = NS_ERROR_FILE_NOT_FOUND;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// nsLocalFile::nsIFile,nsILocalFile
|
|
//-----------------------------------------------------------------------------
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Clone(nsIFile **file)
|
|
{
|
|
// Just copy-construct ourselves
|
|
*file = new nsLocalFile(*this);
|
|
if (!*file)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
NS_ADDREF(*file);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::InitWithNativePath(const nsACString &filePath)
|
|
{
|
|
MakeDirty();
|
|
|
|
nsACString::const_iterator begin, end;
|
|
filePath.BeginReading(begin);
|
|
filePath.EndReading(end);
|
|
|
|
// input string must not be empty
|
|
if (begin == end)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
char firstChar = *begin;
|
|
char secondChar = *(++begin);
|
|
|
|
// just do a sanity check. if it has any forward slashes, it is not a Native path
|
|
// on windows. Also, it must have a colon at after the first char.
|
|
|
|
char *path = nsnull;
|
|
PRInt32 pathLen = 0;
|
|
|
|
if ( ( (secondChar == ':') && !FindCharInReadable('/', begin, end) ) || // normal path
|
|
( (firstChar == '\\') && (secondChar == '\\') ) ) // network path
|
|
{
|
|
// This is a native path
|
|
path = ToNewCString(filePath);
|
|
pathLen = filePath.Length();
|
|
}
|
|
|
|
if (path == nsnull)
|
|
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
|
|
|
|
// kill any trailing '\' provided it isn't the second char of DBCS
|
|
PRInt32 len = pathLen - 1;
|
|
if (path[len] == '\\' && (!::IsDBCSLeadByte(path[len-1]) ||
|
|
_mbsrchr((const unsigned char *)path, '\\') == (const unsigned char *)path+len))
|
|
{
|
|
path[len] = '\0';
|
|
pathLen = len;
|
|
}
|
|
|
|
mWorkingPath.Adopt(path, pathLen);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval)
|
|
{
|
|
// check to see if it is shortcut, i.e., it has ".lnk" in it
|
|
PRBool isShortcut = IsShortcut(mWorkingPath.get(), mWorkingPath.Length());
|
|
|
|
if (!isShortcut && mDirty)
|
|
{
|
|
// we will optimize here. If we are not a shortcut and we are opening
|
|
// a file and we are still dirty, assume that the working path is vaild
|
|
// and try to open it. The working path will be different from its
|
|
// resolved path for a shortcut file.
|
|
// If it does work, get the stat info via the file descriptor
|
|
mResolvedPath.Assign(mWorkingPath);
|
|
*_retval = PR_Open(mResolvedPath.get(), flags, mode);
|
|
if (*_retval)
|
|
{
|
|
PRStatus status = PR_GetOpenFileInfo64(*_retval, &mFileInfo64);
|
|
if (status == PR_SUCCESS)
|
|
{
|
|
mDirty = PR_FALSE;
|
|
mLastResolution = PR_TRUE;
|
|
}
|
|
else
|
|
NS_ERROR("FileInfo64 invalid while PR_Open succeeded.");
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
nsresult rv = ResolveAndStat(PR_TRUE);
|
|
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
|
|
return rv;
|
|
|
|
*_retval = PR_Open(mResolvedPath.get(), flags, mode);
|
|
|
|
if (*_retval)
|
|
return NS_OK;
|
|
|
|
return NS_ErrorAccordingToNSPR();
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::OpenANSIFileDesc(const char *mode, FILE * *_retval)
|
|
{
|
|
nsresult rv = ResolveAndStat(PR_TRUE);
|
|
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
|
|
return rv;
|
|
|
|
*_retval = fopen(mResolvedPath.get(), mode);
|
|
|
|
if (*_retval)
|
|
return NS_OK;
|
|
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Create(PRUint32 type, PRUint32 attributes)
|
|
{
|
|
if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE)
|
|
return NS_ERROR_FILE_UNKNOWN_TYPE;
|
|
|
|
nsresult rv = ResolveAndStat(PR_FALSE);
|
|
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
|
|
return rv;
|
|
|
|
// create directories to target
|
|
//
|
|
// A given local file can be either one of these forms:
|
|
//
|
|
// - normal: X:\some\path\on\this\drive
|
|
// ^--- start here
|
|
//
|
|
// - UNC path: \\machine\volume\some\path\on\this\drive
|
|
// ^--- start here
|
|
//
|
|
// Skip the first 'X:\' for the first form, and skip the first full
|
|
// '\\machine\volume\' segment for the second form.
|
|
|
|
const unsigned char* path = (const unsigned char*) mResolvedPath.get();
|
|
if (path[0] == '\\' && path[1] == '\\')
|
|
{
|
|
// dealing with a UNC path here; skip past '\\machine\'
|
|
path = _mbschr(path + 2, '\\');
|
|
if (!path)
|
|
return NS_ERROR_FILE_INVALID_PATH;
|
|
++path;
|
|
}
|
|
|
|
// search for first slash after the drive (or volume) name
|
|
unsigned char* slash = _mbschr(path, '\\');
|
|
|
|
if (slash)
|
|
{
|
|
// skip the first '\\'
|
|
++slash;
|
|
slash = _mbschr(slash, '\\');
|
|
|
|
while (slash)
|
|
{
|
|
*slash = '\0';
|
|
|
|
if (!CreateDirectoryA(mResolvedPath.get(), NULL)) {
|
|
rv = ConvertWinError(GetLastError());
|
|
// perhaps the base path already exists, or perhaps we don't have
|
|
// permissions to create the directory. NOTE: access denied could
|
|
// occur on a parent directory even though it exists.
|
|
if (rv != NS_ERROR_FILE_ALREADY_EXISTS &&
|
|
rv != NS_ERROR_FILE_ACCESS_DENIED)
|
|
return rv;
|
|
}
|
|
*slash = '\\';
|
|
++slash;
|
|
slash = _mbschr(slash, '\\');
|
|
}
|
|
}
|
|
|
|
if (type == NORMAL_FILE_TYPE)
|
|
{
|
|
PRFileDesc* file = PR_Open(mResolvedPath.get(), PR_RDONLY | PR_CREATE_FILE | PR_APPEND | PR_EXCL, attributes);
|
|
if (!file) return NS_ERROR_FILE_ALREADY_EXISTS;
|
|
|
|
PR_Close(file);
|
|
return NS_OK;
|
|
}
|
|
|
|
if (type == DIRECTORY_TYPE)
|
|
{
|
|
if (!CreateDirectoryA(mResolvedPath.get(), NULL))
|
|
return ConvertWinError(GetLastError());
|
|
else
|
|
return NS_OK;
|
|
}
|
|
|
|
return NS_ERROR_FILE_UNKNOWN_TYPE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::AppendNative(const nsACString &node)
|
|
{
|
|
if (node.IsEmpty())
|
|
return NS_OK;
|
|
|
|
// Append only one component. Check for subdirs.
|
|
// XXX can we avoid the PromiseFlatCString call?
|
|
if (_mbschr((const unsigned char*) PromiseFlatCString(node).get(), '\\') != nsnull)
|
|
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
|
|
|
|
return AppendRelativeNativePath(node);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::AppendRelativeNativePath(const nsACString &node)
|
|
{
|
|
// Cannot start with a / or have .. or have / anywhere
|
|
nsACString::const_iterator begin, end;
|
|
node.BeginReading(begin);
|
|
node.EndReading(end);
|
|
if (node.IsEmpty() || FindCharInReadable('/', begin, end))
|
|
{
|
|
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
|
|
}
|
|
MakeDirty();
|
|
mWorkingPath.Append(NS_LITERAL_CSTRING("\\") + node);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Normalize()
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetNativeLeafName(nsACString &aLeafName)
|
|
{
|
|
aLeafName.Truncate();
|
|
|
|
const char* temp = mWorkingPath.get();
|
|
if(temp == nsnull)
|
|
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
|
|
|
|
const char* leaf = (const char*) _mbsrchr((const unsigned char*) temp, '\\');
|
|
|
|
// if the working path is just a node without any lashes.
|
|
if (leaf == nsnull)
|
|
leaf = temp;
|
|
else
|
|
leaf++;
|
|
|
|
aLeafName.Assign(leaf);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetNativeLeafName(const nsACString &aLeafName)
|
|
{
|
|
MakeDirty();
|
|
|
|
const unsigned char* temp = (const unsigned char*) mWorkingPath.get();
|
|
if(temp == nsnull)
|
|
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
|
|
|
|
// cannot use nsCString::RFindChar() due to 0x5c problem
|
|
PRInt32 offset = (PRInt32) (_mbsrchr(temp, '\\') - temp);
|
|
if (offset)
|
|
{
|
|
mWorkingPath.Truncate(offset+1);
|
|
}
|
|
mWorkingPath.Append(aLeafName);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetNativePath(nsACString &_retval)
|
|
{
|
|
_retval = mWorkingPath;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsLocalFile::CopySingleFile(nsIFile *sourceFile, nsIFile *destParent, const nsACString &newName, PRBool followSymlinks, PRBool move)
|
|
{
|
|
nsresult rv;
|
|
nsCAutoString filePath;
|
|
|
|
// get the path that we are going to copy to.
|
|
// Since windows does not know how to auto
|
|
// resolve shortcust, we must work with the
|
|
// target.
|
|
nsCAutoString destPath;
|
|
destParent->GetNativeTarget(destPath);
|
|
|
|
destPath.Append("\\");
|
|
|
|
if (newName.IsEmpty())
|
|
{
|
|
nsCAutoString aFileName;
|
|
sourceFile->GetNativeLeafName(aFileName);
|
|
destPath.Append(aFileName);
|
|
}
|
|
else
|
|
{
|
|
destPath.Append(newName);
|
|
}
|
|
|
|
|
|
if (followSymlinks)
|
|
{
|
|
rv = sourceFile->GetNativeTarget(filePath);
|
|
if (filePath.IsEmpty())
|
|
rv = sourceFile->GetNativePath(filePath);
|
|
}
|
|
else
|
|
{
|
|
rv = sourceFile->GetNativePath(filePath);
|
|
}
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
int copyOK;
|
|
|
|
if (!move)
|
|
copyOK = CopyFile(filePath.get(), destPath.get(), PR_TRUE);
|
|
else
|
|
{
|
|
// What we have to do is check to see if the destPath exists. If it
|
|
// does, we have to move it out of the say so that MoveFile will
|
|
// succeed. However, we don't want to just remove it since MoveFile
|
|
// can fail leaving us without a file.
|
|
|
|
nsCAutoString backup;
|
|
PRFileInfo64 fileInfo64;
|
|
PRStatus status = PR_GetFileInfo64(destPath.get(), &fileInfo64);
|
|
if (status == PR_SUCCESS)
|
|
{
|
|
|
|
// the file exists. Check to make sure it is not a directory,
|
|
// then move it out of the way.
|
|
if (fileInfo64.type == PR_FILE_FILE)
|
|
{
|
|
backup.Append(destPath);
|
|
backup.Append(".moztmp");
|
|
|
|
// remove any existing backup file that we may already have.
|
|
// maybe we should be doing some kind of unique naming here,
|
|
// but why bother.
|
|
remove(backup.get());
|
|
|
|
// move destination file to backup file
|
|
copyOK = MoveFile(destPath.get(), backup.get());
|
|
if (!copyOK)
|
|
{
|
|
// I guess we can't do the backup copy, so return.
|
|
rv = ConvertWinError(GetLastError());
|
|
return rv;
|
|
}
|
|
}
|
|
}
|
|
// move source file to destination file
|
|
copyOK = MoveFile(filePath.get(), destPath.get());
|
|
|
|
if (!backup.IsEmpty())
|
|
{
|
|
if (copyOK)
|
|
{
|
|
// remove the backup copy.
|
|
remove(backup.get());
|
|
}
|
|
else
|
|
{
|
|
// restore backup
|
|
int backupOk = MoveFile(backup.get(), destPath.get());
|
|
NS_ASSERTION(backupOk, "move backup failed");
|
|
}
|
|
}
|
|
}
|
|
if (!copyOK) // CopyFile and MoveFile returns non-zero if succeeds (backward if you ask me).
|
|
rv = ConvertWinError(GetLastError());
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
nsresult
|
|
nsLocalFile::CopyMove(nsIFile *aParentDir, const nsACString &newName, PRBool followSymlinks, PRBool move)
|
|
{
|
|
nsCOMPtr<nsIFile> newParentDir = aParentDir;
|
|
// check to see if this exists, otherwise return an error.
|
|
// we will check this by resolving. If the user wants us
|
|
// to follow links, then we are talking about the target,
|
|
// hence we can use the |followSymlinks| parameter.
|
|
nsresult rv = ResolveAndStat(followSymlinks);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
if (!newParentDir)
|
|
{
|
|
// no parent was specified. We must rename.
|
|
|
|
if (newName.IsEmpty())
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
rv = GetParent(getter_AddRefs(newParentDir));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
}
|
|
|
|
if (!newParentDir)
|
|
return NS_ERROR_FILE_DESTINATION_NOT_DIR;
|
|
|
|
// make sure it exists and is a directory. Create it if not there.
|
|
PRBool exists;
|
|
newParentDir->Exists(&exists);
|
|
if (!exists)
|
|
{
|
|
rv = newParentDir->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
}
|
|
else
|
|
{
|
|
PRBool isDir;
|
|
newParentDir->IsDirectory(&isDir);
|
|
if (isDir == PR_FALSE)
|
|
{
|
|
if (followSymlinks)
|
|
{
|
|
PRBool isLink;
|
|
newParentDir->IsSymlink(&isLink);
|
|
if (isLink)
|
|
{
|
|
nsCAutoString target;
|
|
newParentDir->GetNativeTarget(target);
|
|
|
|
nsCOMPtr<nsILocalFile> realDest = new nsLocalFile();
|
|
if (realDest == nsnull)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
rv = realDest->InitWithNativePath(target);
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
return CopyMove(realDest, newName, followSymlinks, move);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return NS_ERROR_FILE_DESTINATION_NOT_DIR;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check to see if we are a directory, if so enumerate it.
|
|
|
|
PRBool isDir;
|
|
IsDirectory(&isDir);
|
|
PRBool isSymlink;
|
|
IsSymlink(&isSymlink);
|
|
|
|
if (!isDir || (isSymlink && !followSymlinks))
|
|
{
|
|
rv = CopySingleFile(this, newParentDir, newName, followSymlinks, move);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
}
|
|
else
|
|
{
|
|
// create a new target destination in the new parentDir;
|
|
nsCOMPtr<nsIFile> target;
|
|
rv = newParentDir->Clone(getter_AddRefs(target));
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
nsCAutoString allocatedNewName;
|
|
if (newName.IsEmpty())
|
|
{
|
|
PRBool isLink;
|
|
IsSymlink(&isLink);
|
|
if (isLink)
|
|
{
|
|
nsCAutoString temp;
|
|
GetNativeTarget(temp);
|
|
const char* leaf = (const char*) _mbsrchr((const unsigned char*) temp.get(), '\\');
|
|
if (leaf[0] == '\\')
|
|
leaf++;
|
|
allocatedNewName = leaf;
|
|
}
|
|
else
|
|
{
|
|
GetNativeLeafName(allocatedNewName);// this should be the leaf name of the
|
|
}
|
|
}
|
|
else
|
|
{
|
|
allocatedNewName = newName;
|
|
}
|
|
|
|
rv = target->AppendNative(allocatedNewName);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
allocatedNewName.Truncate();
|
|
|
|
// check if the destination directory already exists
|
|
target->Exists(&exists);
|
|
if (!exists)
|
|
{
|
|
// if the destination directory cannot be created, return an error
|
|
rv = target->Create(DIRECTORY_TYPE, 0644); // TODO, what permissions should we use
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
}
|
|
else
|
|
{
|
|
// check if the destination directory is writable and empty
|
|
PRBool isWritable;
|
|
|
|
target->IsWritable(&isWritable);
|
|
if (!isWritable)
|
|
return NS_ERROR_FILE_ACCESS_DENIED;
|
|
|
|
nsCOMPtr<nsISimpleEnumerator> targetIterator;
|
|
rv = target->GetDirectoryEntries(getter_AddRefs(targetIterator));
|
|
|
|
PRBool more;
|
|
targetIterator->HasMoreElements(&more);
|
|
// return error if target directory is not empty
|
|
if (more)
|
|
return NS_ERROR_FILE_DIR_NOT_EMPTY;
|
|
}
|
|
|
|
nsDirEnumerator dirEnum;
|
|
|
|
rv = dirEnum.Init(this);
|
|
if (NS_FAILED(rv)) {
|
|
NS_WARNING("dirEnum initalization failed");
|
|
return rv;
|
|
}
|
|
|
|
PRBool more;
|
|
while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
|
|
{
|
|
nsCOMPtr<nsISupports> item;
|
|
nsCOMPtr<nsIFile> file;
|
|
dirEnum.GetNext(getter_AddRefs(item));
|
|
file = do_QueryInterface(item);
|
|
if (file)
|
|
{
|
|
PRBool isDir, isLink;
|
|
|
|
file->IsDirectory(&isDir);
|
|
file->IsSymlink(&isLink);
|
|
|
|
if (move)
|
|
{
|
|
if (followSymlinks)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
rv = file->MoveToNative(target, nsCString());
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
}
|
|
else
|
|
{
|
|
if (followSymlinks)
|
|
rv = file->CopyToFollowingLinksNative(target, nsCString());
|
|
else
|
|
rv = file->CopyToNative(target, nsCString());
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
}
|
|
}
|
|
}
|
|
// we've finished moving all the children of this directory
|
|
// in the new directory. so now delete the directory
|
|
// note, we don't need to do a recursive delete.
|
|
// MoveTo() is recursive. At this point,
|
|
// we've already moved the children of the current folder
|
|
// to the new location. nothing should be left in the folder.
|
|
if (move)
|
|
{
|
|
rv = Remove(PR_FALSE /* recursive */);
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
}
|
|
}
|
|
|
|
|
|
// If we moved, we want to adjust this.
|
|
if (move)
|
|
{
|
|
MakeDirty();
|
|
|
|
nsCAutoString newParentPath;
|
|
newParentDir->GetNativePath(newParentPath);
|
|
|
|
if (newParentPath.IsEmpty())
|
|
return NS_ERROR_FAILURE;
|
|
|
|
if (newName.IsEmpty())
|
|
{
|
|
nsCAutoString aFileName;
|
|
GetNativeLeafName(aFileName);
|
|
|
|
InitWithNativePath(newParentPath);
|
|
AppendNative(aFileName);
|
|
}
|
|
else
|
|
{
|
|
InitWithNativePath(newParentPath);
|
|
AppendNative(newName);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString &newName)
|
|
{
|
|
return CopyMove(newParentDir, newName, PR_FALSE, PR_FALSE);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString &newName)
|
|
{
|
|
return CopyMove(newParentDir, newName, PR_TRUE, PR_FALSE);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString &newName)
|
|
{
|
|
return CopyMove(newParentDir, newName, PR_FALSE, PR_TRUE);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Load(PRLibrary * *_retval)
|
|
{
|
|
PRBool isFile;
|
|
nsresult rv = IsFile(&isFile);
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
if (! isFile)
|
|
return NS_ERROR_FILE_IS_DIRECTORY;
|
|
|
|
NS_TIMELINE_START_TIMER("PR_LoadLibrary");
|
|
*_retval = PR_LoadLibrary(mResolvedPath.get());
|
|
NS_TIMELINE_STOP_TIMER("PR_LoadLibrary");
|
|
NS_TIMELINE_MARK_TIMER1("PR_LoadLibrary", mResolvedPath.get());
|
|
|
|
if (*_retval)
|
|
return NS_OK;
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Remove(PRBool recursive)
|
|
{
|
|
nsresult rv;
|
|
PRBool isDir, isLink;
|
|
nsXPIDLCString buf;
|
|
const char *filePath;
|
|
|
|
// NOTE:
|
|
//
|
|
// if the working path points to a shortcut, then we will only
|
|
// delete the shortcut itself. even if the shortcut points to
|
|
// a directory, we will not recurse into that directory or
|
|
// delete that directory itself. likewise, if the shortcut
|
|
// points to a normal file, we will not delete the real file.
|
|
// this is done to be consistent with the other platforms that
|
|
// behave this way. we do this even if the followLinks attribute
|
|
// is set to true. this helps protect against misuse that could
|
|
// lead to security bugs (e.g., bug 210588).
|
|
//
|
|
// in the case of non-terminal shortcuts, those are all followed
|
|
// unconditionally. this is done because 1) we only delete
|
|
// terminal nodes and possibly their children, and 2) the remove
|
|
// and rmdir CRT calls don't know how to handle shortcuts.
|
|
|
|
IsSymlink(&isLink);
|
|
if (isLink)
|
|
{
|
|
isDir = PR_FALSE;
|
|
// resolve non-terminal nodes only.
|
|
rv = ResolvePath(mWorkingPath.get(), PR_FALSE, getter_Copies(buf));
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
filePath = buf.get();
|
|
}
|
|
else
|
|
{
|
|
rv = IsDirectory(&isDir);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
// in this case, it doesn't matter that terminal nodes were
|
|
// resolved, so we can safely leverage mResolvedPath.
|
|
filePath = mResolvedPath.get();
|
|
}
|
|
|
|
if (isDir)
|
|
{
|
|
if (recursive)
|
|
{
|
|
nsDirEnumerator dirEnum;
|
|
|
|
rv = dirEnum.Init(this);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
PRBool more;
|
|
while (NS_SUCCEEDED(dirEnum.HasMoreElements(&more)) && more)
|
|
{
|
|
nsCOMPtr<nsISupports> item;
|
|
dirEnum.GetNext(getter_AddRefs(item));
|
|
nsCOMPtr<nsIFile> file = do_QueryInterface(item);
|
|
if (file)
|
|
file->Remove(recursive);
|
|
}
|
|
}
|
|
rv = rmdir(filePath);
|
|
}
|
|
else
|
|
{
|
|
rv = remove(filePath);
|
|
}
|
|
|
|
// fixup error code if necessary...
|
|
if (rv == -1)
|
|
rv = NSRESULT_FOR_ERRNO();
|
|
|
|
MakeDirty();
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime)
|
|
{
|
|
NS_ENSURE_ARG(aLastModifiedTime);
|
|
|
|
*aLastModifiedTime = 0;
|
|
|
|
nsresult rv = ResolveAndStat(PR_TRUE);
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
// microseconds -> milliseconds
|
|
*aLastModifiedTime = mFileInfo64.modifyTime / PR_USEC_PER_MSEC;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTime)
|
|
{
|
|
NS_ENSURE_ARG(aLastModifiedTime);
|
|
|
|
*aLastModifiedTime = 0;
|
|
|
|
nsresult rv = ResolveAndStat(PR_FALSE);
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
// microseconds -> milliseconds
|
|
*aLastModifiedTime = mFileInfo64.modifyTime / PR_USEC_PER_MSEC;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime)
|
|
{
|
|
return nsLocalFile::SetModDate(aLastModifiedTime, PR_TRUE);
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTime)
|
|
{
|
|
return nsLocalFile::SetModDate(aLastModifiedTime, PR_FALSE);
|
|
}
|
|
|
|
nsresult
|
|
nsLocalFile::SetModDate(PRInt64 aLastModifiedTime, PRBool resolveTerminal)
|
|
{
|
|
nsresult rv = ResolveAndStat(resolveTerminal);
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
const char *filePath = mResolvedPath.get();
|
|
|
|
HANDLE file = CreateFile( filePath, // pointer to name of the file
|
|
GENERIC_WRITE, // access (write) mode
|
|
0, // share mode
|
|
NULL, // pointer to security attributes
|
|
OPEN_EXISTING, // how to create
|
|
0, // file attributes
|
|
NULL);
|
|
|
|
MakeDirty();
|
|
|
|
if (!file)
|
|
{
|
|
return ConvertWinError(GetLastError());
|
|
}
|
|
|
|
FILETIME lft, ft;
|
|
SYSTEMTIME st;
|
|
PRExplodedTime pret;
|
|
|
|
// PR_ExplodeTime expects usecs...
|
|
PR_ExplodeTime(aLastModifiedTime * PR_USEC_PER_MSEC, PR_LocalTimeParameters, &pret);
|
|
st.wYear = pret.tm_year;
|
|
st.wMonth = pret.tm_month + 1; // Convert start offset -- Win32: Jan=1; NSPR: Jan=0
|
|
st.wDayOfWeek = pret.tm_wday;
|
|
st.wDay = pret.tm_mday;
|
|
st.wHour = pret.tm_hour;
|
|
st.wMinute = pret.tm_min;
|
|
st.wSecond = pret.tm_sec;
|
|
st.wMilliseconds = pret.tm_usec/1000;
|
|
|
|
if ( 0 == SystemTimeToFileTime(&st, &lft) )
|
|
{
|
|
rv = ConvertWinError(GetLastError());
|
|
}
|
|
else if ( 0 == LocalFileTimeToFileTime(&lft, &ft) )
|
|
{
|
|
rv = ConvertWinError(GetLastError());
|
|
}
|
|
else if ( 0 == SetFileTime(file, NULL, &ft, &ft) )
|
|
{
|
|
// could not set time
|
|
rv = ConvertWinError(GetLastError());
|
|
}
|
|
|
|
CloseHandle( file );
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetPermissions(PRUint32 *aPermissions)
|
|
{
|
|
nsresult rv = ResolveAndStat(PR_TRUE);
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
const char *filePath = mResolvedPath.get();
|
|
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink)
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetPermissions(PRUint32 aPermissions)
|
|
{
|
|
nsresult rv = ResolveAndStat(PR_TRUE);
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
const char *filePath = mResolvedPath.get();
|
|
if( chmod(filePath, aPermissions) == -1 )
|
|
return NS_ERROR_FAILURE;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissions)
|
|
{
|
|
nsresult rv = ResolveAndStat(PR_FALSE);
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
const char *filePath = mResolvedPath.get();
|
|
if( chmod(filePath, aPermissions) == -1 )
|
|
return NS_ERROR_FAILURE;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetFileSize(PRInt64 *aFileSize)
|
|
{
|
|
NS_ENSURE_ARG(aFileSize);
|
|
|
|
*aFileSize = 0;
|
|
|
|
nsresult rv = ResolveAndStat(PR_TRUE);
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
|
|
*aFileSize = mFileInfo64.size;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetFileSize(PRInt64 aFileSize)
|
|
{
|
|
|
|
DWORD status;
|
|
HANDLE hFile;
|
|
|
|
nsresult rv = ResolveAndStat(PR_TRUE);
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
const char *filePath = mResolvedPath.get();
|
|
|
|
|
|
// Leave it to Microsoft to open an existing file with a function
|
|
// named "CreateFile".
|
|
hFile = CreateFile(filePath,
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
MakeDirty();
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// Seek to new, desired end of file
|
|
PRInt32 hi, lo;
|
|
myLL_L2II(aFileSize, &hi, &lo );
|
|
|
|
status = SetFilePointer(hFile, lo, NULL, FILE_BEGIN);
|
|
if (status == 0xffffffff)
|
|
goto error;
|
|
|
|
// Truncate file at current cursor position
|
|
if (!SetEndOfFile(hFile))
|
|
goto error;
|
|
|
|
if (!CloseHandle(hFile))
|
|
return NS_ERROR_FAILURE;
|
|
|
|
MakeDirty();
|
|
return NS_OK;
|
|
|
|
error:
|
|
MakeDirty();
|
|
CloseHandle(hFile);
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSize)
|
|
{
|
|
NS_ENSURE_ARG(aFileSize);
|
|
|
|
*aFileSize = 0;
|
|
|
|
nsresult rv = ResolveAndStat(PR_FALSE);
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
*aFileSize = mFileInfo64.size;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable)
|
|
{
|
|
NS_ENSURE_ARG(aDiskSpaceAvailable);
|
|
|
|
ResolveAndStat(PR_FALSE);
|
|
|
|
PRInt64 int64;
|
|
|
|
LL_I2L(int64 , LONG_MAX);
|
|
|
|
// Check disk space
|
|
DWORD dwSecPerClus, dwBytesPerSec, dwFreeClus, dwTotalClus;
|
|
ULARGE_INTEGER liFreeBytesAvailableToCaller, liTotalNumberOfBytes, liTotalNumberOfFreeBytes;
|
|
double nBytes = 0;
|
|
|
|
BOOL (WINAPI* getDiskFreeSpaceExA)(LPCTSTR lpDirectoryName,
|
|
PULARGE_INTEGER lpFreeBytesAvailableToCaller,
|
|
PULARGE_INTEGER lpTotalNumberOfBytes,
|
|
PULARGE_INTEGER lpTotalNumberOfFreeBytes) = NULL;
|
|
|
|
HINSTANCE hInst = LoadLibrary("KERNEL32.DLL");
|
|
NS_ASSERTION(hInst != NULL, "COULD NOT LOAD KERNEL32.DLL");
|
|
if (hInst != NULL)
|
|
{
|
|
getDiskFreeSpaceExA = (BOOL (WINAPI*)(LPCTSTR lpDirectoryName,
|
|
PULARGE_INTEGER lpFreeBytesAvailableToCaller,
|
|
PULARGE_INTEGER lpTotalNumberOfBytes,
|
|
PULARGE_INTEGER lpTotalNumberOfFreeBytes))
|
|
GetProcAddress(hInst, "GetDiskFreeSpaceExA");
|
|
FreeLibrary(hInst);
|
|
}
|
|
|
|
if (getDiskFreeSpaceExA && (*getDiskFreeSpaceExA)(mResolvedPath.get(),
|
|
&liFreeBytesAvailableToCaller,
|
|
&liTotalNumberOfBytes,
|
|
&liTotalNumberOfFreeBytes))
|
|
{
|
|
nBytes = (double)(signed __int64)liFreeBytesAvailableToCaller.QuadPart;
|
|
}
|
|
else {
|
|
char aDrive[_MAX_DRIVE + 2];
|
|
_splitpath( mResolvedPath.get(), aDrive, NULL, NULL, NULL);
|
|
strcat(aDrive, "\\");
|
|
|
|
if ( GetDiskFreeSpace(aDrive, &dwSecPerClus, &dwBytesPerSec, &dwFreeClus, &dwTotalClus))
|
|
{
|
|
nBytes = (double)dwFreeClus*(double)dwSecPerClus*(double) dwBytesPerSec;
|
|
}
|
|
}
|
|
LL_D2L(*aDiskSpaceAvailable, nBytes);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetParent(nsIFile * *aParent)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aParent);
|
|
|
|
nsCAutoString parentPath(mWorkingPath);
|
|
|
|
// cannot use nsCString::RFindChar() due to 0x5c problem
|
|
PRInt32 offset = (PRInt32) (_mbsrchr((const unsigned char *) parentPath.get(), '\\')
|
|
- (const unsigned char *) parentPath.get());
|
|
if (offset < 0)
|
|
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
|
|
|
|
parentPath.Truncate(offset);
|
|
|
|
nsCOMPtr<nsILocalFile> localFile;
|
|
nsresult rv = NS_NewNativeLocalFile(parentPath, mFollowSymlinks, getter_AddRefs(localFile));
|
|
|
|
if(NS_SUCCEEDED(rv) && localFile)
|
|
{
|
|
return CallQueryInterface(localFile, aParent);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Exists(PRBool *_retval)
|
|
{
|
|
NS_ENSURE_ARG(_retval);
|
|
|
|
MakeDirty();
|
|
nsresult rv = ResolveAndStat( PR_TRUE );
|
|
|
|
if (NS_SUCCEEDED(rv))
|
|
*_retval = PR_TRUE;
|
|
else
|
|
*_retval = PR_FALSE;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::IsWritable(PRBool *_retval)
|
|
{
|
|
NS_ENSURE_ARG(_retval);
|
|
*_retval = PR_FALSE;
|
|
|
|
nsresult rv = ResolveAndStat(PR_TRUE);
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
const char *workingFilePath = mWorkingPath.get();
|
|
DWORD word = GetFileAttributes(workingFilePath);
|
|
|
|
*_retval = !((word & FILE_ATTRIBUTE_READONLY) != 0);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::IsReadable(PRBool *_retval)
|
|
{
|
|
NS_ENSURE_ARG(_retval);
|
|
*_retval = PR_FALSE;
|
|
|
|
nsresult rv = ResolveAndStat( PR_TRUE );
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
*_retval = PR_TRUE;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::IsExecutable(PRBool *_retval)
|
|
{
|
|
NS_ENSURE_ARG(_retval);
|
|
*_retval = PR_FALSE;
|
|
|
|
|
|
nsresult rv = ResolveAndStat( PR_TRUE );
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
nsCAutoString path;
|
|
PRBool symLink;
|
|
|
|
rv = IsSymlink(&symLink);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
if (symLink)
|
|
GetNativeTarget(path);
|
|
else
|
|
GetNativePath(path);
|
|
|
|
// Get extension.
|
|
char* ext = ::strrchr( path.get(), '.' );
|
|
if ( ext ) {
|
|
// Convert extension to lower case.
|
|
for( char *p = ext; *p; p++ ) {
|
|
if ( ::isupper( *p ) ) {
|
|
*p = ::tolower( *p );
|
|
}
|
|
}
|
|
// Search for any of the set of executable extensions.
|
|
const char * const executableExts[] = { ".exe",
|
|
".bat",
|
|
".com",
|
|
".pif",
|
|
".cmd",
|
|
".js",
|
|
".vbs",
|
|
".lnk",
|
|
".reg",
|
|
".wsf",
|
|
0 };
|
|
for ( int i = 0; executableExts[i]; i++ ) {
|
|
if ( ::strcmp( executableExts[i], ext ) == 0 ) {
|
|
// Found a match. Set result and quit.
|
|
*_retval = PR_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::IsDirectory(PRBool *_retval)
|
|
{
|
|
NS_PRECONDITION(_retval, "null pointer");
|
|
|
|
nsresult rv = ResolveAndStat(PR_TRUE);
|
|
if (NS_FAILED(rv)) {
|
|
*_retval = PR_FALSE;
|
|
return rv;
|
|
}
|
|
|
|
*_retval = (mFileInfo64.type == PR_FILE_DIRECTORY);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::IsFile(PRBool *_retval)
|
|
{
|
|
NS_ENSURE_ARG(_retval);
|
|
*_retval = PR_FALSE;
|
|
|
|
nsresult rv = ResolveAndStat(PR_TRUE);
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
*_retval = (mFileInfo64.type == PR_FILE_FILE);
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::IsHidden(PRBool *_retval)
|
|
{
|
|
NS_ENSURE_ARG(_retval);
|
|
*_retval = PR_FALSE;
|
|
|
|
nsresult rv = ResolveAndStat(PR_TRUE);
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
DWORD word;
|
|
if (mFollowSymlinks)
|
|
{
|
|
const char *resolvedFilePath = mResolvedPath.get();
|
|
word = GetFileAttributes(resolvedFilePath);
|
|
}
|
|
else
|
|
{
|
|
const char *workingFilePath = mWorkingPath.get();
|
|
word = GetFileAttributes(workingFilePath);
|
|
}
|
|
|
|
*_retval = ((word & FILE_ATTRIBUTE_HIDDEN) != 0);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::IsSymlink(PRBool *_retval)
|
|
{
|
|
NS_PRECONDITION(_retval, "null pointer");
|
|
|
|
PRUint32 len = mWorkingPath.Length();
|
|
if (len < 4)
|
|
*_retval = PR_FALSE;
|
|
else {
|
|
const char* leaf = mWorkingPath.get() + len - 4;
|
|
*_retval = (strnicmp(leaf, ".lnk", 4) == 0);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::IsSpecial(PRBool *_retval)
|
|
{
|
|
NS_ENSURE_ARG(_retval);
|
|
*_retval = PR_FALSE;
|
|
|
|
nsresult rv = ResolveAndStat(PR_TRUE);
|
|
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
const char *workingFilePath = mWorkingPath.get();
|
|
DWORD word = GetFileAttributes(workingFilePath);
|
|
|
|
*_retval = ((word & FILE_ATTRIBUTE_SYSTEM) != 0);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval)
|
|
{
|
|
NS_ENSURE_ARG(inFile);
|
|
NS_ENSURE_ARG(_retval);
|
|
|
|
nsCAutoString inFilePath;
|
|
inFile->GetNativePath(inFilePath);
|
|
|
|
*_retval = inFilePath.Equals(mWorkingPath);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval)
|
|
{
|
|
*_retval = PR_FALSE;
|
|
|
|
nsCAutoString myFilePath;
|
|
if ( NS_FAILED(GetNativeTarget(myFilePath)))
|
|
GetNativePath(myFilePath);
|
|
|
|
PRInt32 myFilePathLen = myFilePath.Length();
|
|
|
|
nsCAutoString inFilePath;
|
|
if ( NS_FAILED(inFile->GetNativeTarget(inFilePath)))
|
|
inFile->GetNativePath(inFilePath);
|
|
|
|
if ( strnicmp( myFilePath.get(), inFilePath.get(), myFilePathLen) == 0)
|
|
{
|
|
// now make sure that the |inFile|'s path has a trailing
|
|
// separator.
|
|
|
|
if (inFilePath[myFilePathLen] == '\\')
|
|
{
|
|
*_retval = PR_TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetNativeTarget(nsACString &_retval)
|
|
{
|
|
_retval.Truncate();
|
|
#if STRICT_FAKE_SYMLINKS
|
|
PRBool symLink;
|
|
|
|
nsresult rv = IsSymlink(&symLink);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
|
|
if (!symLink)
|
|
{
|
|
return NS_ERROR_FILE_INVALID_PATH;
|
|
}
|
|
#endif
|
|
ResolveAndStat(PR_TRUE);
|
|
|
|
_retval = mResolvedPath;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
/* attribute PRBool followLinks; */
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetFollowLinks(PRBool *aFollowLinks)
|
|
{
|
|
*aFollowLinks = mFollowSymlinks;
|
|
return NS_OK;
|
|
}
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetFollowLinks(PRBool aFollowLinks)
|
|
{
|
|
MakeDirty();
|
|
mFollowSymlinks = aFollowLinks;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator * *entries)
|
|
{
|
|
nsresult rv;
|
|
|
|
*entries = nsnull;
|
|
|
|
PRBool isDir;
|
|
rv = IsDirectory(&isDir);
|
|
if (NS_FAILED(rv))
|
|
return rv;
|
|
if (!isDir)
|
|
return NS_ERROR_FILE_NOT_DIRECTORY;
|
|
|
|
nsDirEnumerator* dirEnum = new nsDirEnumerator();
|
|
if (dirEnum == nsnull)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
NS_ADDREF(dirEnum);
|
|
rv = dirEnum->Init(this);
|
|
if (NS_FAILED(rv))
|
|
{
|
|
NS_RELEASE(dirEnum);
|
|
return rv;
|
|
}
|
|
|
|
*entries = dirEnum;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::GetPersistentDescriptor(nsACString &aPersistentDescriptor)
|
|
{
|
|
return GetNativePath(aPersistentDescriptor);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::SetPersistentDescriptor(const nsACString &aPersistentDescriptor)
|
|
{
|
|
return InitWithNativePath(aPersistentDescriptor);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Reveal()
|
|
{
|
|
nsresult rv = NS_OK;
|
|
PRBool isDirectory = PR_FALSE;
|
|
nsCAutoString path;
|
|
nsAutoString unicodePath;
|
|
|
|
IsDirectory(&isDirectory);
|
|
if (isDirectory)
|
|
{
|
|
GetNativePath(path);
|
|
}
|
|
else
|
|
{
|
|
nsCOMPtr<nsIFile> parent;
|
|
GetParent(getter_AddRefs(parent));
|
|
if (parent)
|
|
{
|
|
parent->GetNativePath(path);
|
|
parent->GetPath(unicodePath);
|
|
}
|
|
}
|
|
|
|
// Remember the current fg window.
|
|
HWND origWin, fgWin;
|
|
origWin = fgWin = ::GetForegroundWindow();
|
|
|
|
// use the app registry name to launch a shell execute....
|
|
LONG r = (LONG) ::ShellExecute( NULL, "open", path.get(), NULL, NULL, SW_SHOWNORMAL);
|
|
if (r < 32)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
// If this is a directory, then we don't need to select a file.
|
|
if (isDirectory)
|
|
return NS_OK;
|
|
|
|
// Resources we may need to free when done.
|
|
IShellFolder *desktopFolder = 0;
|
|
IMalloc *shellMalloc = 0;
|
|
IShellFolder *folder = 0;
|
|
LPITEMIDLIST folder_pidl = 0;
|
|
LPITEMIDLIST file_pidl = 0;
|
|
LPITEMIDLIST win95_file_pidl = 0;
|
|
HMODULE shell32 = 0;
|
|
|
|
// We break out of this do/while non-loop at any point where we have to give up.
|
|
do {
|
|
// Wait for the window to open. We wait a maximum of 2 seconds.
|
|
// If we get the wrong window, that will be dealt with below.
|
|
for (int iter = 10; iter; iter--)
|
|
{
|
|
fgWin = ::GetForegroundWindow();
|
|
if (fgWin != origWin)
|
|
break; // for loopo
|
|
::Sleep(200);
|
|
}
|
|
// If we failed to locate the new window, give up.
|
|
if (origWin == fgWin)
|
|
break; // do/while
|
|
|
|
// Now we have the explorer window. We need to send it the "select item"
|
|
// message (which isn't trivial, so buckly your seat belt)...
|
|
|
|
// We need the explorer's process id.
|
|
DWORD pid = 0;
|
|
::GetWindowThreadProcessId(fgWin, &pid);
|
|
|
|
// Get desktop folder.
|
|
HRESULT rc = ::SHGetDesktopFolder(&desktopFolder);
|
|
if (!desktopFolder)
|
|
break;
|
|
|
|
// Get IMalloc interface to use for shell pidls.
|
|
rc = ::SHGetMalloc(&shellMalloc);
|
|
if (!shellMalloc)
|
|
break;
|
|
|
|
// Convert folder path to pidl. This requires the Unicode path name.
|
|
// It returns a pidl that must be freed via shellMalloc->Free.
|
|
ULONG eaten = 0;
|
|
rc = desktopFolder->ParseDisplayName( 0,
|
|
0,
|
|
(LPOLESTR)unicodePath.get(),
|
|
&eaten,
|
|
&folder_pidl,
|
|
0 );
|
|
if (!folder_pidl)
|
|
break;
|
|
|
|
// Now get IShellFolder interface for the folder we opened.
|
|
rc = desktopFolder->BindToObject( folder_pidl,
|
|
0,
|
|
IID_IShellFolder,
|
|
(void**)&folder );
|
|
if (!folder)
|
|
break;
|
|
|
|
// Now get file name pidl from that folder.
|
|
nsAutoString unicodeLeaf;
|
|
if (NS_FAILED(GetLeafName(unicodeLeaf)))
|
|
break;
|
|
rc = folder->ParseDisplayName( 0,
|
|
0,
|
|
(LPOLESTR)unicodeLeaf.get(),
|
|
&eaten,
|
|
&file_pidl,
|
|
0 );
|
|
if (!file_pidl)
|
|
break;
|
|
|
|
// We need the module handle for shell32.dll.
|
|
shell32 = ::GetModuleHandle("shell32.dll");
|
|
if (!shell32)
|
|
break;
|
|
|
|
// Allocate shared memory copy of pidl. This uses the undocumented "SHAllocShared"
|
|
// function. Note that it is freed automatically after the ::SendMessage so we
|
|
// don't have to free it.
|
|
static HANDLE(WINAPI*SHAllocShared)(LPVOID,ULONG,DWORD) = (HANDLE(WINAPI*)(LPVOID,ULONG,DWORD))::GetProcAddress(shell32, (LPCTSTR)520);
|
|
HANDLE pidlHandle = 0;
|
|
if (SHAllocShared)
|
|
{
|
|
// We need the size of the pidl, which we get via another undocumented
|
|
// API: "ILGetSize".
|
|
UINT (WINAPI*ILGetSize)(LPCITEMIDLIST) = (UINT(WINAPI*)(LPCITEMIDLIST))::GetProcAddress(shell32, (LPCTSTR)152);
|
|
if (!ILGetSize)
|
|
break;
|
|
pidlHandle = SHAllocShared((void*)(ITEMIDLIST*)file_pidl,
|
|
ILGetSize(file_pidl),
|
|
pid);
|
|
if (!pidlHandle)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// On Win95, there is no SHAllocShared. Instead, we clone the file's pidl in
|
|
// the shell's global heap (via ILGlobalClone) and pass that.
|
|
LPITEMIDLIST(WINAPI*ILGlobalClone)(LPCITEMIDLIST) = (LPITEMIDLIST(WINAPI*)(LPCITEMIDLIST))::GetProcAddress(shell32, (LPCTSTR)20);
|
|
if (!ILGlobalClone)
|
|
break;
|
|
win95_file_pidl = ILGlobalClone(file_pidl);
|
|
if (!win95_file_pidl)
|
|
break;
|
|
// Arrange so that this pidl is passed on the ::SendMessage.
|
|
pidlHandle = win95_file_pidl;
|
|
}
|
|
|
|
// Send message to select this file.
|
|
::SendMessage(fgWin,
|
|
WM_USER+5,
|
|
SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE,
|
|
(LPARAM)pidlHandle );
|
|
} while ( PR_FALSE );
|
|
|
|
// Clean up (freeing stuff as needed, in reverse order).
|
|
if (win95_file_pidl)
|
|
{
|
|
// We need to free this using ILGlobalFree, another undocumented API.
|
|
static void (WINAPI*ILGlobalFree)(LPCITEMIDLIST) = (void(WINAPI*)(LPCITEMIDLIST))::GetProcAddress(shell32,(LPCTSTR)156);
|
|
if (ILGlobalFree)
|
|
ILGlobalFree(win95_file_pidl);
|
|
}
|
|
if (file_pidl)
|
|
shellMalloc->Free(file_pidl);
|
|
if (folder_pidl)
|
|
shellMalloc->Free(folder_pidl);
|
|
if (folder)
|
|
folder->Release();
|
|
if (shellMalloc)
|
|
shellMalloc->Release();
|
|
if (desktopFolder)
|
|
desktopFolder->Release();
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsLocalFile::Launch()
|
|
{
|
|
nsresult rv = NS_OK;
|
|
const nsCString &path = mWorkingPath;
|
|
|
|
// use the app registry name to launch a shell execute....
|
|
LONG r = (LONG) ::ShellExecute( NULL, NULL, path.get(), NULL, NULL, SW_SHOWNORMAL);
|
|
|
|
// if the file has no association, we launch windows' "what do you want to do" dialog
|
|
if (r == SE_ERR_NOASSOC) {
|
|
nsCAutoString shellArg;
|
|
shellArg.Assign(NS_LITERAL_CSTRING("shell32.dll,OpenAs_RunDLL ") + path);
|
|
r = (LONG) ::ShellExecute(NULL, NULL, "RUNDLL32.EXE", shellArg.get(),
|
|
NULL, SW_SHOWNORMAL);
|
|
}
|
|
if (r < 32)
|
|
rv = NS_ERROR_FAILURE;
|
|
else
|
|
rv = NS_OK;
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
NS_NewNativeLocalFile(const nsACString &path, PRBool followLinks, nsILocalFile* *result)
|
|
{
|
|
nsLocalFile* file = new nsLocalFile();
|
|
if (file == nsnull)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
NS_ADDREF(file);
|
|
|
|
file->SetFollowLinks(followLinks);
|
|
|
|
if (!path.IsEmpty()) {
|
|
nsresult rv = file->InitWithNativePath(path);
|
|
if (NS_FAILED(rv)) {
|
|
NS_RELEASE(file);
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
*result = file;
|
|
return NS_OK;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// UCS2 interface
|
|
//-----------------------------------------------------------------------------
|
|
|
|
nsresult
|
|
nsLocalFile::InitWithPath(const nsAString &filePath)
|
|
{
|
|
nsCAutoString tmp;
|
|
nsresult rv = NS_CopyUnicodeToNative(filePath, tmp);
|
|
if (NS_SUCCEEDED(rv))
|
|
return InitWithNativePath(tmp);
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsLocalFile::Append(const nsAString &node)
|
|
{
|
|
nsCAutoString tmp;
|
|
nsresult rv = NS_CopyUnicodeToNative(node, tmp);
|
|
if (NS_SUCCEEDED(rv))
|
|
return AppendNative(tmp);
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsLocalFile::AppendRelativePath(const nsAString &node)
|
|
{
|
|
nsCAutoString tmp;
|
|
nsresult rv = NS_CopyUnicodeToNative(node, tmp);
|
|
if (NS_SUCCEEDED(rv))
|
|
return AppendRelativeNativePath(tmp);
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsLocalFile::GetLeafName(nsAString &aLeafName)
|
|
{
|
|
nsCAutoString tmp;
|
|
nsresult rv = GetNativeLeafName(tmp);
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = NS_CopyNativeToUnicode(tmp, aLeafName);
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsLocalFile::SetLeafName(const nsAString &aLeafName)
|
|
{
|
|
nsCAutoString tmp;
|
|
nsresult rv = NS_CopyUnicodeToNative(aLeafName, tmp);
|
|
if (NS_SUCCEEDED(rv))
|
|
return SetNativeLeafName(tmp);
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsLocalFile::GetPath(nsAString &_retval)
|
|
{
|
|
return NS_CopyNativeToUnicode(mWorkingPath, _retval);
|
|
}
|
|
|
|
nsresult
|
|
nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString &newName)
|
|
{
|
|
if (newName.IsEmpty())
|
|
return CopyToNative(newParentDir, nsCString());
|
|
|
|
nsCAutoString tmp;
|
|
nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
|
|
if (NS_SUCCEEDED(rv))
|
|
return CopyToNative(newParentDir, tmp);
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString &newName)
|
|
{
|
|
if (newName.IsEmpty())
|
|
return CopyToFollowingLinksNative(newParentDir, nsCString());
|
|
|
|
nsCAutoString tmp;
|
|
nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
|
|
if (NS_SUCCEEDED(rv))
|
|
return CopyToFollowingLinksNative(newParentDir, tmp);
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString &newName)
|
|
{
|
|
if (newName.IsEmpty())
|
|
return MoveToNative(newParentDir, nsCString());
|
|
|
|
nsCAutoString tmp;
|
|
nsresult rv = NS_CopyUnicodeToNative(newName, tmp);
|
|
if (NS_SUCCEEDED(rv))
|
|
return MoveToNative(newParentDir, tmp);
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsLocalFile::GetTarget(nsAString &_retval)
|
|
{
|
|
nsCAutoString tmp;
|
|
nsresult rv = GetNativeTarget(tmp);
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = NS_CopyNativeToUnicode(tmp, _retval);
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
NS_NewLocalFile(const nsAString &path, PRBool followLinks, nsILocalFile* *result)
|
|
{
|
|
nsCAutoString buf;
|
|
nsresult rv = NS_CopyUnicodeToNative(path, buf);
|
|
if (NS_FAILED(rv)) {
|
|
*result = nsnull;
|
|
return rv;
|
|
}
|
|
return NS_NewNativeLocalFile(buf, followLinks, result);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// nsLocalFile <static members>
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void
|
|
nsLocalFile::GlobalInit()
|
|
{
|
|
nsresult rv = NS_CreateShortcutResolver();
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "Shortcut resolver could not be created");
|
|
}
|
|
|
|
void
|
|
nsLocalFile::GlobalShutdown()
|
|
{
|
|
NS_DestroyShortcutResolver();
|
|
}
|