diff options
Diffstat (limited to '')
-rw-r--r-- | src/libs/xpcom18a4/xpcom/io/nsLocalFileOSX.cpp | 2542 |
1 files changed, 2542 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/xpcom/io/nsLocalFileOSX.cpp b/src/libs/xpcom18a4/xpcom/io/nsLocalFileOSX.cpp new file mode 100644 index 00000000..dfc431d8 --- /dev/null +++ b/src/libs/xpcom18a4/xpcom/io/nsLocalFileOSX.cpp @@ -0,0 +1,2542 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla 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/MPL/ + * + * 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.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2001, 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Conrad Carlen <ccarlen@netscape.com> + * Jungshik Shin <jshin@mailaps.org> + * Asaf Romano <mozilla.mano@sent.com> + * Mark Mentovai <mark@moxienet.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsLocalFile.h" +#include "nsDirectoryServiceDefs.h" + +#include "nsString.h" +#include "nsReadableUtils.h" +#include "nsIDirectoryEnumerator.h" +#include "nsISimpleEnumerator.h" +#include "nsITimelineService.h" +#include "nsVoidArray.h" + +#include "plbase64.h" +#include "prmem.h" +#include "nsCRT.h" +#include "nsHashKeys.h" + +#include "MoreFilesX.h" +#include "FSCopyObject.h" +#include "nsAutoBuffer.h" +#include "nsTraceRefcntImpl.h" + +// Mac Includes +#include <Carbon/Carbon.h> + +// Unix Includes +#include <unistd.h> +#include <sys/stat.h> +#include <stdlib.h> + +#if !defined(MAC_OS_X_VERSION_10_4) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4 +#define GetAliasSizeFromRecord(aliasRecord) aliasRecord.aliasSize +#else +#define GetAliasSizeFromRecord(aliasRecord) GetAliasSizeFromPtr(&aliasRecord) +#endif + +#define CHECK_mBaseRef() \ + PR_BEGIN_MACRO \ + if (!mBaseRef) \ + return NS_ERROR_NOT_INITIALIZED; \ + PR_END_MACRO + +//***************************************************************************** +// Static Function Prototypes +//***************************************************************************** + +static nsresult MacErrorMapper(OSErr inErr); +static OSErr FindRunningAppBySignature(OSType aAppSig, ProcessSerialNumber& outPsn); +static void CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult); + +//***************************************************************************** +// Local Helper Classes +//***************************************************************************** + +#pragma mark - +#pragma mark [FSRef operator==] + +bool operator==(const FSRef& lhs, const FSRef& rhs) +{ + return (::FSCompareFSRefs(&lhs, &rhs) == noErr); +} + +#pragma mark - +#pragma mark [StFollowLinksState] + +class StFollowLinksState +{ + public: + StFollowLinksState(nsLocalFile& aFile) : + mFile(aFile) + { + mFile.GetFollowLinks(&mSavedState); + } + + StFollowLinksState(nsLocalFile& aFile, PRBool followLinksState) : + mFile(aFile) + { + mFile.GetFollowLinks(&mSavedState); + mFile.SetFollowLinks(followLinksState); + } + + ~StFollowLinksState() + { + mFile.SetFollowLinks(mSavedState); + } + + private: + nsLocalFile& mFile; + PRBool mSavedState; +}; + +#pragma mark - +#pragma mark [nsDirEnumerator] + +class nsDirEnumerator : public nsISimpleEnumerator, + public nsIDirectoryEnumerator +{ + public: + + NS_DECL_ISUPPORTS + + nsDirEnumerator() : + mIterator(nsnull), + mFSRefsArray(nsnull), + mArrayCnt(0), mArrayIndex(0) + { + } + + nsresult Init(nsILocalFileMac* parent) + { + NS_ENSURE_ARG(parent); + + OSErr err; + nsresult rv; + FSRef parentRef; + + rv = parent->GetFSRef(&parentRef); + if (NS_FAILED(rv)) + return rv; + + mFSRefsArray = (FSRef *)nsMemory::Alloc(sizeof(FSRef) + * kRequestCountPerIteration); + if (!mFSRefsArray) + return NS_ERROR_OUT_OF_MEMORY; + + err = ::FSOpenIterator(&parentRef, kFSIterateFlat, &mIterator); + if (err != noErr) + return MacErrorMapper(err); + + return NS_OK; + } + + NS_IMETHOD HasMoreElements(PRBool *result) + { + if (mNext == nsnull) { + if (mArrayIndex >= mArrayCnt) { + ItemCount actualCnt; + OSErr err = ::FSGetCatalogInfoBulk(mIterator, + kRequestCountPerIteration, + &actualCnt, + nsnull, + kFSCatInfoNone, + nsnull, + mFSRefsArray, + nsnull, + nsnull); + + if (err == noErr || err == errFSNoMoreItems) { + mArrayCnt = actualCnt; + mArrayIndex = 0; + } + } + if (mArrayIndex < mArrayCnt) { + nsLocalFile *newFile = new nsLocalFile; + if (!newFile) + return NS_ERROR_OUT_OF_MEMORY; + FSRef fsRef = mFSRefsArray[mArrayIndex]; + if (NS_FAILED(newFile->InitWithFSRef(&fsRef))) + return NS_ERROR_FAILURE; + mArrayIndex++; + mNext = newFile; + } + } + *result = mNext != nsnull; + if (!*result) + Close(); + return NS_OK; + } + + NS_IMETHOD GetNext(nsISupports **result) + { + NS_ENSURE_ARG_POINTER(result); + *result = nsnull; + + 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; + } + + NS_IMETHOD GetNextFile(nsIFile **result) + { + *result = nsnull; + PRBool hasMore = PR_FALSE; + nsresult rv = HasMoreElements(&hasMore); + if (NS_FAILED(rv) || !hasMore) + return rv; + *result = mNext; + NS_IF_ADDREF(*result); + mNext = nsnull; + return NS_OK; + } + + NS_IMETHOD Close() + { + if (mIterator) { + ::FSCloseIterator(mIterator); + mIterator = nsnull; + } + if (mFSRefsArray) { + nsMemory::Free(mFSRefsArray); + mFSRefsArray = nsnull; + } + return NS_OK; + } + + private: + ~nsDirEnumerator() + { + Close(); + } + + protected: + // According to Apple doc, request the number of objects + // per call that will fit in 4 VM pages. + enum { + kRequestCountPerIteration = ((4096 * 4) / sizeof(FSRef)) + }; + + nsCOMPtr<nsILocalFileMac> mNext; + + FSIterator mIterator; + FSRef *mFSRefsArray; + PRInt32 mArrayCnt, mArrayIndex; +}; + +NS_IMPL_ISUPPORTS2(nsDirEnumerator, nsISimpleEnumerator, nsIDirectoryEnumerator) + +#pragma mark - +#pragma mark [StAEDesc] + +class StAEDesc: public AEDesc +{ +public: + StAEDesc() + { + descriptorType = typeNull; + dataHandle = nil; + } + + ~StAEDesc() + { + ::AEDisposeDesc(this); + } +}; + +#define FILENAME_BUFFER_SIZE 512 + +//***************************************************************************** +// nsLocalFile +//***************************************************************************** + +const char nsLocalFile::kPathSepChar = '/'; +const PRUnichar nsLocalFile::kPathSepUnichar = '/'; + +// The HFS+ epoch is Jan. 1, 1904 GMT - differs from HFS in which times were local +// The NSPR epoch is Jan. 1, 1970 GMT +// 2082844800 is the difference in seconds between those dates +const PRInt64 nsLocalFile::kJanuaryFirst1970Seconds = 2082844800LL; + +#pragma mark - +#pragma mark [CTORs/DTOR] + +nsLocalFile::nsLocalFile() : + mBaseRef(nsnull), + mTargetRef(nsnull), + mCachedFSRefValid(PR_FALSE), + mFollowLinks(PR_TRUE), + mFollowLinksDirty(PR_TRUE) +{ +} + +nsLocalFile::nsLocalFile(const nsLocalFile& src) : + mBaseRef(src.mBaseRef), + mTargetRef(src.mTargetRef), + mCachedFSRef(src.mCachedFSRef), + mCachedFSRefValid(src.mCachedFSRefValid), + mFollowLinks(src.mFollowLinks), + mFollowLinksDirty(src.mFollowLinksDirty) +{ + // A CFURLRef is immutable so no need to copy, just retain. + if (mBaseRef) + ::CFRetain(mBaseRef); + if (mTargetRef) + ::CFRetain(mTargetRef); +} + +nsLocalFile::~nsLocalFile() +{ + if (mBaseRef) + ::CFRelease(mBaseRef); + if (mTargetRef) + ::CFRelease(mTargetRef); +} + + +//***************************************************************************** +// nsLocalFile::nsISupports +//***************************************************************************** +#pragma mark - +#pragma mark [nsISupports] + +NS_IMPL_THREADSAFE_ISUPPORTS4(nsLocalFile, + nsILocalFileMac, + nsILocalFile, + nsIFile, + nsIHashable) + +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::nsIFile +//***************************************************************************** +#pragma mark - +#pragma mark [nsIFile] + +/* void append (in AString node); */ +NS_IMETHODIMP nsLocalFile::Append(const nsAString& aNode) +{ + return AppendNative(NS_ConvertUTF16toUTF8(aNode)); +} + +/* [noscript] void appendNative (in ACString node); */ +NS_IMETHODIMP nsLocalFile::AppendNative(const nsACString& aNode) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + nsACString::const_iterator start, end; + aNode.BeginReading(start); + aNode.EndReading(end); + if (FindCharInReadable(kPathSepChar, start, end)) + return NS_ERROR_FILE_UNRECOGNIZED_PATH; + + CFStringRef nodeStrRef = ::CFStringCreateWithCString(kCFAllocatorDefault, + PromiseFlatCString(aNode).get(), + kCFStringEncodingUTF8); + if (nodeStrRef) { + CFURLRef newRef = ::CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, + mBaseRef, nodeStrRef, PR_FALSE); + ::CFRelease(nodeStrRef); + if (newRef) { + SetBaseRef(newRef); + ::CFRelease(newRef); + return NS_OK; + } + } + return NS_ERROR_FAILURE; +} + +/* void normalize (); */ +NS_IMETHODIMP nsLocalFile::Normalize() +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + // CFURL doesn't doesn't seem to resolve paths containing relative + // components, so we'll nick the stdlib code from nsLocalFileUnix + UInt8 path[PATH_MAX] = ""; + Boolean success; + success = ::CFURLGetFileSystemRepresentation(mBaseRef, true, path, PATH_MAX); + if (!success) + return NS_ERROR_FAILURE; + + char resolved_path[PATH_MAX] = ""; + char *resolved_path_ptr = nsnull; + resolved_path_ptr = realpath((char*)path, resolved_path); + + // if there is an error, the return is null. + if (!resolved_path_ptr) + return NSRESULT_FOR_ERRNO(); + + // Need to know whether we're a directory to create a new CFURLRef + PRBool isDirectory; + nsresult rv = IsDirectory(&isDirectory); + NS_ENSURE_SUCCESS(rv, rv); + + rv = NS_ERROR_FAILURE; + CFStringRef pathStrRef = + ::CFStringCreateWithCString(kCFAllocatorDefault, + resolved_path, + kCFStringEncodingUTF8); + if (pathStrRef) { + CFURLRef newURLRef = + ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault, pathStrRef, + kCFURLPOSIXPathStyle, isDirectory); + if (newURLRef) { + SetBaseRef(newURLRef); + ::CFRelease(newURLRef); + rv = NS_OK; + } + ::CFRelease(pathStrRef); + } + + return rv; +} + +/* void create (in unsigned long type, in unsigned long permissions); */ +NS_IMETHODIMP nsLocalFile::Create(PRUint32 type, PRUint32 permissions) +{ + if (type != NORMAL_FILE_TYPE && type != DIRECTORY_TYPE) + return NS_ERROR_FILE_UNKNOWN_TYPE; + + // Check we are correctly initialized. + CHECK_mBaseRef(); + + nsStringArray nonExtantNodes; + CFURLRef pathURLRef = mBaseRef; + FSRef pathFSRef; + CFStringRef leafStrRef = nsnull; + nsAutoBuffer<UniChar, FILENAME_BUFFER_SIZE> buffer; + Boolean success; + + // Work backwards through the path to find the last node which + // exists. Place the nodes which don't exist in an array and we'll + // create those below. + while ((success = ::CFURLGetFSRef(pathURLRef, &pathFSRef)) == false) { + leafStrRef = ::CFURLCopyLastPathComponent(pathURLRef); + if (!leafStrRef) + break; + CFIndex leafLen = ::CFStringGetLength(leafStrRef); + if (!buffer.EnsureElemCapacity(leafLen + 1)) + break; + ::CFStringGetCharacters(leafStrRef, CFRangeMake(0, leafLen), buffer.get()); + buffer.get()[leafLen] = '\0'; + nonExtantNodes.AppendString(nsString(nsDependentString(buffer.get()))); + ::CFRelease(leafStrRef); + leafStrRef = nsnull; + + // Get the parent of the leaf for the next go round + CFURLRef parent = ::CFURLCreateCopyDeletingLastPathComponent(NULL, pathURLRef); + if (!parent) + break; + if (pathURLRef != mBaseRef) + ::CFRelease(pathURLRef); + pathURLRef = parent; + } + if (pathURLRef != mBaseRef) + ::CFRelease(pathURLRef); + if (leafStrRef != nsnull) + ::CFRelease(leafStrRef); + if (!success) + return NS_ERROR_FAILURE; + PRInt32 nodesToCreate = nonExtantNodes.Count(); + if (nodesToCreate == 0) + return NS_ERROR_FILE_ALREADY_EXISTS; + + OSErr err; + nsAutoString nextNodeName; + for (PRInt32 i = nodesToCreate - 1; i > 0; i--) { + nonExtantNodes.StringAt(i, nextNodeName); + err = ::FSCreateDirectoryUnicode(&pathFSRef, + nextNodeName.Length(), + (const UniChar *)nextNodeName.get(), + kFSCatInfoNone, + nsnull, &pathFSRef, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + } + nonExtantNodes.StringAt(0, nextNodeName); + if (type == NORMAL_FILE_TYPE) { + err = ::FSCreateFileUnicode(&pathFSRef, + nextNodeName.Length(), + (const UniChar *)nextNodeName.get(), + kFSCatInfoNone, + nsnull, nsnull, nsnull); + } + else { + err = ::FSCreateDirectoryUnicode(&pathFSRef, + nextNodeName.Length(), + (const UniChar *)nextNodeName.get(), + kFSCatInfoNone, + nsnull, nsnull, nsnull, nsnull); + } + + return MacErrorMapper(err); +} + +/* attribute AString leafName; */ +NS_IMETHODIMP nsLocalFile::GetLeafName(nsAString& aLeafName) +{ + nsCAutoString nativeString; + nsresult rv = GetNativeLeafName(nativeString); + if (NS_FAILED(rv)) + return rv; + CopyUTF8toUTF16NFC(nativeString, aLeafName); + return NS_OK; +} + +NS_IMETHODIMP nsLocalFile::SetLeafName(const nsAString& aLeafName) +{ + return SetNativeLeafName(NS_ConvertUTF16toUTF8(aLeafName)); +} + +/* [noscript] attribute ACString nativeLeafName; */ +NS_IMETHODIMP nsLocalFile::GetNativeLeafName(nsACString& aNativeLeafName) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + nsresult rv = NS_ERROR_FAILURE; + CFStringRef leafStrRef = ::CFURLCopyLastPathComponent(mBaseRef); + if (leafStrRef) { + rv = CFStringReftoUTF8(leafStrRef, aNativeLeafName); + ::CFRelease(leafStrRef); + } + return rv; +} + +NS_IMETHODIMP nsLocalFile::SetNativeLeafName(const nsACString& aNativeLeafName) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + nsresult rv = NS_ERROR_FAILURE; + CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef); + if (parentURLRef) { + CFStringRef nodeStrRef = ::CFStringCreateWithCString(kCFAllocatorDefault, + PromiseFlatCString(aNativeLeafName).get(), + kCFStringEncodingUTF8); + + if (nodeStrRef) { + CFURLRef newURLRef = ::CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, + parentURLRef, nodeStrRef, PR_FALSE); + if (newURLRef) { + SetBaseRef(newURLRef); + ::CFRelease(newURLRef); + rv = NS_OK; + } + ::CFRelease(nodeStrRef); + } + ::CFRelease(parentURLRef); + } + return rv; +} + +/* void copyTo (in nsIFile newParentDir, in AString newName); */ +NS_IMETHODIMP nsLocalFile::CopyTo(nsIFile *newParentDir, const nsAString& newName) +{ + return CopyInternal(newParentDir, newName, PR_FALSE); +} + +/* [noscrpit] void CopyToNative (in nsIFile newParentDir, in ACString newName); */ +NS_IMETHODIMP nsLocalFile::CopyToNative(nsIFile *newParentDir, const nsACString& newName) +{ + return CopyInternal(newParentDir, NS_ConvertUTF8toUTF16(newName), PR_FALSE); +} + +/* void copyToFollowingLinks (in nsIFile newParentDir, in AString newName); */ +NS_IMETHODIMP nsLocalFile::CopyToFollowingLinks(nsIFile *newParentDir, const nsAString& newName) +{ + return CopyInternal(newParentDir, newName, PR_TRUE); +} + +/* [noscript] void copyToFollowingLinksNative (in nsIFile newParentDir, in ACString newName); */ +NS_IMETHODIMP nsLocalFile::CopyToFollowingLinksNative(nsIFile *newParentDir, const nsACString& newName) +{ + return CopyInternal(newParentDir, NS_ConvertUTF8toUTF16(newName), PR_TRUE); +} + +/* void moveTo (in nsIFile newParentDir, in AString newName); */ +NS_IMETHODIMP nsLocalFile::MoveTo(nsIFile *newParentDir, const nsAString& newName) +{ + return MoveToNative(newParentDir, NS_ConvertUTF16toUTF8(newName)); +} + +/* [noscript] void moveToNative (in nsIFile newParentDir, in ACString newName); */ +NS_IMETHODIMP nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString& newName) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + StFollowLinksState followLinks(*this, PR_FALSE); + + PRBool isDirectory; + nsresult rv = IsDirectory(&isDirectory); + if (NS_FAILED(rv)) + return rv; + + // Get the source path. + nsCAutoString srcPath; + rv = GetNativePath(srcPath); + if (NS_FAILED(rv)) + return rv; + + // Build the destination path. + nsCOMPtr<nsIFile> parentDir = newParentDir; + if (!parentDir) { + if (newName.IsEmpty()) + return NS_ERROR_INVALID_ARG; + rv = GetParent(getter_AddRefs(parentDir)); + if (NS_FAILED(rv)) + return rv; + } + else { + PRBool exists; + rv = parentDir->Exists(&exists); + if (NS_FAILED(rv)) + return rv; + if (!exists) { + rv = parentDir->Create(nsIFile::DIRECTORY_TYPE, 0777); + if (NS_FAILED(rv)) + return rv; + } + } + + nsCAutoString destPath; + rv = parentDir->GetNativePath(destPath); + if (NS_FAILED(rv)) + return rv; + + if (!newName.IsEmpty()) + destPath.Append(NS_LITERAL_CSTRING("/") + newName); + else { + nsCAutoString leafName; + rv = GetNativeLeafName(leafName); + if (NS_FAILED(rv)) + return rv; + destPath.Append(NS_LITERAL_CSTRING("/") + leafName); + } + + // Perform the move. + if (rename(srcPath.get(), destPath.get()) != 0) { + if (errno == EXDEV) { + // Can't move across volume (device) boundaries. Copy and remove. + rv = CopyToNative(parentDir, newName); + if (NS_SUCCEEDED(rv)) { + // Permit removal failure. + Remove(PR_TRUE); + } + } + else + rv = NSRESULT_FOR_ERRNO(); + + if (NS_FAILED(rv)) + return rv; + } + + // Update |this| to refer to the moved file. + CFURLRef newBaseRef = + ::CFURLCreateFromFileSystemRepresentation(NULL, (UInt8*)destPath.get(), + destPath.Length(), isDirectory); + if (!newBaseRef) + return NS_ERROR_FAILURE; + SetBaseRef(newBaseRef); + ::CFRelease(newBaseRef); + + return rv; +} + +/* void remove (in boolean recursive); */ +NS_IMETHODIMP nsLocalFile::Remove(PRBool recursive) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + // XXX If we're an alias, never remove target + StFollowLinksState followLinks(*this, PR_FALSE); + + PRBool isDirectory; + nsresult rv = IsDirectory(&isDirectory); + if (NS_FAILED(rv)) + return rv; + + if (recursive && isDirectory) { + FSRef fsRef; + rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + // Call MoreFilesX to do a recursive removal. + OSStatus err = ::FSDeleteContainer(&fsRef); + rv = MacErrorMapper(err); + } + else { + nsCAutoString path; + rv = GetNativePath(path); + if (NS_FAILED(rv)) + return rv; + + const char* pathPtr = path.get(); + int status; + if (isDirectory) + status = rmdir(pathPtr); + else + status = unlink(pathPtr); + + if (status != 0) + rv = NSRESULT_FOR_ERRNO(); + } + + mCachedFSRefValid = PR_FALSE; + return rv; +} + +/* attribute unsigned long permissions; */ +NS_IMETHODIMP nsLocalFile::GetPermissions(PRUint32 *aPermissions) +{ + NS_ENSURE_ARG_POINTER(aPermissions); + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FSCatalogInfo catalogInfo; + OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoPermissions, &catalogInfo, + nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + FSPermissionInfo *permPtr = (FSPermissionInfo*)catalogInfo.permissions; + *aPermissions = permPtr->mode; + return NS_OK; +} + +NS_IMETHODIMP nsLocalFile::SetPermissions(PRUint32 aPermissions) +{ + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FSCatalogInfo catalogInfo; + OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoPermissions, &catalogInfo, + nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + FSPermissionInfo *permPtr = (FSPermissionInfo*)catalogInfo.permissions; + permPtr->mode = (UInt16)aPermissions; + err = ::FSSetCatalogInfo(&fsRef, kFSCatInfoPermissions, &catalogInfo); + return MacErrorMapper(err); +} + +/* attribute unsigned long permissionsOfLink; */ +NS_IMETHODIMP nsLocalFile::GetPermissionsOfLink(PRUint32 *aPermissionsOfLink) +{ + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsLocalFile::SetPermissionsOfLink(PRUint32 aPermissionsOfLink) +{ + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* attribute PRInt64 lastModifiedTime; */ +NS_IMETHODIMP nsLocalFile::GetLastModifiedTime(PRInt64 *aLastModifiedTime) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + NS_ENSURE_ARG_POINTER(aLastModifiedTime); + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FSCatalogInfo catalogInfo; + OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoContentMod, &catalogInfo, + nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + *aLastModifiedTime = HFSPlustoNSPRTime(catalogInfo.contentModDate); + return NS_OK; +} + +NS_IMETHODIMP nsLocalFile::SetLastModifiedTime(PRInt64 aLastModifiedTime) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + OSErr err; + nsresult rv; + FSRef fsRef; + FSCatalogInfo catalogInfo; + + rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FSRef parentRef; + PRBool notifyParent; + + /* Get the node flags, the content modification date and time, and the parent ref */ + err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags + kFSCatInfoContentMod, + &catalogInfo, NULL, NULL, &parentRef); + if (err != noErr) + return MacErrorMapper(err); + + /* Notify the parent if this is a file */ + notifyParent = (0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)); + + NSPRtoHFSPlusTime(aLastModifiedTime, catalogInfo.contentModDate); + err = ::FSSetCatalogInfo(&fsRef, kFSCatInfoContentMod, &catalogInfo); + if (err != noErr) + return MacErrorMapper(err); + + /* Send a notification for the parent of the file, or for the directory */ + err = FNNotify(notifyParent ? &parentRef : &fsRef, kFNDirectoryModifiedMessage, kNilOptions); + if (err != noErr) + return MacErrorMapper(err); + + return NS_OK; +} + +/* attribute PRInt64 lastModifiedTimeOfLink; */ +NS_IMETHODIMP nsLocalFile::GetLastModifiedTimeOfLink(PRInt64 *aLastModifiedTimeOfLink) +{ + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; +} +NS_IMETHODIMP nsLocalFile::SetLastModifiedTimeOfLink(PRInt64 aLastModifiedTimeOfLink) +{ + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* attribute PRInt64 fileSize; */ +NS_IMETHODIMP nsLocalFile::GetFileSize(PRInt64 *aFileSize) +{ + NS_ENSURE_ARG_POINTER(aFileSize); + *aFileSize = 0; + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FSCatalogInfo catalogInfo; + OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags + kFSCatInfoDataSizes, &catalogInfo, + nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + + // FSGetCatalogInfo can return a bogus size for directories sometimes, so only + // rely on the answer for files + if ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) == 0) + *aFileSize = catalogInfo.dataLogicalSize; + return NS_OK; +} + +NS_IMETHODIMP nsLocalFile::SetFileSize(PRInt64 aFileSize) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + SInt16 refNum; + OSErr err = ::FSOpenFork(&fsRef, 0, nsnull, fsWrPerm, &refNum); + if (err != noErr) + return MacErrorMapper(err); + err = ::FSSetForkSize(refNum, fsFromStart, aFileSize); + ::FSCloseFork(refNum); + + return MacErrorMapper(err); +} + +/* readonly attribute PRInt64 fileSizeOfLink; */ +NS_IMETHODIMP nsLocalFile::GetFileSizeOfLink(PRInt64 *aFileSizeOfLink) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + NS_ENSURE_ARG_POINTER(aFileSizeOfLink); + + StFollowLinksState followLinks(*this, PR_FALSE); + return GetFileSize(aFileSizeOfLink); +} + +/* readonly attribute AString target; */ +NS_IMETHODIMP nsLocalFile::GetTarget(nsAString& aTarget) +{ + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* [noscript] readonly attribute ACString nativeTarget; */ +NS_IMETHODIMP nsLocalFile::GetNativeTarget(nsACString& aNativeTarget) +{ + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* readonly attribute AString path; */ +NS_IMETHODIMP nsLocalFile::GetPath(nsAString& aPath) +{ + nsCAutoString nativeString; + nsresult rv = GetNativePath(nativeString); + if (NS_FAILED(rv)) + return rv; + CopyUTF8toUTF16NFC(nativeString, aPath); + return NS_OK; +} + +/* [noscript] readonly attribute ACString nativePath; */ +NS_IMETHODIMP nsLocalFile::GetNativePath(nsACString& aNativePath) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + nsresult rv = NS_ERROR_FAILURE; + CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mBaseRef, kCFURLPOSIXPathStyle); + if (pathStrRef) { + rv = CFStringReftoUTF8(pathStrRef, aNativePath); + ::CFRelease(pathStrRef); + } + return rv; +} + +/* boolean exists (); */ +NS_IMETHODIMP nsLocalFile::Exists(PRBool *_retval) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + NS_ENSURE_ARG_POINTER(_retval); + *_retval = PR_FALSE; + + FSRef fsRef; + if (NS_SUCCEEDED(GetFSRefInternal(fsRef, PR_TRUE))) { + *_retval = PR_TRUE; + } + + return NS_OK; +} + +/* boolean isWritable (); */ +NS_IMETHODIMP nsLocalFile::IsWritable(PRBool *_retval) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + NS_ENSURE_ARG_POINTER(_retval); + *_retval = PR_FALSE; + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + if (::FSCheckLock(&fsRef) == noErr) { + PRUint32 permissions; + rv = GetPermissions(&permissions); + if (NS_FAILED(rv)) + return rv; + *_retval = ((permissions & S_IWUSR) != 0); + } + return NS_OK; +} + +/* boolean isReadable (); */ +NS_IMETHODIMP nsLocalFile::IsReadable(PRBool *_retval) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + NS_ENSURE_ARG_POINTER(_retval); + *_retval = PR_FALSE; + + PRUint32 permissions; + nsresult rv = GetPermissions(&permissions); + if (NS_FAILED(rv)) + return rv; + *_retval = ((permissions & S_IRUSR) != 0); + return NS_OK; +} + +/* boolean isExecutable (); */ +NS_IMETHODIMP nsLocalFile::IsExecutable(PRBool *_retval) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + NS_ENSURE_ARG_POINTER(_retval); + *_retval = PR_FALSE; + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + LSRequestedInfo theInfoRequest = kLSRequestAllInfo; + LSItemInfoRecord theInfo; + if (::LSCopyItemInfoForRef(&fsRef, theInfoRequest, &theInfo) == noErr) { + if ((theInfo.flags & kLSItemInfoIsApplication) != 0) + *_retval = PR_TRUE; + } + return NS_OK; +} + +/* boolean isHidden (); */ +NS_IMETHODIMP nsLocalFile::IsHidden(PRBool *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = PR_FALSE; + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FSCatalogInfo catalogInfo; + HFSUniStr255 leafName; + OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catalogInfo, + &leafName, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + + FileInfo *fInfoPtr = (FileInfo *)(catalogInfo.finderInfo); // Finder flags are in the same place whether we use FileInfo or FolderInfo + if ((fInfoPtr->finderFlags & kIsInvisible) != 0) { + *_retval = PR_TRUE; + } + else { + // If the leaf name begins with a '.', consider it invisible + if (leafName.length >= 1 && leafName.unicode[0] == UniChar('.')) + *_retval = PR_TRUE; + } + return NS_OK; +} + +/* boolean isDirectory (); */ +NS_IMETHODIMP nsLocalFile::IsDirectory(PRBool *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = PR_FALSE; + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef, PR_FALSE); + if (NS_FAILED(rv)) + return rv; + + FSCatalogInfo catalogInfo; + OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags, &catalogInfo, + nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + *_retval = ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0); + return NS_OK; +} + +/* boolean isFile (); */ +NS_IMETHODIMP nsLocalFile::IsFile(PRBool *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = PR_FALSE; + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef, PR_FALSE); + if (NS_FAILED(rv)) + return rv; + + FSCatalogInfo catalogInfo; + OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags, &catalogInfo, + nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + *_retval = ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) == 0); + return NS_OK; +} + +/* boolean isSymlink (); */ +NS_IMETHODIMP nsLocalFile::IsSymlink(PRBool *_retval) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + NS_ENSURE_ARG(_retval); + *_retval = PR_FALSE; + + // Check we are correctly initialized. + CHECK_mBaseRef(); + + FSRef fsRef; + if (::CFURLGetFSRef(mBaseRef, &fsRef)) { + Boolean isAlias, isFolder; + if (::FSIsAliasFile(&fsRef, &isAlias, &isFolder) == noErr) + *_retval = isAlias; + } + return NS_OK; +} + +/* boolean isSpecial (); */ +NS_IMETHODIMP nsLocalFile::IsSpecial(PRBool *_retval) +{ + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* nsIFile clone (); */ +NS_IMETHODIMP nsLocalFile::Clone(nsIFile **_retval) +{ + // Just copy-construct ourselves + *_retval = new nsLocalFile(*this); + if (!*_retval) + return NS_ERROR_OUT_OF_MEMORY; + + NS_ADDREF(*_retval); + + return NS_OK; +} + +/* boolean equals (in nsIFile inFile); */ +NS_IMETHODIMP nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval) +{ + return EqualsInternal(inFile, PR_TRUE, _retval); +} + +nsresult +nsLocalFile::EqualsInternal(nsISupports* inFile, PRBool aUpdateCache, + PRBool *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = PR_FALSE; + + nsCOMPtr<nsILocalFileMac> inMacFile(do_QueryInterface(inFile)); + if (!inFile) + return NS_OK; + + nsLocalFile* inLF = + static_cast<nsLocalFile*>((nsILocalFileMac*) inMacFile); + + // If both exist, compare FSRefs + FSRef thisFSRef, inFSRef; + nsresult rv1 = GetFSRefInternal(thisFSRef, aUpdateCache); + nsresult rv2 = inLF->GetFSRefInternal(inFSRef, aUpdateCache); + if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) { + *_retval = (thisFSRef == inFSRef); + return NS_OK; + } + // If one exists and the other doesn't, not equal + if (rv1 != rv2) + return NS_OK; + + // Arg, we have to get their paths and compare + nsCAutoString thisPath, inPath; + if (NS_FAILED(GetNativePath(thisPath))) + return NS_ERROR_FAILURE; + if (NS_FAILED(inMacFile->GetNativePath(inPath))) + return NS_ERROR_FAILURE; + *_retval = thisPath.Equals(inPath); + + return NS_OK; +} + +/* boolean contains (in nsIFile inFile, in boolean recur); */ +NS_IMETHODIMP nsLocalFile::Contains(nsIFile *inFile, PRBool recur, PRBool *_retval) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + NS_ENSURE_ARG_POINTER(_retval); + *_retval = PR_FALSE; + + PRBool isDir; + nsresult rv = IsDirectory(&isDir); + if (NS_FAILED(rv)) + return rv; + if (!isDir) + return NS_OK; // must be a dir to contain someone + + nsCAutoString thisPath, inPath; + if (NS_FAILED(GetNativePath(thisPath)) || NS_FAILED(inFile->GetNativePath(inPath))) + return NS_ERROR_FAILURE; + size_t thisPathLen = thisPath.Length(); + if ((inPath.Length() > thisPathLen + 1) && (strncasecmp(thisPath.get(), inPath.get(), thisPathLen) == 0)) { + // Now make sure that the |inFile|'s path has a separator at thisPathLen, + // and there's at least one more character after that. + if (inPath[thisPathLen] == kPathSepChar) + *_retval = PR_TRUE; + } + return NS_OK; +} + +/* readonly attribute nsIFile parent; */ +NS_IMETHODIMP nsLocalFile::GetParent(nsIFile * *aParent) +{ + NS_ENSURE_ARG_POINTER(aParent); + *aParent = nsnull; + + // Check we are correctly initialized. + CHECK_mBaseRef(); + + nsLocalFile *newFile = nsnull; + + // If it can be determined without error that a file does not + // have a parent, return nsnull for the parent and NS_OK as the result. + // See bug 133617. + nsresult rv = NS_OK; + CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef); + if (parentURLRef) { + // If the parent path is longer than file's path then + // CFURLCreateCopyDeletingLastPathComponent must have simply added + // two dots at the end - in this case indicate that there is no parent. + // See bug 332389. + CFStringRef path = ::CFURLGetString(mBaseRef); + CFStringRef newPath = ::CFURLGetString(parentURLRef); + if (::CFStringGetLength(newPath) < ::CFStringGetLength(path)) { + rv = NS_ERROR_FAILURE; + newFile = new nsLocalFile; + if (newFile) { + rv = newFile->InitWithCFURL(parentURLRef); + if (NS_SUCCEEDED(rv)) { + NS_ADDREF(*aParent = newFile); + rv = NS_OK; + } + } + } + ::CFRelease(parentURLRef); + } + return rv; +} + +/* readonly attribute nsISimpleEnumerator directoryEntries; */ +NS_IMETHODIMP nsLocalFile::GetDirectoryEntries(nsISimpleEnumerator **aDirectoryEntries) +{ + NS_ENSURE_ARG_POINTER(aDirectoryEntries); + *aDirectoryEntries = nsnull; + + nsresult rv; + 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; + } + *aDirectoryEntries = dirEnum; + + return NS_OK; +} + + +//***************************************************************************** +// nsLocalFile::nsILocalFile +//***************************************************************************** +#pragma mark - +#pragma mark [nsILocalFile] + +/* void initWithPath (in AString filePath); */ +NS_IMETHODIMP nsLocalFile::InitWithPath(const nsAString& filePath) +{ + return InitWithNativePath(NS_ConvertUTF16toUTF8(filePath)); +} + +/* [noscript] void initWithNativePath (in ACString filePath); */ +NS_IMETHODIMP nsLocalFile::InitWithNativePath(const nsACString& filePath) +{ + nsCAutoString fixedPath; + if (Substring(filePath, 0, 2).EqualsLiteral("~/")) { + nsCOMPtr<nsIFile> homeDir; + nsCAutoString homePath; + nsresult rv = NS_GetSpecialDirectory(NS_OS_HOME_DIR, + getter_AddRefs(homeDir)); + NS_ENSURE_SUCCESS(rv, rv); + rv = homeDir->GetNativePath(homePath); + NS_ENSURE_SUCCESS(rv, rv); + + fixedPath = homePath + Substring(filePath, 1, filePath.Length() - 1); + } + else if (filePath.IsEmpty() || filePath.First() != '/') + return NS_ERROR_FILE_UNRECOGNIZED_PATH; + else + fixedPath.Assign(filePath); + + // A path with consecutive '/'s which are not between + // nodes crashes CFURLGetFSRef(). Consecutive '/'s which + // are between actual nodes are OK. So, convert consecutive + // '/'s to a single one. + fixedPath.ReplaceSubstring("//", "/"); + +#if 1 // bird: hack to fix RegistryLocationForSpec issues with /path/to/./components + fixedPath.ReplaceSubstring("/./", "/"); + size_t len = fixedPath.Length(); + while (len > 2) + { + size_t choplen = 0; + if (!strcmp(fixedPath.get() + len - 2, "/.")) + choplen = 2; + else if (!strcmp(fixedPath.get() + len - 1, "/")) + choplen = 1; + else + break; + fixedPath = StringHead(fixedPath, len - choplen); + } + // bird: another hack for the issue with VirtualBoxVM and symlinks... + char tmpBuf[PATH_MAX]; + if (realpath(fixedPath.get(), tmpBuf)) + fixedPath = tmpBuf; +#endif + + // On 10.2, huge paths also crash CFURLGetFSRef() + if (fixedPath.Length() > PATH_MAX) + return NS_ERROR_FILE_NAME_TOO_LONG; + + CFStringRef pathAsCFString; + CFURLRef pathAsCFURL; + + pathAsCFString = ::CFStringCreateWithCString(nsnull, fixedPath.get(), kCFStringEncodingUTF8); + if (!pathAsCFString) + return NS_ERROR_FAILURE; + pathAsCFURL = ::CFURLCreateWithFileSystemPath(nsnull, pathAsCFString, kCFURLPOSIXPathStyle, PR_FALSE); + if (!pathAsCFURL) { + ::CFRelease(pathAsCFString); + return NS_ERROR_FAILURE; + } + SetBaseRef(pathAsCFURL); + ::CFRelease(pathAsCFURL); + ::CFRelease(pathAsCFString); + return NS_OK; +} + +/* void initWithFile (in nsILocalFile aFile); */ +NS_IMETHODIMP nsLocalFile::InitWithFile(nsILocalFile *aFile) +{ + NS_ENSURE_ARG(aFile); + + nsCOMPtr<nsILocalFileMac> aFileMac(do_QueryInterface(aFile)); + if (!aFileMac) + return NS_ERROR_UNEXPECTED; + CFURLRef urlRef; + nsresult rv = aFileMac->GetCFURL(&urlRef); + if (NS_FAILED(rv)) + return rv; + rv = InitWithCFURL(urlRef); + ::CFRelease(urlRef); + return rv; +} + +/* attribute PRBool followLinks; */ +NS_IMETHODIMP nsLocalFile::GetFollowLinks(PRBool *aFollowLinks) +{ + NS_ENSURE_ARG_POINTER(aFollowLinks); + + *aFollowLinks = mFollowLinks; + return NS_OK; +} + +NS_IMETHODIMP nsLocalFile::SetFollowLinks(PRBool aFollowLinks) +{ + if (aFollowLinks != mFollowLinks) { + mFollowLinks = aFollowLinks; + UpdateTargetRef(); + } + return NS_OK; +} + +/* [noscript] PRFileDescStar openNSPRFileDesc (in long flags, in long mode); */ +NS_IMETHODIMP nsLocalFile::OpenNSPRFileDesc(PRInt32 flags, PRInt32 mode, PRFileDesc **_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + + nsCAutoString path; + nsresult rv = GetPathInternal(path); + if (NS_FAILED(rv)) + return rv; + + *_retval = PR_Open(path.get(), flags, mode); + if (! *_retval) + return NS_ErrorAccordingToNSPR(); + + return NS_OK; +} + +/* [noscript] FILE openANSIFileDesc (in string mode); */ +NS_IMETHODIMP nsLocalFile::OpenANSIFileDesc(const char *mode, FILE **_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + + nsCAutoString path; + nsresult rv = GetPathInternal(path); + if (NS_FAILED(rv)) + return rv; + + *_retval = fopen(path.get(), mode); + if (! *_retval) + return NS_ERROR_FAILURE; + + return NS_OK; +} + +/* [noscript] PRLibraryStar load (); */ +NS_IMETHODIMP nsLocalFile::Load(PRLibrary **_retval) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + NS_ENSURE_ARG_POINTER(_retval); + + NS_TIMELINE_START_TIMER("PR_LoadLibrary"); + + nsCAutoString path; + nsresult rv = GetPathInternal(path); + if (NS_FAILED(rv)) + return rv; + +#ifdef NS_BUILD_REFCNT_LOGGING + nsTraceRefcntImpl::SetActivityIsLegal(PR_FALSE); +#endif + + *_retval = PR_LoadLibrary(path.get()); + +#ifdef NS_BUILD_REFCNT_LOGGING + nsTraceRefcntImpl::SetActivityIsLegal(PR_TRUE); +#endif + + NS_TIMELINE_STOP_TIMER("PR_LoadLibrary"); + NS_TIMELINE_MARK_TIMER1("PR_LoadLibrary", path.get()); + + if (!*_retval) + return NS_ERROR_FAILURE; + + return NS_OK; +} + +/* readonly attribute PRInt64 diskSpaceAvailable; */ +NS_IMETHODIMP nsLocalFile::GetDiskSpaceAvailable(PRInt64 *aDiskSpaceAvailable) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + NS_ENSURE_ARG_POINTER(aDiskSpaceAvailable); + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + OSErr err; + FSCatalogInfo catalogInfo; + err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoVolume, &catalogInfo, + nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + + FSVolumeInfo volumeInfo; + err = ::FSGetVolumeInfo(catalogInfo.volume, 0, nsnull, kFSVolInfoSizes, + &volumeInfo, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + + *aDiskSpaceAvailable = volumeInfo.freeBytes; + return NS_OK; +} + +/* void appendRelativePath (in AString relativeFilePath); */ +NS_IMETHODIMP nsLocalFile::AppendRelativePath(const nsAString& relativeFilePath) +{ + return AppendRelativeNativePath(NS_ConvertUTF16toUTF8(relativeFilePath)); +} + +/* [noscript] void appendRelativeNativePath (in ACString relativeFilePath); */ +NS_IMETHODIMP nsLocalFile::AppendRelativeNativePath(const nsACString& relativeFilePath) +{ + if (relativeFilePath.IsEmpty()) + return NS_OK; + // No leading '/' + if (relativeFilePath.First() == '/') + return NS_ERROR_FILE_UNRECOGNIZED_PATH; + + // Parse the nodes and call Append() for each + nsACString::const_iterator nodeBegin, pathEnd; + relativeFilePath.BeginReading(nodeBegin); + relativeFilePath.EndReading(pathEnd); + nsACString::const_iterator nodeEnd(nodeBegin); + + while (nodeEnd != pathEnd) { + FindCharInReadable(kPathSepChar, nodeEnd, pathEnd); + nsresult rv = AppendNative(Substring(nodeBegin, nodeEnd)); + if (NS_FAILED(rv)) + return rv; + if (nodeEnd != pathEnd) // If there's more left in the string, inc over the '/' nodeEnd is on. + ++nodeEnd; + nodeBegin = nodeEnd; + } + return NS_OK; +} + +/* attribute ACString persistentDescriptor; */ +NS_IMETHODIMP nsLocalFile::GetPersistentDescriptor(nsACString& aPersistentDescriptor) +{ + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + AliasHandle aliasH; + OSErr err = ::FSNewAlias(nsnull, &fsRef, &aliasH); + if (err != noErr) + return MacErrorMapper(err); + + PRUint32 bytes = ::GetHandleSize((Handle) aliasH); + ::HLock((Handle) aliasH); + // Passing nsnull for dest makes NULL-term string + char* buf = PL_Base64Encode((const char*)*aliasH, bytes, nsnull); + ::DisposeHandle((Handle) aliasH); + NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY); + + aPersistentDescriptor = buf; + PR_Free(buf); + + return NS_OK; +} + +NS_IMETHODIMP nsLocalFile::SetPersistentDescriptor(const nsACString& aPersistentDescriptor) +{ + if (aPersistentDescriptor.IsEmpty()) + return NS_ERROR_INVALID_ARG; + + // Support pathnames as user-supplied descriptors if they begin with '/' + // or '~'. These characters do not collide with the base64 set used for + // encoding alias records. + char first = aPersistentDescriptor.First(); + if (first == '/' || first == '~') + return InitWithNativePath(aPersistentDescriptor); + + nsresult rv = NS_OK; + + PRUint32 dataSize = aPersistentDescriptor.Length(); + char* decodedData = PL_Base64Decode(PromiseFlatCString(aPersistentDescriptor).get(), dataSize, nsnull); + if (!decodedData) { + NS_ERROR("SetPersistentDescriptor was given bad data"); + return NS_ERROR_FAILURE; + } + + // Cast to an alias record and resolve. + AliasRecord aliasHeader = *(AliasPtr)decodedData; + PRInt32 aliasSize = GetAliasSizeFromRecord(aliasHeader); + if (aliasSize > ((PRInt32)dataSize * 3) / 4) { // be paranoid about having too few data + PR_Free(decodedData); + return NS_ERROR_FAILURE; + } + + // Move the now-decoded data into the Handle. + // The size of the decoded data is 3/4 the size of the encoded data. See plbase64.h + Handle newHandle = nsnull; + if (::PtrToHand(decodedData, &newHandle, aliasSize) != noErr) + rv = NS_ERROR_OUT_OF_MEMORY; + PR_Free(decodedData); + if (NS_FAILED(rv)) + return rv; + + Boolean changed; + FSRef resolvedFSRef; + OSErr err = ::FSResolveAlias(nsnull, (AliasHandle)newHandle, &resolvedFSRef, &changed); + + rv = MacErrorMapper(err); + DisposeHandle(newHandle); + if (NS_FAILED(rv)) + return rv; + + return InitWithFSRef(&resolvedFSRef); +} + +/* void reveal (); */ +NS_IMETHODIMP nsLocalFile::Reveal() +{ + FSRef fsRefToReveal; + AppleEvent aeEvent = {0, nil}; + AppleEvent aeReply = {0, nil}; + StAEDesc aeDirDesc, listElem, myAddressDesc, fileList; + OSErr err; + ProcessSerialNumber process; + + nsresult rv = GetFSRefInternal(fsRefToReveal); + if (NS_FAILED(rv)) + return rv; + + err = ::FindRunningAppBySignature ('MACS', process); + if (err == noErr) { + err = ::AECreateDesc(typeProcessSerialNumber, (Ptr)&process, sizeof(process), &myAddressDesc); + if (err == noErr) { + // Create the FinderEvent + err = ::AECreateAppleEvent(kAEMiscStandards, kAEMakeObjectsVisible, &myAddressDesc, + kAutoGenerateReturnID, kAnyTransactionID, &aeEvent); + if (err == noErr) { + // Create the file list + err = ::AECreateList(nil, 0, false, &fileList); + if (err == noErr) { + FSSpec fsSpecToReveal; + err = ::FSRefMakeFSSpec(&fsRefToReveal, &fsSpecToReveal); + if (err == noErr) { + err = ::AEPutPtr(&fileList, 0, typeFSS, &fsSpecToReveal, sizeof(FSSpec)); + if (err == noErr) { + err = ::AEPutParamDesc(&aeEvent, keyDirectObject, &fileList); + if (err == noErr) { + err = ::AESend(&aeEvent, &aeReply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, nil, nil); + if (err == noErr) + ::SetFrontProcess(&process); + } + } + } + } + } + } + } + + return NS_OK; +} + +/* void launch (); */ +NS_IMETHODIMP nsLocalFile::Launch() +{ + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + OSErr err = ::LSOpenFSRef(&fsRef, NULL); + return MacErrorMapper(err); +} + + +//***************************************************************************** +// nsLocalFile::nsILocalFileMac +//***************************************************************************** +#pragma mark - +#pragma mark [nsILocalFileMac] + +/* void initWithCFURL (in CFURLRef aCFURL); */ +NS_IMETHODIMP nsLocalFile::InitWithCFURL(CFURLRef aCFURL) +{ + NS_ENSURE_ARG(aCFURL); + + SetBaseRef(aCFURL); + return NS_OK; +} + +/* void initWithFSRef ([const] in FSRefPtr aFSRef); */ +NS_IMETHODIMP nsLocalFile::InitWithFSRef(const FSRef *aFSRef) +{ + NS_ENSURE_ARG(aFSRef); + nsresult rv = NS_ERROR_FAILURE; + + CFURLRef newURLRef = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aFSRef); + if (newURLRef) { + SetBaseRef(newURLRef); + ::CFRelease(newURLRef); + rv = NS_OK; + } + return rv; +} + +/* void initWithFSSpec ([const] in FSSpecPtr aFileSpec); */ +NS_IMETHODIMP nsLocalFile::InitWithFSSpec(const FSSpec *aFileSpec) +{ + NS_ENSURE_ARG(aFileSpec); + + FSRef fsRef; + OSErr err = ::FSpMakeFSRef(aFileSpec, &fsRef); + if (err == noErr) + return InitWithFSRef(&fsRef); + else if (err == fnfErr) { + CInfoPBRec pBlock; + FSSpec parentDirSpec; + + memset(&pBlock, 0, sizeof(CInfoPBRec)); + parentDirSpec.name[0] = 0; + pBlock.dirInfo.ioVRefNum = aFileSpec->vRefNum; + pBlock.dirInfo.ioDrDirID = aFileSpec->parID; + pBlock.dirInfo.ioNamePtr = (StringPtr)parentDirSpec.name; + pBlock.dirInfo.ioFDirIndex = -1; //get info on parID + err = ::PBGetCatInfoSync(&pBlock); + if (err != noErr) + return MacErrorMapper(err); + + parentDirSpec.vRefNum = aFileSpec->vRefNum; + parentDirSpec.parID = pBlock.dirInfo.ioDrParID; + err = ::FSpMakeFSRef(&parentDirSpec, &fsRef); + if (err != noErr) + return MacErrorMapper(err); + HFSUniStr255 unicodeName; + err = ::HFSNameGetUnicodeName(aFileSpec->name, kTextEncodingUnknown, &unicodeName); + if (err != noErr) + return MacErrorMapper(err); + nsresult rv = InitWithFSRef(&fsRef); + if (NS_FAILED(rv)) + return rv; + return Append(nsDependentString(unicodeName.unicode, unicodeName.length)); + } + return MacErrorMapper(err); +} + +/* void initToAppWithCreatorCode (in OSType aAppCreator); */ +NS_IMETHODIMP nsLocalFile::InitToAppWithCreatorCode(OSType aAppCreator) +{ + FSRef fsRef; + OSErr err = ::LSFindApplicationForInfo(aAppCreator, nsnull, nsnull, &fsRef, nsnull); + if (err != noErr) + return MacErrorMapper(err); + return InitWithFSRef(&fsRef); +} + +/* CFURLRef getCFURL (); */ +NS_IMETHODIMP nsLocalFile::GetCFURL(CFURLRef *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef; + if (whichURLRef) + ::CFRetain(whichURLRef); + *_retval = whichURLRef; + return whichURLRef ? NS_OK : NS_ERROR_FAILURE; +} + +/* FSRef getFSRef (); */ +NS_IMETHODIMP nsLocalFile::GetFSRef(FSRef *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + return GetFSRefInternal(*_retval); +} + +/* FSSpec getFSSpec (); */ +NS_IMETHODIMP nsLocalFile::GetFSSpec(FSSpec *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + + // Check we are correctly initialized. + CHECK_mBaseRef(); + + OSErr err; + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_SUCCEEDED(rv)) { + // If the leaf node exists, things are simple. + err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNone, + nsnull, nsnull, _retval, nsnull); + return MacErrorMapper(err); + } + else if (rv == NS_ERROR_FILE_NOT_FOUND) { + // If the parent of the leaf exists, make an FSSpec from that. + CFURLRef parentURLRef = ::CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mBaseRef); + if (!parentURLRef) + return NS_ERROR_FAILURE; + + err = fnfErr; + if (::CFURLGetFSRef(parentURLRef, &fsRef)) { + FSCatalogInfo catalogInfo; + if ((err = ::FSGetCatalogInfo(&fsRef, + kFSCatInfoVolume + kFSCatInfoNodeID + kFSCatInfoTextEncoding, + &catalogInfo, nsnull, nsnull, nsnull)) == noErr) { + nsAutoString leafName; + if (NS_SUCCEEDED(GetLeafName(leafName))) { + Str31 hfsName; + if ((err = ::UnicodeNameGetHFSName(leafName.Length(), + leafName.get(), + catalogInfo.textEncodingHint, + catalogInfo.nodeID == fsRtDirID, + hfsName)) == noErr) + err = ::FSMakeFSSpec(catalogInfo.volume, catalogInfo.nodeID, hfsName, _retval); + } + } + } + ::CFRelease(parentURLRef); + rv = MacErrorMapper(err); + } + return rv; +} + +/* readonly attribute PRInt64 fileSizeWithResFork; */ +NS_IMETHODIMP nsLocalFile::GetFileSizeWithResFork(PRInt64 *aFileSizeWithResFork) +{ + NS_ENSURE_ARG_POINTER(aFileSizeWithResFork); + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FSCatalogInfo catalogInfo; + OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoDataSizes + kFSCatInfoRsrcSizes, + &catalogInfo, nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + + *aFileSizeWithResFork = catalogInfo.dataLogicalSize + catalogInfo.rsrcLogicalSize; + return NS_OK; +} + +/* attribute OSType fileType; */ +NS_IMETHODIMP nsLocalFile::GetFileType(OSType *aFileType) +{ + NS_ENSURE_ARG_POINTER(aFileType); + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FinderInfo fInfo; + OSErr err = ::FSGetFinderInfo(&fsRef, &fInfo, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + *aFileType = fInfo.file.fileType; + return NS_OK; +} + +NS_IMETHODIMP nsLocalFile::SetFileType(OSType aFileType) +{ + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + OSErr err = ::FSChangeCreatorType(&fsRef, 0, aFileType); + return MacErrorMapper(err); +} + +/* attribute OSType fileCreator; */ +NS_IMETHODIMP nsLocalFile::GetFileCreator(OSType *aFileCreator) +{ + NS_ENSURE_ARG_POINTER(aFileCreator); + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FinderInfo fInfo; + OSErr err = ::FSGetFinderInfo(&fsRef, &fInfo, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + *aFileCreator = fInfo.file.fileCreator; + return NS_OK; +} + +NS_IMETHODIMP nsLocalFile::SetFileCreator(OSType aFileCreator) +{ + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + OSErr err = ::FSChangeCreatorType(&fsRef, aFileCreator, 0); + return MacErrorMapper(err); +} + +/* void setFileTypeAndCreatorFromMIMEType (in string aMIMEType); */ +NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromMIMEType(const char *aMIMEType) +{ + // XXX - This should be cut from the API. Would create an evil dependency. + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* void setFileTypeAndCreatorFromExtension (in string aExtension); */ +NS_IMETHODIMP nsLocalFile::SetFileTypeAndCreatorFromExtension(const char *aExtension) +{ + // XXX - This should be cut from the API. Would create an evil dependency. + NS_ERROR("NS_ERROR_NOT_IMPLEMENTED"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +/* void launchWithDoc (in nsILocalFile aDocToLoad, in boolean aLaunchInBackground); */ +NS_IMETHODIMP nsLocalFile::LaunchWithDoc(nsILocalFile *aDocToLoad, PRBool aLaunchInBackground) +{ + PRBool isExecutable; + nsresult rv = IsExecutable(&isExecutable); + if (NS_FAILED(rv)) + return rv; + if (!isExecutable) + return NS_ERROR_FILE_EXECUTION_FAILED; + + FSRef appFSRef, docFSRef; + rv = GetFSRefInternal(appFSRef); + if (NS_FAILED(rv)) + return rv; + + if (aDocToLoad) { + nsCOMPtr<nsILocalFileMac> macDoc = do_QueryInterface(aDocToLoad); + rv = macDoc->GetFSRef(&docFSRef); + if (NS_FAILED(rv)) + return rv; + } + + LSLaunchFlags theLaunchFlags = kLSLaunchDefaults; + LSLaunchFSRefSpec thelaunchSpec; + + if (aLaunchInBackground) + theLaunchFlags |= kLSLaunchDontSwitch; + memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec)); + + thelaunchSpec.appRef = &appFSRef; + if (aDocToLoad) { + thelaunchSpec.numDocs = 1; + thelaunchSpec.itemRefs = &docFSRef; + } + thelaunchSpec.launchFlags = theLaunchFlags; + + OSErr err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL); + if (err != noErr) + return MacErrorMapper(err); + + return NS_OK; +} + +/* void openDocWithApp (in nsILocalFile aAppToOpenWith, in boolean aLaunchInBackground); */ +NS_IMETHODIMP nsLocalFile::OpenDocWithApp(nsILocalFile *aAppToOpenWith, PRBool aLaunchInBackground) +{ + nsresult rv; + OSErr err; + + FSRef docFSRef, appFSRef; + rv = GetFSRefInternal(docFSRef); + if (NS_FAILED(rv)) + return rv; + + if (aAppToOpenWith) { + nsCOMPtr<nsILocalFileMac> appFileMac = do_QueryInterface(aAppToOpenWith, &rv); + if (!appFileMac) + return rv; + + PRBool isExecutable; + rv = appFileMac->IsExecutable(&isExecutable); + if (NS_FAILED(rv)) + return rv; + if (!isExecutable) + return NS_ERROR_FILE_EXECUTION_FAILED; + + rv = appFileMac->GetFSRef(&appFSRef); + if (NS_FAILED(rv)) + return rv; + } + else { + OSType fileCreator; + rv = GetFileCreator(&fileCreator); + if (NS_FAILED(rv)) + return rv; + + err = ::LSFindApplicationForInfo(fileCreator, nsnull, nsnull, &appFSRef, nsnull); + if (err != noErr) + return MacErrorMapper(err); + } + + LSLaunchFlags theLaunchFlags = kLSLaunchDefaults; + LSLaunchFSRefSpec thelaunchSpec; + + if (aLaunchInBackground) + theLaunchFlags |= kLSLaunchDontSwitch; + memset(&thelaunchSpec, 0, sizeof(LSLaunchFSRefSpec)); + + thelaunchSpec.appRef = &appFSRef; + thelaunchSpec.numDocs = 1; + thelaunchSpec.itemRefs = &docFSRef; + thelaunchSpec.launchFlags = theLaunchFlags; + + err = ::LSOpenFromRefSpec(&thelaunchSpec, NULL); + if (err != noErr) + return MacErrorMapper(err); + + return NS_OK; +} + +/* boolean isPackage (); */ +NS_IMETHODIMP nsLocalFile::IsPackage(PRBool *_retval) +{ + NS_ENSURE_ARG(_retval); + *_retval = PR_FALSE; + + FSRef fsRef; + nsresult rv = GetFSRefInternal(fsRef); + if (NS_FAILED(rv)) + return rv; + + FSCatalogInfo catalogInfo; + OSErr err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoNodeFlags + kFSCatInfoFinderInfo, + &catalogInfo, nsnull, nsnull, nsnull); + if (err != noErr) + return MacErrorMapper(err); + if ((catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0) { + FileInfo *fInfoPtr = (FileInfo *)(catalogInfo.finderInfo); + if ((fInfoPtr->finderFlags & kHasBundle) != 0) { + *_retval = PR_TRUE; + } + else { + // Folders ending with ".app" are also considered to + // be packages, even if the top-level folder doesn't have bundle set + nsCAutoString name; + if (NS_SUCCEEDED(rv = GetNativeLeafName(name))) { + const char *extPtr = strrchr(name.get(), '.'); + if (extPtr) { + if ((nsCRT::strcasecmp(extPtr, ".app") == 0)) + *_retval = PR_TRUE; + } + } + } + } + return NS_OK; +} + +NS_IMETHODIMP +nsLocalFile::GetBundleDisplayName(nsAString& outBundleName) +{ + PRBool isPackage = PR_FALSE; + nsresult rv = IsPackage(&isPackage); + if (NS_FAILED(rv) || !isPackage) + return NS_ERROR_FAILURE; + + nsAutoString name; + rv = GetLeafName(name); + if (NS_FAILED(rv)) + return rv; + + PRInt32 length = name.Length(); + if (Substring(name, length - 4, length).EqualsLiteral(".app")) { + // 4 characters in ".app" + outBundleName = Substring(name, 0, length - 4); + } + else + outBundleName = name; + + return NS_OK; +} + +NS_IMETHODIMP +nsLocalFile::GetBundleIdentifier(nsACString& outBundleIdentifier) +{ + nsresult rv = NS_ERROR_FAILURE; + + CFURLRef urlRef; + if (NS_SUCCEEDED(GetCFURL(&urlRef))) { + CFBundleRef bundle = ::CFBundleCreate(NULL, urlRef); + if (bundle) { + CFStringRef bundleIdentifier = ::CFBundleGetIdentifier(bundle); + if (bundleIdentifier) + rv = CFStringReftoUTF8(bundleIdentifier, outBundleIdentifier); + + ::CFRelease(bundle); + } + ::CFRelease(urlRef); + } + + return rv; +} + + +//***************************************************************************** +// nsLocalFile Methods +//***************************************************************************** +#pragma mark - +#pragma mark [Protected Methods] + +nsresult nsLocalFile::SetBaseRef(CFURLRef aCFURLRef) +{ + NS_ENSURE_ARG(aCFURLRef); + + ::CFRetain(aCFURLRef); + if (mBaseRef) + ::CFRelease(mBaseRef); + mBaseRef = aCFURLRef; + + mFollowLinksDirty = PR_TRUE; + UpdateTargetRef(); + mCachedFSRefValid = PR_FALSE; + return NS_OK; +} + +nsresult nsLocalFile::UpdateTargetRef() +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + if (mFollowLinksDirty) { + if (mTargetRef) { + ::CFRelease(mTargetRef); + mTargetRef = nsnull; + } + if (mFollowLinks) { + mTargetRef = mBaseRef; + ::CFRetain(mTargetRef); + + FSRef fsRef; + if (::CFURLGetFSRef(mBaseRef, &fsRef)) { + Boolean targetIsFolder, wasAliased; + if (FSResolveAliasFile(&fsRef, true /*resolveAliasChains*/, + &targetIsFolder, &wasAliased) == noErr && wasAliased) { + ::CFRelease(mTargetRef); + mTargetRef = CFURLCreateFromFSRef(NULL, &fsRef); + if (!mTargetRef) + return NS_ERROR_FAILURE; + } + } + mFollowLinksDirty = PR_FALSE; + } + } + return NS_OK; +} + +nsresult nsLocalFile::GetFSRefInternal(FSRef& aFSRef, PRBool bForceUpdateCache) +{ + if (bForceUpdateCache || !mCachedFSRefValid) { + mCachedFSRefValid = PR_FALSE; + CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef; + NS_ENSURE_TRUE(whichURLRef, NS_ERROR_NULL_POINTER); + if (::CFURLGetFSRef(whichURLRef, &mCachedFSRef)) + mCachedFSRefValid = PR_TRUE; + } + if (mCachedFSRefValid) { + aFSRef = mCachedFSRef; + return NS_OK; + } + // CFURLGetFSRef only returns a Boolean for success, + // so we have to assume what the error was. This is + // the only probable cause. + return NS_ERROR_FILE_NOT_FOUND; +} + +nsresult nsLocalFile::GetPathInternal(nsACString& path) +{ + nsresult rv = NS_ERROR_FAILURE; + + CFURLRef whichURLRef = mFollowLinks ? mTargetRef : mBaseRef; + NS_ENSURE_TRUE(whichURLRef, NS_ERROR_NULL_POINTER); + + CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(whichURLRef, kCFURLPOSIXPathStyle); + if (pathStrRef) { + rv = CFStringReftoUTF8(pathStrRef, path); + ::CFRelease(pathStrRef); + } + return rv; +} + +nsresult nsLocalFile::CopyInternal(nsIFile* aParentDir, + const nsAString& newName, + PRBool followLinks) +{ + // Check we are correctly initialized. + CHECK_mBaseRef(); + + StFollowLinksState srcFollowState(*this, followLinks); + + nsresult rv; + OSErr err; + FSRef srcFSRef, newFSRef; + + rv = GetFSRefInternal(srcFSRef); + if (NS_FAILED(rv)) + return rv; + + nsCOMPtr<nsIFile> newParentDir = aParentDir; + + if (!newParentDir) { + if (newName.IsEmpty()) + return NS_ERROR_INVALID_ARG; + rv = GetParent(getter_AddRefs(newParentDir)); + if (NS_FAILED(rv)) + return rv; + } + + // If newParentDir does not exist, create it + PRBool exists; + rv = newParentDir->Exists(&exists); + if (NS_FAILED(rv)) + return rv; + if (!exists) { + rv = newParentDir->Create(nsIFile::DIRECTORY_TYPE, 0777); + if (NS_FAILED(rv)) + return rv; + } + + FSRef destFSRef; + nsCOMPtr<nsILocalFileMac> newParentDirMac(do_QueryInterface(newParentDir)); + if (!newParentDirMac) + return NS_ERROR_NO_INTERFACE; + rv = newParentDirMac->GetFSRef(&destFSRef); + if (NS_FAILED(rv)) + return rv; + + err = + ::FSCopyObject(&srcFSRef, &destFSRef, newName.Length(), + newName.Length() ? PromiseFlatString(newName).get() : NULL, + 0, kFSCatInfoNone, false, false, NULL, NULL, &newFSRef); + + return MacErrorMapper(err); +} + +const PRInt64 kMillisecsPerSec = 1000LL; +const PRInt64 kUTCDateTimeFractionDivisor = 65535LL; + +PRInt64 nsLocalFile::HFSPlustoNSPRTime(const UTCDateTime& utcTime) +{ + // Start with seconds since Jan. 1, 1904 GMT + PRInt64 result = ((PRInt64)utcTime.highSeconds << 32) + (PRInt64)utcTime.lowSeconds; + // Subtract to convert to NSPR epoch of 1970 + result -= kJanuaryFirst1970Seconds; + // Convert to millisecs + result *= kMillisecsPerSec; + // Convert the fraction to millisecs and add it + result += ((PRInt64)utcTime.fraction * kMillisecsPerSec) / kUTCDateTimeFractionDivisor; + + return result; +} + +void nsLocalFile::NSPRtoHFSPlusTime(PRInt64 nsprTime, UTCDateTime& utcTime) +{ + PRInt64 fraction = nsprTime % kMillisecsPerSec; + PRInt64 seconds = (nsprTime / kMillisecsPerSec) + kJanuaryFirst1970Seconds; + utcTime.highSeconds = (UInt16)((PRUint64)seconds >> 32); + utcTime.lowSeconds = (UInt32)seconds; + utcTime.fraction = (UInt16)((fraction * kUTCDateTimeFractionDivisor) / kMillisecsPerSec); +} + +nsresult nsLocalFile::CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr) +{ + nsresult rv = NS_ERROR_FAILURE; + CFIndex usedBufLen, inStrLen = ::CFStringGetLength(aInStrRef); + CFIndex charsConverted = ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen), + kCFStringEncodingUTF8, 0, PR_FALSE, nsnull, 0, &usedBufLen); + if (charsConverted == inStrLen) { +#if 0 /* bird: too new? */ + aOutStr.SetLength(usedBufLen); + if (aOutStr.Length() != (unsigned int)usedBufLen) + return NS_ERROR_OUT_OF_MEMORY; + UInt8 *buffer = (UInt8*) aOutStr.BeginWriting(); + + ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen), + kCFStringEncodingUTF8, 0, false, buffer, usedBufLen, &usedBufLen); + rv = NS_OK; +#else + nsAutoBuffer<UInt8, FILENAME_BUFFER_SIZE> buffer; + if (buffer.EnsureElemCapacity(usedBufLen + 1)) { + ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen), + kCFStringEncodingUTF8, 0, false, buffer.get(), usedBufLen, &usedBufLen); + buffer.get()[usedBufLen] = '\0'; + aOutStr.Assign(nsDependentCString((char*)buffer.get())); + rv = NS_OK; + } +#endif + } + return rv; +} + +// nsIHashable + +NS_IMETHODIMP +nsLocalFile::Equals(nsIHashable* aOther, PRBool *aResult) +{ + return EqualsInternal(aOther, PR_FALSE, aResult); +} + +NS_IMETHODIMP +nsLocalFile::GetHashCode(PRUint32 *aResult) +{ + CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mBaseRef, kCFURLPOSIXPathStyle); + nsCAutoString path; + CFStringReftoUTF8(pathStrRef, path); + ::CFRelease(pathStrRef); + *aResult = HashString(path); + return NS_OK; +} + +//***************************************************************************** +// Global Functions +//***************************************************************************** +#pragma mark - +#pragma mark [Global Functions] + +void nsLocalFile::GlobalInit() +{ +} + +void nsLocalFile::GlobalShutdown() +{ +} + +nsresult NS_NewLocalFile(const nsAString& 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->InitWithPath(path); + if (NS_FAILED(rv)) { + NS_RELEASE(file); + return rv; + } + } + *result = file; + return NS_OK; +} + +nsresult NS_NewNativeLocalFile(const nsACString& path, PRBool followLinks, nsILocalFile **result) +{ + return NS_NewLocalFile(NS_ConvertUTF8toUTF16(path), followLinks, result); +} + +nsresult NS_NewLocalFileWithFSSpec(const FSSpec* inSpec, PRBool followLinks, nsILocalFileMac **result) +{ + nsLocalFile* file = new nsLocalFile(); + if (file == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(file); + + file->SetFollowLinks(followLinks); + + nsresult rv = file->InitWithFSSpec(inSpec); + if (NS_FAILED(rv)) { + NS_RELEASE(file); + return rv; + } + *result = file; + return NS_OK; +} + +nsresult NS_NewLocalFileWithFSRef(const FSRef* aFSRef, PRBool aFollowLinks, nsILocalFileMac** result) +{ + nsLocalFile* file = new nsLocalFile(); + if (file == nsnull) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(file); + + file->SetFollowLinks(aFollowLinks); + + nsresult rv = file->InitWithFSRef(aFSRef); + if (NS_FAILED(rv)) { + NS_RELEASE(file); + return rv; + } + *result = file; + return NS_OK; +} + +//***************************************************************************** +// Static Functions +//***************************************************************************** + +static nsresult MacErrorMapper(OSErr inErr) +{ + nsresult outErr; + + switch (inErr) + { + case noErr: + outErr = NS_OK; + break; + + case fnfErr: + case afpObjectNotFound: + case afpDirNotFound: + outErr = NS_ERROR_FILE_NOT_FOUND; + break; + + case dupFNErr: + case afpObjectExists: + outErr = NS_ERROR_FILE_ALREADY_EXISTS; + break; + + case dskFulErr: + case afpDiskFull: + outErr = NS_ERROR_FILE_DISK_FULL; + break; + + case fLckdErr: + case afpVolLocked: + outErr = NS_ERROR_FILE_IS_LOCKED; + break; + + case afpAccessDenied: + outErr = NS_ERROR_FILE_ACCESS_DENIED; + break; + + case afpDirNotEmpty: + outErr = NS_ERROR_FILE_DIR_NOT_EMPTY; + break; + + // Can't find good map for some + case bdNamErr: + outErr = NS_ERROR_FAILURE; + break; + + default: + outErr = NS_ERROR_FAILURE; + break; + } + return outErr; +} + +static OSErr FindRunningAppBySignature(OSType aAppSig, ProcessSerialNumber& outPsn) +{ + ProcessInfoRec info; + OSErr err = noErr; + + outPsn.highLongOfPSN = 0; + outPsn.lowLongOfPSN = kNoProcess; + + while (PR_TRUE) + { + err = ::GetNextProcess(&outPsn); + if (err == procNotFound) + break; + if (err != noErr) + return err; + info.processInfoLength = sizeof(ProcessInfoRec); + info.processName = nil; + info.processAppSpec = nil; + err = ::GetProcessInformation(&outPsn, &info); + if (err != noErr) + return err; + + if (info.processSignature == aAppSig) + return noErr; + } + return procNotFound; +} + +// Convert a UTF-8 string to a UTF-16 string while normalizing to +// Normalization Form C (composed Unicode). We need this because +// Mac OS X file system uses NFD (Normalization Form D : decomposed Unicode) +// while most other OS', server-side programs usually expect NFC. + +typedef void (*UnicodeNormalizer) (CFMutableStringRef, CFStringNormalizationForm); +static void CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult) +{ + static PRBool sChecked = PR_FALSE; + static UnicodeNormalizer sUnicodeNormalizer = NULL; + + // CFStringNormalize was not introduced until Mac OS 10.2 + if (!sChecked) { + CFBundleRef carbonBundle = + CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Carbon")); + if (carbonBundle) + sUnicodeNormalizer = (UnicodeNormalizer) + ::CFBundleGetFunctionPointerForName(carbonBundle, + CFSTR("CFStringNormalize")); + sChecked = PR_TRUE; + } + + if (!sUnicodeNormalizer) { // OS X 10.2 or earlier + CopyUTF8toUTF16(aSrc, aResult); + return; + } + + const nsAFlatCString &inFlatSrc = PromiseFlatCString(aSrc); + + // The number of 16bit code units in a UTF-16 string will never be + // larger than the number of bytes in the corresponding UTF-8 string. + CFMutableStringRef inStr = + ::CFStringCreateMutable(NULL, inFlatSrc.Length()); + + if (!inStr) { + CopyUTF8toUTF16(aSrc, aResult); + return; + } + + ::CFStringAppendCString(inStr, inFlatSrc.get(), kCFStringEncodingUTF8); + + sUnicodeNormalizer(inStr, kCFStringNormalizationFormC); + + CFIndex length = CFStringGetLength(inStr); + const UniChar* chars = CFStringGetCharactersPtr(inStr); + + if (chars) + aResult.Assign(chars, length); + else { + nsAutoBuffer<UniChar, FILENAME_BUFFER_SIZE> buffer; + if (!buffer.EnsureElemCapacity(length)) + CopyUTF8toUTF16(aSrc, aResult); + else { + CFStringGetCharacters(inStr, CFRangeMake(0, length), buffer.get()); + aResult.Assign(buffer.get(), length); + } + } + CFRelease(inStr); +} |