/ CFBundle.c
CFBundle.c
1 /* 2 * Copyright (c) 2015 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 /* CFBundle.c 25 Copyright (c) 1999-2014, Apple Inc. All rights reserved. 26 Responsibility: Tony Parker 27 */ 28 29 #include "CFBundle_Internal.h" 30 #include <CoreFoundation/CFPropertyList.h> 31 #include <CoreFoundation/CFNumber.h> 32 #include <CoreFoundation/CFSet.h> 33 #include <CoreFoundation/CFURLAccess.h> 34 #include <CoreFoundation/CFError.h> 35 #include <string.h> 36 #include <CoreFoundation/CFPriv.h> 37 #include "CFInternal.h" 38 #include <CoreFoundation/CFByteOrder.h> 39 #include "CFBundle_BinaryTypes.h" 40 #include <ctype.h> 41 #include <sys/stat.h> 42 #include <stdlib.h> 43 #include <stdio.h> 44 45 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_WINDOWS 46 #else 47 #error Unknown deployment target 48 #endif 49 50 #define AVOID_WEAK_COLLECTIONS 1 51 52 #if !AVOID_WEAK_COLLECTIONS 53 #include "CFHashTable.h" 54 #include "CFMapTable.h" 55 #include "CFPointerArray.h" 56 #endif /* !AVOID_WEAK_COLLECTIONS */ 57 58 #if defined(BINARY_SUPPORT_DYLD) 59 #include <unistd.h> 60 #include <fcntl.h> 61 #include <sys/mman.h> 62 #include <crt_externs.h> 63 #endif /* BINARY_SUPPORT_DYLD */ 64 65 #if defined(BINARY_SUPPORT_DLFCN) 66 #include <dlfcn.h> 67 #endif /* BINARY_SUPPORT_DLFCN */ 68 69 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 70 #include <fcntl.h> 71 #elif DEPLOYMENT_TARGET_WINDOWS 72 #include <fcntl.h> 73 #include <io.h> 74 #endif 75 76 77 static void _CFBundleFlushBundleCachesAlreadyLocked(CFBundleRef bundle, Boolean alreadyLocked); 78 79 #define LOG_BUNDLE_LOAD 0 80 81 // Public CFBundle Info plist keys 82 CONST_STRING_DECL(kCFBundleInfoDictionaryVersionKey, "CFBundleInfoDictionaryVersion") 83 CONST_STRING_DECL(kCFBundleExecutableKey, "CFBundleExecutable") 84 CONST_STRING_DECL(kCFBundleIdentifierKey, "CFBundleIdentifier") 85 CONST_STRING_DECL(kCFBundleVersionKey, "CFBundleVersion") 86 CONST_STRING_DECL(kCFBundleDevelopmentRegionKey, "CFBundleDevelopmentRegion") 87 CONST_STRING_DECL(kCFBundleLocalizationsKey, "CFBundleLocalizations") 88 89 // Private CFBundle Info plist keys, possible candidates for public constants 90 CONST_STRING_DECL(_kCFBundleAllowMixedLocalizationsKey, "CFBundleAllowMixedLocalizations") 91 CONST_STRING_DECL(_kCFBundleSupportedPlatformsKey, "CFBundleSupportedPlatforms") 92 CONST_STRING_DECL(_kCFBundleResourceSpecificationKey, "CFBundleResourceSpecification") 93 94 // Finder stuff 95 CONST_STRING_DECL(_kCFBundlePackageTypeKey, "CFBundlePackageType") 96 CONST_STRING_DECL(_kCFBundleSignatureKey, "CFBundleSignature") 97 CONST_STRING_DECL(_kCFBundleIconFileKey, "CFBundleIconFile") 98 CONST_STRING_DECL(_kCFBundleDocumentTypesKey, "CFBundleDocumentTypes") 99 CONST_STRING_DECL(_kCFBundleURLTypesKey, "CFBundleURLTypes") 100 101 // Keys that are usually localized in InfoPlist.strings 102 CONST_STRING_DECL(kCFBundleNameKey, "CFBundleName") 103 CONST_STRING_DECL(_kCFBundleDisplayNameKey, "CFBundleDisplayName") 104 CONST_STRING_DECL(_kCFBundleShortVersionStringKey, "CFBundleShortVersionString") 105 CONST_STRING_DECL(_kCFBundleGetInfoStringKey, "CFBundleGetInfoString") 106 CONST_STRING_DECL(_kCFBundleGetInfoHTMLKey, "CFBundleGetInfoHTML") 107 108 // Sub-keys for CFBundleDocumentTypes dictionaries 109 CONST_STRING_DECL(_kCFBundleTypeNameKey, "CFBundleTypeName") 110 CONST_STRING_DECL(_kCFBundleTypeRoleKey, "CFBundleTypeRole") 111 CONST_STRING_DECL(_kCFBundleTypeIconFileKey, "CFBundleTypeIconFile") 112 CONST_STRING_DECL(_kCFBundleTypeOSTypesKey, "CFBundleTypeOSTypes") 113 CONST_STRING_DECL(_kCFBundleTypeExtensionsKey, "CFBundleTypeExtensions") 114 CONST_STRING_DECL(_kCFBundleTypeMIMETypesKey, "CFBundleTypeMIMETypes") 115 116 // Sub-keys for CFBundleURLTypes dictionaries 117 CONST_STRING_DECL(_kCFBundleURLNameKey, "CFBundleURLName") 118 CONST_STRING_DECL(_kCFBundleURLIconFileKey, "CFBundleURLIconFile") 119 CONST_STRING_DECL(_kCFBundleURLSchemesKey, "CFBundleURLSchemes") 120 121 // Compatibility key names 122 CONST_STRING_DECL(_kCFBundleOldExecutableKey, "NSExecutable") 123 CONST_STRING_DECL(_kCFBundleOldInfoDictionaryVersionKey, "NSInfoPlistVersion") 124 CONST_STRING_DECL(_kCFBundleOldNameKey, "NSHumanReadableName") 125 CONST_STRING_DECL(_kCFBundleOldIconFileKey, "NSIcon") 126 CONST_STRING_DECL(_kCFBundleOldDocumentTypesKey, "NSTypes") 127 CONST_STRING_DECL(_kCFBundleOldShortVersionStringKey, "NSAppVersion") 128 129 // Compatibility CFBundleDocumentTypes key names 130 CONST_STRING_DECL(_kCFBundleOldTypeNameKey, "NSName") 131 CONST_STRING_DECL(_kCFBundleOldTypeRoleKey, "NSRole") 132 CONST_STRING_DECL(_kCFBundleOldTypeIconFileKey, "NSIcon") 133 CONST_STRING_DECL(_kCFBundleOldTypeExtensions1Key, "NSUnixExtensions") 134 CONST_STRING_DECL(_kCFBundleOldTypeExtensions2Key, "NSDOSExtensions") 135 CONST_STRING_DECL(_kCFBundleOldTypeOSTypesKey, "NSMacOSType") 136 137 // Internally used keys for loaded Info plists. 138 CONST_STRING_DECL(_kCFBundleInfoPlistURLKey, "CFBundleInfoPlistURL") 139 CONST_STRING_DECL(_kCFBundleRawInfoPlistURLKey, "CFBundleRawInfoPlistURL") 140 CONST_STRING_DECL(_kCFBundleNumericVersionKey, "CFBundleNumericVersion") 141 CONST_STRING_DECL(_kCFBundleExecutablePathKey, "CFBundleExecutablePath") 142 CONST_STRING_DECL(_kCFBundleResourcesFileMappedKey, "CSResourcesFileMapped") 143 CONST_STRING_DECL(_kCFBundleCFMLoadAsBundleKey, "CFBundleCFMLoadAsBundle") 144 145 // Keys used by NSBundle for loaded Info plists. 146 CONST_STRING_DECL(_kCFBundlePrincipalClassKey, "NSPrincipalClass") 147 148 static char __CFBundleMainID__[1026] = {0}; 149 CF_PRIVATE char *__CFBundleMainID = __CFBundleMainID__; 150 151 static CFTypeID __kCFBundleTypeID = _kCFRuntimeNotATypeID; 152 153 static pthread_mutex_t CFBundleGlobalDataLock = PTHREAD_MUTEX_INITIALIZER; 154 155 static CFMutableDictionaryRef _bundlesByIdentifier = NULL; 156 #if AVOID_WEAK_COLLECTIONS 157 static CFMutableDictionaryRef _bundlesByURL = NULL; 158 static CFMutableArrayRef _allBundles = NULL; 159 static CFMutableSetRef _bundlesToUnload = NULL; 160 #else /* AVOID_WEAK_COLLECTIONS */ 161 static __CFHashTable *_allBundles = nil; 162 static __CFHashTable *_bundlesToUnload = nil; 163 #endif /* AVOID_WEAK_COLLECTIONS */ 164 static Boolean _scheduledBundlesAreUnloading = false; 165 166 static Boolean _initedMainBundle = false; 167 static CFBundleRef _mainBundle = NULL; 168 169 // Forward declares functions. 170 static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL, Boolean alreadyLocked, Boolean doFinalProcessing, Boolean noCaches); 171 static CFURLRef _CFBundleCopyExecutableURLIgnoringCache(CFBundleRef bundle); 172 static void _CFBundleEnsureBundlesUpToDateWithHintAlreadyLocked(CFStringRef hint); 173 static void _CFBundleEnsureAllBundlesUpToDateAlreadyLocked(void); 174 static void _CFBundleEnsureBundleExistsForImagePath(CFStringRef imagePath); 175 static void _CFBundleEnsureBundlesExistForImagePaths(CFArrayRef imagePaths); 176 177 #pragma mark - 178 179 180 181 182 #if AVOID_WEAK_COLLECTIONS 183 184 static void _CFBundleAddToTables(CFBundleRef bundle, Boolean alreadyLocked) { 185 CFStringRef bundleID = CFBundleGetIdentifier(bundle); 186 187 if (!alreadyLocked) pthread_mutex_lock(&CFBundleGlobalDataLock); 188 189 // Add to the _allBundles list 190 if (!_allBundles) { 191 CFArrayCallBacks nonRetainingArrayCallbacks = kCFTypeArrayCallBacks; 192 nonRetainingArrayCallbacks.retain = NULL; 193 nonRetainingArrayCallbacks.release = NULL; 194 _allBundles = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &nonRetainingArrayCallbacks); 195 } 196 CFArrayAppendValue(_allBundles, bundle); 197 198 // Add to the table that maps urls to bundles 199 if (!_bundlesByURL) { 200 CFDictionaryValueCallBacks nonRetainingDictionaryValueCallbacks = kCFTypeDictionaryValueCallBacks; 201 nonRetainingDictionaryValueCallbacks.retain = NULL; 202 nonRetainingDictionaryValueCallbacks.release = NULL; 203 _bundlesByURL = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &nonRetainingDictionaryValueCallbacks); 204 } 205 CFDictionarySetValue(_bundlesByURL, bundle->_url, bundle); 206 207 // Add to the table that maps identifiers to bundles 208 if (bundleID) { 209 CFMutableArrayRef bundlesWithThisID = NULL; 210 CFBundleRef existingBundle = NULL; 211 if (!_bundlesByIdentifier) { 212 _bundlesByIdentifier = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 213 } 214 bundlesWithThisID = (CFMutableArrayRef)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); 215 if (bundlesWithThisID) { 216 CFIndex i, count = CFArrayGetCount(bundlesWithThisID); 217 UInt32 existingVersion, newVersion = CFBundleGetVersionNumber(bundle); 218 for (i = 0; i < count; i++) { 219 existingBundle = (CFBundleRef)CFArrayGetValueAtIndex(bundlesWithThisID, i); 220 existingVersion = CFBundleGetVersionNumber(existingBundle); 221 // If you load two bundles with the same identifier and the same version, the last one wins. 222 if (newVersion >= existingVersion) break; 223 } 224 CFArrayInsertValueAtIndex(bundlesWithThisID, i, bundle); 225 } else { 226 CFArrayCallBacks nonRetainingArrayCallbacks = kCFTypeArrayCallBacks; 227 nonRetainingArrayCallbacks.retain = NULL; 228 nonRetainingArrayCallbacks.release = NULL; 229 bundlesWithThisID = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &nonRetainingArrayCallbacks); 230 CFArrayAppendValue(bundlesWithThisID, bundle); 231 CFDictionarySetValue(_bundlesByIdentifier, bundleID, bundlesWithThisID); 232 CFRelease(bundlesWithThisID); 233 } 234 } 235 if (!alreadyLocked) pthread_mutex_unlock(&CFBundleGlobalDataLock); 236 } 237 238 static void _CFBundleRemoveFromTables(CFBundleRef bundle, CFURLRef bundleURL, CFStringRef bundleID) { 239 pthread_mutex_lock(&CFBundleGlobalDataLock); 240 // Remove from the various lists 241 if (_allBundles) { 242 CFIndex i = CFArrayGetFirstIndexOfValue(_allBundles, CFRangeMake(0, CFArrayGetCount(_allBundles)), bundle); 243 if (i >= 0) CFArrayRemoveValueAtIndex(_allBundles, i); 244 } 245 246 // Remove from the table that maps urls to bundles 247 if (bundleURL && _bundlesByURL) { 248 CFBundleRef bundleForURL = (CFBundleRef)CFDictionaryGetValue(_bundlesByURL, bundleURL); 249 if (bundleForURL == bundle) CFDictionaryRemoveValue(_bundlesByURL, bundleURL); 250 } 251 252 // Remove from the table that maps identifiers to bundles 253 if (bundleID && _bundlesByIdentifier) { 254 CFMutableArrayRef bundlesWithThisID = (CFMutableArrayRef)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); 255 if (bundlesWithThisID) { 256 CFIndex count = CFArrayGetCount(bundlesWithThisID); 257 while (count-- > 0) if (bundle == (CFBundleRef)CFArrayGetValueAtIndex(bundlesWithThisID, count)) CFArrayRemoveValueAtIndex(bundlesWithThisID, count); 258 if (0 == CFArrayGetCount(bundlesWithThisID)) CFDictionaryRemoveValue(_bundlesByIdentifier, bundleID); 259 } 260 } 261 pthread_mutex_unlock(&CFBundleGlobalDataLock); 262 } 263 264 static CFBundleRef _CFBundleCopyBundleForURL(CFURLRef url, Boolean alreadyLocked) { 265 CFBundleRef result = NULL; 266 if (!alreadyLocked) pthread_mutex_lock(&CFBundleGlobalDataLock); 267 if (_bundlesByURL) result = (CFBundleRef)CFDictionaryGetValue(_bundlesByURL, url); 268 if (result && !result->_url) { 269 result = NULL; 270 CFDictionaryRemoveValue(_bundlesByURL, url); 271 } 272 if (result) CFRetain(result); 273 if (!alreadyLocked) pthread_mutex_unlock(&CFBundleGlobalDataLock); 274 return result; 275 } 276 277 static CFBundleRef _CFBundlePrimitiveGetBundleWithIdentifierAlreadyLocked(CFStringRef bundleID) { 278 CFBundleRef result = NULL, bundle; 279 if (_bundlesByIdentifier && bundleID) { 280 // Note that this array is maintained in descending order by version number 281 CFArrayRef bundlesWithThisID = (CFArrayRef)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); 282 if (bundlesWithThisID) { 283 CFIndex i, count = CFArrayGetCount(bundlesWithThisID); 284 if (count > 0) { 285 // First check for loaded bundles so we will always prefer a loaded to an unloaded bundle 286 for (i = 0; !result && i < count; i++) { 287 bundle = (CFBundleRef)CFArrayGetValueAtIndex(bundlesWithThisID, i); 288 if (CFBundleIsExecutableLoaded(bundle)) result = bundle; 289 } 290 // If no loaded bundle, simply take the first item in the array, i.e. the one with the latest version number 291 if (!result) result = (CFBundleRef)CFArrayGetValueAtIndex(bundlesWithThisID, 0); 292 } 293 } 294 } 295 return result; 296 } 297 298 #else /* AVOID_WEAK_COLLECTIONS */ 299 300 /* 301 An explanation of what I'm doing here is probably in order. 302 8029300 has cast suspicion on the correctness of __CFMapTable with strong keys and weak values, at least under non-GC. 303 An early attempt to work around it by inserting dummy values instead of removing things succeeded, as did turning on the AVOID_WEAK_COLLECTIONS #ifdef 304 This indicates that it's not an overrelease in securityd, since AVOID_WEAK_COLLECTIONS wouldn't help in that case. 305 Therefore, these functions following this comment allow us to have _bundlesByURL be a CFDictionary on non-GC and keep __CFMapTable to GC where it's needed. 306 */ 307 static inline id _getBundlesByURL() { 308 static id _bundles = nil; 309 static dispatch_once_t onceToken; 310 dispatch_once(&onceToken, ^{ 311 if (CF_USING_COLLECTABLE_MEMORY) { 312 _bundles = [[__CFMapTable alloc] initWithKeyOptions:CFPointerFunctionsStrongMemory valueOptions:CFPointerFunctionsZeroingWeakMemory capacity:0]; 313 } else { 314 CFDictionaryValueCallBacks nonRetainingDictionaryValueCallbacks = kCFTypeDictionaryValueCallBacks; 315 nonRetainingDictionaryValueCallbacks.retain = NULL; 316 nonRetainingDictionaryValueCallbacks.release = NULL; 317 _bundles = (id)CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &nonRetainingDictionaryValueCallbacks); 318 } 319 }); 320 return _bundles; 321 } 322 323 #define _bundlesByURL _getBundlesByURL() 324 325 static void _setInBundlesByURL(CFURLRef key, CFBundleRef bundle) { 326 if (CF_USING_COLLECTABLE_MEMORY) { 327 [(__CFMapTable *)_bundlesByURL setObject:(id)bundle forKey:(id)key]; 328 } else { 329 CFDictionarySetValue((CFMutableDictionaryRef)_bundlesByURL, key, bundle); 330 } 331 } 332 333 static void _removeFromBundlesByURL(CFURLRef key) { 334 if (CF_USING_COLLECTABLE_MEMORY) { 335 [(__CFMapTable *)_bundlesByURL removeObjectForKey:(id)key]; 336 } else { 337 CFDictionaryRemoveValue((CFMutableDictionaryRef)_bundlesByURL, key); 338 } 339 } 340 341 static CFBundleRef _getFromBundlesByURL(CFURLRef key) { 342 if (CF_USING_COLLECTABLE_MEMORY) { 343 return (CFBundleRef)[(__CFMapTable *)_bundlesByURL objectForKey:(id)key]; 344 } else { 345 return (CFBundleRef)CFDictionaryGetValue((CFMutableDictionaryRef)_bundlesByURL, key); 346 } 347 } 348 349 static void _CFBundleAddToTables(CFBundleRef bundle, Boolean alreadyLocked) { 350 CFStringRef bundleID = CFBundleGetIdentifier(bundle); 351 352 if (!alreadyLocked) pthread_mutex_lock(&CFBundleGlobalDataLock); 353 354 // Add to the _allBundles list 355 if (!_allBundles) _allBundles = [[__CFHashTable alloc] initWithOptions:CFPointerFunctionsZeroingWeakMemory capacity:0]; 356 [_allBundles addObject:(id)bundle]; 357 358 // Add to the table that maps urls to bundles 359 _setInBundlesByURL(bundle->_url, bundle); 360 361 // Add to the table that maps identifiers to bundles 362 if (bundleID) { 363 __CFPointerArray *bundlesWithThisID = nil; 364 CFBundleRef existingBundle = NULL; 365 if (!_bundlesByIdentifier) { 366 _bundlesByIdentifier = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 367 } 368 bundlesWithThisID = (__CFPointerArray *)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); 369 if (bundlesWithThisID) { 370 CFIndex i, count = (CFIndex)[bundlesWithThisID count]; 371 UInt32 existingVersion, newVersion = CFBundleGetVersionNumber(bundle); 372 for (i = 0; i < count; i++) { 373 existingBundle = (CFBundleRef)[bundlesWithThisID pointerAtIndex:i]; 374 if (!existingBundle) continue; 375 existingVersion = CFBundleGetVersionNumber(existingBundle); 376 // If you load two bundles with the same identifier and the same version, the last one wins. 377 if (newVersion >= existingVersion) break; 378 } 379 if (i < count) { 380 [bundlesWithThisID insertPointer:bundle atIndex:i]; 381 } else { 382 [bundlesWithThisID addPointer:bundle]; 383 } 384 } else { 385 bundlesWithThisID = [[__CFPointerArray alloc] initWithOptions:CFPointerFunctionsZeroingWeakMemory]; 386 [bundlesWithThisID addPointer:bundle]; 387 CFDictionarySetValue(_bundlesByIdentifier, bundleID, bundlesWithThisID); 388 [bundlesWithThisID release]; 389 } 390 } 391 if (!alreadyLocked) pthread_mutex_unlock(&CFBundleGlobalDataLock); 392 } 393 394 static void _CFBundleRemoveFromTables(CFBundleRef bundle, CFURLRef bundleURL, CFStringRef bundleID) { 395 pthread_mutex_lock(&CFBundleGlobalDataLock); 396 // Remove from the various lists 397 if (_allBundles && [_allBundles member:(id)bundle]) [_allBundles removeObject:(id)bundle]; 398 399 // Remove from the table that maps urls to bundles 400 if (bundleURL) { 401 _removeFromBundlesByURL(bundleURL); 402 } 403 404 // Remove from the table that maps identifiers to bundles 405 if (bundleID && _bundlesByIdentifier) { 406 __CFPointerArray *bundlesWithThisID = (__CFPointerArray *)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); 407 if (bundlesWithThisID) { 408 CFIndex count = (CFIndex)[bundlesWithThisID count]; 409 while (count-- > 0) if (bundle == (CFBundleRef)[bundlesWithThisID pointerAtIndex:count]) [bundlesWithThisID removePointerAtIndex:count]; 410 [bundlesWithThisID compact]; 411 if (0 == [bundlesWithThisID count]) CFDictionaryRemoveValue(_bundlesByIdentifier, bundleID); 412 } 413 } 414 pthread_mutex_unlock(&CFBundleGlobalDataLock); 415 } 416 417 static CFBundleRef _CFBundleCopyBundleForURL(CFURLRef url, Boolean alreadyLocked) { 418 CFBundleRef result = NULL; 419 if (!alreadyLocked) pthread_mutex_lock(&CFBundleGlobalDataLock); 420 result = _getFromBundlesByURL(url); 421 if (result && !result->_url) { 422 result = NULL; 423 _removeFromBundlesByURL(url); 424 } 425 if (result) CFRetain(result); 426 if (!alreadyLocked) pthread_mutex_unlock(&CFBundleGlobalDataLock); 427 return result; 428 } 429 430 static CFBundleRef _CFBundlePrimitiveGetBundleWithIdentifierAlreadyLocked(CFStringRef bundleID) { 431 CFBundleRef result = NULL; 432 if (_bundlesByIdentifier && bundleID) { 433 // Note that this array is maintained in descending order by version number 434 __CFPointerArray *bundlesWithThisID = (__CFPointerArray *)CFDictionaryGetValue(_bundlesByIdentifier, bundleID); 435 if (bundlesWithThisID && [bundlesWithThisID count] > 0) { 436 // First check for loaded bundles so we will always prefer a loaded to an unloaded bundle 437 for (id bundle in bundlesWithThisID) { 438 if (bundle && CFBundleIsExecutableLoaded((CFBundleRef)bundle)) { 439 result = (CFBundleRef)bundle; 440 break; 441 } 442 } 443 // If no loaded bundle, simply take the first item in the array, i.e. the one with the latest version number 444 if (!result) { 445 for (id bundle in bundlesWithThisID) { 446 if (bundle) { 447 result = (CFBundleRef)bundle; 448 break; 449 } 450 } 451 } 452 } 453 } 454 return result; 455 } 456 457 #endif /* AVOID_WEAK_COLLECTIONS */ 458 459 static CFURLRef _CFBundleCopyBundleURLForExecutablePath(CFStringRef str) { 460 //!!! need to handle frameworks, NT; need to integrate with NSBundle - drd 461 UniChar buff[CFMaxPathSize]; 462 CFIndex buffLen; 463 CFURLRef url = NULL; 464 CFStringRef outstr; 465 466 buffLen = CFStringGetLength(str); 467 if (buffLen > CFMaxPathSize) buffLen = CFMaxPathSize; 468 CFStringGetCharacters(str, CFRangeMake(0, buffLen), buff); 469 470 #if DEPLOYMENT_TARGET_WINDOWS 471 // Is this a .dll or .exe? 472 if (buffLen >= 5 && (_wcsnicmp((wchar_t *)&(buff[buffLen-4]), L".dll", 4) == 0 || _wcsnicmp((wchar_t *)&(buff[buffLen-4]), L".exe", 4) == 0)) { 473 CFIndex extensionLength = CFStringGetLength(_CFBundleWindowsResourceDirectoryExtension); 474 buffLen -= 4; 475 // If this is an _debug, we should strip that before looking for the bundle 476 if (buffLen >= 7 && (_wcsnicmp((wchar_t *)&buff[buffLen-6], L"_debug", 6) == 0)) buffLen -= 6; 477 478 if (buffLen + 1 + extensionLength < CFMaxPathSize) { 479 buff[buffLen] = '.'; 480 buffLen ++; 481 CFStringGetCharacters(_CFBundleWindowsResourceDirectoryExtension, CFRangeMake(0, extensionLength), buff + buffLen); 482 buffLen += extensionLength; 483 outstr = CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault, buff, buffLen, kCFAllocatorNull); 484 url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, outstr, PLATFORM_PATH_STYLE, true); 485 CFRelease(outstr); 486 } 487 } 488 #endif 489 490 if (!url) { 491 buffLen = _CFLengthAfterDeletingLastPathComponent(buff, buffLen); // Remove exe name 492 493 if (buffLen > 0) { 494 // See if this is a new bundle. If it is, we have to remove more path components. 495 CFIndex startOfLastDir = _CFStartOfLastPathComponent(buff, buffLen); 496 if (startOfLastDir > 0 && startOfLastDir < buffLen) { 497 CFStringRef lastDirName = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, &(buff[startOfLastDir]), buffLen - startOfLastDir); 498 499 if (CFEqual(lastDirName, _CFBundleGetPlatformExecutablesSubdirectoryName()) || CFEqual(lastDirName, _CFBundleGetAlternatePlatformExecutablesSubdirectoryName()) || CFEqual(lastDirName, _CFBundleGetOtherPlatformExecutablesSubdirectoryName()) || CFEqual(lastDirName, _CFBundleGetOtherAlternatePlatformExecutablesSubdirectoryName())) { 500 // This is a new bundle. Back off a few more levels 501 if (buffLen > 0) { 502 // Remove platform folder 503 buffLen = _CFLengthAfterDeletingLastPathComponent(buff, buffLen); 504 } 505 if (buffLen > 0) { 506 // Remove executables folder (if present) 507 CFIndex startOfNextDir = _CFStartOfLastPathComponent(buff, buffLen); 508 if (startOfNextDir > 0 && startOfNextDir < buffLen) { 509 CFStringRef nextDirName = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, &(buff[startOfNextDir]), buffLen - startOfNextDir); 510 if (CFEqual(nextDirName, _CFBundleExecutablesDirectoryName)) buffLen = _CFLengthAfterDeletingLastPathComponent(buff, buffLen); 511 CFRelease(nextDirName); 512 } 513 } 514 if (buffLen > 0) { 515 // Remove support files folder 516 buffLen = _CFLengthAfterDeletingLastPathComponent(buff, buffLen); 517 } 518 } 519 CFRelease(lastDirName); 520 } 521 } 522 523 if (buffLen > 0) { 524 outstr = CFStringCreateWithCharactersNoCopy(kCFAllocatorSystemDefault, buff, buffLen, kCFAllocatorNull); 525 url = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, outstr, PLATFORM_PATH_STYLE, true); 526 CFRelease(outstr); 527 } 528 } 529 return url; 530 } 531 532 static CFURLRef _CFBundleCopyResolvedURLForExecutableURL(CFURLRef url) { 533 // this is necessary so that we match any sanitization CFURL may perform on the result of _CFBundleCopyBundleURLForExecutableURL() 534 CFURLRef absoluteURL, url1, url2, outURL = NULL; 535 CFStringRef str, str1, str2; 536 absoluteURL = CFURLCopyAbsoluteURL(url); 537 str = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); 538 if (str) { 539 UniChar buff[CFMaxPathSize]; 540 CFIndex buffLen = CFStringGetLength(str), len1; 541 if (buffLen > CFMaxPathSize) buffLen = CFMaxPathSize; 542 CFStringGetCharacters(str, CFRangeMake(0, buffLen), buff); 543 len1 = _CFLengthAfterDeletingLastPathComponent(buff, buffLen); 544 if (len1 > 0 && len1 + 1 < buffLen) { 545 str1 = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, buff, len1); 546 CFIndex skipSlashCount = 1; 547 #if DEPLOYMENT_TARGET_WINDOWS 548 // On Windows, _CFLengthAfterDeletingLastPathComponent will return a value of 3 if the path is at the root (e.g. C:\). This includes the \, which is not the case for URLs with subdirectories 549 if (len1 == 3 && buff[1] == ':' && buff[2] == '\\') { 550 skipSlashCount = 0; 551 } 552 #endif 553 str2 = CFStringCreateWithCharacters(kCFAllocatorSystemDefault, buff + len1 + skipSlashCount, buffLen - len1 - skipSlashCount); 554 if (str1 && str2) { 555 url1 = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str1, PLATFORM_PATH_STYLE, true); 556 if (url1) { 557 url2 = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, str2, PLATFORM_PATH_STYLE, false, url1); 558 if (url2) { 559 outURL = CFURLCopyAbsoluteURL(url2); 560 CFRelease(url2); 561 } 562 CFRelease(url1); 563 } 564 } 565 if (str1) CFRelease(str1); 566 if (str2) CFRelease(str2); 567 } 568 CFRelease(str); 569 } 570 if (!outURL) { 571 outURL = absoluteURL; 572 } else { 573 CFRelease(absoluteURL); 574 } 575 return outURL; 576 } 577 578 CFURLRef _CFBundleCopyBundleURLForExecutableURL(CFURLRef url) { 579 CFURLRef resolvedURL, outurl = NULL; 580 CFStringRef str; 581 resolvedURL = _CFBundleCopyResolvedURLForExecutableURL(url); 582 str = CFURLCopyFileSystemPath(resolvedURL, PLATFORM_PATH_STYLE); 583 if (str) { 584 outurl = _CFBundleCopyBundleURLForExecutablePath(str); 585 CFRelease(str); 586 } 587 CFRelease(resolvedURL); 588 return outurl; 589 } 590 591 static uint8_t _CFBundleEffectiveLayoutVersion(CFBundleRef bundle) { 592 uint8_t localVersion = bundle->_version; 593 // exclude type 0 bundles with no binary (or CFM binary) and no Info.plist, since they give too many false positives 594 if (0 == localVersion) { 595 CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); 596 if (!infoDict || 0 == CFDictionaryGetCount(infoDict)) { 597 #if defined(BINARY_SUPPORT_DYLD) 598 CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); 599 if (executableURL) { 600 if (bundle->_binaryType == __CFBundleUnknownBinary) bundle->_binaryType = _CFBundleGrokBinaryType(executableURL); 601 if (bundle->_binaryType == __CFBundleCFMBinary || bundle->_binaryType == __CFBundleUnreadableBinary) { 602 localVersion = 4; 603 } else { 604 bundle->_resourceData._executableLacksResourceFork = true; 605 } 606 CFRelease(executableURL); 607 } else { 608 localVersion = 4; 609 } 610 #else 611 CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); 612 if (executableURL) { 613 CFRelease(executableURL); 614 } else { 615 localVersion = 4; 616 } 617 #endif /* BINARY_SUPPORT_DYLD */ 618 } 619 } 620 return localVersion; 621 } 622 623 CFBundleRef _CFBundleCreateIfLooksLikeBundle(CFAllocatorRef allocator, CFURLRef url) { 624 CFBundleRef bundle = CFBundleCreate(allocator, url); 625 if (bundle) { 626 uint8_t localVersion = _CFBundleEffectiveLayoutVersion(bundle); 627 if (3 == localVersion || 4 == localVersion) { 628 CFRelease(bundle); 629 bundle = NULL; 630 } 631 } 632 return bundle; 633 } 634 635 CF_EXPORT Boolean _CFBundleURLLooksLikeBundle(CFURLRef url) { 636 Boolean result = false; 637 CFBundleRef bundle = _CFBundleCreateIfLooksLikeBundle(kCFAllocatorSystemDefault, url); 638 if (bundle) { 639 result = true; 640 CFRelease(bundle); 641 } 642 return result; 643 } 644 645 CFBundleRef _CFBundleGetMainBundleIfLooksLikeBundle(void) { 646 CFBundleRef mainBundle = CFBundleGetMainBundle(); 647 if (mainBundle && (3 == mainBundle->_version || 4 == mainBundle->_version)) mainBundle = NULL; 648 return mainBundle; 649 } 650 651 Boolean _CFBundleMainBundleInfoDictionaryComesFromResourceFork(void) { 652 CFBundleRef mainBundle = CFBundleGetMainBundle(); 653 return (mainBundle && mainBundle->_resourceData._infoDictionaryFromResourceFork); 654 } 655 656 CFBundleRef _CFBundleCreateWithExecutableURLIfLooksLikeBundle(CFAllocatorRef allocator, CFURLRef url) { 657 CFBundleRef bundle = NULL; 658 CFURLRef bundleURL = _CFBundleCopyBundleURLForExecutableURL(url), resolvedURL = _CFBundleCopyResolvedURLForExecutableURL(url); 659 if (bundleURL && resolvedURL) { 660 // We used to call _CFBundleCreateIfLooksLikeBundle here, but switched to the regular CFBundleCreate because we want this to return a result for certain flat bundles as well. 661 bundle = CFBundleCreate(allocator, bundleURL); 662 if (bundle) { 663 CFURLRef executableURL = _CFBundleCopyExecutableURLIgnoringCache(bundle); 664 char buff1[CFMaxPathSize], buff2[CFMaxPathSize]; 665 if (!executableURL || !CFURLGetFileSystemRepresentation(resolvedURL, true, (uint8_t *)buff1, CFMaxPathSize) || !CFURLGetFileSystemRepresentation(executableURL, true, (uint8_t *)buff2, CFMaxPathSize) || 0 != strcmp(buff1, buff2)) { 666 CFRelease(bundle); 667 bundle = NULL; 668 } 669 if (executableURL) CFRelease(executableURL); 670 } 671 } 672 if (bundleURL) CFRelease(bundleURL); 673 if (resolvedURL) CFRelease(resolvedURL); 674 return bundle; 675 } 676 677 CFBundleRef _CFBundleCreateIfMightBeBundle(CFAllocatorRef allocator, CFURLRef url) { 678 // This function is obsolete 679 CFBundleRef bundle = CFBundleCreate(allocator, url); 680 return bundle; 681 } 682 683 CFBundleRef _CFBundleCreateWithExecutableURLIfMightBeBundle(CFAllocatorRef allocator, CFURLRef url) { 684 CFBundleRef result = _CFBundleCreateWithExecutableURLIfLooksLikeBundle(allocator, url); 685 686 // This function applies additional requirements on a bundle to return a result 687 // The above makes sure that: 688 // 0. CFBundleCreate must succeed using a URL derived from the executable URL 689 // 1. The bundle must have an executableURL, and it must match the passed in executable URL 690 691 // This function additionally requires that 692 // 2. If flat, the bundle must have a non-empty Info.plist. (15663535) 693 if (result) { 694 uint8_t localVersion = _CFBundleEffectiveLayoutVersion(result); 695 if (3 == localVersion || 4 == localVersion) { 696 CFDictionaryRef infoPlist = CFBundleGetInfoDictionary(result); 697 if (!infoPlist || (infoPlist && CFDictionaryGetCount(infoPlist) == 0)) { 698 CFRelease(result); 699 result = NULL; 700 } 701 } 702 } 703 return result; 704 } 705 706 CFURLRef _CFBundleCopyMainBundleExecutableURL(Boolean *looksLikeBundle) { 707 // This function is for internal use only; _mainBundle is deliberately accessed outside of the lock to get around a reentrancy issue 708 const char *processPath; 709 CFStringRef str = NULL; 710 CFURLRef executableURL = NULL; 711 processPath = _CFProcessPath(); 712 if (processPath) { 713 str = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, processPath); 714 if (str) { 715 executableURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, PLATFORM_PATH_STYLE, false); 716 CFRelease(str); 717 } 718 } 719 if (looksLikeBundle) { 720 CFBundleRef mainBundle = _mainBundle; 721 if (mainBundle && (3 == mainBundle->_version || 4 == mainBundle->_version)) mainBundle = NULL; 722 *looksLikeBundle = (mainBundle ? true : false); 723 } 724 return executableURL; 725 } 726 727 static void _CFBundleInitializeMainBundleInfoDictionaryAlreadyLocked(CFStringRef executablePath) { 728 CFBundleGetInfoDictionary(_mainBundle); 729 if (!_mainBundle->_infoDict || CFDictionaryGetCount(_mainBundle->_infoDict) == 0) { 730 // if type 3 bundle and no Info.plist, treat as unbundled, since this gives too many false positives 731 if (_mainBundle->_version == 3) _mainBundle->_version = 4; 732 if (_mainBundle->_version == 0) { 733 // if type 0 bundle and no Info.plist and not main executable for bundle, treat as unbundled, since this gives too many false positives 734 CFStringRef executableName = _CFBundleCopyExecutableName(_mainBundle, NULL, NULL); 735 if (!executableName || !executablePath || !CFStringHasSuffix(executablePath, executableName)) _mainBundle->_version = 4; 736 if (executableName) CFRelease(executableName); 737 } 738 #if defined(BINARY_SUPPORT_DYLD) 739 if (_mainBundle->_binaryType == __CFBundleDYLDExecutableBinary) { 740 if (_mainBundle->_infoDict && !(0)) CFRelease(_mainBundle->_infoDict); 741 _mainBundle->_infoDict = (CFDictionaryRef)_CFBundleCreateInfoDictFromMainExecutable(); 742 } 743 #endif /* BINARY_SUPPORT_DYLD */ 744 } else { 745 #if defined(BINARY_SUPPORT_DYLD) 746 if (_mainBundle->_binaryType == __CFBundleDYLDExecutableBinary) { 747 // if dyld and not main executable for bundle, prefer info dictionary from executable 748 CFStringRef executableName = _CFBundleCopyExecutableName(_mainBundle, NULL, NULL); 749 if (!executableName || !executablePath || !CFStringHasSuffix(executablePath, executableName)) { 750 CFDictionaryRef infoDictFromExecutable = (CFDictionaryRef)_CFBundleCreateInfoDictFromMainExecutable(); 751 if (infoDictFromExecutable && CFDictionaryGetCount(infoDictFromExecutable) > 0) { 752 if (_mainBundle->_infoDict) CFRelease(_mainBundle->_infoDict); 753 _mainBundle->_infoDict = infoDictFromExecutable; 754 } else if (infoDictFromExecutable) { 755 CFRelease(infoDictFromExecutable); 756 } 757 } 758 if (executableName) CFRelease(executableName); 759 } 760 #endif /* BINARY_SUPPORT_DYLD */ 761 } 762 if (!_mainBundle->_infoDict) _mainBundle->_infoDict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 763 if (!_mainBundle->_executablePath && executablePath) _mainBundle->_executablePath = (CFStringRef)CFRetain(executablePath); 764 CFStringRef bundleID = (CFStringRef)CFDictionaryGetValue(_mainBundle->_infoDict, kCFBundleIdentifierKey); 765 if (bundleID) { 766 if (!CFStringGetCString(bundleID, __CFBundleMainID__, sizeof(__CFBundleMainID__) - 2, kCFStringEncodingUTF8)) { 767 __CFBundleMainID__[0] = '\0'; 768 } 769 } 770 } 771 772 static void _CFBundleFlushBundleCachesAlreadyLocked(CFBundleRef bundle, Boolean alreadyLocked) { 773 CFDictionaryRef oldInfoDict = bundle->_infoDict; 774 CFTypeRef val; 775 776 bundle->_infoDict = NULL; 777 if (bundle->_localInfoDict) { 778 CFRelease(bundle->_localInfoDict); 779 bundle->_localInfoDict = NULL; 780 } 781 if (bundle->_developmentRegion) { 782 CFRelease(bundle->_developmentRegion); 783 bundle->_developmentRegion = NULL; 784 } 785 if (bundle->_executablePath) { 786 CFRelease(bundle->_executablePath); 787 bundle->_executablePath = NULL; 788 } 789 if (bundle->_searchLanguages) { 790 CFRelease(bundle->_searchLanguages); 791 bundle->_searchLanguages = NULL; 792 } 793 if (bundle->_stringTable) { 794 CFRelease(bundle->_stringTable); 795 bundle->_stringTable = NULL; 796 } 797 if (bundle == _mainBundle) { 798 CFStringRef executablePath = bundle->_executablePath; 799 if (!alreadyLocked) pthread_mutex_lock(&CFBundleGlobalDataLock); 800 _CFBundleInitializeMainBundleInfoDictionaryAlreadyLocked(executablePath); 801 if (!alreadyLocked) pthread_mutex_unlock(&CFBundleGlobalDataLock); 802 } else { 803 CFBundleGetInfoDictionary(bundle); 804 } 805 if (oldInfoDict) { 806 if (!bundle->_infoDict) bundle->_infoDict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 807 val = CFDictionaryGetValue(oldInfoDict, _kCFBundlePrincipalClassKey); 808 if (val) CFDictionarySetValue((CFMutableDictionaryRef)bundle->_infoDict, _kCFBundlePrincipalClassKey, val); 809 CFRelease(oldInfoDict); 810 } 811 812 _CFBundleFlushQueryTableCache(bundle); 813 } 814 815 CF_EXPORT void _CFBundleFlushBundleCaches(CFBundleRef bundle) { 816 _CFBundleFlushBundleCachesAlreadyLocked(bundle, false); 817 } 818 819 static CFBundleRef _CFBundleGetMainBundleAlreadyLocked(void) { 820 if (!_initedMainBundle) { 821 const char *processPath; 822 CFStringRef str = NULL; 823 CFURLRef executableURL = NULL, bundleURL = NULL; 824 _initedMainBundle = true; 825 processPath = _CFProcessPath(); 826 if (processPath) { 827 str = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, processPath); 828 if (!executableURL) executableURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, str, PLATFORM_PATH_STYLE, false); 829 } 830 if (executableURL) bundleURL = _CFBundleCopyBundleURLForExecutableURL(executableURL); 831 if (bundleURL) { 832 // make sure that main bundle has executable path 833 //??? what if we are not the main executable in the bundle? 834 // NB doFinalProcessing must be false here, see below 835 _mainBundle = _CFBundleCreate(kCFAllocatorSystemDefault, bundleURL, true, false, false); 836 if (_mainBundle) { 837 // make sure that the main bundle is listed as loaded, and mark it as executable 838 _mainBundle->_isLoaded = true; 839 #if defined(BINARY_SUPPORT_DYLD) 840 if (_mainBundle->_binaryType == __CFBundleUnknownBinary) { 841 if (!executableURL) { 842 _mainBundle->_binaryType = __CFBundleNoBinary; 843 } else { 844 _mainBundle->_binaryType = _CFBundleGrokBinaryType(executableURL); 845 if (_mainBundle->_binaryType != __CFBundleCFMBinary && _mainBundle->_binaryType != __CFBundleUnreadableBinary) _mainBundle->_resourceData._executableLacksResourceFork = true; 846 } 847 } 848 #endif /* BINARY_SUPPORT_DYLD */ 849 // get cookie for already-loaded main bundle 850 #if defined(BINARY_SUPPORT_DLFCN) 851 if (!_mainBundle->_handleCookie) { 852 _mainBundle->_handleCookie = dlopen(NULL, RTLD_NOLOAD | RTLD_FIRST); 853 #if LOG_BUNDLE_LOAD 854 printf("main bundle %p getting handle %p\n", _mainBundle, _mainBundle->_handleCookie); 855 #endif /* LOG_BUNDLE_LOAD */ 856 } 857 #elif defined(BINARY_SUPPORT_DYLD) 858 if (_mainBundle->_binaryType == __CFBundleDYLDExecutableBinary && !_mainBundle->_imageCookie) { 859 _mainBundle->_imageCookie = (void *)_dyld_get_image_header(0); 860 #if LOG_BUNDLE_LOAD 861 printf("main bundle %p getting image %p\n", _mainBundle, _mainBundle->_imageCookie); 862 #endif /* LOG_BUNDLE_LOAD */ 863 } 864 #endif /* BINARY_SUPPORT_DLFCN */ 865 _CFBundleInitializeMainBundleInfoDictionaryAlreadyLocked(str); 866 // Perform delayed final processing steps. 867 // This must be done after _isLoaded has been set, for security reasons (3624341). 868 if (_CFBundleNeedsInitPlugIn(_mainBundle)) { 869 pthread_mutex_unlock(&CFBundleGlobalDataLock); 870 _CFBundleInitPlugIn(_mainBundle); 871 pthread_mutex_lock(&CFBundleGlobalDataLock); 872 } 873 } 874 } 875 if (bundleURL) CFRelease(bundleURL); 876 if (str) CFRelease(str); 877 if (executableURL) CFRelease(executableURL); 878 } 879 return _mainBundle; 880 } 881 882 CFBundleRef CFBundleGetMainBundle(void) { 883 CFBundleRef mainBundle; 884 pthread_mutex_lock(&CFBundleGlobalDataLock); 885 mainBundle = _CFBundleGetMainBundleAlreadyLocked(); 886 pthread_mutex_unlock(&CFBundleGlobalDataLock); 887 return mainBundle; 888 } 889 890 CFBundleRef CFBundleGetBundleWithIdentifier(CFStringRef bundleID) { 891 CFBundleRef result = NULL; 892 if (bundleID) { 893 pthread_mutex_lock(&CFBundleGlobalDataLock); 894 (void)_CFBundleGetMainBundleAlreadyLocked(); 895 result = _CFBundlePrimitiveGetBundleWithIdentifierAlreadyLocked(bundleID); 896 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 897 if (!result) { 898 // Try to create the bundle for the caller and try again 899 void *p = __builtin_return_address(0); 900 if (p) { 901 CFStringRef imagePath = _CFBundleCopyLoadedImagePathForPointer(p); 902 if (imagePath) { 903 _CFBundleEnsureBundleExistsForImagePath(imagePath); 904 CFRelease(imagePath); 905 } 906 result = _CFBundlePrimitiveGetBundleWithIdentifierAlreadyLocked(bundleID); 907 } 908 } 909 #endif 910 if (!result) { 911 // Try to guess the bundle from the identifier and try again 912 _CFBundleEnsureBundlesUpToDateWithHintAlreadyLocked(bundleID); 913 result = _CFBundlePrimitiveGetBundleWithIdentifierAlreadyLocked(bundleID); 914 } 915 if (!result) { 916 // Make sure all bundles have been created and try again. 917 _CFBundleEnsureAllBundlesUpToDateAlreadyLocked(); 918 result = _CFBundlePrimitiveGetBundleWithIdentifierAlreadyLocked(bundleID); 919 } 920 pthread_mutex_unlock(&CFBundleGlobalDataLock); 921 } 922 return result; 923 } 924 925 static CFStringRef __CFBundleCopyDescription(CFTypeRef cf) { 926 char buff[CFMaxPathSize]; 927 CFStringRef path = NULL, binaryType = NULL, retval = NULL; 928 if (((CFBundleRef)cf)->_url && CFURLGetFileSystemRepresentation(((CFBundleRef)cf)->_url, true, (uint8_t *)buff, CFMaxPathSize)) path = CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, buff); 929 switch (((CFBundleRef)cf)->_binaryType) { 930 case __CFBundleCFMBinary: 931 binaryType = CFSTR(""); 932 break; 933 case __CFBundleDYLDExecutableBinary: 934 binaryType = CFSTR("executable, "); 935 break; 936 case __CFBundleDYLDBundleBinary: 937 binaryType = CFSTR("bundle, "); 938 break; 939 case __CFBundleDYLDFrameworkBinary: 940 binaryType = CFSTR("framework, "); 941 break; 942 case __CFBundleDLLBinary: 943 binaryType = CFSTR("DLL, "); 944 break; 945 case __CFBundleUnreadableBinary: 946 binaryType = CFSTR(""); 947 break; 948 default: 949 binaryType = CFSTR(""); 950 break; 951 } 952 if (((CFBundleRef)cf)->_plugInData._isPlugIn) { 953 retval = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("CFBundle/CFPlugIn %p <%@> (%@%@loaded)"), cf, path, binaryType, ((CFBundleRef)cf)->_isLoaded ? CFSTR("") : CFSTR("not ")); 954 } else { 955 retval = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("CFBundle %p <%@> (%@%@loaded)"), cf, path, binaryType, ((CFBundleRef)cf)->_isLoaded ? CFSTR("") : CFSTR("not ")); 956 } 957 if (path) CFRelease(path); 958 return retval; 959 } 960 961 static void _CFBundleDeallocateGlue(const void *key, const void *value, void *context) { 962 CFAllocatorRef allocator = (CFAllocatorRef)context; 963 if (value) CFAllocatorDeallocate(allocator, (void *)value); 964 } 965 966 static void __CFBundleDeallocate(CFTypeRef cf) { 967 CFBundleRef bundle = (CFBundleRef)cf; 968 CFURLRef bundleURL; 969 CFStringRef bundleID = NULL; 970 971 __CFGenericValidateType(cf, CFBundleGetTypeID()); 972 bundleURL = bundle->_url; 973 bundle->_url = NULL; 974 if (bundle->_infoDict) bundleID = (CFStringRef)CFDictionaryGetValue(bundle->_infoDict, kCFBundleIdentifierKey); 975 _CFBundleRemoveFromTables(bundle, bundleURL, bundleID); 976 CFBundleUnloadExecutable(bundle); 977 _CFBundleDeallocatePlugIn(bundle); 978 if (bundleURL) { 979 CFRelease(bundleURL); 980 } 981 if (bundle->_infoDict && !(0)) CFRelease(bundle->_infoDict); 982 if (bundle->_modDate) CFRelease(bundle->_modDate); 983 if (bundle->_localInfoDict && !(0)) CFRelease(bundle->_localInfoDict); 984 if (bundle->_searchLanguages) CFRelease(bundle->_searchLanguages); 985 if (bundle->_executablePath) CFRelease(bundle->_executablePath); 986 if (bundle->_developmentRegion) CFRelease(bundle->_developmentRegion); 987 if (bundle->_glueDict) { 988 CFDictionaryApplyFunction(bundle->_glueDict, _CFBundleDeallocateGlue, (void *)CFGetAllocator(bundle)); 989 CFRelease(bundle->_glueDict); 990 } 991 if (bundle->_stringTable) CFRelease(bundle->_stringTable); 992 993 if (bundle->_bundleBasePath) CFRelease(bundle->_bundleBasePath); 994 if (bundle->_queryTable) CFRelease(bundle->_queryTable); 995 996 if (bundle->_localizations) CFRelease(bundle->_localizations); 997 if (bundle->_resourceDirectoryContents) CFRelease(bundle->_resourceDirectoryContents); 998 999 pthread_mutex_destroy(&(bundle->_bundleLoadingLock)); 1000 } 1001 1002 static const CFRuntimeClass __CFBundleClass = { 1003 _kCFRuntimeScannedObject, 1004 "CFBundle", 1005 NULL, // init 1006 NULL, // copy 1007 __CFBundleDeallocate, 1008 NULL, // equal 1009 NULL, // hash 1010 NULL, // 1011 __CFBundleCopyDescription 1012 }; 1013 1014 // From CFBundle_Resources.c 1015 CF_PRIVATE void _CFBundleResourcesInitialize(); 1016 1017 CFTypeID CFBundleGetTypeID(void) { 1018 static dispatch_once_t initOnce; 1019 dispatch_once(&initOnce, ^{ __kCFBundleTypeID = _CFRuntimeRegisterClass(&__CFBundleClass); _CFBundleResourcesInitialize(); }); 1020 return __kCFBundleTypeID; 1021 } 1022 1023 CFBundleRef _CFBundleGetExistingBundleWithBundleURL(CFURLRef bundleURL) { 1024 CFBundleRef bundle = NULL; 1025 char buff[CFMaxPathSize]; 1026 CFURLRef newURL = NULL; 1027 1028 if (!CFURLGetFileSystemRepresentation(bundleURL, true, (uint8_t *)buff, CFMaxPathSize)) return NULL; 1029 1030 newURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorSystemDefault, (uint8_t *)buff, strlen(buff), true); 1031 if (!newURL) newURL = (CFURLRef)CFRetain(bundleURL); 1032 bundle = _CFBundleCopyBundleForURL(newURL, false); 1033 if (bundle) CFRelease(bundle); 1034 CFRelease(newURL); 1035 return bundle; 1036 } 1037 1038 static CFBundleRef _CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL, Boolean alreadyLocked, Boolean doFinalProcessing, Boolean noCaches) { 1039 CFBundleRef bundle = NULL; 1040 char buff[CFMaxPathSize]; 1041 CFDateRef modDate = NULL; // do not actually fetch the modDate, since that can cause something like 7609956, unless absolutely found to be necessary in the future 1042 Boolean exists = false; 1043 SInt32 mode = 0; 1044 CFURLRef newURL = NULL; 1045 uint8_t localVersion = 0; 1046 1047 if (!CFURLGetFileSystemRepresentation(bundleURL, true, (uint8_t *)buff, CFMaxPathSize)) return NULL; 1048 1049 newURL = CFURLCreateFromFileSystemRepresentation(allocator, (uint8_t *)buff, strlen(buff), true); 1050 if (!newURL) newURL = (CFURLRef)CFRetain(bundleURL); 1051 1052 if (!noCaches) { 1053 bundle = _CFBundleCopyBundleForURL(newURL, alreadyLocked); 1054 if (bundle) { 1055 CFRelease(newURL); 1056 return bundle; 1057 } 1058 } 1059 1060 localVersion = _CFBundleGetBundleVersionForURL(newURL); 1061 if (localVersion == 3) { 1062 SInt32 res = _CFGetPathProperties(allocator, (char *)buff, &exists, &mode, NULL, NULL, NULL, NULL); 1063 #if DEPLOYMENT_TARGET_WINDOWS 1064 if (!(res == 0 && exists && ((mode & S_IFMT) == S_IFDIR))) { 1065 // 2nd chance at finding a bundle path - remove the last path component (e.g., mybundle.resources) and try again 1066 if (modDate) { 1067 CFRelease(modDate); 1068 modDate = NULL; 1069 } 1070 CFURLRef shorterPath = CFURLCreateCopyDeletingLastPathComponent(allocator, newURL); 1071 CFRelease(newURL); 1072 newURL = shorterPath; 1073 res = _CFGetFileProperties(allocator, newURL, &exists, &mode, NULL, NULL, NULL, NULL); 1074 } 1075 #endif 1076 if (res == 0) { 1077 if (!exists || ((mode & S_IFMT) != S_IFDIR)) { 1078 if (modDate) CFRelease(modDate); 1079 CFRelease(newURL); 1080 return NULL; 1081 } 1082 } else { 1083 CFRelease(newURL); 1084 return NULL; 1085 } 1086 } 1087 1088 bundle = (CFBundleRef)_CFRuntimeCreateInstance(allocator, CFBundleGetTypeID(), sizeof(struct __CFBundle) - sizeof(CFRuntimeBase), NULL); 1089 if (!bundle) { 1090 CFRelease(newURL); 1091 return NULL; 1092 } 1093 1094 bundle->_url = newURL; 1095 1096 bundle->_modDate = modDate; 1097 bundle->_version = localVersion; 1098 bundle->_infoDict = NULL; 1099 bundle->_localInfoDict = NULL; 1100 bundle->_searchLanguages = NULL; 1101 bundle->_executablePath = NULL; 1102 bundle->_developmentRegion = NULL; 1103 bundle->_developmentRegionCalculated = 0; 1104 #if defined(BINARY_SUPPORT_DYLD) 1105 /* We'll have to figure it out later */ 1106 bundle->_binaryType = __CFBundleUnknownBinary; 1107 #elif defined(BINARY_SUPPORT_DLL) 1108 /* We support DLL only */ 1109 bundle->_binaryType = __CFBundleDLLBinary; 1110 bundle->_hModule = NULL; 1111 #else 1112 /* We'll have to figure it out later */ 1113 bundle->_binaryType = __CFBundleUnknownBinary; 1114 #endif /* BINARY_SUPPORT_DYLD */ 1115 1116 bundle->_isLoaded = false; 1117 bundle->_sharesStringsFiles = false; 1118 1119 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 1120 if (!__CFgetenv("CFBundleDisableStringsSharing") && 1121 (strncmp(buff, "/System/Library/Frameworks", 26) == 0) && 1122 (strncmp(buff + strlen(buff) - 10, ".framework", 10) == 0)) bundle->_sharesStringsFiles = true; 1123 #endif 1124 1125 bundle->_connectionCookie = NULL; 1126 bundle->_handleCookie = NULL; 1127 bundle->_imageCookie = NULL; 1128 bundle->_moduleCookie = NULL; 1129 1130 bundle->_glueDict = NULL; 1131 1132 bundle->_resourceData._executableLacksResourceFork = false; 1133 bundle->_resourceData._infoDictionaryFromResourceFork = false; 1134 1135 bundle->_stringTable = NULL; 1136 1137 bundle->_plugInData._isPlugIn = false; 1138 bundle->_plugInData._loadOnDemand = false; 1139 bundle->_plugInData._isDoingDynamicRegistration = false; 1140 bundle->_plugInData._instanceCount = 0; 1141 bundle->_plugInData._factories = NULL; 1142 1143 pthread_mutexattr_t mattr; 1144 pthread_mutexattr_init(&mattr); 1145 pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_DEFAULT); 1146 int32_t mret = pthread_mutex_init(&(bundle->_bundleLoadingLock), &mattr); 1147 pthread_mutexattr_destroy(&mattr); 1148 if (0 != mret) { 1149 CFLog(4, CFSTR("%s: failed to initialize bundle loading lock for bundle %@."), __PRETTY_FUNCTION__, bundle); 1150 } 1151 1152 bundle->_lock = CFLockInit; 1153 bundle->_resourceDirectoryContents = NULL; 1154 1155 bundle->_localizations = NULL; 1156 bundle->_lookedForLocalizations = false; 1157 1158 bundle->_queryLock = CFLockInit; 1159 bundle->_queryTable = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 1160 CFURLRef absoURL = CFURLCopyAbsoluteURL(bundle->_url); 1161 bundle->_bundleBasePath = CFURLCopyFileSystemPath(absoURL, PLATFORM_PATH_STYLE); 1162 CFRelease(absoURL); 1163 1164 CFBundleGetInfoDictionary(bundle); 1165 1166 // Do this so that we can use the dispatch_once on the ivar of this bundle safely 1167 OSMemoryBarrier(); 1168 1169 if (!noCaches) 1170 _CFBundleAddToTables(bundle, alreadyLocked); 1171 1172 if (doFinalProcessing) { 1173 if (_CFBundleNeedsInitPlugIn(bundle)) { 1174 if (alreadyLocked) pthread_mutex_unlock(&CFBundleGlobalDataLock); 1175 _CFBundleInitPlugIn(bundle); 1176 if (alreadyLocked) pthread_mutex_lock(&CFBundleGlobalDataLock); 1177 } 1178 } 1179 1180 return bundle; 1181 } 1182 1183 CFBundleRef CFBundleCreate(CFAllocatorRef allocator, CFURLRef bundleURL) { 1184 return _CFBundleCreate(allocator, bundleURL, false, true, false); 1185 } 1186 1187 CFArrayRef CFBundleCreateBundlesFromDirectory(CFAllocatorRef alloc, CFURLRef directoryURL, CFStringRef bundleType) { 1188 CFMutableArrayRef bundles = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks); 1189 CFArrayRef URLs = _CFCreateContentsOfDirectory(alloc, NULL, NULL, directoryURL, bundleType); 1190 if (URLs) { 1191 CFIndex i, c = CFArrayGetCount(URLs); 1192 CFURLRef curURL; 1193 CFBundleRef curBundle; 1194 1195 for (i = 0; i < c; i++) { 1196 curURL = (CFURLRef)CFArrayGetValueAtIndex(URLs, i); 1197 curBundle = CFBundleCreate(alloc, curURL); 1198 if (curBundle) CFArrayAppendValue(bundles, curBundle); 1199 } 1200 CFRelease(URLs); 1201 } 1202 1203 return bundles; 1204 } 1205 1206 CFURLRef CFBundleCopyBundleURL(CFBundleRef bundle) { 1207 if (bundle->_url) CFRetain(bundle->_url); 1208 return bundle->_url; 1209 } 1210 1211 #define DEVELOPMENT_STAGE 0x20 1212 #define ALPHA_STAGE 0x40 1213 #define BETA_STAGE 0x60 1214 #define RELEASE_STAGE 0x80 1215 1216 #define MAX_VERS_LEN 10 1217 1218 CF_INLINE Boolean _isDigit(UniChar aChar) {return ((aChar >= (UniChar)'0' && aChar <= (UniChar)'9') ? true : false);} 1219 1220 CF_PRIVATE CFStringRef _CFCreateStringFromVersionNumber(CFAllocatorRef alloc, UInt32 vers) { 1221 CFStringRef result = NULL; 1222 uint8_t major1, major2, minor1, minor2, stage, build; 1223 1224 major1 = (vers & 0xF0000000) >> 28; 1225 major2 = (vers & 0x0F000000) >> 24; 1226 minor1 = (vers & 0x00F00000) >> 20; 1227 minor2 = (vers & 0x000F0000) >> 16; 1228 stage = (vers & 0x0000FF00) >> 8; 1229 build = (vers & 0x000000FF); 1230 1231 if (stage == RELEASE_STAGE) { 1232 if (major1 > 0) { 1233 result = CFStringCreateWithFormat(alloc, NULL, CFSTR("%d%d.%d.%d"), major1, major2, minor1, minor2); 1234 } else { 1235 result = CFStringCreateWithFormat(alloc, NULL, CFSTR("%d.%d.%d"), major2, minor1, minor2); 1236 } 1237 } else { 1238 if (major1 > 0) { 1239 result = CFStringCreateWithFormat(alloc, NULL, CFSTR("%d%d.%d.%d%c%d"), major1, major2, minor1, minor2, ((stage == DEVELOPMENT_STAGE) ? 'd' : ((stage == ALPHA_STAGE) ? 'a' : 'b')), build); 1240 } else { 1241 result = CFStringCreateWithFormat(alloc, NULL, CFSTR("%d.%d.%d%c%d"), major2, minor1, minor2, ((stage == DEVELOPMENT_STAGE) ? 'd' : ((stage == ALPHA_STAGE) ? 'a' : 'b')), build); 1242 } 1243 } 1244 return result; 1245 } 1246 1247 CF_PRIVATE UInt32 _CFVersionNumberFromString(CFStringRef versStr) { 1248 // Parse version number from string. 1249 // String can begin with "." for major version number 0. String can end at any point, but elements within the string cannot be skipped. 1250 UInt32 major1 = 0, major2 = 0, minor1 = 0, minor2 = 0, stage = RELEASE_STAGE, build = 0; 1251 UniChar versChars[MAX_VERS_LEN]; 1252 UniChar *chars = NULL; 1253 CFIndex len; 1254 UInt32 theVers; 1255 Boolean digitsDone = false; 1256 1257 if (!versStr) return 0; 1258 len = CFStringGetLength(versStr); 1259 if (len <= 0 || len > MAX_VERS_LEN) return 0; 1260 1261 CFStringGetCharacters(versStr, CFRangeMake(0, len), versChars); 1262 chars = versChars; 1263 1264 // Get major version number. 1265 major1 = major2 = 0; 1266 if (_isDigit(*chars)) { 1267 major2 = *chars - (UniChar)'0'; 1268 chars++; 1269 len--; 1270 if (len > 0) { 1271 if (_isDigit(*chars)) { 1272 major1 = major2; 1273 major2 = *chars - (UniChar)'0'; 1274 chars++; 1275 len--; 1276 if (len > 0) { 1277 if (*chars == (UniChar)'.') { 1278 chars++; 1279 len--; 1280 } else { 1281 digitsDone = true; 1282 } 1283 } 1284 } else if (*chars == (UniChar)'.') { 1285 chars++; 1286 len--; 1287 } else { 1288 digitsDone = true; 1289 } 1290 } 1291 } else if (*chars == (UniChar)'.') { 1292 chars++; 1293 len--; 1294 } else { 1295 digitsDone = true; 1296 } 1297 1298 // Now major1 and major2 contain first and second digit of the major version number as ints. 1299 // Now either len is 0 or chars points at the first char beyond the first decimal point. 1300 1301 // Get the first minor version number. 1302 if (len > 0 && !digitsDone) { 1303 if (_isDigit(*chars)) { 1304 minor1 = *chars - (UniChar)'0'; 1305 chars++; 1306 len--; 1307 if (len > 0) { 1308 if (*chars == (UniChar)'.') { 1309 chars++; 1310 len--; 1311 } else { 1312 digitsDone = true; 1313 } 1314 } 1315 } else { 1316 digitsDone = true; 1317 } 1318 } 1319 1320 // Now minor1 contains the first minor version number as an int. 1321 // Now either len is 0 or chars points at the first char beyond the second decimal point. 1322 1323 // Get the second minor version number. 1324 if (len > 0 && !digitsDone) { 1325 if (_isDigit(*chars)) { 1326 minor2 = *chars - (UniChar)'0'; 1327 chars++; 1328 len--; 1329 } else { 1330 digitsDone = true; 1331 } 1332 } 1333 1334 // Now minor2 contains the second minor version number as an int. 1335 // Now either len is 0 or chars points at the build stage letter. 1336 1337 // Get the build stage letter. We must find 'd', 'a', 'b', or 'f' next, if there is anything next. 1338 if (len > 0) { 1339 if (*chars == (UniChar)'d') { 1340 stage = DEVELOPMENT_STAGE; 1341 } else if (*chars == (UniChar)'a') { 1342 stage = ALPHA_STAGE; 1343 } else if (*chars == (UniChar)'b') { 1344 stage = BETA_STAGE; 1345 } else if (*chars == (UniChar)'f') { 1346 stage = RELEASE_STAGE; 1347 } else { 1348 return 0; 1349 } 1350 chars++; 1351 len--; 1352 } 1353 1354 // Now stage contains the release stage. 1355 // Now either len is 0 or chars points at the build number. 1356 1357 // Get the first digit of the build number. 1358 if (len > 0) { 1359 if (_isDigit(*chars)) { 1360 build = *chars - (UniChar)'0'; 1361 chars++; 1362 len--; 1363 } else { 1364 return 0; 1365 } 1366 } 1367 // Get the second digit of the build number. 1368 if (len > 0) { 1369 if (_isDigit(*chars)) { 1370 build *= 10; 1371 build += *chars - (UniChar)'0'; 1372 chars++; 1373 len--; 1374 } else { 1375 return 0; 1376 } 1377 } 1378 // Get the third digit of the build number. 1379 if (len > 0) { 1380 if (_isDigit(*chars)) { 1381 build *= 10; 1382 build += *chars - (UniChar)'0'; 1383 chars++; 1384 len--; 1385 } else { 1386 return 0; 1387 } 1388 } 1389 1390 // Range check the build number and make sure we exhausted the string. 1391 if (build > 0xFF || len > 0) return 0; 1392 1393 // Build the number 1394 theVers = major1 << 28; 1395 theVers += major2 << 24; 1396 theVers += minor1 << 20; 1397 theVers += minor2 << 16; 1398 theVers += stage << 8; 1399 theVers += build; 1400 1401 return theVers; 1402 } 1403 1404 UInt32 CFBundleGetVersionNumber(CFBundleRef bundle) { 1405 CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); 1406 CFNumberRef versionValue = (CFNumberRef)CFDictionaryGetValue(infoDict, _kCFBundleNumericVersionKey); 1407 if (!versionValue || CFGetTypeID(versionValue) != CFNumberGetTypeID()) return 0; 1408 1409 UInt32 vers = 0; 1410 CFNumberGetValue(versionValue, kCFNumberSInt32Type, &vers); 1411 return vers; 1412 } 1413 1414 CFStringRef CFBundleGetDevelopmentRegion(CFBundleRef bundle) { 1415 dispatch_once(&bundle->_developmentRegionCalculated, ^{ 1416 CFStringRef devRegion = NULL; 1417 CFDictionaryRef infoDict = CFBundleGetInfoDictionary(bundle); 1418 if (infoDict) { 1419 devRegion = (CFStringRef)CFDictionaryGetValue(infoDict, kCFBundleDevelopmentRegionKey); 1420 if (devRegion && (CFGetTypeID(devRegion) != CFStringGetTypeID() || CFStringGetLength(devRegion) == 0)) { 1421 devRegion = NULL; 1422 } 1423 } 1424 1425 if (devRegion) bundle->_developmentRegion = (CFStringRef)CFRetain(devRegion); 1426 }); 1427 return bundle->_developmentRegion; 1428 } 1429 1430 Boolean _CFBundleGetHasChanged(CFBundleRef bundle) { 1431 CFDateRef modDate; 1432 Boolean result = false; 1433 Boolean exists = false; 1434 SInt32 mode = 0; 1435 1436 if (_CFGetFileProperties(CFGetAllocator(bundle), bundle->_url, &exists, &mode, NULL, &modDate, NULL, NULL) == 0) { 1437 // If the bundle no longer exists or is not a folder, it must have "changed" 1438 if (!exists || ((mode & S_IFMT) != S_IFDIR)) result = true; 1439 } else { 1440 // Something is wrong. The stat failed. 1441 result = true; 1442 } 1443 if (bundle->_modDate && !CFEqual(bundle->_modDate, modDate)) { 1444 // mod date is different from when we created. 1445 result = true; 1446 } 1447 CFRelease(modDate); 1448 return result; 1449 } 1450 1451 void _CFBundleSetStringsFilesShared(CFBundleRef bundle, Boolean flag) { 1452 bundle->_sharesStringsFiles = flag; 1453 } 1454 1455 Boolean _CFBundleGetStringsFilesShared(CFBundleRef bundle) { 1456 return bundle->_sharesStringsFiles; 1457 } 1458 1459 static Boolean _urlExists(CFURLRef url) { 1460 Boolean exists; 1461 return url && (0 == _CFGetFileProperties(kCFAllocatorSystemDefault, url, &exists, NULL, NULL, NULL, NULL, NULL)) && exists; 1462 } 1463 1464 // This is here because on iPhoneOS with the dyld shared cache, we remove binaries from their 1465 // original locations on disk, so checking whether a binary's path exists is no longer sufficient. 1466 // For performance reasons, we only call dlopen_preflight() after we've verified that the binary 1467 // does not exist at its original path with _urlExists(). 1468 // See <rdar://problem/6956670> 1469 static Boolean _binaryLoadable(CFURLRef url) { 1470 Boolean loadable = _urlExists(url); 1471 #if DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 1472 if (!loadable) { 1473 uint8_t path[PATH_MAX]; 1474 if (url && CFURLGetFileSystemRepresentation(url, true, path, sizeof(path))) { 1475 loadable = dlopen_preflight((char *)path); 1476 } 1477 } 1478 #endif 1479 return loadable; 1480 } 1481 1482 CF_PRIVATE CFURLRef _CFBundleCopySupportFilesDirectoryURLInDirectory(CFURLRef bundleURL, uint8_t version) { 1483 CFURLRef result = NULL; 1484 if (bundleURL) { 1485 if (1 == version) { 1486 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleSupportFilesURLFromBase1, bundleURL); 1487 } else if (2 == version) { 1488 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleSupportFilesURLFromBase2, bundleURL); 1489 } else { 1490 result = (CFURLRef)CFRetain(bundleURL); 1491 } 1492 } 1493 return result; 1494 } 1495 1496 CF_EXPORT CFURLRef CFBundleCopySupportFilesDirectoryURL(CFBundleRef bundle) { 1497 return _CFBundleCopySupportFilesDirectoryURLInDirectory(bundle->_url, bundle->_version); 1498 } 1499 1500 CF_PRIVATE CFURLRef _CFBundleCopyResourcesDirectoryURLInDirectory(CFURLRef bundleURL, uint8_t version) { 1501 CFURLRef result = NULL; 1502 if (bundleURL) { 1503 if (0 == version) { 1504 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleResourcesURLFromBase0, bundleURL); 1505 } else if (1 == version) { 1506 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleResourcesURLFromBase1, bundleURL); 1507 } else if (2 == version) { 1508 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleResourcesURLFromBase2, bundleURL); 1509 } else { 1510 result = (CFURLRef)CFRetain(bundleURL); 1511 } 1512 } 1513 return result; 1514 } 1515 1516 CF_EXPORT CFURLRef CFBundleCopyResourcesDirectoryURL(CFBundleRef bundle) { 1517 return _CFBundleCopyResourcesDirectoryURLInDirectory(bundle->_url, bundle->_version); 1518 } 1519 1520 CF_PRIVATE CFURLRef _CFBundleCopyAppStoreReceiptURLInDirectory(CFURLRef bundleURL, uint8_t version) { 1521 CFURLRef result = NULL; 1522 if (bundleURL) { 1523 if (0 == version) { 1524 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleAppStoreReceiptURLFromBase0, bundleURL); 1525 } else if (1 == version) { 1526 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleAppStoreReceiptURLFromBase1, bundleURL); 1527 } else if (2 == version) { 1528 result = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleAppStoreReceiptURLFromBase2, bundleURL); 1529 } 1530 } 1531 return result; 1532 } 1533 1534 CFURLRef _CFBundleCopyAppStoreReceiptURL(CFBundleRef bundle) { 1535 return _CFBundleCopyAppStoreReceiptURLInDirectory(bundle->_url, bundle->_version); 1536 } 1537 1538 static CFURLRef _CFBundleCopyExecutableURLRaw(CFURLRef urlPath, CFStringRef exeName) { 1539 // Given an url to a folder and a name, this returns the url to the executable in that folder with that name, if it exists, and NULL otherwise. This function deals with appending the ".exe" or ".dll" on Windows. 1540 CFURLRef executableURL = NULL; 1541 if (!urlPath || !exeName) return NULL; 1542 1543 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 1544 const uint8_t *image_suffix = (uint8_t *)__CFgetenv("DYLD_IMAGE_SUFFIX"); 1545 if (image_suffix) { 1546 CFStringRef newExeName, imageSuffix; 1547 imageSuffix = CFStringCreateWithCString(kCFAllocatorSystemDefault, (char *)image_suffix, kCFStringEncodingUTF8); 1548 if (CFStringHasSuffix(exeName, CFSTR(".dylib"))) { 1549 CFStringRef bareExeName = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, exeName, CFRangeMake(0, CFStringGetLength(exeName)-6)); 1550 newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@.dylib"), exeName, imageSuffix); 1551 CFRelease(bareExeName); 1552 } else { 1553 newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@"), exeName, imageSuffix); 1554 } 1555 executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, newExeName, kCFURLPOSIXPathStyle, false, urlPath); 1556 if (executableURL && !_binaryLoadable(executableURL)) { 1557 CFRelease(executableURL); 1558 executableURL = NULL; 1559 } 1560 CFRelease(newExeName); 1561 CFRelease(imageSuffix); 1562 } 1563 if (!executableURL) { 1564 executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, exeName, kCFURLPOSIXPathStyle, false, urlPath); 1565 if (executableURL && !_binaryLoadable(executableURL)) { 1566 CFRelease(executableURL); 1567 executableURL = NULL; 1568 } 1569 } 1570 #elif DEPLOYMENT_TARGET_WINDOWS 1571 if (!executableURL) { 1572 executableURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, exeName, kCFURLWindowsPathStyle, false, urlPath); 1573 if (executableURL && !_urlExists(executableURL)) { 1574 CFRelease(executableURL); 1575 executableURL = NULL; 1576 } 1577 } 1578 if (!executableURL) { 1579 if (!CFStringFindWithOptions(exeName, CFSTR(".dll"), CFRangeMake(0, CFStringGetLength(exeName)), kCFCompareAnchored|kCFCompareBackwards|kCFCompareCaseInsensitive, NULL)) { 1580 #if defined(DEBUG) 1581 CFStringRef extension = CFSTR("_debug.dll"); 1582 #else 1583 CFStringRef extension = CFSTR(".dll"); 1584 #endif 1585 CFStringRef newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@"), exeName, extension); 1586 executableURL = CFURLCreateWithString(kCFAllocatorSystemDefault, newExeName, urlPath); 1587 if (executableURL && !_binaryLoadable(executableURL)) { 1588 CFRelease(executableURL); 1589 executableURL = NULL; 1590 } 1591 CFRelease(newExeName); 1592 } 1593 } 1594 if (!executableURL) { 1595 if (!CFStringFindWithOptions(exeName, CFSTR(".exe"), CFRangeMake(0, CFStringGetLength(exeName)), kCFCompareAnchored|kCFCompareBackwards|kCFCompareCaseInsensitive, NULL)) { 1596 #if defined(DEBUG) 1597 CFStringRef extension = CFSTR("_debug.exe"); 1598 #else 1599 CFStringRef extension = CFSTR(".exe"); 1600 #endif 1601 CFStringRef newExeName = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@%@"), exeName, extension); 1602 executableURL = CFURLCreateWithString(kCFAllocatorSystemDefault, newExeName, urlPath); 1603 if (executableURL && !_binaryLoadable(executableURL)) { 1604 CFRelease(executableURL); 1605 executableURL = NULL; 1606 } 1607 CFRelease(newExeName); 1608 } 1609 } 1610 #endif 1611 return executableURL; 1612 } 1613 1614 CF_PRIVATE CFStringRef _CFBundleCopyExecutableName(CFBundleRef bundle, CFURLRef url, CFDictionaryRef infoDict) { 1615 CFStringRef executableName = NULL; 1616 1617 if (!infoDict && bundle) infoDict = CFBundleGetInfoDictionary(bundle); 1618 if (!url && bundle) url = bundle->_url; 1619 1620 if (infoDict) { 1621 // Figure out the name of the executable. 1622 // First try for the new key in the plist. 1623 executableName = (CFStringRef)CFDictionaryGetValue(infoDict, kCFBundleExecutableKey); 1624 // Second try for the old key in the plist. 1625 if (!executableName) executableName = (CFStringRef)CFDictionaryGetValue(infoDict, _kCFBundleOldExecutableKey); 1626 if (executableName && CFGetTypeID(executableName) == CFStringGetTypeID() && CFStringGetLength(executableName) > 0) { 1627 CFRetain(executableName); 1628 } else { 1629 executableName = NULL; 1630 } 1631 } 1632 if (!executableName && url) { 1633 // Third, take the name of the bundle itself (with path extension stripped) 1634 CFURLRef absoluteURL = CFURLCopyAbsoluteURL(url); 1635 CFStringRef bundlePath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE); 1636 CFRelease(absoluteURL); 1637 if (bundlePath) { 1638 CFIndex len = CFStringGetLength(bundlePath); 1639 CFIndex startOfBundleName = _CFStartOfLastPathComponent2(bundlePath); 1640 CFIndex endOfBundleName = _CFLengthAfterDeletingPathExtension2(bundlePath); 1641 1642 if (startOfBundleName <= len && endOfBundleName <= len && startOfBundleName < endOfBundleName) { 1643 executableName = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, bundlePath, CFRangeMake(startOfBundleName, endOfBundleName - startOfBundleName)); 1644 } 1645 CFRelease(bundlePath); 1646 } 1647 } 1648 1649 return executableName; 1650 } 1651 1652 static CFURLRef _CFBundleCopyExecutableURLInDirectory2(CFBundleRef bundle, CFURLRef url, CFStringRef executableName, Boolean ignoreCache, Boolean useOtherPlatform) { 1653 uint8_t version = 0; 1654 CFDictionaryRef infoDict = NULL; 1655 CFStringRef executablePath = NULL; 1656 CFURLRef executableURL = NULL; 1657 Boolean foundIt = false; 1658 Boolean lookupMainExe = (executableName ? false : true); 1659 1660 if (bundle) { 1661 infoDict = CFBundleGetInfoDictionary(bundle); 1662 version = bundle->_version; 1663 } else { 1664 infoDict = _CFBundleCopyInfoDictionaryInDirectory(kCFAllocatorSystemDefault, url, &version); 1665 } 1666 1667 // If we have a bundle instance and an info dict, see if we have already cached the path 1668 if (lookupMainExe && !ignoreCache && !useOtherPlatform && bundle && bundle->_executablePath) { 1669 __CFLock(&bundle->_lock); 1670 executablePath = bundle->_executablePath; 1671 if (executablePath) CFRetain(executablePath); 1672 __CFUnlock(&bundle->_lock); 1673 if (executablePath) { 1674 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 1675 executableURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, executablePath, kCFURLPOSIXPathStyle, false); 1676 #elif DEPLOYMENT_TARGET_WINDOWS 1677 executableURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, executablePath, kCFURLWindowsPathStyle, false); 1678 #endif 1679 if (executableURL) { 1680 foundIt = true; 1681 } 1682 CFRelease(executablePath); 1683 } 1684 } 1685 1686 if (!foundIt) { 1687 if (lookupMainExe) executableName = _CFBundleCopyExecutableName(bundle, url, infoDict); 1688 if (executableName) { 1689 #if (DEPLOYMENT_TARGET_EMBEDDED && !TARGET_IPHONE_SIMULATOR) 1690 Boolean doExecSearch = false; 1691 #else 1692 Boolean doExecSearch = true; 1693 #endif 1694 // Now, look for the executable inside the bundle. 1695 if (doExecSearch && 0 != version) { 1696 CFURLRef exeDirURL = NULL; 1697 CFURLRef exeSubdirURL; 1698 1699 if (1 == version) { 1700 exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleExecutablesURLFromBase1, url); 1701 } else if (2 == version) { 1702 exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, _CFBundleExecutablesURLFromBase2, url); 1703 } else { 1704 #if DEPLOYMENT_TARGET_WINDOWS 1705 // On Windows, if the bundle URL is foo.resources, then the executable is at the same level as the .resources directory 1706 CFStringRef extension = CFURLCopyPathExtension(url); 1707 if (extension && CFEqual(extension, _CFBundleWindowsResourceDirectoryExtension)) { 1708 exeDirURL = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorSystemDefault, url); 1709 } else { 1710 exeDirURL = (CFURLRef)CFRetain(url); 1711 } 1712 #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 1713 exeDirURL = (CFURLRef)CFRetain(url); 1714 #endif 1715 } 1716 CFStringRef platformSubDir = useOtherPlatform ? _CFBundleGetOtherPlatformExecutablesSubdirectoryName() : _CFBundleGetPlatformExecutablesSubdirectoryName(); 1717 exeSubdirURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, platformSubDir, kCFURLPOSIXPathStyle, true, exeDirURL); 1718 executableURL = _CFBundleCopyExecutableURLRaw(exeSubdirURL, executableName); 1719 if (!executableURL) { 1720 CFRelease(exeSubdirURL); 1721 platformSubDir = useOtherPlatform ? _CFBundleGetOtherAlternatePlatformExecutablesSubdirectoryName() : _CFBundleGetAlternatePlatformExecutablesSubdirectoryName(); 1722 exeSubdirURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, platformSubDir, kCFURLPOSIXPathStyle, true, exeDirURL); 1723 executableURL = _CFBundleCopyExecutableURLRaw(exeSubdirURL, executableName); 1724 } 1725 if (!executableURL) { 1726 CFRelease(exeSubdirURL); 1727 platformSubDir = useOtherPlatform ? _CFBundleGetPlatformExecutablesSubdirectoryName() : _CFBundleGetOtherPlatformExecutablesSubdirectoryName(); 1728 exeSubdirURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, platformSubDir, kCFURLPOSIXPathStyle, true, exeDirURL); 1729 executableURL = _CFBundleCopyExecutableURLRaw(exeSubdirURL, executableName); 1730 } 1731 if (!executableURL) { 1732 CFRelease(exeSubdirURL); 1733 platformSubDir = useOtherPlatform ? _CFBundleGetAlternatePlatformExecutablesSubdirectoryName() : _CFBundleGetOtherAlternatePlatformExecutablesSubdirectoryName(); 1734 exeSubdirURL = CFURLCreateWithFileSystemPathRelativeToBase(kCFAllocatorSystemDefault, platformSubDir, kCFURLPOSIXPathStyle, true, exeDirURL); 1735 executableURL = _CFBundleCopyExecutableURLRaw(exeSubdirURL, executableName); 1736 } 1737 if (!executableURL) executableURL = _CFBundleCopyExecutableURLRaw(exeDirURL, executableName); 1738 CFRelease(exeDirURL); 1739 CFRelease(exeSubdirURL); 1740 } 1741 1742 // If this was an old bundle, or we did not find the executable in the Executables subdirectory, look directly in the bundle wrapper. 1743 if (!executableURL) executableURL = _CFBundleCopyExecutableURLRaw(url, executableName); 1744 1745 #if DEPLOYMENT_TARGET_WINDOWS 1746 // Windows only: If we still haven't found the exe, look in the Executables folder. 1747 // But only for the main bundle exe 1748 if (lookupMainExe && !executableURL) { 1749 CFURLRef exeDirURL = CFURLCreateWithString(kCFAllocatorSystemDefault, CFSTR("../../Executables"), url); 1750 executableURL = _CFBundleCopyExecutableURLRaw(exeDirURL, executableName); 1751 CFRelease(exeDirURL); 1752 } 1753 #endif 1754 1755 if (lookupMainExe && !ignoreCache && !useOtherPlatform && bundle && executableURL) { 1756 // We found it. Cache the path. 1757 CFURLRef absURL = CFURLCopyAbsoluteURL(executableURL); 1758 #if DEPLOYMENT_TARGET_WINDOWS 1759 executablePath = CFURLCopyFileSystemPath(absURL, kCFURLWindowsPathStyle); 1760 #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI 1761 executablePath = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle); 1762 #endif 1763 CFRelease(absURL); 1764 __CFLock(&bundle->_lock); 1765 bundle->_executablePath = (CFStringRef)CFRetain(executablePath); 1766 __CFUnlock(&bundle->_lock); 1767 CFRelease(executablePath); 1768 } 1769 if (lookupMainExe && !useOtherPlatform && bundle && !executableURL) bundle->_binaryType = __CFBundleNoBinary; 1770 if (lookupMainExe) CFRelease(executableName); 1771 } 1772 } 1773 if (!bundle && infoDict && !(0)) CFRelease(infoDict); 1774 return executableURL; 1775 } 1776 1777 1778 CFURLRef _CFBundleCopyExecutableURLInDirectory(CFURLRef url) { 1779 return _CFBundleCopyExecutableURLInDirectory2(NULL, url, NULL, true, false); 1780 } 1781 1782 CFURLRef _CFBundleCopyOtherExecutableURLInDirectory(CFURLRef url) { 1783 return _CFBundleCopyExecutableURLInDirectory2(NULL, url, NULL, true, true); 1784 } 1785 1786 CFURLRef CFBundleCopyExecutableURL(CFBundleRef bundle) { 1787 return _CFBundleCopyExecutableURLInDirectory2(bundle, bundle->_url, NULL, false, false); 1788 } 1789 1790 static CFURLRef _CFBundleCopyExecutableURLIgnoringCache(CFBundleRef bundle) { 1791 return _CFBundleCopyExecutableURLInDirectory2(bundle, bundle->_url, NULL, true, false); 1792 } 1793 1794 CFURLRef CFBundleCopyAuxiliaryExecutableURL(CFBundleRef bundle, CFStringRef executableName) { 1795 return _CFBundleCopyExecutableURLInDirectory2(bundle, bundle->_url, executableName, true, false); 1796 } 1797 1798 Boolean CFBundleIsExecutableLoaded(CFBundleRef bundle) { 1799 return bundle->_isLoaded; 1800 } 1801 1802 CFBundleExecutableType CFBundleGetExecutableType(CFBundleRef bundle) { 1803 CFBundleExecutableType result = kCFBundleOtherExecutableType; 1804 CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); 1805 1806 if (!executableURL) bundle->_binaryType = __CFBundleNoBinary; 1807 #if defined(BINARY_SUPPORT_DYLD) 1808 if (bundle->_binaryType == __CFBundleUnknownBinary) { 1809 bundle->_binaryType = _CFBundleGrokBinaryType(executableURL); 1810 if (bundle->_binaryType != __CFBundleCFMBinary && bundle->_binaryType != __CFBundleUnreadableBinary) bundle->_resourceData._executableLacksResourceFork = true; 1811 } 1812 #endif /* BINARY_SUPPORT_DYLD */ 1813 if (executableURL) CFRelease(executableURL); 1814 1815 if (bundle->_binaryType == __CFBundleCFMBinary) { 1816 result = kCFBundlePEFExecutableType; 1817 } else if (bundle->_binaryType == __CFBundleDYLDExecutableBinary || bundle->_binaryType == __CFBundleDYLDBundleBinary || bundle->_binaryType == __CFBundleDYLDFrameworkBinary) { 1818 result = kCFBundleMachOExecutableType; 1819 } else if (bundle->_binaryType == __CFBundleDLLBinary) { 1820 result = kCFBundleDLLExecutableType; 1821 } else if (bundle->_binaryType == __CFBundleELFBinary) { 1822 result = kCFBundleELFExecutableType; 1823 } 1824 return result; 1825 } 1826 1827 void _CFBundleSetCFMConnectionID(CFBundleRef bundle, void *connectionID) { 1828 bundle->_connectionCookie = connectionID; 1829 bundle->_isLoaded = true; 1830 } 1831 1832 static CFStringRef _CFBundleCopyLastPathComponent(CFBundleRef bundle) { 1833 CFURLRef bundleURL = CFBundleCopyBundleURL(bundle); 1834 if (!bundleURL) { 1835 return CFSTR("<unknown>"); 1836 } 1837 CFStringRef str = CFURLCopyFileSystemPath(bundleURL, kCFURLPOSIXPathStyle); 1838 UniChar buff[CFMaxPathSize]; 1839 CFIndex buffLen = CFStringGetLength(str), startOfLastDir = 0; 1840 1841 CFRelease(bundleURL); 1842 if (buffLen > CFMaxPathSize) buffLen = CFMaxPathSize; 1843 CFStringGetCharacters(str, CFRangeMake(0, buffLen), buff); 1844 CFRelease(str); 1845 if (buffLen > 0) startOfLastDir = _CFStartOfLastPathComponent(buff, buffLen); 1846 return CFStringCreateWithCharacters(kCFAllocatorSystemDefault, &(buff[startOfLastDir]), buffLen - startOfLastDir); 1847 } 1848 1849 #pragma mark - 1850 1851 CF_PRIVATE CFErrorRef _CFBundleCreateErrorDebug(CFAllocatorRef allocator, CFBundleRef bundle, CFIndex code, CFStringRef debugString) { 1852 const void *userInfoKeys[6], *userInfoValues[6]; 1853 CFIndex numKeys = 0; 1854 CFURLRef bundleURL = CFBundleCopyBundleURL(bundle), absoluteURL = CFURLCopyAbsoluteURL(bundleURL), executableURL = CFBundleCopyExecutableURL(bundle); 1855 CFBundleRef bdl = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CoreFoundation")); 1856 CFStringRef bundlePath = CFURLCopyFileSystemPath(absoluteURL, PLATFORM_PATH_STYLE), executablePath = executableURL ? CFURLCopyFileSystemPath(executableURL, PLATFORM_PATH_STYLE) : NULL, descFormat = NULL, desc = NULL, reason = NULL, suggestion = NULL; 1857 CFErrorRef error; 1858 if (bdl) { 1859 CFStringRef name = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(bundle, kCFBundleNameKey); 1860 name = name ? (CFStringRef)CFRetain(name) : _CFBundleCopyLastPathComponent(bundle); 1861 if (CFBundleExecutableNotFoundError == code) { 1862 descFormat = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr4"), CFSTR("Error"), bdl, CFSTR("The bundle \\U201c%@\\U201d couldn\\U2019t be loaded because its executable couldn\\U2019t be located."), "NSFileNoSuchFileError"); 1863 reason = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr4-C"), CFSTR("Error"), bdl, CFSTR("The bundle\\U2019s executable couldn\\U2019t be located."), "NSFileNoSuchFileError"); 1864 suggestion = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr4-R"), CFSTR("Error"), bdl, CFSTR("Try reinstalling the bundle."), "NSFileNoSuchFileError"); 1865 } else if (CFBundleExecutableNotLoadableError == code) { 1866 descFormat = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3584"), CFSTR("Error"), bdl, CFSTR("The bundle \\U201c%@\\U201d couldn\\U2019t be loaded because its executable isn\\U2019t loadable."), "NSExecutableNotLoadableError"); 1867 reason = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3584-C"), CFSTR("Error"), bdl, CFSTR("The bundle\\U2019s executable isn\\U2019t loadable."), "NSExecutableNotLoadableError"); 1868 suggestion = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3584-R"), CFSTR("Error"), bdl, CFSTR("Try reinstalling the bundle."), "NSExecutableNotLoadableError"); 1869 } else if (CFBundleExecutableArchitectureMismatchError == code) { 1870 descFormat = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3585"), CFSTR("Error"), bdl, CFSTR("The bundle \\U201c%@\\U201d couldn\\U2019t be loaded because it doesn\\U2019t contain a version for the current architecture."), "NSExecutableArchitectureMismatchError"); 1871 reason = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3585-C"), CFSTR("Error"), bdl, CFSTR("The bundle doesn\\U2019t contain a version for the current architecture."), "NSExecutableArchitectureMismatchError"); 1872 suggestion = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3585-R"), CFSTR("Error"), bdl, CFSTR("Try installing a universal version of the bundle."), "NSExecutableArchitectureMismatchError"); 1873 } else if (CFBundleExecutableRuntimeMismatchError == code) { 1874 descFormat = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3586"), CFSTR("Error"), bdl, CFSTR("The bundle \\U201c%@\\U201d couldn\\U2019t be loaded because it isn\\U2019t compatible with the current application."), "NSExecutableRuntimeMismatchError"); 1875 reason = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3586-C"), CFSTR("Error"), bdl, CFSTR("The bundle isn\\U2019t compatible with this application."), "NSExecutableRuntimeMismatchError"); 1876 suggestion = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3586-R"), CFSTR("Error"), bdl, CFSTR("Try installing a newer version of the bundle."), "NSExecutableRuntimeMismatchError"); 1877 } else if (CFBundleExecutableLoadError == code) { 1878 descFormat = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3587"), CFSTR("Error"), bdl, CFSTR("The bundle \\U201c%@\\U201d couldn\\U2019t be loaded because it is damaged or missing necessary resources."), "NSExecutableLoadError"); 1879 reason = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3587-C"), CFSTR("Error"), bdl, CFSTR("The bundle is damaged or missing necessary resources."), "NSExecutableLoadError"); 1880 suggestion = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3587-R"), CFSTR("Error"), bdl, CFSTR("Try reinstalling the bundle."), "NSExecutableLoadError"); 1881 } else if (CFBundleExecutableLinkError == code) { 1882 descFormat = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3588"), CFSTR("Error"), bdl, CFSTR("The bundle \\U201c%@\\U201d couldn\\U2019t be loaded."), "NSExecutableLinkError"); 1883 reason = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3588-C"), CFSTR("Error"), bdl, CFSTR("The bundle couldn\\U2019t be loaded."), "NSExecutableLinkError"); 1884 suggestion = CFCopyLocalizedStringWithDefaultValue(CFSTR("BundleErr3588-R"), CFSTR("Error"), bdl, CFSTR("Try reinstalling the bundle."), "NSExecutableLinkError"); 1885 } 1886 if (descFormat) { 1887 desc = CFStringCreateWithFormat(allocator, NULL, descFormat, name); 1888 CFRelease(descFormat); 1889 } 1890 CFRelease(name); 1891 } 1892 if (bundlePath) { 1893 userInfoKeys[numKeys] = CFSTR("NSBundlePath"); 1894 userInfoValues[numKeys] = bundlePath; 1895 numKeys++; 1896 } 1897 if (executablePath) { 1898 userInfoKeys[numKeys] = CFSTR("NSFilePath"); 1899 userInfoValues[numKeys] = executablePath; 1900 numKeys++; 1901 } 1902 if (desc) { 1903 userInfoKeys[numKeys] = kCFErrorLocalizedDescriptionKey; 1904 userInfoValues[numKeys] = desc; 1905 numKeys++; 1906 } 1907 if (reason) { 1908 userInfoKeys[numKeys] = kCFErrorLocalizedFailureReasonKey; 1909 userInfoValues[numKeys] = reason; 1910 numKeys++; 1911 } 1912 if (suggestion) { 1913 userInfoKeys[numKeys] = kCFErrorLocalizedRecoverySuggestionKey; 1914 userInfoValues[numKeys] = suggestion; 1915 numKeys++; 1916 } 1917 if (debugString) { 1918 userInfoKeys[numKeys] = CFSTR("NSDebugDescription"); 1919 userInfoValues[numKeys] = debugString; 1920 numKeys++; 1921 } 1922 error = CFErrorCreateWithUserInfoKeysAndValues(allocator, kCFErrorDomainCocoa, code, userInfoKeys, userInfoValues, numKeys); 1923 if (bundleURL) CFRelease(bundleURL); 1924 if (absoluteURL) CFRelease(absoluteURL); 1925 if (executableURL) CFRelease(executableURL); 1926 if (bundlePath) CFRelease(bundlePath); 1927 if (executablePath) CFRelease(executablePath); 1928 if (desc) CFRelease(desc); 1929 if (reason) CFRelease(reason); 1930 if (suggestion) CFRelease(suggestion); 1931 return error; 1932 } 1933 1934 CFErrorRef _CFBundleCreateError(CFAllocatorRef allocator, CFBundleRef bundle, CFIndex code) { 1935 return _CFBundleCreateErrorDebug(allocator, bundle, code, NULL); 1936 } 1937 1938 #pragma mark - 1939 1940 Boolean _CFBundleLoadExecutableAndReturnError(CFBundleRef bundle, Boolean forceGlobal, CFErrorRef *error) { 1941 Boolean result = false; 1942 CFErrorRef localError = NULL, *subError = (error ? &localError : NULL); 1943 CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); 1944 1945 1946 pthread_mutex_lock(&(bundle->_bundleLoadingLock)); 1947 if (!executableURL) bundle->_binaryType = __CFBundleNoBinary; 1948 // make sure we know whether bundle is already loaded or not 1949 #if defined(BINARY_SUPPORT_DLFCN) 1950 if (!bundle->_isLoaded) _CFBundleDlfcnCheckLoaded(bundle); 1951 #elif defined(BINARY_SUPPORT_DYLD) 1952 if (!bundle->_isLoaded) _CFBundleDYLDCheckLoaded(bundle); 1953 #endif /* BINARY_SUPPORT_DLFCN */ 1954 #if defined(BINARY_SUPPORT_DYLD) 1955 // We might need to figure out what it is 1956 if (bundle->_binaryType == __CFBundleUnknownBinary) { 1957 bundle->_binaryType = _CFBundleGrokBinaryType(executableURL); 1958 if (bundle->_binaryType != __CFBundleCFMBinary && bundle->_binaryType != __CFBundleUnreadableBinary) bundle->_resourceData._executableLacksResourceFork = true; 1959 } 1960 #endif /* BINARY_SUPPORT_DYLD */ 1961 if (executableURL) CFRelease(executableURL); 1962 1963 if (bundle->_isLoaded) { 1964 pthread_mutex_unlock(&(bundle->_bundleLoadingLock)); 1965 // Remove from the scheduled unload set if we are there. 1966 pthread_mutex_lock(&CFBundleGlobalDataLock); 1967 #if AVOID_WEAK_COLLECTIONS 1968 if (_bundlesToUnload) CFSetRemoveValue(_bundlesToUnload, bundle); 1969 #else /* AVOID_WEAK_COLLECTIONS */ 1970 if (_bundlesToUnload) [_bundlesToUnload removeObject:(id)bundle]; 1971 #endif /* AVOID_WEAK_COLLECTIONS */ 1972 pthread_mutex_unlock(&CFBundleGlobalDataLock); 1973 return true; 1974 } 1975 1976 // Unload bundles scheduled for unloading 1977 if (!_scheduledBundlesAreUnloading) { 1978 pthread_mutex_unlock(&(bundle->_bundleLoadingLock)); 1979 _CFBundleUnloadScheduledBundles(); 1980 pthread_mutex_lock(&(bundle->_bundleLoadingLock)); 1981 } 1982 1983 if (bundle->_isLoaded) { 1984 pthread_mutex_unlock(&(bundle->_bundleLoadingLock)); 1985 // Remove from the scheduled unload set if we are there. 1986 pthread_mutex_lock(&CFBundleGlobalDataLock); 1987 #if AVOID_WEAK_COLLECTIONS 1988 if (_bundlesToUnload) CFSetRemoveValue(_bundlesToUnload, bundle); 1989 #else /* AVOID_WEAK_COLLECTIONS */ 1990 if (_bundlesToUnload) [_bundlesToUnload removeObject:(id)bundle]; 1991 #endif /* AVOID_WEAK_COLLECTIONS */ 1992 pthread_mutex_unlock(&CFBundleGlobalDataLock); 1993 return true; 1994 } 1995 pthread_mutex_unlock(&(bundle->_bundleLoadingLock)); 1996 1997 switch (bundle->_binaryType) { 1998 #if defined(BINARY_SUPPORT_DLFCN) 1999 case __CFBundleUnreadableBinary: 2000 result = _CFBundleDlfcnLoadBundle(bundle, forceGlobal, subError); 2001 break; 2002 #endif /* BINARY_SUPPORT_DLFCN */ 2003 #if defined(BINARY_SUPPORT_DYLD) 2004 case __CFBundleDYLDBundleBinary: 2005 #if defined(BINARY_SUPPORT_DLFCN) 2006 result = _CFBundleDlfcnLoadBundle(bundle, forceGlobal, subError); 2007 #else /* BINARY_SUPPORT_DLFCN */ 2008 result = _CFBundleDYLDLoadBundle(bundle, forceGlobal, subError); 2009 #endif /* BINARY_SUPPORT_DLFCN */ 2010 break; 2011 case __CFBundleDYLDFrameworkBinary: 2012 #if defined(BINARY_SUPPORT_DLFCN) 2013 result = _CFBundleDlfcnLoadFramework(bundle, subError); 2014 #else /* BINARY_SUPPORT_DLFCN */ 2015 result = _CFBundleDYLDLoadFramework(bundle, subError); 2016 #endif /* BINARY_SUPPORT_DLFCN */ 2017 break; 2018 case __CFBundleDYLDExecutableBinary: 2019 if (error) { 2020 localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotLoadableError); 2021 } else { 2022 CFLog(__kCFLogBundle, CFSTR("Attempt to load executable of a type that cannot be dynamically loaded for %@"), bundle); 2023 } 2024 break; 2025 #endif /* BINARY_SUPPORT_DYLD */ 2026 #if defined(BINARY_SUPPORT_DLFCN) 2027 case __CFBundleUnknownBinary: 2028 case __CFBundleELFBinary: 2029 result = _CFBundleDlfcnLoadBundle(bundle, forceGlobal, subError); 2030 break; 2031 #endif /* BINARY_SUPPORT_DLFCN */ 2032 #if defined(BINARY_SUPPORT_DLL) 2033 case __CFBundleDLLBinary: 2034 result = _CFBundleDLLLoad(bundle, subError); 2035 break; 2036 #endif /* BINARY_SUPPORT_DLL */ 2037 case __CFBundleNoBinary: 2038 if (error) { 2039 localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotFoundError); 2040 } else { 2041 CFLog(__kCFLogBundle, CFSTR("Cannot find executable for %@"), bundle); 2042 } 2043 break; 2044 default: 2045 if (error) { 2046 localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotLoadableError); 2047 } else { 2048 CFLog(__kCFLogBundle, CFSTR("Cannot recognize type of executable for %@"), bundle); 2049 } 2050 break; 2051 } 2052 if (result && bundle->_plugInData._isPlugIn) _CFBundlePlugInLoaded(bundle); 2053 if (!result && error) *error = localError; 2054 return result; 2055 } 2056 2057 Boolean CFBundleLoadExecutableAndReturnError(CFBundleRef bundle, CFErrorRef *error) { 2058 return _CFBundleLoadExecutableAndReturnError(bundle, false, error); 2059 } 2060 2061 Boolean CFBundleLoadExecutable(CFBundleRef bundle) { 2062 return _CFBundleLoadExecutableAndReturnError(bundle, false, NULL); 2063 } 2064 2065 Boolean CFBundlePreflightExecutable(CFBundleRef bundle, CFErrorRef *error) { 2066 Boolean result = false; 2067 CFErrorRef localError = NULL; 2068 #if defined(BINARY_SUPPORT_DLFCN) 2069 CFErrorRef *subError = (error ? &localError : NULL); 2070 #endif 2071 CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); 2072 2073 pthread_mutex_lock(&(bundle->_bundleLoadingLock)); 2074 if (!executableURL) bundle->_binaryType = __CFBundleNoBinary; 2075 // make sure we know whether bundle is already loaded or not 2076 #if defined(BINARY_SUPPORT_DLFCN) 2077 if (!bundle->_isLoaded) _CFBundleDlfcnCheckLoaded(bundle); 2078 #elif defined(BINARY_SUPPORT_DYLD) 2079 if (!bundle->_isLoaded) _CFBundleDYLDCheckLoaded(bundle); 2080 #endif /* BINARY_SUPPORT_DLFCN */ 2081 #if defined(BINARY_SUPPORT_DYLD) 2082 // We might need to figure out what it is 2083 if (bundle->_binaryType == __CFBundleUnknownBinary) { 2084 bundle->_binaryType = _CFBundleGrokBinaryType(executableURL); 2085 if (bundle->_binaryType != __CFBundleCFMBinary && bundle->_binaryType != __CFBundleUnreadableBinary) bundle->_resourceData._executableLacksResourceFork = true; 2086 } 2087 #endif /* BINARY_SUPPORT_DYLD */ 2088 if (executableURL) CFRelease(executableURL); 2089 2090 if (bundle->_isLoaded) { 2091 pthread_mutex_unlock(&(bundle->_bundleLoadingLock)); 2092 return true; 2093 } 2094 pthread_mutex_unlock(&(bundle->_bundleLoadingLock)); 2095 2096 switch (bundle->_binaryType) { 2097 #if defined(BINARY_SUPPORT_DLFCN) 2098 case __CFBundleUnreadableBinary: 2099 result = _CFBundleDlfcnPreflight(bundle, subError); 2100 break; 2101 #endif /* BINARY_SUPPORT_DLFCN */ 2102 #if defined(BINARY_SUPPORT_DYLD) 2103 case __CFBundleDYLDBundleBinary: 2104 result = true; 2105 #if defined(BINARY_SUPPORT_DLFCN) 2106 result = _CFBundleDlfcnPreflight(bundle, subError); 2107 #endif /* BINARY_SUPPORT_DLFCN */ 2108 break; 2109 case __CFBundleDYLDFrameworkBinary: 2110 result = true; 2111 #if defined(BINARY_SUPPORT_DLFCN) 2112 result = _CFBundleDlfcnPreflight(bundle, subError); 2113 #endif /* BINARY_SUPPORT_DLFCN */ 2114 break; 2115 case __CFBundleDYLDExecutableBinary: 2116 if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotLoadableError); 2117 break; 2118 #endif /* BINARY_SUPPORT_DYLD */ 2119 #if defined(BINARY_SUPPORT_DLFCN) 2120 case __CFBundleUnknownBinary: 2121 case __CFBundleELFBinary: 2122 result = _CFBundleDlfcnPreflight(bundle, subError); 2123 break; 2124 #endif /* BINARY_SUPPORT_DLFCN */ 2125 #if defined(BINARY_SUPPORT_DLL) 2126 case __CFBundleDLLBinary: 2127 result = true; 2128 break; 2129 #endif /* BINARY_SUPPORT_DLL */ 2130 case __CFBundleNoBinary: 2131 if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotFoundError); 2132 break; 2133 default: 2134 if (error) localError = _CFBundleCreateError(CFGetAllocator(bundle), bundle, CFBundleExecutableNotLoadableError); 2135 break; 2136 } 2137 if (!result && error) *error = localError; 2138 return result; 2139 } 2140 2141 CFArrayRef CFBundleCopyExecutableArchitectures(CFBundleRef bundle) { 2142 CFArrayRef result = NULL; 2143 CFURLRef executableURL = CFBundleCopyExecutableURL(bundle); 2144 if (executableURL) { 2145 result = _CFBundleCopyArchitecturesForExecutable(executableURL); 2146 CFRelease(executableURL); 2147 } 2148 return result; 2149 } 2150 2151 void CFBundleUnloadExecutable(CFBundleRef bundle) { 2152 // First unload bundles scheduled for unloading (if that's not what we are already doing.) 2153 if (!_scheduledBundlesAreUnloading) _CFBundleUnloadScheduledBundles(); 2154 2155 if (!bundle->_isLoaded) return; 2156 2157 // Remove from the scheduled unload set if we are there. 2158 if (!_scheduledBundlesAreUnloading) pthread_mutex_lock(&CFBundleGlobalDataLock); 2159 #if AVOID_WEAK_COLLECTIONS 2160 if (_bundlesToUnload) CFSetRemoveValue(_bundlesToUnload, bundle); 2161 #else /* AVOID_WEAK_COLLECTIONS */ 2162 if (_bundlesToUnload) [_bundlesToUnload removeObject:(id)bundle]; 2163 #endif /* AVOID_WEAK_COLLECTIONS */ 2164 if (!_scheduledBundlesAreUnloading) pthread_mutex_unlock(&CFBundleGlobalDataLock); 2165 2166 // Give the plugIn code a chance to realize this... 2167 _CFPlugInWillUnload(bundle); 2168 2169 pthread_mutex_lock(&(bundle->_bundleLoadingLock)); 2170 if (!bundle->_isLoaded) { 2171 pthread_mutex_unlock(&(bundle->_bundleLoadingLock)); 2172 return; 2173 } 2174 pthread_mutex_unlock(&(bundle->_bundleLoadingLock)); 2175 2176 switch (bundle->_binaryType) { 2177 #if defined(BINARY_SUPPORT_DYLD) 2178 case __CFBundleDYLDBundleBinary: 2179 #if defined(BINARY_SUPPORT_DLFCN) 2180 if (bundle->_handleCookie) _CFBundleDlfcnUnload(bundle); 2181 #else /* BINARY_SUPPORT_DLFCN */ 2182 _CFBundleDYLDUnloadBundle(bundle); 2183 #endif /* BINARY_SUPPORT_DLFCN */ 2184 break; 2185 case __CFBundleDYLDFrameworkBinary: 2186 #if defined(BINARY_SUPPORT_DLFCN) 2187 if (bundle->_handleCookie && _CFExecutableLinkedOnOrAfter(CFSystemVersionLeopard)) _CFBundleDlfcnUnload(bundle); 2188 #endif /* BINARY_SUPPORT_DLFCN */ 2189 break; 2190 #endif /* BINARY_SUPPORT_DYLD */ 2191 #if defined(BINARY_SUPPORT_DLL) 2192 case __CFBundleDLLBinary: 2193 _CFBundleDLLUnload(bundle); 2194 break; 2195 #endif /* BINARY_SUPPORT_DLL */ 2196 default: 2197 #if defined(BINARY_SUPPORT_DLFCN) 2198 if (bundle->_handleCookie) _CFBundleDlfcnUnload(bundle); 2199 #endif /* BINARY_SUPPORT_DLFCN */ 2200 break; 2201 } 2202 if (!bundle->_isLoaded && bundle->_glueDict) { 2203 CFDictionaryApplyFunction(bundle->_glueDict, _CFBundleDeallocateGlue, (void *)CFGetAllocator(bundle)); 2204 CFRelease(bundle->_glueDict); 2205 bundle->_glueDict = NULL; 2206 } 2207 } 2208 2209 #if AVOID_WEAK_COLLECTIONS 2210 2211 CF_PRIVATE void _CFBundleScheduleForUnloading(CFBundleRef bundle) { 2212 pthread_mutex_lock(&CFBundleGlobalDataLock); 2213 if (!_bundlesToUnload) { 2214 CFSetCallBacks nonRetainingCallbacks = kCFTypeSetCallBacks; 2215 nonRetainingCallbacks.retain = NULL; 2216 nonRetainingCallbacks.release = NULL; 2217 _bundlesToUnload = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &nonRetainingCallbacks); 2218 } 2219 CFSetAddValue(_bundlesToUnload, bundle); 2220 pthread_mutex_unlock(&CFBundleGlobalDataLock); 2221 } 2222 2223 CF_PRIVATE void _CFBundleUnscheduleForUnloading(CFBundleRef bundle) { 2224 pthread_mutex_lock(&CFBundleGlobalDataLock); 2225 if (_bundlesToUnload) CFSetRemoveValue(_bundlesToUnload, bundle); 2226 pthread_mutex_unlock(&CFBundleGlobalDataLock); 2227 } 2228 2229 CF_PRIVATE void _CFBundleUnloadScheduledBundles(void) { 2230 pthread_mutex_lock(&CFBundleGlobalDataLock); 2231 if (_bundlesToUnload) { 2232 CFIndex i, c = CFSetGetCount(_bundlesToUnload); 2233 if (c > 0) { 2234 CFBundleRef *unloadThese = (CFBundleRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(CFBundleRef) * c, 0); 2235 CFSetGetValues(_bundlesToUnload, (const void **)unloadThese); 2236 _scheduledBundlesAreUnloading = true; 2237 for (i = 0; i < c; i++) { 2238 // This will cause them to be removed from the set. (Which is why we copied all the values out of the set up front.) 2239 CFBundleUnloadExecutable(unloadThese[i]); 2240 } 2241 _scheduledBundlesAreUnloading = false; 2242 CFAllocatorDeallocate(kCFAllocatorSystemDefault, unloadThese); 2243 } 2244 } 2245 pthread_mutex_unlock(&CFBundleGlobalDataLock); 2246 } 2247 2248 #else /* AVOID_WEAK_COLLECTIONS */ 2249 2250 CF_PRIVATE void _CFBundleScheduleForUnloading(CFBundleRef bundle) { 2251 pthread_mutex_lock(&CFBundleGlobalDataLock); 2252 if (!_bundlesToUnload) _bundlesToUnload = [[__CFHashTable alloc] initWithOptions:CFPointerFunctionsZeroingWeakMemory capacity:0]; 2253 [_bundlesToUnload addObject:(id)bundle]; 2254 pthread_mutex_unlock(&CFBundleGlobalDataLock); 2255 } 2256 2257 CF_PRIVATE void _CFBundleUnscheduleForUnloading(CFBundleRef bundle) { 2258 pthread_mutex_lock(&CFBundleGlobalDataLock); 2259 if (_bundlesToUnload) [_bundlesToUnload removeObject:(id)bundle]; 2260 pthread_mutex_unlock(&CFBundleGlobalDataLock); 2261 } 2262 2263 CF_PRIVATE void _CFBundleUnloadScheduledBundles(void) { 2264 pthread_mutex_lock(&CFBundleGlobalDataLock); 2265 if (_bundlesToUnload && [_bundlesToUnload count] > 0) { 2266 CFIndex i, c; 2267 CFMutableArrayRef unloadThese = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 2268 for (id value in _bundlesToUnload) CFArrayAppendValue(unloadThese, value); 2269 c = CFArrayGetCount(unloadThese); 2270 if (c > 0) { 2271 _scheduledBundlesAreUnloading = true; 2272 for (i = 0; i < c; i++) { 2273 // This will cause them to be removed from the set. (Which is why we copied all the values out of the set up front.) 2274 CFBundleUnloadExecutable((CFBundleRef)CFArrayGetValueAtIndex(unloadThese, i)); 2275 } 2276 _scheduledBundlesAreUnloading = false; 2277 } 2278 CFRelease(unloadThese); 2279 } 2280 pthread_mutex_unlock(&CFBundleGlobalDataLock); 2281 } 2282 2283 #endif /* AVOID_WEAK_COLLECTIONS */ 2284 2285 #pragma mark - 2286 2287 CF_PRIVATE _CFResourceData *__CFBundleGetResourceData(CFBundleRef bundle) { 2288 return &(bundle->_resourceData); 2289 } 2290 2291 CFPlugInRef CFBundleGetPlugIn(CFBundleRef bundle) { 2292 return (bundle->_plugInData._isPlugIn) ? (CFPlugInRef)bundle : NULL; 2293 } 2294 2295 CF_PRIVATE _CFPlugInData *__CFBundleGetPlugInData(CFBundleRef bundle) { 2296 return &(bundle->_plugInData); 2297 } 2298 2299 CF_PRIVATE Boolean _CFBundleCouldBeBundle(CFURLRef url) { 2300 Boolean result = false; 2301 Boolean exists; 2302 SInt32 mode; 2303 if (_CFGetFileProperties(kCFAllocatorSystemDefault, url, &exists, &mode, NULL, NULL, NULL, NULL) == 0) result = (exists && (mode & S_IFMT) == S_IFDIR && (mode & 0444) != 0); 2304 return result; 2305 } 2306 2307 #define LENGTH_OF(A) (sizeof(A) / sizeof(A[0])) 2308 2309 //If 'permissive' is set, we will maintain the historical behavior of returning frameworks with names that don't match, and frameworks for executables in Resources/ 2310 static CFURLRef __CFBundleCopyFrameworkURLForExecutablePath(CFStringRef executablePath, Boolean permissive) { 2311 // MF:!!! Implement me. We need to be able to find the bundle from the exe, dealing with old vs. new as well as the Executables dir business on Windows. 2312 #if DEPLOYMENT_TARGET_WINDOWS 2313 UniChar executablesToFrameworksPathBuff[] = {'.', '.', '\\', 'F', 'r', 'a', 'm', 'e', 'w', 'o', 'r', 'k', 's'}; 2314 UniChar executablesToPrivateFrameworksPathBuff[] = {'.', '.', '\\', 'P', 'r', 'i', 'v', 'a', 't', 'e', 'F', 'r', 'a', 'm', 'e', 'w', 'o', 'r', 'k', 's'}; 2315 UniChar frameworksExtension[] = {'f', 'r', 'a', 'm', 'e', 'w', 'o', 'r', 'k'}; 2316 #endif 2317 UniChar pathBuff[CFMaxPathSize] = {0}; 2318 UniChar nameBuff[CFMaxPathSize] = {0}; 2319 CFIndex length, nameStart, nameLength, savedLength; 2320 CFMutableStringRef cheapStr = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorSystemDefault, NULL, 0, 0, NULL); 2321 CFURLRef bundleURL = NULL; 2322 2323 length = CFStringGetLength(executablePath); 2324 if (length > CFMaxPathSize) length = CFMaxPathSize; 2325 CFStringGetCharacters(executablePath, CFRangeMake(0, length), pathBuff); 2326 2327 // Save the name in nameBuff 2328 length = _CFLengthAfterDeletingPathExtension(pathBuff, length); 2329 nameStart = _CFStartOfLastPathComponent(pathBuff, length); 2330 nameLength = length - nameStart; 2331 memmove(nameBuff, &(pathBuff[nameStart]), nameLength * sizeof(UniChar)); 2332 2333 // Strip the name from pathBuff 2334 length = _CFLengthAfterDeletingLastPathComponent(pathBuff, length); 2335 savedLength = length; 2336 2337 #if DEPLOYMENT_TARGET_WINDOWS 2338 // * (Windows-only) First check the "Executables" directory parallel to the "Frameworks" directory case. 2339 if (_CFAppendPathComponent(pathBuff, &length, CFMaxPathSize, executablesToFrameworksPathBuff, LENGTH_OF(executablesToFrameworksPathBuff)) && _CFAppendPathComponent(pathBuff, &length, CFMaxPathSize, nameBuff, nameLength) && _CFAppendPathExtension(pathBuff, &length, CFMaxPathSize, frameworksExtension, LENGTH_OF(frameworksExtension))) { 2340 CFStringSetExternalCharactersNoCopy(cheapStr, pathBuff, length, CFMaxPathSize); 2341 bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, cheapStr, PLATFORM_PATH_STYLE, true); 2342 if (!_CFBundleCouldBeBundle(bundleURL)) { 2343 CFRelease(bundleURL); 2344 bundleURL = NULL; 2345 } 2346 } 2347 // * (Windows-only) Next check the "Executables" directory parallel to the "PrivateFrameworks" directory case. 2348 if (!bundleURL) { 2349 length = savedLength; 2350 if (_CFAppendPathComponent(pathBuff, &length, CFMaxPathSize, executablesToPrivateFrameworksPathBuff, LENGTH_OF(executablesToPrivateFrameworksPathBuff)) && _CFAppendPathComponent(pathBuff, &length, CFMaxPathSize, nameBuff, nameLength) && _CFAppendPathExtension(pathBuff, &length, CFMaxPathSize, frameworksExtension, LENGTH_OF(frameworksExtension))) { 2351 CFStringSetExternalCharactersNoCopy(cheapStr, pathBuff, length, CFMaxPathSize); 2352 bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, cheapStr, PLATFORM_PATH_STYLE, true); 2353 if (!_CFBundleCouldBeBundle(bundleURL)) { 2354 CFRelease(bundleURL); 2355 bundleURL = NULL; 2356 } 2357 } 2358 } 2359 #endif 2360 // * Finally check the executable inside the framework case. 2361 if (!bundleURL) { 2362 length = savedLength; 2363 // To catch all the cases, we just peel off level looking for one ending in .framework or one called "Supporting Files". 2364 2365 CFStringRef name = permissive ? CFSTR("") : CFStringCreateWithFileSystemRepresentation(kCFAllocatorSystemDefault, (const char *)nameBuff); 2366 2367 while (length > 0) { 2368 CFIndex curStart = _CFStartOfLastPathComponent(pathBuff, length); 2369 if (curStart >= length) break; 2370 CFStringSetExternalCharactersNoCopy(cheapStr, &(pathBuff[curStart]), length - curStart, CFMaxPathSize - curStart); 2371 if (!permissive && CFEqual(cheapStr, _CFBundleResourcesDirectoryName)) break; 2372 if (CFEqual(cheapStr, _CFBundleSupportFilesDirectoryName1) || CFEqual(cheapStr, _CFBundleSupportFilesDirectoryName2)) { 2373 if (!permissive) { 2374 CFIndex fmwkStart = _CFStartOfLastPathComponent(pathBuff, length); 2375 CFStringSetExternalCharactersNoCopy(cheapStr, &(pathBuff[fmwkStart]), length - fmwkStart, CFMaxPathSize - fmwkStart); 2376 } 2377 if (permissive || CFStringHasPrefix(cheapStr, name)) { 2378 length = _CFLengthAfterDeletingLastPathComponent(pathBuff, length); 2379 CFStringSetExternalCharactersNoCopy(cheapStr, pathBuff, length, CFMaxPathSize); 2380 2381 bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, cheapStr, PLATFORM_PATH_STYLE, true); 2382 if (!_CFBundleCouldBeBundle(bundleURL)) { 2383 CFRelease(bundleURL); 2384 bundleURL = NULL; 2385 } 2386 break; 2387 } 2388 } else if (CFStringHasSuffix(cheapStr, CFSTR(".framework")) && (permissive || CFStringHasPrefix(cheapStr, name))) { 2389 CFStringSetExternalCharactersNoCopy(cheapStr, pathBuff, length, CFMaxPathSize); 2390 bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorSystemDefault, cheapStr, PLATFORM_PATH_STYLE, true); 2391 if (!_CFBundleCouldBeBundle(bundleURL)) { 2392 CFRelease(bundleURL); 2393 bundleURL = NULL; 2394 } 2395 break; 2396 } 2397 length = _CFLengthAfterDeletingLastPathComponent(pathBuff, length); 2398 } 2399 if (!permissive) CFRelease(name); 2400 } 2401 CFStringSetExternalCharactersNoCopy(cheapStr, NULL, 0, 0); 2402 CFRelease(cheapStr); 2403 2404 return bundleURL; 2405 } 2406 2407 //SPI version; separated out to minimize linkage changes 2408 CFURLRef _CFBundleCopyFrameworkURLForExecutablePath(CFStringRef executablePath) { 2409 return __CFBundleCopyFrameworkURLForExecutablePath(executablePath, false); 2410 } 2411 2412 static void _CFBundleEnsureBundleExistsForImagePath(CFStringRef imagePath) { 2413 // This finds the bundle for the given path. 2414 // If an image path corresponds to a bundle, we see if there is already a bundle instance. If there is and it is NOT in the _dynamicBundles array, it is added to the staticBundles. Do not add the main bundle to the list here. 2415 CFBundleRef bundle; 2416 CFURLRef curURL = __CFBundleCopyFrameworkURLForExecutablePath(imagePath, true); 2417 Boolean createdBundle = false; 2418 2419 if (curURL) { 2420 bundle = _CFBundleCopyBundleForURL(curURL, true); 2421 if (!bundle) { 2422 // Ensure bundle exists by creating it if necessary 2423 // NB doFinalProcessing must be false here, see below 2424 bundle = _CFBundleCreate(kCFAllocatorSystemDefault, curURL, true, false, false); 2425 createdBundle = true; 2426 } 2427 if (bundle) { 2428 pthread_mutex_lock(&(bundle->_bundleLoadingLock)); 2429 if (!bundle->_isLoaded) { 2430 // make sure that these bundles listed as loaded, and mark them frameworks (we probably can't see anything else here, and we cannot unload them) 2431 #if defined(BINARY_SUPPORT_DLFCN) 2432 if (!bundle->_isLoaded) _CFBundleDlfcnCheckLoaded(bundle); 2433 #elif defined(BINARY_SUPPORT_DYLD) 2434 if (!bundle->_isLoaded) _CFBundleDYLDCheckLoaded(bundle); 2435 #endif /* BINARY_SUPPORT_DLFCN */ 2436 #if defined(BINARY_SUPPORT_DYLD) 2437 if (bundle->_binaryType == __CFBundleUnknownBinary) bundle->_binaryType = __CFBundleDYLDFrameworkBinary; 2438 if (bundle->_binaryType != __CFBundleCFMBinary && bundle->_binaryType != __CFBundleUnreadableBinary) bundle->_resourceData._executableLacksResourceFork = true; 2439 #endif /* BINARY_SUPPORT_DYLD */ 2440 #if LOG_BUNDLE_LOAD 2441 if (!bundle->_isLoaded) printf("ensure bundle %p set loaded fallback, handle %p image %p conn %p\n", bundle, bundle->_handleCookie, bundle->_imageCookie, bundle->_connectionCookie); 2442 #endif /* LOG_BUNDLE_LOAD */ 2443 bundle->_isLoaded = true; 2444 } 2445 pthread_mutex_unlock(&(bundle->_bundleLoadingLock)); 2446 if (createdBundle) { 2447 // Perform delayed final processing steps. 2448 // This must be done after _isLoaded has been set, for security reasons (3624341). 2449 if (_CFBundleNeedsInitPlugIn(bundle)) { 2450 pthread_mutex_unlock(&CFBundleGlobalDataLock); 2451 _CFBundleInitPlugIn(bundle); 2452 pthread_mutex_lock(&CFBundleGlobalDataLock); 2453 } 2454 } else { 2455 // Release the bundle if we did not create it here 2456 CFRelease(bundle); 2457 } 2458 } 2459 CFRelease(curURL); 2460 } 2461 } 2462 2463 static void _CFBundleEnsureBundlesExistForImagePaths(CFArrayRef imagePaths) { 2464 // This finds the bundles for the given paths. 2465 // If an image path corresponds to a bundle, we see if there is already a bundle instance. If there is and it is NOT in the _dynamicBundles array, it is added to the staticBundles. Do not add the main bundle to the list here (even if it appears in imagePaths). 2466 CFIndex i, imagePathCount = CFArrayGetCount(imagePaths); 2467 for (i = 0; i < imagePathCount; i++) _CFBundleEnsureBundleExistsForImagePath((CFStringRef)CFArrayGetValueAtIndex(imagePaths, i)); 2468 } 2469 2470 static void _CFBundleEnsureBundlesUpToDateWithHintAlreadyLocked(CFStringRef hint) { 2471 CFArrayRef imagePaths = NULL; 2472 // Tickle the main bundle into existence 2473 (void)_CFBundleGetMainBundleAlreadyLocked(); 2474 #if defined(BINARY_SUPPORT_DYLD) 2475 imagePaths = _CFBundleDYLDCopyLoadedImagePathsForHint(hint); 2476 #endif /* BINARY_SUPPORT_DYLD */ 2477 if (imagePaths) { 2478 _CFBundleEnsureBundlesExistForImagePaths(imagePaths); 2479 CFRelease(imagePaths); 2480 } 2481 } 2482 2483 static void _CFBundleEnsureAllBundlesUpToDateAlreadyLocked(void) { 2484 // This method returns all the statically linked bundles. This includes the main bundle as well as any frameworks that the process was linked against at launch time. It does not include frameworks or opther bundles that were loaded dynamically. 2485 CFArrayRef imagePaths = NULL; 2486 // Tickle the main bundle into existence 2487 (void)_CFBundleGetMainBundleAlreadyLocked(); 2488 2489 #if defined(BINARY_SUPPORT_DLL) 2490 // Dont know how to find static bundles for DLLs 2491 #endif /* BINARY_SUPPORT_DLL */ 2492 2493 #if defined(BINARY_SUPPORT_DYLD) 2494 imagePaths = _CFBundleDYLDCopyLoadedImagePathsIfChanged(); 2495 #endif /* BINARY_SUPPORT_DYLD */ 2496 if (imagePaths) { 2497 _CFBundleEnsureBundlesExistForImagePaths(imagePaths); 2498 CFRelease(imagePaths); 2499 } 2500 } 2501 2502 CFArrayRef CFBundleGetAllBundles(void) { 2503 // To answer this properly, we have to have created the static bundles! 2504 #if !AVOID_WEAK_COLLECTIONS 2505 static CFMutableArrayRef externalAllBundles = NULL; 2506 #endif /* AVOID_WEAK_COLLECTIONS */ 2507 CFArrayRef bundles; 2508 pthread_mutex_lock(&CFBundleGlobalDataLock); 2509 _CFBundleEnsureAllBundlesUpToDateAlreadyLocked(); 2510 #if AVOID_WEAK_COLLECTIONS 2511 bundles = _allBundles; 2512 #else /* AVOID_WEAK_COLLECTIONS */ 2513 if (!externalAllBundles) { 2514 CFArrayCallBacks nonRetainingArrayCallbacks = kCFTypeArrayCallBacks; 2515 nonRetainingArrayCallbacks.retain = NULL; 2516 nonRetainingArrayCallbacks.release = NULL; 2517 externalAllBundles = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &nonRetainingArrayCallbacks); 2518 } 2519 CFArrayRemoveAllValues(externalAllBundles); 2520 for (id value in _allBundles) CFArrayAppendValue(externalAllBundles, value); 2521 bundles = externalAllBundles; 2522 #endif /* AVOID_WEAK_COLLECTIONS */ 2523 pthread_mutex_unlock(&CFBundleGlobalDataLock); 2524 return bundles; 2525 } 2526 2527 CF_EXPORT CFArrayRef _CFBundleCopyAllBundles(void) { 2528 // To answer this properly, we have to have created the static bundles! 2529 pthread_mutex_lock(&CFBundleGlobalDataLock); 2530 _CFBundleEnsureAllBundlesUpToDateAlreadyLocked(); 2531 #if AVOID_WEAK_COLLECTIONS 2532 CFArrayRef bundles = CFArrayCreateCopy(kCFAllocatorSystemDefault, _allBundles); 2533 #else /* AVOID_WEAK_COLLECTIONS */ 2534 CFMutableArrayRef bundles = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); 2535 for (id value in _allBundles) CFArrayAppendValue(bundles, value); 2536 #endif /* AVOID_WEAK_COLLECTIONS */ 2537 pthread_mutex_unlock(&CFBundleGlobalDataLock); 2538 return bundles; 2539 } 2540 2541 CF_PRIVATE uint8_t _CFBundleLayoutVersion(CFBundleRef bundle) { 2542 return bundle->_version; 2543 } 2544 2545 CF_EXPORT CFURLRef _CFBundleCopyPrivateFrameworksURL(CFBundleRef bundle) { 2546 return CFBundleCopyPrivateFrameworksURL(bundle); 2547 } 2548 2549 CF_EXPORT CFURLRef CFBundleCopyPrivateFrameworksURL(CFBundleRef bundle) { 2550 CFURLRef result = NULL; 2551 2552 if (1 == bundle->_version) { 2553 result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundlePrivateFrameworksURLFromBase1, bundle->_url); 2554 } else if (2 == bundle->_version) { 2555 result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundlePrivateFrameworksURLFromBase2, bundle->_url); 2556 } else { 2557 result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundlePrivateFrameworksURLFromBase0, bundle->_url); 2558 } 2559 return result; 2560 } 2561 2562 CF_EXPORT CFURLRef _CFBundleCopySharedFrameworksURL(CFBundleRef bundle) { 2563 return CFBundleCopySharedFrameworksURL(bundle); 2564 } 2565 2566 CF_EXPORT CFURLRef CFBundleCopySharedFrameworksURL(CFBundleRef bundle) { 2567 CFURLRef result = NULL; 2568 2569 if (1 == bundle->_version) { 2570 result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedFrameworksURLFromBase1, bundle->_url); 2571 } else if (2 == bundle->_version) { 2572 result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedFrameworksURLFromBase2, bundle->_url); 2573 } else { 2574 result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedFrameworksURLFromBase0, bundle->_url); 2575 } 2576 return result; 2577 } 2578 2579 CF_EXPORT CFURLRef _CFBundleCopySharedSupportURL(CFBundleRef bundle) { 2580 return CFBundleCopySharedSupportURL(bundle); 2581 } 2582 2583 CF_EXPORT CFURLRef CFBundleCopySharedSupportURL(CFBundleRef bundle) { 2584 CFURLRef result = NULL; 2585 2586 if (1 == bundle->_version) { 2587 result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedSupportURLFromBase1, bundle->_url); 2588 } else if (2 == bundle->_version) { 2589 result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedSupportURLFromBase2, bundle->_url); 2590 } else { 2591 result = CFURLCreateWithString(CFGetAllocator(bundle), _CFBundleSharedSupportURLFromBase0, bundle->_url); 2592 } 2593 return result; 2594 } 2595 2596 CF_PRIVATE CFURLRef _CFBundleCopyBuiltInPlugInsURL(CFBundleRef bundle) { 2597 return CFBundleCopyBuiltInPlugInsURL(bundle); 2598 } 2599 2600 CF_EXPORT CFURLRef CFBundleCopyBuiltInPlugInsURL(CFBundleRef bundle) { 2601 CFURLRef result = NULL, alternateResult = NULL; 2602 2603 CFAllocatorRef alloc = CFGetAllocator(bundle); 2604 if (1 == bundle->_version) { 2605 result = CFURLCreateWithString(alloc, _CFBundleBuiltInPlugInsURLFromBase1, bundle->_url); 2606 } else if (2 == bundle->_version) { 2607 result = CFURLCreateWithString(alloc, _CFBundleBuiltInPlugInsURLFromBase2, bundle->_url); 2608 } else { 2609 result = CFURLCreateWithString(alloc, _CFBundleBuiltInPlugInsURLFromBase0, bundle->_url); 2610 } 2611 if (!result || !_urlExists(result)) { 2612 if (1 == bundle->_version) { 2613 alternateResult = CFURLCreateWithString(alloc, _CFBundleAlternateBuiltInPlugInsURLFromBase1, bundle->_url); 2614 } else if (2 == bundle->_version) { 2615 alternateResult = CFURLCreateWithString(alloc, _CFBundleAlternateBuiltInPlugInsURLFromBase2, bundle->_url); 2616 } else { 2617 alternateResult = CFURLCreateWithString(alloc, _CFBundleAlternateBuiltInPlugInsURLFromBase0, bundle->_url); 2618 } 2619 if (alternateResult && _urlExists(alternateResult)) { 2620 if (result) CFRelease(result); 2621 result = alternateResult; 2622 } else { 2623 if (alternateResult) CFRelease(alternateResult); 2624 } 2625 } 2626 return result; 2627 } 2628 2629 CF_EXPORT CFBundleRef _CFBundleCreateUnique(CFAllocatorRef allocator, CFURLRef bundleURL) { 2630 return _CFBundleCreate(allocator, bundleURL, false, true, true); 2631 }