1 /* 2 * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #import <WebKit/WebBasePluginPackage.h> 30 31 #import <WebKit/WebKitNSStringExtras.h> 32 #import <WebKit/WebNetscapePluginPackage.h> 33 #import <WebKit/WebNSObjectExtras.h> 34 #import <WebKit/WebPluginPackage.h> 35 #import <WebCore/WebCoreObjCExtras.h> 36 #import <runtime/InitializeThreading.h> 37 #import <wtf/Assertions.h> 38 #import <wtf/Vector.h> 39 40 #import <WebKitSystemInterface.h> 41 42 #import "WebKitLogging.h" 43 #import "WebTypesInternal.h" 44 45 #import <mach-o/arch.h> 46 #import <mach-o/fat.h> 47 #import <mach-o/loader.h> 48 49 50 #define JavaCocoaPluginIdentifier @"com.apple.JavaPluginCocoa" 51 #define JavaCarbonPluginIdentifier @"com.apple.JavaAppletPlugin" 52 #define JavaCFMPluginFilename @"Java Applet Plugin Enabler" 53 54 #define QuickTimeCarbonPluginIdentifier @"com.apple.QuickTime Plugin.plugin" 55 #define QuickTimeCocoaPluginIdentifier @"com.apple.quicktime.webplugin" 56 57 @interface NSArray (WebPluginExtensions) 58 - (NSArray *)_web_lowercaseStrings; 59 @end; 60 61 @implementation WebBasePluginPackage 62 63 + (void)initialize 64 { 65 JSC::initializeThreading(); 66 #ifndef BUILDING_ON_TIGER 67 WebCoreObjCFinalizeOnMainThread(self); 68 #endif 69 } 70 71 + (WebBasePluginPackage *)pluginWithPath:(NSString *)pluginPath 72 { 73 74 WebBasePluginPackage *pluginPackage = [[WebPluginPackage alloc] initWithPath:pluginPath]; 75 76 if (!pluginPackage) { 77 #if ENABLE(NETSCAPE_PLUGIN_API) 78 pluginPackage = [[WebNetscapePluginPackage alloc] initWithPath:pluginPath]; 79 #else 80 return nil; 81 #endif 82 } 83 84 return [pluginPackage autorelease]; 85 } 86 87 + (NSString *)preferredLocalizationName 88 { 89 return WebCFAutorelease(WKCopyCFLocalizationPreferredName(NULL)); 90 } 91 92 - (NSString *)pathByResolvingSymlinksAndAliasesInPath:(NSString *)thePath 93 { 94 NSString *newPath = [thePath stringByResolvingSymlinksInPath]; 95 96 FSRef fref; 97 OSStatus err; 98 99 err = FSPathMakeRef((const UInt8 *)[thePath fileSystemRepresentation], &fref, NULL); 100 if (err != noErr) 101 return newPath; 102 103 Boolean targetIsFolder; 104 Boolean wasAliased; 105 err = FSResolveAliasFileWithMountFlags(&fref, TRUE, &targetIsFolder, &wasAliased, kResolveAliasFileNoUI); 106 if (err != noErr) 107 return newPath; 108 109 if (wasAliased) { 110 CFURLRef URL = CFURLCreateFromFSRef(kCFAllocatorDefault, &fref); 111 newPath = [(NSURL *)URL path]; 112 CFRelease(URL); 113 } 114 115 return newPath; 116 } 117 118 - (id)initWithPath:(NSString *)pluginPath 119 { 120 if (!(self = [super init])) 121 return nil; 122 123 path = [[self pathByResolvingSymlinksAndAliasesInPath:pluginPath] retain]; 124 bundle = [[NSBundle alloc] initWithPath:path]; 125 #ifndef __ppc__ 126 // 32-bit PowerPC is the only platform where non-bundled CFM plugins are supported 127 if (!bundle) { 128 [self release]; 129 return nil; 130 } 131 #endif 132 cfBundle = CFBundleCreate(NULL, (CFURLRef)[NSURL fileURLWithPath:path]); 133 extensionToMIME = [[NSMutableDictionary alloc] init]; 134 135 return self; 136 } 137 138 - (BOOL)getPluginInfoFromBundleAndMIMEDictionary:(NSDictionary *)MIMETypes 139 { 140 if (!bundle) 141 return NO; 142 143 if (!MIMETypes) { 144 MIMETypes = [bundle objectForInfoDictionaryKey:WebPluginMIMETypesKey]; 145 if (!MIMETypes) 146 return NO; 147 } 148 149 NSMutableDictionary *MIMEToExtensionsDictionary = [NSMutableDictionary dictionary]; 150 NSMutableDictionary *MIMEToDescriptionDictionary = [NSMutableDictionary dictionary]; 151 NSEnumerator *keyEnumerator = [MIMETypes keyEnumerator]; 152 NSDictionary *MIMEDictionary; 153 NSString *MIME, *description; 154 NSArray *extensions; 155 156 while ((MIME = [keyEnumerator nextObject]) != nil) { 157 MIMEDictionary = [MIMETypes objectForKey:MIME]; 158 159 // FIXME: Consider storing disabled MIME types. 160 NSNumber *isEnabled = [MIMEDictionary objectForKey:WebPluginTypeEnabledKey]; 161 if (isEnabled && [isEnabled boolValue] == NO) 162 continue; 163 164 extensions = [[MIMEDictionary objectForKey:WebPluginExtensionsKey] _web_lowercaseStrings]; 165 if ([extensions count] == 0) 166 extensions = [NSArray arrayWithObject:@""]; 167 168 MIME = [MIME lowercaseString]; 169 170 [MIMEToExtensionsDictionary setObject:extensions forKey:MIME]; 171 172 description = [MIMEDictionary objectForKey:WebPluginTypeDescriptionKey]; 173 if (!description) 174 description = @""; 175 176 [MIMEToDescriptionDictionary setObject:description forKey:MIME]; 177 } 178 179 [self setMIMEToExtensionsDictionary:MIMEToExtensionsDictionary]; 180 [self setMIMEToDescriptionDictionary:MIMEToDescriptionDictionary]; 181 182 NSString *filename = [self filename]; 183 184 NSString *theName = [bundle objectForInfoDictionaryKey:WebPluginNameKey]; 185 if (!theName) 186 theName = filename; 187 [self setName:theName]; 188 189 description = [bundle objectForInfoDictionaryKey:WebPluginDescriptionKey]; 190 if (!description) 191 description = filename; 192 [self setPluginDescription:description]; 193 194 return YES; 195 } 196 197 - (void)unload 198 { 199 } 200 201 - (void)createPropertyListFile 202 { 203 if ([self load] && BP_CreatePluginMIMETypesPreferences) { 204 BP_CreatePluginMIMETypesPreferences(); 205 [self unload]; 206 } 207 } 208 209 - (NSDictionary *)pListForPath:(NSString *)pListPath createFile:(BOOL)createFile 210 { 211 if (createFile) 212 [self createPropertyListFile]; 213 214 NSDictionary *pList = nil; 215 NSData *data = [NSData dataWithContentsOfFile:pListPath]; 216 if (data) { 217 pList = [NSPropertyListSerialization propertyListFromData:data 218 mutabilityOption:NSPropertyListImmutable 219 format:nil 220 errorDescription:nil]; 221 } 222 223 return pList; 224 } 225 226 - (BOOL)getPluginInfoFromPLists 227 { 228 if (!bundle) 229 return NO; 230 231 NSDictionary *MIMETypes = nil; 232 NSString *pListFilename = [bundle objectForInfoDictionaryKey:WebPluginMIMETypesFilenameKey]; 233 234 // Check if the MIME types are claimed in a plist in the user's preferences directory. 235 if (pListFilename) { 236 NSString *pListPath = [NSString stringWithFormat:@"%@/Library/Preferences/%@", NSHomeDirectory(), pListFilename]; 237 NSDictionary *pList = [self pListForPath:pListPath createFile:NO]; 238 if (pList) { 239 // If the plist isn't localized, have the plug-in recreate it in the preferred language. 240 NSString *localizationName = [pList objectForKey:WebPluginLocalizationNameKey]; 241 if (![localizationName isEqualToString:[[self class] preferredLocalizationName]]) 242 pList = [self pListForPath:pListPath createFile:YES]; 243 MIMETypes = [pList objectForKey:WebPluginMIMETypesKey]; 244 } else 245 // Plist doesn't exist, ask the plug-in to create it. 246 MIMETypes = [[self pListForPath:pListPath createFile:YES] objectForKey:WebPluginMIMETypesKey]; 247 } 248 249 // Pass the MIME dictionary to the superclass to parse it. 250 return [self getPluginInfoFromBundleAndMIMEDictionary:MIMETypes]; 251 } 252 253 - (BOOL)load 254 { 255 if (bundle && !BP_CreatePluginMIMETypesPreferences) 256 BP_CreatePluginMIMETypesPreferences = (BP_CreatePluginMIMETypesPreferencesFuncPtr)CFBundleGetFunctionPointerForName(cfBundle, CFSTR("BP_CreatePluginMIMETypesPreferences")); 257 258 return YES; 259 } 260 261 - (void)dealloc 262 { 263 ASSERT(!pluginDatabases || [pluginDatabases count] == 0); 264 [pluginDatabases release]; 265 266 [name release]; 267 [path release]; 268 [pluginDescription release]; 269 270 [MIMEToDescription release]; 271 [MIMEToExtensions release]; 272 [extensionToMIME release]; 273 274 [bundle release]; 275 if (cfBundle) 276 CFRelease(cfBundle); 277 278 [super dealloc]; 279 } 280 281 - (void)finalize 282 { 283 ASSERT_MAIN_THREAD(); 284 ASSERT(!pluginDatabases || [pluginDatabases count] == 0); 285 [pluginDatabases release]; 286 287 if (cfBundle) 288 CFRelease(cfBundle); 289 290 [super finalize]; 291 } 292 293 - (NSString *)name 294 { 295 return name; 296 } 297 298 - (NSString *)path 299 { 300 return path; 301 } 302 303 - (NSString *)filename 304 { 305 return [path lastPathComponent]; 306 } 307 308 - (NSString *)pluginDescription 309 { 310 return pluginDescription; 311 } 312 313 - (NSEnumerator *)extensionEnumerator 314 { 315 return [extensionToMIME keyEnumerator]; 316 } 317 318 - (NSEnumerator *)MIMETypeEnumerator 319 { 320 return [MIMEToExtensions keyEnumerator]; 321 } 322 323 - (NSString *)descriptionForMIMEType:(NSString *)MIMEType 324 { 325 return [MIMEToDescription objectForKey:MIMEType]; 326 } 327 328 - (NSString *)MIMETypeForExtension:(NSString *)extension 329 { 330 return [extensionToMIME objectForKey:extension]; 331 } 332 333 - (NSArray *)extensionsForMIMEType:(NSString *)MIMEType 334 { 335 return [MIMEToExtensions objectForKey:MIMEType]; 336 } 337 338 - (NSBundle *)bundle 339 { 340 return bundle; 341 } 342 343 - (void)setName:(NSString *)theName 344 { 345 [name release]; 346 name = [theName retain]; 347 } 348 349 - (void)setPath:(NSString *)thePath 350 { 351 [path release]; 352 path = [thePath retain]; 353 } 354 355 - (void)setPluginDescription:(NSString *)description 356 { 357 [pluginDescription release]; 358 pluginDescription = [description retain]; 359 } 360 361 - (void)setMIMEToDescriptionDictionary:(NSDictionary *)MIMEToDescriptionDictionary 362 { 363 [MIMEToDescription release]; 364 MIMEToDescription = [MIMEToDescriptionDictionary retain]; 365 } 366 367 - (void)setMIMEToExtensionsDictionary:(NSDictionary *)MIMEToExtensionsDictionary 368 { 369 [MIMEToExtensions release]; 370 MIMEToExtensions = [MIMEToExtensionsDictionary retain]; 371 372 // Reverse the mapping 373 [extensionToMIME removeAllObjects]; 374 375 NSEnumerator *MIMEEnumerator = [MIMEToExtensions keyEnumerator], *extensionEnumerator; 376 NSString *MIME, *extension; 377 NSArray *extensions; 378 379 while ((MIME = [MIMEEnumerator nextObject]) != nil) { 380 extensions = [MIMEToExtensions objectForKey:MIME]; 381 extensionEnumerator = [extensions objectEnumerator]; 382 383 while ((extension = [extensionEnumerator nextObject]) != nil) { 384 if (![extension isEqualToString:@""]) 385 [extensionToMIME setObject:MIME forKey:extension]; 386 } 387 } 388 } 389 390 - (NSString *)description 391 { 392 return [NSString stringWithFormat:@"name: %@\npath: %@\nmimeTypes:\n%@\npluginDescription:%@", 393 name, path, [MIMEToExtensions description], [MIMEToDescription description], pluginDescription]; 394 } 395 396 - (BOOL)isQuickTimePlugIn 397 { 398 NSString *bundleIdentifier = [[self bundle] bundleIdentifier]; 399 return [bundleIdentifier _webkit_isCaseInsensitiveEqualToString:QuickTimeCarbonPluginIdentifier] || 400 [bundleIdentifier _webkit_isCaseInsensitiveEqualToString:QuickTimeCocoaPluginIdentifier]; 401 } 402 403 - (BOOL)isJavaPlugIn 404 { 405 NSString *bundleIdentifier = [[self bundle] bundleIdentifier]; 406 return [bundleIdentifier _webkit_isCaseInsensitiveEqualToString:JavaCocoaPluginIdentifier] || 407 [bundleIdentifier _webkit_isCaseInsensitiveEqualToString:JavaCarbonPluginIdentifier] || 408 [[path lastPathComponent] _webkit_isCaseInsensitiveEqualToString:JavaCFMPluginFilename]; 409 } 410 411 static inline void swapIntsInHeader(uint8_t* bytes, unsigned length) 412 { 413 for (unsigned i = 0; i < length; i += 4) 414 *(uint32_t*)(bytes + i) = OSSwapInt32(*(uint32_t *)(bytes + i)); 415 } 416 417 - (BOOL)isNativeLibraryData:(NSData *)data 418 { 419 Vector<uint8_t, 512> bytes([data length]); 420 memcpy(bytes.data(), [data bytes], bytes.size()); 421 422 unsigned numArchs = 0; 423 struct fat_arch singleArch = { 0, 0, 0, 0, 0 }; 424 struct fat_arch* archs = 0; 425 426 if (bytes.size() >= sizeof(struct mach_header_64)) { 427 uint32_t magic = *reinterpret_cast<uint32_t*>(bytes.data()); 428 429 if (magic == MH_MAGIC || magic == MH_CIGAM) { 430 // We have a 32-bit thin binary 431 struct mach_header* header = (struct mach_header*)bytes.data(); 432 433 // Check if we need to swap the bytes 434 if (magic == MH_CIGAM) 435 swapIntsInHeader(bytes.data(), bytes.size()); 436 437 singleArch.cputype = header->cputype; 438 singleArch.cpusubtype = header->cpusubtype; 439 440 archs = &singleArch; 441 numArchs = 1; 442 } else if (magic == MH_MAGIC_64 || magic == MH_CIGAM_64) { 443 // We have a 64-bit thin binary 444 struct mach_header_64* header = (struct mach_header_64*)bytes.data(); 445 446 // Check if we need to swap the bytes 447 if (magic == MH_CIGAM_64) 448 swapIntsInHeader(bytes.data(), bytes.size()); 449 450 singleArch.cputype = header->cputype; 451 singleArch.cpusubtype = header->cpusubtype; 452 453 archs = &singleArch; 454 numArchs = 1; 455 } else if (magic == FAT_MAGIC || magic == FAT_CIGAM) { 456 // We have a fat (universal) binary 457 458 // Check if we need to swap the bytes 459 if (magic == FAT_CIGAM) 460 swapIntsInHeader(bytes.data(), bytes.size()); 461 462 archs = (struct fat_arch*)(bytes.data() + sizeof(struct fat_header)); 463 numArchs = ((struct fat_header *)bytes.data())->nfat_arch; 464 465 unsigned maxArchs = (bytes.size() - sizeof(struct fat_header)) / sizeof(struct fat_arch); 466 if (numArchs > maxArchs) 467 numArchs = maxArchs; 468 } 469 } 470 471 if (!archs || !numArchs) 472 return NO; 473 474 const NXArchInfo* localArch = NXGetLocalArchInfo(); 475 if (!localArch) 476 return NO; 477 478 cpu_type_t cputype = localArch->cputype; 479 cpu_subtype_t cpusubtype = localArch->cpusubtype; 480 481 #ifdef __x86_64__ 482 // NXGetLocalArchInfo returns CPU_TYPE_X86 even when running in 64-bit. 483 // See <rdar://problem/4996965> for more information. 484 cputype = CPU_TYPE_X86_64; 485 #endif 486 487 return NXFindBestFatArch(cputype, cpusubtype, archs, numArchs) != 0; 488 } 489 490 - (UInt32)versionNumber 491 { 492 // CFBundleGetVersionNumber doesn't work with all possible versioning schemes, but we think for now it's good enough for us. 493 return CFBundleGetVersionNumber(cfBundle); 494 } 495 496 - (void)wasAddedToPluginDatabase:(WebPluginDatabase *)database 497 { 498 if (!pluginDatabases) 499 pluginDatabases = [[NSMutableSet alloc] init]; 500 501 ASSERT(![pluginDatabases containsObject:database]); 502 [pluginDatabases addObject:database]; 503 } 504 505 - (void)wasRemovedFromPluginDatabase:(WebPluginDatabase *)database 506 { 507 ASSERT(pluginDatabases); 508 ASSERT([pluginDatabases containsObject:database]); 509 510 [pluginDatabases removeObject:database]; 511 } 512 513 @end 514 515 @implementation NSArray (WebPluginExtensions) 516 517 - (NSArray *)_web_lowercaseStrings 518 { 519 NSMutableArray *lowercaseStrings = [NSMutableArray arrayWithCapacity:[self count]]; 520 NSEnumerator *strings = [self objectEnumerator]; 521 NSString *string; 522 523 while ((string = [strings nextObject]) != nil) { 524 if ([string isKindOfClass:[NSString class]]) 525 [lowercaseStrings addObject:[string lowercaseString]]; 526 } 527 528 return lowercaseStrings; 529 } 530 531 @end 532