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 411static 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