]> git.saurik.com Git - apple/cf.git/blob - CFBundle_InfoPlist.c
CF-855.14.tar.gz
[apple/cf.git] / CFBundle_InfoPlist.c
1 /*
2 * Copyright (c) 2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 //
25 // CFBundle_InfoPlist.c
26 // CoreFoundation
27 //
28 // Created by Tony Parker on 5/30/12.
29 //
30 //
31
32 #include <CoreFoundation/CFBundle.h>
33 #include <CoreFoundation/CFNumber.h>
34 #include "CFBundle_Internal.h"
35 #include "CFByteOrder.h"
36 #include "CFURLAccess.h"
37
38 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_EMBEDDED_MINI
39 #include <dirent.h>
40 #include <sys/sysctl.h>
41 #endif
42
43 // The following strings are initialized 'later' (i.e., not at static initialization time) because static init time is too early for CFSTR to work, on platforms without constant CF strings
44 #if !__CONSTANT_STRINGS__
45
46 #define _CFBundleNumberOfPlatforms 7
47 static CFStringRef _CFBundleSupportedPlatforms[_CFBundleNumberOfPlatforms] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
48 static const char *_CFBundleSupportedPlatformStrings[_CFBundleNumberOfPlatforms] = { "iphoneos", "macos", "windows", "linux", "freebsd", "solaris", "hpux" };
49
50 #define _CFBundleNumberOfProducts 3
51 static CFStringRef _CFBundleSupportedProducts[_CFBundleNumberOfProducts] = { NULL, NULL, NULL };
52 static const char *_CFBundleSupportedProductStrings[_CFBundleNumberOfProducts] = { "iphone", "ipod", "ipad" };
53
54 #define _CFBundleNumberOfiPhoneOSPlatformProducts 3
55 static CFStringRef _CFBundleSupportediPhoneOSPlatformProducts[_CFBundleNumberOfiPhoneOSPlatformProducts] = { NULL, NULL, NULL };
56 static const char *_CFBundleSupportediPhoneOSPlatformProductStrings[_CFBundleNumberOfiPhoneOSPlatformProducts] = { "iphone", "ipod", "ipad" };
57
58 CF_PRIVATE void _CFBundleResourcesInitialize() {
59 for (unsigned int i = 0; i < _CFBundleNumberOfPlatforms; i++) _CFBundleSupportedPlatforms[i] = CFStringCreateWithCString(kCFAllocatorSystemDefault, _CFBundleSupportedPlatformStrings[i], kCFStringEncodingUTF8);
60
61 for (unsigned int i = 0; i < _CFBundleNumberOfProducts; i++) _CFBundleSupportedProducts[i] = CFStringCreateWithCString(kCFAllocatorSystemDefault, _CFBundleSupportedProductStrings[i], kCFStringEncodingUTF8);
62
63 for (unsigned int i = 0; i < _CFBundleNumberOfiPhoneOSPlatformProducts; i++) _CFBundleSupportediPhoneOSPlatformProducts[i] = CFStringCreateWithCString(kCFAllocatorSystemDefault, _CFBundleSupportediPhoneOSPlatformProductStrings[i], kCFStringEncodingUTF8);
64 }
65
66 #else
67
68 #if DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
69 // On iOS, we only support one platform
70 #define _CFBundleNumberOfPlatforms 1
71 static CFStringRef _CFBundleSupportedPlatforms[_CFBundleNumberOfPlatforms] = { CFSTR("iphoneos") };
72 #else
73 // On other platforms, we support the following platforms
74 #define _CFBundleNumberOfPlatforms 7
75 static CFStringRef _CFBundleSupportedPlatforms[_CFBundleNumberOfPlatforms] = { CFSTR("iphoneos"), CFSTR("macos"), CFSTR("windows"), CFSTR("linux"), CFSTR("freebsd"), CFSTR("solaris"), CFSTR("hpux") };
76 #endif
77
78 #define _CFBundleNumberOfProducts 3
79 static CFStringRef _CFBundleSupportedProducts[_CFBundleNumberOfProducts] = { CFSTR("iphone"), CFSTR("ipod"), CFSTR("ipad") };
80
81 #define _CFBundleNumberOfiPhoneOSPlatformProducts 3
82 static CFStringRef _CFBundleSupportediPhoneOSPlatformProducts[_CFBundleNumberOfiPhoneOSPlatformProducts] = { CFSTR("iphone"), CFSTR("ipod"), CFSTR("ipad") };
83
84 CF_PRIVATE void _CFBundleResourcesInitialize() { }
85 #endif
86
87 #pragma mark -
88 #pragma mark Product and Platform Getters - Exported
89
90 static CFStringRef _cfBundlePlatform = NULL;
91 CF_EXPORT void _CFSetProductName(CFStringRef str) {
92 // TODO: This should be removed. The "CLASSIC" check below removes the need to set the product name manually.
93 if (str) CFRetain(str);
94 _cfBundlePlatform = str;
95 // Note that the previous value is leaked, which is fine normally
96 // because the initial values would tend to be the constant strings
97 // below. That is required for thread-safety value due to the Get
98 // function [not being Copy]. It is also fine because people
99 // shouldn't be screwing around with this value casually.
100 }
101
102 CF_EXPORT CFStringRef _CFGetProductName(void) {
103 #if DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
104 if (!_cfBundlePlatform) {
105 const char *isClassic = __CFgetenv("CLASSIC");
106 if (isClassic && strnlen(isClassic, 1) >= 1 && isClassic[0] == '1') {
107 _cfBundlePlatform = CFSTR("iphone");
108 } else {
109 char buffer[256];
110 memset(buffer, 0, sizeof(buffer));
111 size_t buflen = sizeof(buffer);
112 int ret = sysctlbyname("hw.machine", buffer, &buflen, NULL, 0);
113 if (0 == ret || (-1 == ret && ENOMEM == errno)) {
114 if (6 <= buflen && 0 == memcmp(buffer, "iPhone", 6)) {
115 _cfBundlePlatform = CFSTR("iphone");
116 } else if (4 <= buflen && 0 == memcmp(buffer, "iPod", 4)) {
117 _cfBundlePlatform = CFSTR("ipod");
118 } else if (4 <= buflen && 0 == memcmp(buffer, "iPad", 4)) {
119 _cfBundlePlatform = CFSTR("ipad");
120 } else {
121 const char *env = __CFgetenv("IPHONE_SIMULATOR_DEVICE");
122 if (env) {
123 if (0 == strcmp(env, "iPhone")) {
124 _cfBundlePlatform = CFSTR("iphone");
125 } else if (0 == strcmp(env, "iPad")) {
126 _cfBundlePlatform = CFSTR("ipad");
127 } else {
128 // fallback, unrecognized IPHONE_SIMULATOR_DEVICE
129 }
130 } else {
131 // fallback, unrecognized hw.machine and no IPHONE_SIMULATOR_DEVICE
132 }
133 }
134 }
135 }
136 if (!_cfBundlePlatform) _cfBundlePlatform = CFSTR("iphone"); // fallback
137 }
138 return _cfBundlePlatform;
139 #endif
140 return CFSTR("");
141 }
142
143 // All new-style bundles will have these extensions.
144 CF_EXPORT CFStringRef _CFGetPlatformName(void) {
145 #if DEPLOYMENT_TARGET_MACOSX
146 return _CFBundleMacOSXPlatformName;
147 #elif DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
148 return _CFBundleiPhoneOSPlatformName;
149 #elif DEPLOYMENT_TARGET_WINDOWS
150 return _CFBundleWindowsPlatformName;
151 #elif DEPLOYMENT_TARGET_SOLARIS
152 return _CFBundleSolarisPlatformName;
153 #elif DEPLOYMENT_TARGET_HPUX
154 return _CFBundleHPUXPlatformName;
155 #elif DEPLOYMENT_TARGET_LINUX
156 return _CFBundleLinuxPlatformName;
157 #elif DEPLOYMENT_TARGET_FREEBSD
158 return _CFBundleFreeBSDPlatformName;
159 #else
160 #error Unknown or unspecified DEPLOYMENT_TARGET
161 #endif
162 }
163
164 CF_EXPORT CFStringRef _CFGetAlternatePlatformName(void) {
165 #if DEPLOYMENT_TARGET_MACOSX
166 return _CFBundleAlternateMacOSXPlatformName;
167 #elif DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
168 return _CFBundleMacOSXPlatformName;
169 #elif DEPLOYMENT_TARGET_WINDOWS
170 return CFSTR("");
171 #else
172 #error Unknown or unspecified DEPLOYMENT_TARGET
173 #endif
174 }
175
176 #pragma mark -
177 #pragma mark Product and Platform Suffix Processing - Internal
178
179 // TODO: Merge with below function, they do the same thing
180 static Boolean _isValidPlatformSuffix(CFStringRef suffix) {
181 for (CFIndex idx = 0; idx < _CFBundleNumberOfPlatforms; idx++) {
182 if (CFEqual(suffix, _CFBundleSupportedPlatforms[idx])) return true;
183 }
184 return false;
185 }
186
187 // Returns true if the searchRange of the fileName is equal to a valid platform name (e.g., macos, iphoneos)
188 CF_PRIVATE Boolean _CFBundleSupportedPlatformName(CFStringRef fileName, CFRange searchRange) {
189 for (CFIndex i = 0; i < _CFBundleNumberOfPlatforms; i++) {
190 if (CFStringFindWithOptions(fileName, _CFBundleSupportedPlatforms[i], searchRange, kCFCompareAnchored, NULL)) {
191 return true;
192 }
193 }
194 return false;
195 }
196
197 // TODO: Merge with below function, they do the same thing
198 static Boolean _isValidProductSuffix(CFStringRef suffix) {
199 for (CFIndex idx = 0; idx < _CFBundleNumberOfProducts; idx++) {
200 if (CFEqual(suffix, _CFBundleSupportedProducts[idx])) return true;
201 }
202 return false;
203 }
204
205 // Returns true if the searchRange of the fileName is equal to a a valid product name (e.g., ipod, ipad)
206 CF_PRIVATE Boolean _CFBundleSupportedProductName(CFStringRef fileName, CFRange searchRange) {
207 for (CFIndex i = 0; i < _CFBundleNumberOfProducts; i++) {
208 if (CFStringFindWithOptions(fileName, _CFBundleSupportedProducts[i], searchRange, kCFCompareAnchored, NULL)) {
209 return true;
210 }
211 }
212 return false;
213 }
214
215 static Boolean _isValidiPhoneOSPlatformProductSuffix(CFStringRef suffix) {
216 for (CFIndex idx = 0; idx < _CFBundleNumberOfiPhoneOSPlatformProducts; idx++) {
217 if (CFEqual(suffix, _CFBundleSupportediPhoneOSPlatformProducts[idx])) return true;
218 }
219 return false;
220 }
221
222 static Boolean _isValidPlatformAndProductSuffixPair(CFStringRef platform, CFStringRef product) {
223 if (!platform && !product) return true;
224 if (!platform) {
225 return _isValidProductSuffix(product);
226 }
227 if (!product) {
228 return _isValidPlatformSuffix(platform);
229 }
230 if (CFEqual(platform, _CFBundleiPhoneOSPlatformName)) {
231 return _isValidiPhoneOSPlatformProductSuffix(product);
232 }
233 return false;
234 }
235
236 static Boolean _isBlacklistedKey(CFStringRef keyName) {
237 #if __CONSTANT_STRINGS__
238 #define _CFBundleNumberOfBlacklistedInfoDictionaryKeys 2
239 static const CFStringRef _CFBundleBlacklistedInfoDictionaryKeys[_CFBundleNumberOfBlacklistedInfoDictionaryKeys] = { CFSTR("CFBundleExecutable"), CFSTR("CFBundleIdentifier") };
240
241 for (CFIndex idx = 0; idx < _CFBundleNumberOfBlacklistedInfoDictionaryKeys; idx++) {
242 if (CFEqual(keyName, _CFBundleBlacklistedInfoDictionaryKeys[idx])) return true;
243 }
244 #endif
245 return false;
246 }
247
248 static Boolean _isOverrideKey(CFStringRef fullKey, CFStringRef *outBaseKey, CFStringRef *outPlatformSuffix, CFStringRef *outProductSuffix) {
249 if (outBaseKey) {
250 *outBaseKey = NULL;
251 }
252 if (outPlatformSuffix) {
253 *outPlatformSuffix = NULL;
254 }
255 if (outProductSuffix) {
256 *outProductSuffix = NULL;
257 }
258 if (!fullKey)
259 return false;
260 CFRange minusRange = CFStringFind(fullKey, CFSTR("-"), kCFCompareBackwards);
261 CFRange tildeRange = CFStringFind(fullKey, CFSTR("~"), kCFCompareBackwards);
262 if (minusRange.location == kCFNotFound && tildeRange.location == kCFNotFound) return false;
263 // minus must come before tilde if both are present
264 if (minusRange.location != kCFNotFound && tildeRange.location != kCFNotFound && tildeRange.location <= minusRange.location) return false;
265
266 CFIndex strLen = CFStringGetLength(fullKey);
267 CFRange baseKeyRange = (minusRange.location != kCFNotFound) ? CFRangeMake(0, minusRange.location) : CFRangeMake(0, tildeRange.location);
268 CFRange platformRange = CFRangeMake(kCFNotFound, 0);
269 CFRange productRange = CFRangeMake(kCFNotFound, 0);
270 if (minusRange.location != kCFNotFound) {
271 platformRange.location = minusRange.location + minusRange.length;
272 platformRange.length = ((tildeRange.location != kCFNotFound) ? tildeRange.location : strLen) - platformRange.location;
273 }
274 if (tildeRange.location != kCFNotFound) {
275 productRange.location = tildeRange.location + tildeRange.length;
276 productRange.length = strLen - productRange.location;
277 }
278 if (baseKeyRange.length < 1) return false;
279 if (platformRange.location != kCFNotFound && platformRange.length < 1) return false;
280 if (productRange.location != kCFNotFound && productRange.length < 1) return false;
281
282 CFStringRef platform = (platformRange.location != kCFNotFound) ? CFStringCreateWithSubstring(kCFAllocatorSystemDefault, fullKey, platformRange) : NULL;
283 CFStringRef product = (productRange.location != kCFNotFound) ? CFStringCreateWithSubstring(kCFAllocatorSystemDefault, fullKey, productRange) : NULL;
284 Boolean result = _isValidPlatformAndProductSuffixPair(platform, product);
285
286 if (result) {
287 if (outBaseKey) {
288 *outBaseKey = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, fullKey, baseKeyRange);
289 }
290 if (outPlatformSuffix) {
291 *outPlatformSuffix = platform;
292 } else {
293 if (platform && !(0)) CFRelease(platform);
294 }
295 if (outProductSuffix) {
296 *outProductSuffix = product;
297 } else {
298 if (product && !(0)) CFRelease(product);
299 }
300 } else {
301 if (platform && !(0)) CFRelease(platform);
302 if (product && !(0)) CFRelease(product);
303 }
304 return result;
305 }
306
307 static Boolean _isCurrentPlatformAndProduct(CFStringRef platform, CFStringRef product) {
308 if (!platform && !product) return true;
309 if (!platform) {
310 return CFEqual(_CFGetProductName(), product);
311 }
312 if (!product) {
313 return CFEqual(_CFGetPlatformName(), platform);
314 }
315
316 return CFEqual(_CFGetProductName(), product) && CFEqual(_CFGetPlatformName(), platform);
317 }
318
319 static CFArrayRef _CopySortedOverridesForBaseKey(CFStringRef keyName, CFDictionaryRef dict) {
320 CFMutableArrayRef overrides = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
321 CFStringRef keyNameWithBoth = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@-%@~%@"), keyName, _CFGetPlatformName(), _CFGetProductName());
322 CFStringRef keyNameWithProduct = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@~%@"), keyName, _CFGetProductName());
323 CFStringRef keyNameWithPlatform = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@-%@"), keyName, _CFGetPlatformName());
324
325 CFIndex count = CFDictionaryGetCount(dict);
326
327 if (count > 0) {
328 CFTypeRef *keys = (CFTypeRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * count * sizeof(CFTypeRef), 0);
329 CFTypeRef *values = &(keys[count]);
330
331 CFDictionaryGetKeysAndValues(dict, keys, values);
332 for (CFIndex idx = 0; idx < count; idx++) {
333 if (CFEqual(keys[idx], keyNameWithBoth)) {
334 CFArrayAppendValue(overrides, keys[idx]);
335 break;
336 }
337 }
338 for (CFIndex idx = 0; idx < count; idx++) {
339 if (CFEqual(keys[idx], keyNameWithProduct)) {
340 CFArrayAppendValue(overrides, keys[idx]);
341 break;
342 }
343 }
344 for (CFIndex idx = 0; idx < count; idx++) {
345 if (CFEqual(keys[idx], keyNameWithPlatform)) {
346 CFArrayAppendValue(overrides, keys[idx]);
347 break;
348 }
349 }
350 for (CFIndex idx = 0; idx < count; idx++) {
351 if (CFEqual(keys[idx], keyName)) {
352 CFArrayAppendValue(overrides, keys[idx]);
353 break;
354 }
355 }
356
357 CFAllocatorDeallocate(kCFAllocatorSystemDefault, keys);
358 }
359
360 CFRelease(keyNameWithProduct);
361 CFRelease(keyNameWithPlatform);
362 CFRelease(keyNameWithBoth);
363
364 return overrides;
365 }
366
367 CF_PRIVATE void _CFBundleInfoPlistProcessInfoDictionary(CFMutableDictionaryRef dict) {
368 CFIndex count = CFDictionaryGetCount(dict);
369
370 if (count > 0) {
371 CFTypeRef *keys = (CFTypeRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * count * sizeof(CFTypeRef), 0);
372 CFTypeRef *values = &(keys[count]);
373 CFMutableArrayRef guard = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
374
375 CFDictionaryGetKeysAndValues(dict, keys, values);
376 for (CFIndex idx = 0; idx < count; idx++) {
377 CFStringRef keyPlatformSuffix, keyProductSuffix, keyName;
378 if (_isOverrideKey((CFStringRef)keys[idx], &keyName, &keyPlatformSuffix, &keyProductSuffix)) {
379 CFArrayRef keysForBaseKey = NULL;
380 if (_isCurrentPlatformAndProduct(keyPlatformSuffix, keyProductSuffix) && !_isBlacklistedKey(keyName) && CFDictionaryContainsKey(dict, keys[idx])) {
381 keysForBaseKey = _CopySortedOverridesForBaseKey(keyName, dict);
382 CFIndex keysForBaseKeyCount = CFArrayGetCount(keysForBaseKey);
383
384 //make sure the other keys for this base key don't get released out from under us until we're done
385 CFArrayAppendValue(guard, keysForBaseKey);
386
387 //the winner for this base key will be sorted to the front, do the override with it
388 CFTypeRef highestPriorityKey = CFArrayGetValueAtIndex(keysForBaseKey, 0);
389 CFDictionarySetValue(dict, keyName, CFDictionaryGetValue(dict, highestPriorityKey));
390
391 //remove everything except the now-overridden key; this will cause them to fail the CFDictionaryContainsKey(dict, keys[idx]) check in the enclosing if() and not be reprocessed
392 for (CFIndex presentKeysIdx = 0; presentKeysIdx < keysForBaseKeyCount; presentKeysIdx++) {
393 CFStringRef currentKey = (CFStringRef)CFArrayGetValueAtIndex(keysForBaseKey, presentKeysIdx);
394 if (!CFEqual(currentKey, keyName))
395 CFDictionaryRemoveValue(dict, currentKey);
396 }
397 } else {
398 CFDictionaryRemoveValue(dict, keys[idx]);
399 }
400
401
402 if (keyPlatformSuffix) CFRelease(keyPlatformSuffix);
403 if (keyProductSuffix) CFRelease(keyProductSuffix);
404 CFRelease(keyName);
405 if (keysForBaseKey) CFRelease(keysForBaseKey);
406 }
407 }
408
409 CFAllocatorDeallocate(kCFAllocatorSystemDefault, keys);
410 CFRelease(guard);
411 }
412 }
413
414 #pragma mark -
415 #pragma mark Info Plist Functions
416
417 CF_PRIVATE CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectory(CFAllocatorRef alloc, CFURLRef url, uint8_t *version) {
418 CFDictionaryRef dict = NULL;
419 unsigned char buff[CFMaxPathSize];
420 uint8_t localVersion = 0;
421
422 if (CFURLGetFileSystemRepresentation(url, true, buff, CFMaxPathSize)) {
423 CFURLRef newURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, buff, strlen((char *)buff), true);
424 if (!newURL) newURL = (CFURLRef)CFRetain(url);
425
426 localVersion = _CFBundleGetBundleVersionForURL(newURL);
427
428 dict = _CFBundleCopyInfoDictionaryInDirectoryWithVersion(alloc, newURL, localVersion);
429 CFRelease(newURL);
430 }
431 if (version) *version = localVersion;
432 return dict;
433 }
434
435 CF_PRIVATE CFDictionaryRef _CFBundleCopyInfoDictionaryInDirectoryWithVersion(CFAllocatorRef alloc, CFURLRef url, uint8_t version) {
436 // We only return NULL for a bad URL, otherwise we create a dummy dictionary
437 if (!url) return NULL;
438
439 CFDictionaryRef result = NULL;
440
441 // We're going to search for two files here - Info.plist and Info-macos.plist (platform specific). The platform-specific one takes precedence.
442 // First, construct the URL to the directory we'll search by using the passed in URL as a base
443 CFStringRef platformInfoURLFromBase = _CFBundlePlatformInfoURLFromBase0;
444 CFStringRef infoURLFromBase = _CFBundleInfoURLFromBase0;
445 CFURLRef directoryURL = NULL;
446
447 if (0 == version) {
448 directoryURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleResourcesURLFromBase0, url);
449 platformInfoURLFromBase = _CFBundlePlatformInfoURLFromBase0;
450 infoURLFromBase = _CFBundleInfoURLFromBase0;
451 } else if (1 == version) {
452 directoryURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleSupportFilesURLFromBase1, url);
453 platformInfoURLFromBase = _CFBundlePlatformInfoURLFromBase1;
454 infoURLFromBase = _CFBundleInfoURLFromBase1;
455 } else if (2 == version) {
456 directoryURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleSupportFilesURLFromBase2, url);
457 platformInfoURLFromBase = _CFBundlePlatformInfoURLFromBase2;
458 infoURLFromBase = _CFBundleInfoURLFromBase2;
459 } else if (3 == version) {
460 CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
461 // this test is necessary to exclude the case where a bundle is spuriously created from the innards of another bundle
462 if (path) {
463 if (!(CFStringHasSuffix(path, _CFBundleSupportFilesDirectoryName1) || CFStringHasSuffix(path, _CFBundleSupportFilesDirectoryName2) || CFStringHasSuffix(path, _CFBundleResourcesDirectoryName))) {
464 directoryURL = (CFURLRef)CFRetain(url);
465 platformInfoURLFromBase = _CFBundlePlatformInfoURLFromBase3;
466 infoURLFromBase = _CFBundleInfoURLFromBase3;
467 }
468 CFRelease(path);
469 }
470 }
471
472 CFURLRef absoluteURL;
473 if (directoryURL) {
474 absoluteURL = CFURLCopyAbsoluteURL(directoryURL);
475 CFStringRef directoryPath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
476 CFRelease(absoluteURL);
477
478 __block CFURLRef infoPlistURL = NULL;
479 __block CFURLRef platformInfoPlistURL = NULL;
480
481 CFIndex infoPlistLength = CFStringGetLength(_CFBundleInfoPlistName);
482 CFIndex platformInfoPlistLength = CFStringGetLength(_CFBundlePlatformInfoPlistName);
483
484 // Look inside this directory for the platform-specific and global Info.plist
485 // For compatability reasons, we support case-insensitive versions of Info.plist. That means that we must do a search of all the file names in the directory so we can compare. Otherwise, perhaps a couple of stats would be more efficient than the readdir.
486 _CFIterateDirectory(directoryPath, ^Boolean(CFStringRef fileName, uint8_t fileType) {
487 // Only do the platform check on platforms where the string is different than the normal one
488 if (_CFBundlePlatformInfoPlistName != _CFBundleInfoPlistName) {
489 if (!platformInfoPlistURL && CFStringGetLength(fileName) == platformInfoPlistLength && CFStringCompareWithOptions(fileName, _CFBundlePlatformInfoPlistName, CFRangeMake(0, platformInfoPlistLength), kCFCompareCaseInsensitive | kCFCompareAnchored) == kCFCompareEqualTo) {
490 // Make a URL out of this file
491 platformInfoPlistURL = CFURLCreateWithString(kCFAllocatorSystemDefault, platformInfoURLFromBase, url);
492 }
493 }
494
495 if (!infoPlistURL && CFStringGetLength(fileName) == infoPlistLength && CFStringCompareWithOptions(fileName, _CFBundleInfoPlistName, CFRangeMake(0, infoPlistLength), kCFCompareCaseInsensitive | kCFCompareAnchored) == kCFCompareEqualTo) {
496 // Make a URL out of this file
497 infoPlistURL = CFURLCreateWithString(kCFAllocatorSystemDefault, infoURLFromBase, url);
498 }
499
500 // If by some chance we have both URLs, just bail early (or just the infoPlistURL on platforms that have no platform-specific name)
501 if (_CFBundlePlatformInfoPlistName != _CFBundleInfoPlistName) {
502 if (infoPlistURL && platformInfoPlistURL) return false;
503 } else {
504 if (infoPlistURL) return false;
505 }
506
507 return true;
508 });
509
510 CFRelease(directoryPath);
511 CFRelease(directoryURL);
512
513 // Attempt to read in the data from the Info.plist we found - first the platform-specific one.
514 CFDataRef infoData = NULL;
515 CFURLRef finalInfoPlistURL = NULL;
516 if (platformInfoPlistURL) {
517 #pragma GCC diagnostic push
518 #pragma GCC diagnostic ignored "-Wdeprecated"
519 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, platformInfoPlistURL, &infoData, NULL, NULL, NULL);
520 #pragma GCC diagnostic pop
521 if (infoData) finalInfoPlistURL = platformInfoPlistURL;
522 }
523
524 if (!infoData && infoPlistURL) {
525 #pragma GCC diagnostic push
526 #pragma GCC diagnostic ignored "-Wdeprecated"
527 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, infoPlistURL, &infoData, NULL, NULL, NULL);
528 #pragma GCC diagnostic pop
529 if (infoData) finalInfoPlistURL = infoPlistURL;
530 }
531
532 if (infoData) {
533 CFErrorRef error = NULL;
534 result = (CFDictionaryRef)CFPropertyListCreateWithData(alloc, infoData, kCFPropertyListMutableContainers, NULL, &error);
535 if (result) {
536 if (CFDictionaryGetTypeID() == CFGetTypeID(result)) {
537 CFDictionarySetValue((CFMutableDictionaryRef)result, _kCFBundleInfoPlistURLKey, finalInfoPlistURL);
538 } else {
539 CFRelease(result);
540 result = NULL;
541 }
542 } else if (error) {
543 CFDictionaryRef userInfo = CFErrorCopyUserInfo(error);
544 CFLog(kCFLogLevelError, CFSTR("There was an error parsing the Info.plist for the bundle at URL %@\n %@\n %@"), infoPlistURL, error, userInfo);
545 if (userInfo) CFRelease(userInfo);
546 CFRelease(error);
547 }
548
549 if (!result) {
550 result = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
551 CFDictionarySetValue((CFMutableDictionaryRef)result, _kCFBundleRawInfoPlistURLKey, finalInfoPlistURL);
552 }
553
554 CFRelease(infoData);
555 }
556
557 if (platformInfoPlistURL) CFRelease(platformInfoPlistURL);
558 if (infoPlistURL) CFRelease(infoPlistURL);
559 }
560
561 if (!result) {
562 result = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
563 }
564
565 // process ~ipad, ~iphone, etc.
566 _CFBundleInfoPlistProcessInfoDictionary((CFMutableDictionaryRef)result);
567
568 return result;
569 }
570
571 CF_EXPORT CFDictionaryRef CFBundleCopyInfoDictionaryForURL(CFURLRef url) {
572 CFDictionaryRef result = NULL;
573 Boolean isDir = false;
574 if (_CFIsResourceAtURL(url, &isDir)) {
575 if (isDir) {
576 result = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefault, url, NULL);
577 } else {
578 result = _CFBundleCopyInfoDictionaryInExecutable(url);
579 }
580 }
581 if (result && (0)) CFRetain(result); // conditionally put on a retain for a Copy function
582 return result;
583 }
584
585 static Boolean _CFBundleGetPackageInfoInDirectoryWithInfoDictionary(CFAllocatorRef alloc, CFURLRef url, CFDictionaryRef infoDict, UInt32 *packageType, UInt32 *packageCreator) {
586 Boolean retVal = false, hasType = false, hasCreator = false, releaseInfoDict = false;
587 CFURLRef tempURL;
588 CFDataRef pkgInfoData = NULL;
589
590 // Check for a "real" new bundle
591 tempURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundlePkgInfoURLFromBase2, url);
592 #pragma GCC diagnostic push
593 #pragma GCC diagnostic ignored "-Wdeprecated"
594 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, tempURL, &pkgInfoData, NULL, NULL, NULL);
595 #pragma GCC diagnostic pop
596 CFRelease(tempURL);
597 if (!pkgInfoData) {
598 tempURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundlePkgInfoURLFromBase1, url);
599 #pragma GCC diagnostic push
600 #pragma GCC diagnostic ignored "-Wdeprecated"
601 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, tempURL, &pkgInfoData, NULL, NULL, NULL);
602 #pragma GCC diagnostic pop
603 CFRelease(tempURL);
604 }
605 if (!pkgInfoData) {
606 // Check for a "pseudo" new bundle
607 tempURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundlePseudoPkgInfoURLFromBase, url);
608 #pragma GCC diagnostic push
609 #pragma GCC diagnostic ignored "-Wdeprecated"
610 CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, tempURL, &pkgInfoData, NULL, NULL, NULL);
611 #pragma GCC diagnostic pop
612 CFRelease(tempURL);
613 }
614
615 // Now, either we have a pkgInfoData or not. If not, then is it because this is a new bundle without one (do we allow this?), or is it dbecause it is an old bundle.
616 // If we allow new bundles to not have a PkgInfo (because they already have the same data in the Info.plist), then we have to go read the info plist which makes failure expensive.
617 // drd: So we assume that a new bundle _must_ have a PkgInfo if they have this data at all, otherwise we manufacture it from the extension.
618
619 if (pkgInfoData && CFDataGetLength(pkgInfoData) >= (int)(sizeof(UInt32) * 2)) {
620 UInt32 *pkgInfo = (UInt32 *)CFDataGetBytePtr(pkgInfoData);
621 if (packageType) *packageType = CFSwapInt32BigToHost(pkgInfo[0]);
622 if (packageCreator) *packageCreator = CFSwapInt32BigToHost(pkgInfo[1]);
623 retVal = hasType = hasCreator = true;
624 }
625 if (pkgInfoData) CFRelease(pkgInfoData);
626 if (!retVal) {
627 if (!infoDict) {
628 infoDict = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefault, url, NULL);
629 releaseInfoDict = true;
630 }
631 if (infoDict) {
632 CFStringRef typeString = (CFStringRef)CFDictionaryGetValue(infoDict, _kCFBundlePackageTypeKey), creatorString = (CFStringRef)CFDictionaryGetValue(infoDict, _kCFBundleSignatureKey);
633 UInt32 tmp;
634 CFIndex usedBufLen = 0;
635 if (typeString && CFGetTypeID(typeString) == CFStringGetTypeID() && CFStringGetLength(typeString) == 4 && 4 == CFStringGetBytes(typeString, CFRangeMake(0, 4), kCFStringEncodingMacRoman, 0, false, (UInt8 *)&tmp, 4, &usedBufLen) && 4 == usedBufLen) {
636 if (packageType) *packageType = CFSwapInt32BigToHost(tmp);
637 retVal = hasType = true;
638 }
639 if (creatorString && CFGetTypeID(creatorString) == CFStringGetTypeID() && CFStringGetLength(creatorString) == 4 && 4 == CFStringGetBytes(creatorString, CFRangeMake(0, 4), kCFStringEncodingMacRoman, 0, false, (UInt8 *)&tmp, 4, &usedBufLen) && 4 == usedBufLen) {
640 if (packageCreator) *packageCreator = CFSwapInt32BigToHost(tmp);
641 retVal = hasCreator = true;
642 }
643 if (releaseInfoDict && !(0)) CFRelease(infoDict);
644 }
645 }
646 if (!hasType || !hasCreator) {
647 // If this looks like a bundle then manufacture the type and creator.
648 if (retVal || _CFBundleURLLooksLikeBundle(url)) {
649 if (packageCreator && !hasCreator) *packageCreator = 0x3f3f3f3f; // '????'
650 if (packageType && !hasType) {
651 CFStringRef urlStr;
652 UniChar buff[CFMaxPathSize];
653 CFIndex strLen, startOfExtension;
654 CFURLRef absoluteURL;
655
656 // Detect "app", "debug", "profile", or "framework" extensions
657 absoluteURL = CFURLCopyAbsoluteURL(url);
658 urlStr = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE);
659 CFRelease(absoluteURL);
660 strLen = CFStringGetLength(urlStr);
661 if (strLen > CFMaxPathSize) strLen = CFMaxPathSize;
662 CFStringGetCharacters(urlStr, CFRangeMake(0, strLen), buff);
663 CFRelease(urlStr);
664 startOfExtension = _CFStartOfPathExtension(buff, strLen);
665 if ((strLen - startOfExtension == 4 || strLen - startOfExtension == 5) && buff[startOfExtension] == (UniChar)'.' && buff[startOfExtension+1] == (UniChar)'a' && buff[startOfExtension+2] == (UniChar)'p' && buff[startOfExtension+3] == (UniChar)'p' && (strLen - startOfExtension == 4 || buff[startOfExtension+4] == (UniChar)PATH_SEP)) {
666 // This is an app
667 *packageType = 0x4150504c; // 'APPL'
668 } else if ((strLen - startOfExtension == 6 || strLen - startOfExtension == 7) && buff[startOfExtension] == (UniChar)'.' && buff[startOfExtension+1] == (UniChar)'d' && buff[startOfExtension+2] == (UniChar)'e' && buff[startOfExtension+3] == (UniChar)'b' && buff[startOfExtension+4] == (UniChar)'u' && buff[startOfExtension+5] == (UniChar)'g' && (strLen - startOfExtension == 6 || buff[startOfExtension+6] == (UniChar)PATH_SEP)) {
669 // This is an app (debug version)
670 *packageType = 0x4150504c; // 'APPL'
671 } else if ((strLen - startOfExtension == 8 || strLen - startOfExtension == 9) && buff[startOfExtension] == (UniChar)'.' && buff[startOfExtension+1] == (UniChar)'p' && buff[startOfExtension+2] == (UniChar)'r' && buff[startOfExtension+3] == (UniChar)'o' && buff[startOfExtension+4] == (UniChar)'f' && buff[startOfExtension+5] == (UniChar)'i' && buff[startOfExtension+6] == (UniChar)'l' && buff[startOfExtension+7] == (UniChar)'e' && (strLen - startOfExtension == 8 || buff[startOfExtension+8] == (UniChar)PATH_SEP)) {
672 // This is an app (profile version)
673 *packageType = 0x4150504c; // 'APPL'
674 } else if ((strLen - startOfExtension == 8 || strLen - startOfExtension == 9) && buff[startOfExtension] == (UniChar)'.' && buff[startOfExtension+1] == (UniChar)'s' && buff[startOfExtension+2] == (UniChar)'e' && buff[startOfExtension+3] == (UniChar)'r' && buff[startOfExtension+4] == (UniChar)'v' && buff[startOfExtension+5] == (UniChar)'i' && buff[startOfExtension+6] == (UniChar)'c' && buff[startOfExtension+7] == (UniChar)'e' && (strLen - startOfExtension == 8 || buff[startOfExtension+8] == (UniChar)PATH_SEP)) {
675 // This is a service
676 *packageType = 0x4150504c; // 'APPL'
677 } else if ((strLen - startOfExtension == 10 || strLen - startOfExtension == 11) && buff[startOfExtension] == (UniChar)'.' && buff[startOfExtension+1] == (UniChar)'f' && buff[startOfExtension+2] == (UniChar)'r' && buff[startOfExtension+3] == (UniChar)'a' && buff[startOfExtension+4] == (UniChar)'m' && buff[startOfExtension+5] == (UniChar)'e' && buff[startOfExtension+6] == (UniChar)'w' && buff[startOfExtension+7] == (UniChar)'o' && buff[startOfExtension+8] == (UniChar)'r' && buff[startOfExtension+9] == (UniChar)'k' && (strLen - startOfExtension == 10 || buff[startOfExtension+10] == (UniChar)PATH_SEP)) {
678 // This is a framework
679 *packageType = 0x464d574b; // 'FMWK'
680 } else {
681 // Default to BNDL for generic bundle
682 *packageType = 0x424e444c; // 'BNDL'
683 }
684 }
685 retVal = true;
686 }
687 }
688 return retVal;
689 }
690
691 CF_EXPORT Boolean _CFBundleGetPackageInfoInDirectory(CFAllocatorRef alloc, CFURLRef url, UInt32 *packageType, UInt32 *packageCreator) {
692 return _CFBundleGetPackageInfoInDirectoryWithInfoDictionary(alloc, url, NULL, packageType, packageCreator);
693 }
694
695 CF_EXPORT void CFBundleGetPackageInfo(CFBundleRef bundle, UInt32 *packageType, UInt32 *packageCreator) {
696 CFURLRef bundleURL = CFBundleCopyBundleURL(bundle);
697 if (!_CFBundleGetPackageInfoInDirectoryWithInfoDictionary(kCFAllocatorSystemDefault, bundleURL, CFBundleGetInfoDictionary(bundle), packageType, packageCreator)) {
698 if (packageType) *packageType = 0x424e444c; // 'BNDL'
699 if (packageCreator) *packageCreator = 0x3f3f3f3f; // '????'
700 }
701 if (bundleURL) CFRelease(bundleURL);
702 }
703
704 CF_EXPORT Boolean CFBundleGetPackageInfoInDirectory(CFURLRef url, UInt32 *packageType, UInt32 *packageCreator) {
705 return _CFBundleGetPackageInfoInDirectory(kCFAllocatorSystemDefault, url, packageType, packageCreator);
706 }
707
708 CFDictionaryRef CFBundleCopyInfoDictionaryInDirectory(CFURLRef url) {
709 CFDictionaryRef dict = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefault, url, NULL);
710 return dict;
711 }
712
713 // The Info.plist should NOT be mutated after being created. If there is any fixing up of the info dictionary to do, do it here.
714 // Call with bundle lock
715 static void _CFBundleInfoPlistFixupInfoDictionary(CFBundleRef bundle, CFMutableDictionaryRef infoDict) {
716 // Version number
717 CFTypeRef unknownVersionValue = CFDictionaryGetValue(infoDict, _kCFBundleNumericVersionKey);
718 CFNumberRef versNum;
719 UInt32 vers = 0;
720
721 if (!unknownVersionValue) unknownVersionValue = CFDictionaryGetValue(infoDict, kCFBundleVersionKey);
722 if (unknownVersionValue) {
723 if (CFGetTypeID(unknownVersionValue) == CFStringGetTypeID()) {
724 // Convert a string version number into a numeric one.
725 vers = _CFVersionNumberFromString((CFStringRef)unknownVersionValue);
726
727 versNum = CFNumberCreate(CFGetAllocator(bundle), kCFNumberSInt32Type, &vers);
728 CFDictionarySetValue(infoDict, _kCFBundleNumericVersionKey, versNum);
729 CFRelease(versNum);
730 } else if (CFGetTypeID(unknownVersionValue) == CFNumberGetTypeID()) {
731 // Nothing to do here
732 } else {
733 CFDictionaryRemoveValue((CFMutableDictionaryRef)infoDict, _kCFBundleNumericVersionKey);
734 }
735 }
736 }
737
738 CFDictionaryRef CFBundleGetInfoDictionary(CFBundleRef bundle) {
739 __CFSpinLock(&bundle->_lock);
740 if (!bundle->_infoDict) {
741 bundle->_infoDict = _CFBundleCopyInfoDictionaryInDirectoryWithVersion(kCFAllocatorSystemDefault, bundle->_url, bundle->_version);
742
743 // Add or fixup any keys that will be expected later
744 if (bundle->_infoDict) _CFBundleInfoPlistFixupInfoDictionary(bundle, (CFMutableDictionaryRef)bundle->_infoDict);
745 }
746 __CFSpinUnlock(&bundle->_lock);
747
748 return bundle->_infoDict;
749 }
750
751 CFDictionaryRef _CFBundleGetLocalInfoDictionary(CFBundleRef bundle) {
752 return CFBundleGetLocalInfoDictionary(bundle);
753 }
754
755 CFDictionaryRef CFBundleGetLocalInfoDictionary(CFBundleRef bundle) {
756 CFDictionaryRef localInfoDict = NULL;
757 __CFSpinLock(&bundle->_lock);
758 localInfoDict = bundle->_localInfoDict;
759 if (!localInfoDict) {
760 // To avoid keeping the spin lock for too long, let go of it here while we create a new dictionary. We'll relock later to set the value. If it turns out that we have already created another local info dictionary in the meantime, then we'll take care of it then.
761 __CFSpinUnlock(&bundle->_lock);
762 CFURLRef url = CFBundleCopyResourceURL(bundle, _CFBundleLocalInfoName, _CFBundleStringTableType, NULL);
763 if (url) {
764 CFDataRef data;
765 SInt32 errCode;
766 CFStringRef errStr = NULL;
767
768 #pragma GCC diagnostic push
769 #pragma GCC diagnostic ignored "-Wdeprecated"
770 if (CFURLCreateDataAndPropertiesFromResource(kCFAllocatorSystemDefault, url, &data, NULL, NULL, &errCode)) {
771 localInfoDict = (CFDictionaryRef)CFPropertyListCreateFromXMLData(kCFAllocatorSystemDefault, data, kCFPropertyListMutableContainers, &errStr);
772 if (errStr) CFRelease(errStr);
773 if (localInfoDict && CFDictionaryGetTypeID() != CFGetTypeID(localInfoDict)) {
774 CFRelease(localInfoDict);
775 localInfoDict = NULL;
776 }
777 CFRelease(data);
778 }
779 #pragma GCC diagnostic pop
780 CFRelease(url);
781 }
782 if (localInfoDict) _CFBundleInfoPlistProcessInfoDictionary((CFMutableDictionaryRef)localInfoDict);
783 // remain locked here until we exit the if statement.
784 __CFSpinLock(&bundle->_lock);
785 if (!bundle->_localInfoDict) {
786 // Still have no info dictionary, so set it
787 bundle->_localInfoDict = localInfoDict;
788 } else {
789 // Oops, some other thread created an info dictionary too. We'll just release this one and use that one.
790 if (localInfoDict) CFRelease(localInfoDict);
791 localInfoDict = bundle->_localInfoDict;
792 }
793 }
794 __CFSpinUnlock(&bundle->_lock);
795
796 return localInfoDict;
797 }
798
799 CFPropertyListRef _CFBundleGetValueForInfoKey(CFBundleRef bundle, CFStringRef key) {
800 return (CFPropertyListRef)CFBundleGetValueForInfoDictionaryKey(bundle, key);
801 }
802
803 CFTypeRef CFBundleGetValueForInfoDictionaryKey(CFBundleRef bundle, CFStringRef key) {
804 // Look in InfoPlist.strings first. Then look in Info.plist
805 CFTypeRef result = NULL;
806 if (bundle && key) {
807 CFDictionaryRef dict = CFBundleGetLocalInfoDictionary(bundle);
808 if (dict) result = CFDictionaryGetValue(dict, key);
809 if (!result) {
810 dict = CFBundleGetInfoDictionary(bundle);
811 if (dict) result = CFDictionaryGetValue(dict, key);
812 }
813 }
814 return result;
815 }
816
817 CFStringRef CFBundleGetIdentifier(CFBundleRef bundle) {
818 CFStringRef bundleID = NULL;
819 CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle);
820 if (infoDict) bundleID = (CFStringRef)CFDictionaryGetValue(infoDict, kCFBundleIdentifierKey);
821 return bundleID;
822 }