summaryrefslogtreecommitdiffstats
path: root/xpcom/io/CocoaFileUtils.mm
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/io/CocoaFileUtils.mm')
-rw-r--r--xpcom/io/CocoaFileUtils.mm289
1 files changed, 289 insertions, 0 deletions
diff --git a/xpcom/io/CocoaFileUtils.mm b/xpcom/io/CocoaFileUtils.mm
new file mode 100644
index 0000000000..b906365a2b
--- /dev/null
+++ b/xpcom/io/CocoaFileUtils.mm
@@ -0,0 +1,289 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:set ts=2 sts=2 sw=2 et cin:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CocoaFileUtils.h"
+#include "nsCocoaFeatures.h"
+#include "nsCocoaUtils.h"
+#include <Cocoa/Cocoa.h>
+#include "nsObjCExceptions.h"
+#include "nsDebug.h"
+#include "nsString.h"
+#include "mozilla/MacStringHelpers.h"
+
+namespace CocoaFileUtils {
+
+nsresult RevealFileInFinder(CFURLRef url) {
+ NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
+
+ if (NS_WARN_IF(!url)) return NS_ERROR_INVALID_ARG;
+
+ NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
+ BOOL success = [[NSWorkspace sharedWorkspace] selectFile:[(NSURL*)url path]
+ inFileViewerRootedAtPath:@""];
+ [ap release];
+
+ return (success ? NS_OK : NS_ERROR_FAILURE);
+
+ NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
+}
+
+nsresult OpenURL(CFURLRef url) {
+ NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
+
+ if (NS_WARN_IF(!url)) return NS_ERROR_INVALID_ARG;
+
+ NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
+ BOOL success = [[NSWorkspace sharedWorkspace] openURL:(NSURL*)url];
+ [ap release];
+
+ return (success ? NS_OK : NS_ERROR_FAILURE);
+
+ NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
+}
+
+nsresult GetFileCreatorCode(CFURLRef url, OSType* creatorCode) {
+ NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
+
+ if (NS_WARN_IF(!url) || NS_WARN_IF(!creatorCode)) return NS_ERROR_INVALID_ARG;
+
+ nsAutoreleasePool localPool;
+
+ NSString* resolvedPath = [[(NSURL*)url path] stringByResolvingSymlinksInPath];
+ if (!resolvedPath) {
+ return NS_ERROR_FAILURE;
+ }
+
+ NSDictionary* dict = [[NSFileManager defaultManager] attributesOfItemAtPath:resolvedPath
+ error:nil];
+ if (!dict) {
+ return NS_ERROR_FAILURE;
+ }
+
+ NSNumber* creatorNum = (NSNumber*)[dict objectForKey:NSFileHFSCreatorCode];
+ if (!creatorNum) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *creatorCode = [creatorNum unsignedLongValue];
+ return NS_OK;
+
+ NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
+}
+
+nsresult SetFileCreatorCode(CFURLRef url, OSType creatorCode) {
+ NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
+
+ if (NS_WARN_IF(!url)) return NS_ERROR_INVALID_ARG;
+
+ NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
+ NSDictionary* dict =
+ [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:creatorCode]
+ forKey:NSFileHFSCreatorCode];
+ BOOL success = [[NSFileManager defaultManager] setAttributes:dict
+ ofItemAtPath:[(NSURL*)url path]
+ error:nil];
+ [ap release];
+ return (success ? NS_OK : NS_ERROR_FAILURE);
+
+ NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
+}
+
+nsresult GetFileTypeCode(CFURLRef url, OSType* typeCode) {
+ NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
+
+ if (NS_WARN_IF(!url) || NS_WARN_IF(!typeCode)) return NS_ERROR_INVALID_ARG;
+
+ nsAutoreleasePool localPool;
+
+ NSString* resolvedPath = [[(NSURL*)url path] stringByResolvingSymlinksInPath];
+ if (!resolvedPath) {
+ return NS_ERROR_FAILURE;
+ }
+
+ NSDictionary* dict = [[NSFileManager defaultManager] attributesOfItemAtPath:resolvedPath
+ error:nil];
+ if (!dict) {
+ return NS_ERROR_FAILURE;
+ }
+
+ NSNumber* typeNum = (NSNumber*)[dict objectForKey:NSFileHFSTypeCode];
+ if (!typeNum) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *typeCode = [typeNum unsignedLongValue];
+ return NS_OK;
+
+ NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
+}
+
+nsresult SetFileTypeCode(CFURLRef url, OSType typeCode) {
+ NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
+
+ if (NS_WARN_IF(!url)) return NS_ERROR_INVALID_ARG;
+
+ NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
+ NSDictionary* dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:typeCode]
+ forKey:NSFileHFSTypeCode];
+ BOOL success = [[NSFileManager defaultManager] setAttributes:dict
+ ofItemAtPath:[(NSURL*)url path]
+ error:nil];
+ [ap release];
+ return (success ? NS_OK : NS_ERROR_FAILURE);
+
+ NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
+}
+
+// Can be called off of the main thread.
+void AddOriginMetadataToFile(const CFStringRef filePath, const CFURLRef sourceURL,
+ const CFURLRef referrerURL) {
+ typedef OSStatus (*MDItemSetAttribute_type)(MDItemRef, CFStringRef, CFTypeRef);
+ static MDItemSetAttribute_type mdItemSetAttributeFunc = NULL;
+
+ static bool did_symbol_lookup = false;
+ if (!did_symbol_lookup) {
+ did_symbol_lookup = true;
+
+ CFBundleRef metadata_bundle = ::CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Metadata"));
+ if (!metadata_bundle) {
+ return;
+ }
+
+ mdItemSetAttributeFunc = (MDItemSetAttribute_type)::CFBundleGetFunctionPointerForName(
+ metadata_bundle, CFSTR("MDItemSetAttribute"));
+ }
+ if (!mdItemSetAttributeFunc) {
+ return;
+ }
+
+ MDItemRef mdItem = ::MDItemCreate(NULL, filePath);
+ if (!mdItem) {
+ return;
+ }
+
+ CFMutableArrayRef list = ::CFArrayCreateMutable(kCFAllocatorDefault, 2, NULL);
+ if (!list) {
+ ::CFRelease(mdItem);
+ return;
+ }
+
+ // The first item in the list is the source URL of the downloaded file.
+ if (sourceURL) {
+ ::CFArrayAppendValue(list, ::CFURLGetString(sourceURL));
+ }
+
+ // If the referrer is known, store that in the second position.
+ if (referrerURL) {
+ ::CFArrayAppendValue(list, ::CFURLGetString(referrerURL));
+ }
+
+ mdItemSetAttributeFunc(mdItem, kMDItemWhereFroms, list);
+
+ ::CFRelease(list);
+ ::CFRelease(mdItem);
+}
+
+// Can be called off of the main thread.
+static CFMutableDictionaryRef CreateQuarantineDictionary(const CFURLRef aFileURL,
+ const bool aCreateProps) {
+ CFDictionaryRef quarantineProps = NULL;
+ if (aCreateProps) {
+ quarantineProps = ::CFDictionaryCreate(NULL, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ } else {
+ Boolean success = ::CFURLCopyResourcePropertyForKey(aFileURL, kCFURLQuarantinePropertiesKey,
+ &quarantineProps, NULL);
+ // If there aren't any quarantine properties then the user probably
+ // set up an exclusion and we don't need to add metadata.
+ if (!success || !quarantineProps) {
+ return NULL;
+ }
+ }
+
+ // We don't know what to do if the props aren't a dictionary.
+ if (::CFGetTypeID(quarantineProps) != ::CFDictionaryGetTypeID()) {
+ ::CFRelease(quarantineProps);
+ return NULL;
+ }
+
+ // Make a mutable copy of the properties.
+ CFMutableDictionaryRef mutQuarantineProps =
+ ::CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, (CFDictionaryRef)quarantineProps);
+ ::CFRelease(quarantineProps);
+
+ return mutQuarantineProps;
+}
+
+// Can be called off of the main thread.
+void AddQuarantineMetadataToFile(const CFStringRef filePath, const CFURLRef sourceURL,
+ const CFURLRef referrerURL, const bool isFromWeb,
+ const bool createProps /* = false */) {
+ CFURLRef fileURL =
+ ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault, filePath, kCFURLPOSIXPathStyle, false);
+
+ CFMutableDictionaryRef mutQuarantineProps = CreateQuarantineDictionary(fileURL, createProps);
+ if (!mutQuarantineProps) {
+ ::CFRelease(fileURL);
+ return;
+ }
+
+ // Add metadata that the OS couldn't infer.
+
+ if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineTypeKey)) {
+ CFStringRef type = isFromWeb ? kLSQuarantineTypeWebDownload : kLSQuarantineTypeOtherDownload;
+ ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineTypeKey, type);
+ }
+
+ if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineOriginURLKey) && referrerURL) {
+ ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineOriginURLKey, referrerURL);
+ }
+
+ if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineDataURLKey) && sourceURL) {
+ ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineDataURLKey, sourceURL);
+ }
+
+ // Set quarantine properties on file.
+ ::CFURLSetResourcePropertyForKey(fileURL, kCFURLQuarantinePropertiesKey, mutQuarantineProps,
+ NULL);
+
+ ::CFRelease(fileURL);
+ ::CFRelease(mutQuarantineProps);
+}
+
+// Can be called off of the main thread.
+void CopyQuarantineReferrerUrl(const CFStringRef aFilePath, nsAString& aReferrer) {
+ CFURLRef fileURL =
+ ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault, aFilePath, kCFURLPOSIXPathStyle, false);
+
+ CFMutableDictionaryRef mutQuarantineProps = CreateQuarantineDictionary(fileURL, false);
+ ::CFRelease(fileURL);
+ if (!mutQuarantineProps) {
+ return;
+ }
+
+ CFTypeRef referrerRef = ::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineOriginURLKey);
+ if (referrerRef && ::CFGetTypeID(referrerRef) == ::CFURLGetTypeID()) {
+ // URL string must be copied prior to releasing the dictionary.
+ mozilla::CopyCocoaStringToXPCOMString(
+ (NSString*)::CFURLGetString(static_cast<CFURLRef>(referrerRef)), aReferrer);
+ }
+
+ ::CFRelease(mutQuarantineProps);
+}
+
+CFURLRef GetTemporaryFolderCFURLRef() {
+ NSString* tempDir = ::NSTemporaryDirectory();
+ return tempDir == nil ? NULL : (CFURLRef)[NSURL fileURLWithPath:tempDir isDirectory:YES];
+}
+
+CFURLRef GetProductDirectoryCFURLRef(bool aLocal) {
+ NSSearchPathDirectory folderType = aLocal ? NSCachesDirectory : NSLibraryDirectory;
+ NSFileManager* manager = [NSFileManager defaultManager];
+ return static_cast<CFURLRef>([[manager URLsForDirectory:folderType
+ inDomains:NSUserDomainMask] firstObject]);
+}
+
+} // namespace CocoaFileUtils