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 "nsCocoaUtils.h"
9 #include <Cocoa/Cocoa.h>
10 #include "nsObjCExceptions.h"
13 #include "mozilla/MacStringHelpers.h"
15 namespace CocoaFileUtils {
17 nsresult RevealFileInFinder(CFURLRef url) {
18 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
20 if (NS_WARN_IF(!url)) return NS_ERROR_INVALID_ARG;
22 NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
23 BOOL success = [[NSWorkspace sharedWorkspace] selectFile:[(NSURL*)url path]
24 inFileViewerRootedAtPath:@""];
27 return (success ? NS_OK : NS_ERROR_FAILURE);
29 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
32 nsresult OpenURL(CFURLRef url) {
33 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
35 if (NS_WARN_IF(!url)) return NS_ERROR_INVALID_ARG;
37 NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
38 BOOL success = [[NSWorkspace sharedWorkspace] openURL:(NSURL*)url];
41 return (success ? NS_OK : NS_ERROR_FAILURE);
43 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
46 nsresult GetFileCreatorCode(CFURLRef url, OSType* creatorCode) {
47 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
49 if (NS_WARN_IF(!url) || NS_WARN_IF(!creatorCode)) return NS_ERROR_INVALID_ARG;
51 nsAutoreleasePool localPool;
53 NSString* resolvedPath = [[(NSURL*)url path] stringByResolvingSymlinksInPath];
55 return NS_ERROR_FAILURE;
59 [[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];
82 NSDictionary* dict = [NSDictionary
83 dictionaryWithObject:[NSNumber numberWithUnsignedLong:creatorCode]
84 forKey:NSFileHFSCreatorCode];
86 [[NSFileManager defaultManager] setAttributes:dict
87 ofItemAtPath:[(NSURL*)url path]
90 return (success ? NS_OK : NS_ERROR_FAILURE);
92 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
95 nsresult GetFileTypeCode(CFURLRef url, OSType* typeCode) {
96 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
98 if (NS_WARN_IF(!url) || NS_WARN_IF(!typeCode)) return NS_ERROR_INVALID_ARG;
100 nsAutoreleasePool localPool;
102 NSString* resolvedPath = [[(NSURL*)url path] stringByResolvingSymlinksInPath];
104 return NS_ERROR_FAILURE;
108 [[NSFileManager defaultManager] attributesOfItemAtPath:resolvedPath
111 return NS_ERROR_FAILURE;
114 NSNumber* typeNum = (NSNumber*)[dict objectForKey:NSFileHFSTypeCode];
116 return NS_ERROR_FAILURE;
119 *typeCode = [typeNum unsignedLongValue];
122 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
125 nsresult SetFileTypeCode(CFURLRef url, OSType typeCode) {
126 NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
128 if (NS_WARN_IF(!url)) return NS_ERROR_INVALID_ARG;
130 NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
131 NSDictionary* dict = [NSDictionary
132 dictionaryWithObject:[NSNumber numberWithUnsignedLong:typeCode]
133 forKey:NSFileHFSTypeCode];
135 [[NSFileManager defaultManager] setAttributes:dict
136 ofItemAtPath:[(NSURL*)url path]
139 return (success ? NS_OK : NS_ERROR_FAILURE);
141 NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE);
144 // Can be called off of the main thread.
145 void AddOriginMetadataToFile(const CFStringRef filePath,
146 const CFURLRef sourceURL,
147 const CFURLRef referrerURL) {
148 typedef OSStatus (*MDItemSetAttribute_type)(MDItemRef, CFStringRef,
150 static MDItemSetAttribute_type mdItemSetAttributeFunc = NULL;
152 static bool did_symbol_lookup = false;
153 if (!did_symbol_lookup) {
154 did_symbol_lookup = true;
156 CFBundleRef metadata_bundle =
157 ::CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Metadata"));
158 if (!metadata_bundle) {
162 mdItemSetAttributeFunc =
163 (MDItemSetAttribute_type)::CFBundleGetFunctionPointerForName(
164 metadata_bundle, CFSTR("MDItemSetAttribute"));
166 if (!mdItemSetAttributeFunc) {
170 MDItemRef mdItem = ::MDItemCreate(NULL, filePath);
175 CFMutableArrayRef list = ::CFArrayCreateMutable(kCFAllocatorDefault, 2, NULL);
181 // The first item in the list is the source URL of the downloaded file.
183 ::CFArrayAppendValue(list, ::CFURLGetString(sourceURL));
186 // If the referrer is known, store that in the second position.
188 ::CFArrayAppendValue(list, ::CFURLGetString(referrerURL));
191 mdItemSetAttributeFunc(mdItem, kMDItemWhereFroms, list);
197 // Can be called off of the main thread.
198 static CFMutableDictionaryRef CreateQuarantineDictionary(
199 const CFURLRef aFileURL, const bool aCreateProps) {
200 CFDictionaryRef quarantineProps = NULL;
202 quarantineProps = ::CFDictionaryCreate(NULL, NULL, NULL, 0,
203 &kCFTypeDictionaryKeyCallBacks,
204 &kCFTypeDictionaryValueCallBacks);
206 Boolean success = ::CFURLCopyResourcePropertyForKey(
207 aFileURL, kCFURLQuarantinePropertiesKey, &quarantineProps, NULL);
208 // If there aren't any quarantine properties then the user probably
209 // set up an exclusion and we don't need to add metadata.
210 if (!success || !quarantineProps) {
215 // We don't know what to do if the props aren't a dictionary.
216 if (::CFGetTypeID(quarantineProps) != ::CFDictionaryGetTypeID()) {
217 ::CFRelease(quarantineProps);
221 // Make a mutable copy of the properties.
222 CFMutableDictionaryRef mutQuarantineProps = ::CFDictionaryCreateMutableCopy(
223 kCFAllocatorDefault, 0, (CFDictionaryRef)quarantineProps);
224 ::CFRelease(quarantineProps);
226 return mutQuarantineProps;
229 // Can be called off of the main thread.
230 void AddQuarantineMetadataToFile(const CFStringRef filePath,
231 const CFURLRef sourceURL,
232 const CFURLRef referrerURL,
233 const bool isFromWeb,
234 const bool createProps /* = false */) {
235 CFURLRef fileURL = ::CFURLCreateWithFileSystemPath(
236 kCFAllocatorDefault, filePath, kCFURLPOSIXPathStyle, false);
238 CFMutableDictionaryRef mutQuarantineProps =
239 CreateQuarantineDictionary(fileURL, createProps);
240 if (!mutQuarantineProps) {
241 ::CFRelease(fileURL);
245 // Add metadata that the OS couldn't infer.
247 if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineTypeKey)) {
248 CFStringRef type = isFromWeb ? kLSQuarantineTypeWebDownload
249 : kLSQuarantineTypeOtherDownload;
250 ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineTypeKey, type);
253 if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineOriginURLKey) &&
255 ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineOriginURLKey,
259 if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineDataURLKey) &&
261 ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineDataURLKey,
265 // Set quarantine properties on file.
266 ::CFURLSetResourcePropertyForKey(fileURL, kCFURLQuarantinePropertiesKey,
267 mutQuarantineProps, NULL);
269 ::CFRelease(fileURL);
270 ::CFRelease(mutQuarantineProps);
273 // Can be called off of the main thread.
274 void CopyQuarantineReferrerUrl(const CFStringRef aFilePath,
275 nsAString& aReferrer) {
276 CFURLRef fileURL = ::CFURLCreateWithFileSystemPath(
277 kCFAllocatorDefault, aFilePath, kCFURLPOSIXPathStyle, false);
279 CFMutableDictionaryRef mutQuarantineProps =
280 CreateQuarantineDictionary(fileURL, false);
281 ::CFRelease(fileURL);
282 if (!mutQuarantineProps) {
286 CFTypeRef referrerRef =
287 ::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineOriginURLKey);
288 if (referrerRef && ::CFGetTypeID(referrerRef) == ::CFURLGetTypeID()) {
289 // URL string must be copied prior to releasing the dictionary.
290 mozilla::CopyCocoaStringToXPCOMString(
291 (NSString*)::CFURLGetString(static_cast<CFURLRef>(referrerRef)),
295 ::CFRelease(mutQuarantineProps);
298 CFURLRef GetTemporaryFolderCFURLRef() {
299 NSString* tempDir = ::NSTemporaryDirectory();
300 return tempDir == nil
302 : (CFURLRef)[NSURL fileURLWithPath:tempDir isDirectory:YES];
305 CFURLRef GetProductDirectoryCFURLRef(bool aLocal) {
306 NSSearchPathDirectory folderType =
307 aLocal ? NSCachesDirectory : NSLibraryDirectory;
308 NSFileManager* manager = [NSFileManager defaultManager];
309 return static_cast<CFURLRef>(
310 [[manager URLsForDirectory:folderType
311 inDomains:NSUserDomainMask] firstObject]);
314 } // namespace CocoaFileUtils