Line data Source code
1 0 : /*
2 :
3 : JAPersistentFileReference.m
4 :
5 :
6 : Copyright (C) 2010-2013 Jens Ayton
7 :
8 : Permission is hereby granted, free of charge, to any person obtaining a copy
9 : of this software and associated documentation files (the "Software"), to deal
10 : in the Software without restriction, including without limitation the rights
11 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : copies of the Software, and to permit persons to whom the Software is
13 : furnished to do so, subject to the following conditions:
14 :
15 : The above copyright notice and this permission notice shall be included in all
16 : copies or substantial portions of the Software.
17 :
18 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 : SOFTWARE.
25 :
26 : */
27 :
28 : #import "JAPersistentFileReference.h"
29 : #import <CoreServices/CoreServices.h>
30 :
31 :
32 0 : #define kURLKey @"url"
33 0 : #define kAliasKey @"alias"
34 0 : #define kBookmarkKey @"bookmark"
35 :
36 :
37 : #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
38 : #define BookmarkDataSupported() (YES)
39 : #else
40 : #if __ppc__ || __ppc64__
41 : // Bookmark data is only available in Snow Leopard and later, which excludes PPC systems.
42 : #define BookmarkDataSupported() (NO)
43 : #else
44 0 : #define BookmarkDataSupported() ([NSURL instancesRespondToSelector:@selector(bookmarkDataWithOptions:includingResourceValuesForKeys:relativeToURL:error:)])
45 : #endif
46 :
47 :
48 : #if MAC_OS_X_VERSION_10_6 > MAC_OS_X_VERSION_MAX_ALLOWED
49 :
50 : @interface NSURL (SnowLeopardMethods)
51 :
52 : - (NSData *)bookmarkDataWithOptions:(unsigned long)options includingResourceValuesForKeys:(NSArray *)keys relativeToURL:(NSURL *)relativeURL error:(NSError **)error;
53 : - (NSURL *)fileReferenceURL;
54 : - (NSURL *)filePathURL;
55 : - (BOOL)isFileReferenceURL;
56 : + (id)URLByResolvingBookmarkData:(NSData *)bookmarkData options:(unsigned long)options relativeToURL:(NSURL *)relativeURL bookmarkDataIsStale:(BOOL *)isStale error:(NSError **)error;
57 :
58 : @end
59 :
60 :
61 : enum
62 : {
63 : NSURLBookmarkResolutionWithoutUI = ( 1UL << 8 ),
64 : NSURLBookmarkResolutionWithoutMounting = ( 1UL << 9 ),
65 : };
66 :
67 : #endif
68 : #endif
69 :
70 :
71 0 : NSDictionary *JAPersistentFileReferenceFromURL(NSURL *url)
72 : {
73 : if (url == nil) return nil;
74 :
75 : NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:3];
76 : [result setObject:[url absoluteString] forKey:kURLKey];
77 :
78 : if ([url isFileURL])
79 : {
80 : FSRef fsRef;
81 : if (CFURLGetFSRef((CFURLRef)[url absoluteURL], &fsRef))
82 : {
83 : AliasHandle alias = NULL;
84 : if (FSNewAlias(NULL, &fsRef, &alias) == noErr)
85 : {
86 : NSData *aliasData = [NSData dataWithBytes:*alias length:GetAliasSize(alias)];
87 : if (aliasData != NULL)
88 : {
89 : [result setObject:aliasData forKey:kAliasKey];
90 : }
91 : DisposeHandle((Handle)alias);
92 : }
93 : }
94 : }
95 :
96 : if (BookmarkDataSupported())
97 : {
98 : NSURL *refURL = [url fileReferenceURL];
99 : if (refURL != nil)
100 : {
101 : NSData *bookmarkData = [refURL bookmarkDataWithOptions:0
102 : includingResourceValuesForKeys:nil
103 : relativeToURL:nil
104 : error:NULL];
105 : if (bookmarkData != nil)
106 : {
107 : [result setObject:bookmarkData forKey:kBookmarkKey];
108 : }
109 : }
110 : }
111 :
112 : return result;
113 : }
114 :
115 :
116 0 : static inline unsigned long BookmarkOptionsFromFlags(JAPersistentFileReferenceResolveFlags flags)
117 : {
118 : unsigned long result = 0;
119 : if (flags & kJAPersistentFileReferenceWithoutUI) result |= NSURLBookmarkResolutionWithoutUI;
120 : if (flags & NSURLBookmarkResolutionWithoutMounting) result |= NSURLBookmarkResolutionWithoutMounting;
121 : return result;
122 : }
123 :
124 :
125 0 : static inline unsigned long AliasMountFlagsFromFlags(JAPersistentFileReferenceResolveFlags flags)
126 : {
127 : unsigned long result = 0;
128 : if (flags & kJAPersistentFileReferenceWithoutUI) result |= kResolveAliasFileNoUI;
129 : return result;
130 : }
131 :
132 :
133 0 : NSURL *JAURLFromPersistentFileReference(NSDictionary *fileRef, JAPersistentFileReferenceResolveFlags flags, BOOL *isStale)
134 : {
135 : NSURL *result = nil;
136 : BOOL stale = NO, staleIfFile = NO;
137 :
138 : // Try bookmark.
139 : if (BookmarkDataSupported())
140 : {
141 : NSData *bookmarkData = [fileRef objectForKey:kBookmarkKey];
142 : if ([bookmarkData isKindOfClass:[NSData class]])
143 : {
144 : result = [NSURL URLByResolvingBookmarkData:bookmarkData
145 : options:BookmarkOptionsFromFlags(flags)
146 : relativeToURL:nil
147 : bookmarkDataIsStale:&stale
148 : error:NULL];
149 : }
150 : else staleIfFile = YES;
151 : }
152 :
153 : // Try alias.
154 : if (result == nil)
155 : {
156 : NSData *aliasData = [fileRef objectForKey:kAliasKey];
157 : if ([aliasData isKindOfClass:[NSData class]])
158 : {
159 : size_t size = [aliasData length];
160 : AliasHandle alias = (AliasHandle)NewHandle(size);
161 : if (alias != NULL)
162 : {
163 : memcpy(*alias, [aliasData bytes], size);
164 : FSRef fsRef;
165 :
166 : Boolean carbonStale;
167 : if (FSResolveAliasWithMountFlags(NULL, alias, &fsRef, &carbonStale, AliasMountFlagsFromFlags(flags)) == noErr)
168 : {
169 : stale = carbonStale;
170 : result = (NSURL *)CFURLCreateFromFSRef(kCFAllocatorDefault, &fsRef);
171 : #if 1050 <= MAC_OS_X_VERSION_MAX_ALLOWED
172 : CFMakeCollectable((CFURLRef)result);
173 : #endif
174 : [result autorelease];
175 : }
176 : DisposeHandle((Handle)alias);
177 : }
178 : }
179 : else staleIfFile = YES;
180 : }
181 :
182 : // Try URL.
183 : if (result == nil)
184 : {
185 : NSString *urlString = [fileRef objectForKey:kURLKey];
186 : if ([urlString isKindOfClass:[NSString class]])
187 : {
188 : result = [NSURL URLWithString:urlString relativeToURL:nil];
189 : if ([result isFileURL] && ![[NSFileManager defaultManager] fileExistsAtPath:[result path]])
190 : {
191 : result = nil;
192 : }
193 : }
194 : }
195 :
196 : // If we got nothing, it's definitely stale.
197 : if (result == nil)
198 : {
199 : stale = YES;
200 : }
201 : else
202 : {
203 : if ([result isFileURL] && staleIfFile) stale = YES;
204 :
205 : // Convert to/from file reference URL as appropriate.
206 : if (BookmarkDataSupported())
207 : {
208 : if (flags & kJAPersistentFileReferenceReturnReferenceURL)
209 : {
210 : if (![result isFileReferenceURL] && [result isFileURL])
211 : {
212 : NSURL *refURL = [result fileReferenceURL];
213 : if (refURL != nil) result = refURL;
214 : }
215 : }
216 : else
217 : {
218 : if ([result isFileReferenceURL])
219 : {
220 : NSURL *pathURL = [result filePathURL];
221 : if (pathURL != nil) result = pathURL;
222 : }
223 : }
224 : }
225 : }
226 :
227 :
228 : if (isStale != NULL) *isStale = stale;
229 : return result;
230 : }
231 :
232 :
233 0 : NSDictionary *JAPersistentFileReferenceFromPath(NSString *path)
234 : {
235 : return JAPersistentFileReferenceFromURL([NSURL fileURLWithPath:path]);
236 : }
237 :
238 :
239 0 : NSString *JAPathFromPersistentFileReference(NSDictionary *fileRef, JAPersistentFileReferenceResolveFlags flags, BOOL *isStale)
240 : {
241 : NSURL *url = JAURLFromPersistentFileReference(fileRef, flags & ~kJAPersistentFileReferenceReturnReferenceURL, isStale);
242 : if ([url isFileURL]) return [url path];
243 : return nil;
244 : }
|