1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim:set ts=2 sts=2 sw=2 et cin:
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "CocoaFileUtils.h"
8 #include "nsCocoaFeatures.h"
9 #include "nsCocoaUtils.h"
10 #include <Cocoa/Cocoa.h>
11 #include "nsObjCExceptions.h"
14 #include "mozilla/MacStringHelpers.h"
16 namespace CocoaFileUtils {
18 nsresult RevealFileInFinder(CFURLRef url) {
19 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
21 if (NS_WARN_IF(!url)) return NS_ERROR_INVALID_ARG;
23 NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
24 BOOL success = [[NSWorkspace sharedWorkspace] selectFile:[(NSURL*)url path]
25 inFileViewerRootedAtPath:@""];
28 return (success ? NS_OK : NS_ERROR_FAILURE);
30 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
33 nsresult OpenURL(CFURLRef url) {
34 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
36 if (NS_WARN_IF(!url)) return NS_ERROR_INVALID_ARG;
38 NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
39 BOOL success = [[NSWorkspace sharedWorkspace] openURL:(NSURL*)url];
42 return (success ? NS_OK : NS_ERROR_FAILURE);
44 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
47 nsresult GetFileCreatorCode(CFURLRef url, OSType* creatorCode) {
48 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
50 if (NS_WARN_IF(!url) || NS_WARN_IF(!creatorCode)) return NS_ERROR_INVALID_ARG;
52 nsAutoreleasePool localPool;
54 NSString* resolvedPath = [[(NSURL*)url path] stringByResolvingSymlinksInPath];
56 return NS_ERROR_FAILURE;
59 NSDictionary* dict = [[NSFileManager defaultManager] attributesOfItemAtPath:resolvedPath
62 return NS_ERROR_FAILURE;
65 NSNumber* creatorNum = (NSNumber*)[dict objectForKey:NSFileHFSCreatorCode];
67 return NS_ERROR_FAILURE;
70 *creatorCode = [creatorNum unsignedLongValue];
73 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
76 nsresult SetFileCreatorCode(CFURLRef url, OSType creatorCode) {
77 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
79 if (NS_WARN_IF(!url)) return NS_ERROR_INVALID_ARG;
81 NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
83 [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:creatorCode]
84 forKey:NSFileHFSCreatorCode];
85 BOOL success = [[NSFileManager defaultManager] setAttributes:dict
86 ofItemAtPath:[(NSURL*)url path]
89 return (success ? NS_OK : NS_ERROR_FAILURE);
91 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
94 nsresult GetFileTypeCode(CFURLRef url, OSType* typeCode) {
95 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
97 if (NS_WARN_IF(!url) || NS_WARN_IF(!typeCode)) return NS_ERROR_INVALID_ARG;
99 nsAutoreleasePool localPool;
101 NSString* resolvedPath = [[(NSURL*)url path] stringByResolvingSymlinksInPath];
103 return NS_ERROR_FAILURE;
106 NSDictionary* dict = [[NSFileManager defaultManager] attributesOfItemAtPath:resolvedPath
109 return NS_ERROR_FAILURE;
112 NSNumber* typeNum = (NSNumber*)[dict objectForKey:NSFileHFSTypeCode];
114 return NS_ERROR_FAILURE;
117 *typeCode = [typeNum unsignedLongValue];
120 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
123 nsresult SetFileTypeCode(CFURLRef url, OSType typeCode) {
124 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
126 if (NS_WARN_IF(!url)) return NS_ERROR_INVALID_ARG;
128 NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
129 NSDictionary* dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:typeCode]
130 forKey:NSFileHFSTypeCode];
131 BOOL success = [[NSFileManager defaultManager] setAttributes:dict
132 ofItemAtPath:[(NSURL*)url path]
135 return (success ? NS_OK : NS_ERROR_FAILURE);
137 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
140 // Can be called off of the main thread.
141 void AddOriginMetadataToFile(const CFStringRef filePath, const CFURLRef sourceURL,
142 const CFURLRef referrerURL) {
143 typedef OSStatus (*MDItemSetAttribute_type)(MDItemRef, CFStringRef, CFTypeRef);
144 static MDItemSetAttribute_type mdItemSetAttributeFunc = NULL;
146 static bool did_symbol_lookup = false;
147 if (!did_symbol_lookup) {
148 did_symbol_lookup = true;
150 CFBundleRef metadata_bundle = ::CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Metadata"));
151 if (!metadata_bundle) {
155 mdItemSetAttributeFunc = (MDItemSetAttribute_type)::CFBundleGetFunctionPointerForName(
156 metadata_bundle, CFSTR("MDItemSetAttribute"));
158 if (!mdItemSetAttributeFunc) {
162 MDItemRef mdItem = ::MDItemCreate(NULL, filePath);
167 CFMutableArrayRef list = ::CFArrayCreateMutable(kCFAllocatorDefault, 2, NULL);
173 // The first item in the list is the source URL of the downloaded file.
175 ::CFArrayAppendValue(list, ::CFURLGetString(sourceURL));
178 // If the referrer is known, store that in the second position.
180 ::CFArrayAppendValue(list, ::CFURLGetString(referrerURL));
183 mdItemSetAttributeFunc(mdItem, kMDItemWhereFroms, list);
189 // Can be called off of the main thread.
190 static CFMutableDictionaryRef CreateQuarantineDictionary(const CFURLRef aFileURL,
191 const bool aCreateProps) {
192 CFDictionaryRef quarantineProps = NULL;
194 quarantineProps = ::CFDictionaryCreate(NULL, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks,
195 &kCFTypeDictionaryValueCallBacks);
197 Boolean success = ::CFURLCopyResourcePropertyForKey(aFileURL, kCFURLQuarantinePropertiesKey,
198 &quarantineProps, NULL);
199 // If there aren't any quarantine properties then the user probably
200 // set up an exclusion and we don't need to add metadata.
201 if (!success || !quarantineProps) {
206 // We don't know what to do if the props aren't a dictionary.
207 if (::CFGetTypeID(quarantineProps) != ::CFDictionaryGetTypeID()) {
208 ::CFRelease(quarantineProps);
212 // Make a mutable copy of the properties.
213 CFMutableDictionaryRef mutQuarantineProps =
214 ::CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, (CFDictionaryRef)quarantineProps);
215 ::CFRelease(quarantineProps);
217 return mutQuarantineProps;
220 // Can be called off of the main thread.
221 void AddQuarantineMetadataToFile(const CFStringRef filePath, const CFURLRef sourceURL,
222 const CFURLRef referrerURL, const bool isFromWeb,
223 const bool createProps /* = false */) {
225 ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault, filePath, kCFURLPOSIXPathStyle, false);
227 CFMutableDictionaryRef mutQuarantineProps = CreateQuarantineDictionary(fileURL, createProps);
228 if (!mutQuarantineProps) {
229 ::CFRelease(fileURL);
233 // Add metadata that the OS couldn't infer.
235 if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineTypeKey)) {
236 CFStringRef type = isFromWeb ? kLSQuarantineTypeWebDownload : kLSQuarantineTypeOtherDownload;
237 ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineTypeKey, type);
240 if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineOriginURLKey) && referrerURL) {
241 ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineOriginURLKey, referrerURL);
244 if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineDataURLKey) && sourceURL) {
245 ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineDataURLKey, sourceURL);
248 // Set quarantine properties on file.
249 ::CFURLSetResourcePropertyForKey(fileURL, kCFURLQuarantinePropertiesKey, mutQuarantineProps,
252 ::CFRelease(fileURL);
253 ::CFRelease(mutQuarantineProps);
256 // Can be called off of the main thread.
257 void CopyQuarantineReferrerUrl(const CFStringRef aFilePath, nsAString& aReferrer) {
259 ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault, aFilePath, kCFURLPOSIXPathStyle, false);
261 CFMutableDictionaryRef mutQuarantineProps = CreateQuarantineDictionary(fileURL, false);
262 ::CFRelease(fileURL);
263 if (!mutQuarantineProps) {
267 CFTypeRef referrerRef = ::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineOriginURLKey);
268 if (referrerRef && ::CFGetTypeID(referrerRef) == ::CFURLGetTypeID()) {
269 // URL string must be copied prior to releasing the dictionary.
270 mozilla::CopyCocoaStringToXPCOMString(
271 (NSString*)::CFURLGetString(static_cast<CFURLRef>(referrerRef)), aReferrer);
274 ::CFRelease(mutQuarantineProps);
277 CFURLRef GetTemporaryFolderCFURLRef() {
278 NSString* tempDir = ::NSTemporaryDirectory();
279 return tempDir == nil ? NULL : (CFURLRef)[NSURL fileURLWithPath:tempDir isDirectory:YES];
282 CFURLRef GetProductDirectoryCFURLRef(bool aLocal) {
283 NSSearchPathDirectory folderType = aLocal ? NSCachesDirectory : NSLibraryDirectory;
284 NSFileManager* manager = [NSFileManager defaultManager];
285 return static_cast<CFURLRef>([[manager URLsForDirectory:folderType
286 inDomains:NSUserDomainMask] firstObject]);
289 } // namespace CocoaFileUtils