Merge mozilla-central to autoland on a CLOSED TREE
[gecko.git] / xpcom / io / CocoaFileUtils.mm
blobb906365a2b3f2f220b6bc92b1d04796506cea4c3
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"
12 #include "nsDebug.h"
13 #include "nsString.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:@""];
26   [ap release];
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];
40   [ap release];
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];
55   if (!resolvedPath) {
56     return NS_ERROR_FAILURE;
57   }
59   NSDictionary* dict = [[NSFileManager defaultManager] attributesOfItemAtPath:resolvedPath
60                                                                         error:nil];
61   if (!dict) {
62     return NS_ERROR_FAILURE;
63   }
65   NSNumber* creatorNum = (NSNumber*)[dict objectForKey:NSFileHFSCreatorCode];
66   if (!creatorNum) {
67     return NS_ERROR_FAILURE;
68   }
70   *creatorCode = [creatorNum unsignedLongValue];
71   return NS_OK;
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 =
83       [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:creatorCode]
84                                   forKey:NSFileHFSCreatorCode];
85   BOOL success = [[NSFileManager defaultManager] setAttributes:dict
86                                                   ofItemAtPath:[(NSURL*)url path]
87                                                          error:nil];
88   [ap release];
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];
102   if (!resolvedPath) {
103     return NS_ERROR_FAILURE;
104   }
106   NSDictionary* dict = [[NSFileManager defaultManager] attributesOfItemAtPath:resolvedPath
107                                                                         error:nil];
108   if (!dict) {
109     return NS_ERROR_FAILURE;
110   }
112   NSNumber* typeNum = (NSNumber*)[dict objectForKey:NSFileHFSTypeCode];
113   if (!typeNum) {
114     return NS_ERROR_FAILURE;
115   }
117   *typeCode = [typeNum unsignedLongValue];
118   return NS_OK;
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]
133                                                          error:nil];
134   [ap release];
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) {
152       return;
153     }
155     mdItemSetAttributeFunc = (MDItemSetAttribute_type)::CFBundleGetFunctionPointerForName(
156         metadata_bundle, CFSTR("MDItemSetAttribute"));
157   }
158   if (!mdItemSetAttributeFunc) {
159     return;
160   }
162   MDItemRef mdItem = ::MDItemCreate(NULL, filePath);
163   if (!mdItem) {
164     return;
165   }
167   CFMutableArrayRef list = ::CFArrayCreateMutable(kCFAllocatorDefault, 2, NULL);
168   if (!list) {
169     ::CFRelease(mdItem);
170     return;
171   }
173   // The first item in the list is the source URL of the downloaded file.
174   if (sourceURL) {
175     ::CFArrayAppendValue(list, ::CFURLGetString(sourceURL));
176   }
178   // If the referrer is known, store that in the second position.
179   if (referrerURL) {
180     ::CFArrayAppendValue(list, ::CFURLGetString(referrerURL));
181   }
183   mdItemSetAttributeFunc(mdItem, kMDItemWhereFroms, list);
185   ::CFRelease(list);
186   ::CFRelease(mdItem);
189 // Can be called off of the main thread.
190 static CFMutableDictionaryRef CreateQuarantineDictionary(const CFURLRef aFileURL,
191                                                          const bool aCreateProps) {
192   CFDictionaryRef quarantineProps = NULL;
193   if (aCreateProps) {
194     quarantineProps = ::CFDictionaryCreate(NULL, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks,
195                                            &kCFTypeDictionaryValueCallBacks);
196   } else {
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) {
202       return NULL;
203     }
204   }
206   // We don't know what to do if the props aren't a dictionary.
207   if (::CFGetTypeID(quarantineProps) != ::CFDictionaryGetTypeID()) {
208     ::CFRelease(quarantineProps);
209     return NULL;
210   }
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 */) {
224   CFURLRef fileURL =
225       ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault, filePath, kCFURLPOSIXPathStyle, false);
227   CFMutableDictionaryRef mutQuarantineProps = CreateQuarantineDictionary(fileURL, createProps);
228   if (!mutQuarantineProps) {
229     ::CFRelease(fileURL);
230     return;
231   }
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);
238   }
240   if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineOriginURLKey) && referrerURL) {
241     ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineOriginURLKey, referrerURL);
242   }
244   if (!::CFDictionaryGetValue(mutQuarantineProps, kLSQuarantineDataURLKey) && sourceURL) {
245     ::CFDictionarySetValue(mutQuarantineProps, kLSQuarantineDataURLKey, sourceURL);
246   }
248   // Set quarantine properties on file.
249   ::CFURLSetResourcePropertyForKey(fileURL, kCFURLQuarantinePropertiesKey, mutQuarantineProps,
250                                    NULL);
252   ::CFRelease(fileURL);
253   ::CFRelease(mutQuarantineProps);
256 // Can be called off of the main thread.
257 void CopyQuarantineReferrerUrl(const CFStringRef aFilePath, nsAString& aReferrer) {
258   CFURLRef fileURL =
259       ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault, aFilePath, kCFURLPOSIXPathStyle, false);
261   CFMutableDictionaryRef mutQuarantineProps = CreateQuarantineDictionary(fileURL, false);
262   ::CFRelease(fileURL);
263   if (!mutQuarantineProps) {
264     return;
265   }
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);
272   }
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