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 <algorithm> 32 #import <WebCore/WebCoreObjCExtras.h> 33 #import <WebKit/WebKitNSStringExtras.h> 34 #import <WebKit/WebNSObjectExtras.h> 35 #import <WebKit/WebNetscapePluginPackage.h> 36 #import <WebKit/WebPluginPackage.h> 37 #import <runtime/InitializeThreading.h> 38 #import <wtf/Assertions.h> 39 #import <wtf/Threading.h> 40 #import <wtf/Vector.h> 41 #import <wtf/text/CString.h> 42 43 #import <WebKitSystemInterface.h> 44 45 #import "WebKitLogging.h" 46 #import "WebTypesInternal.h" 47 48 #import <mach-o/arch.h> 49 #import <mach-o/fat.h> 50 #import <mach-o/loader.h> 51 52 #define JavaCocoaPluginIdentifier "com.apple.JavaPluginCocoa" 53 #define JavaCarbonPluginIdentifier "com.apple.JavaAppletPlugin" 54 #define JavaCFMPluginFilename "Java Applet Plugin Enabler" 55 56 #define QuickTimeCarbonPluginIdentifier "com.apple.QuickTime Plugin.plugin" 57 #define QuickTimeCocoaPluginIdentifier "com.apple.quicktime.webplugin" 58 59 @interface NSArray (WebPluginExtensions) 60 - (NSArray *)_web_lowercaseStrings; 61 @end; 62 63 using namespace std; 64 using namespace WebCore; 65 66 @implementation WebBasePluginPackage 67 68 + (void)initialize 69 { 70 JSC::initializeThreading(); 71 WTF::initializeMainThreadToProcessMainThread(); 72 #ifndef BUILDING_ON_TIGER 73 WebCoreObjCFinalizeOnMainThread(self); 74 #endif 75 } 76 77 + (WebBasePluginPackage *)pluginWithPath:(NSString *)pluginPath 78 { 79 80 WebBasePluginPackage *pluginPackage = [[WebPluginPackage alloc] initWithPath:pluginPath]; 81 82 if (!pluginPackage) { 83 #if ENABLE(NETSCAPE_PLUGIN_API) 84 pluginPackage = [[WebNetscapePluginPackage alloc] initWithPath:pluginPath]; 85 #else 86 return nil; 87 #endif 88 } 89 90 return [pluginPackage autorelease]; 91 } 92 93 + (NSString *)preferredLocalizationName 94 { 95 return WebCFAutorelease(WKCopyCFLocalizationPreferredName(NULL)); 96 } 97 98 static NSString *pathByResolvingSymlinksAndAliases(NSString *thePath) 99 { 100 NSString *newPath = [thePath stringByResolvingSymlinksInPath]; 101 102 FSRef fref; 103 OSStatus err; 104 105 err = FSPathMakeRef((const UInt8 *)[thePath fileSystemRepresentation], &fref, NULL); 106 if (err != noErr) 107 return newPath; 108 109 Boolean targetIsFolder; 110 Boolean wasAliased; 111 err = FSResolveAliasFileWithMountFlags(&fref, TRUE, &targetIsFolder, &wasAliased, kResolveAliasFileNoUI); 112 if (err != noErr) 113 return newPath; 114 115 if (wasAliased) { 116 CFURLRef URL = CFURLCreateFromFSRef(kCFAllocatorDefault, &fref); 117 newPath = [(NSURL *)URL path]; 118 CFRelease(URL); 119 } 120 121 return newPath; 122 } 123 124 - (id)initWithPath:(NSString *)pluginPath 125 { 126 if (!(self = [super init])) 127 return nil; 128 129 path = pathByResolvingSymlinksAndAliases(pluginPath); 130 cfBundle.adoptCF(CFBundleCreate(kCFAllocatorDefault, (CFURLRef)[NSURL fileURLWithPath:path])); 131 132 #ifndef __ppc__ 133 // 32-bit PowerPC is the only platform where non-bundled CFM plugins are supported 134 if (!cfBundle) { 135 [self release]; 136 return nil; 137 } 138 #endif 139 140 return self; 141 } 142 143 - (void)unload 144 { 145 } 146 147 - (void)createPropertyListFile 148 { 149 if ([self load] && BP_CreatePluginMIMETypesPreferences) { 150 BP_CreatePluginMIMETypesPreferences(); 151 [self unload]; 152 } 153 } 154 155 - (NSDictionary *)pListForPath:(NSString *)pListPath createFile:(BOOL)createFile 156 { 157 if (createFile) 158 [self createPropertyListFile]; 159 160 NSDictionary *pList = nil; 161 NSData *data = [NSData dataWithContentsOfFile:pListPath]; 162 if (data) { 163 pList = [NSPropertyListSerialization propertyListFromData:data 164 mutabilityOption:NSPropertyListImmutable 165 format:nil 166 errorDescription:nil]; 167 } 168 169 return pList; 170 } 171 172 - (id)_objectForInfoDictionaryKey:(NSString *)key 173 { 174 CFDictionaryRef bundleInfoDictionary = CFBundleGetInfoDictionary(cfBundle.get()); 175 if (!bundleInfoDictionary) 176 return nil; 177 178 return (id)CFDictionaryGetValue(bundleInfoDictionary, key); 179 } 180 181 - (BOOL)getPluginInfoFromPLists 182 { 183 if (!cfBundle) 184 return NO; 185 186 NSDictionary *MIMETypes = nil; 187 NSString *pListFilename = [self _objectForInfoDictionaryKey:WebPluginMIMETypesFilenameKey]; 188 189 // Check if the MIME types are claimed in a plist in the user's preferences directory. 190 if (pListFilename) { 191 NSString *pListPath = [NSString stringWithFormat:@"%@/Library/Preferences/%@", NSHomeDirectory(), pListFilename]; 192 NSDictionary *pList = [self pListForPath:pListPath createFile:NO]; 193 if (pList) { 194 // If the plist isn't localized, have the plug-in recreate it in the preferred language. 195 NSString *localizationName = [pList objectForKey:WebPluginLocalizationNameKey]; 196 if (![localizationName isEqualToString:[[self class] preferredLocalizationName]]) 197 pList = [self pListForPath:pListPath createFile:YES]; 198 MIMETypes = [pList objectForKey:WebPluginMIMETypesKey]; 199 } else 200 // Plist doesn't exist, ask the plug-in to create it. 201 MIMETypes = [[self pListForPath:pListPath createFile:YES] objectForKey:WebPluginMIMETypesKey]; 202 } 203 204 if (!MIMETypes) { 205 MIMETypes = [self _objectForInfoDictionaryKey:WebPluginMIMETypesKey]; 206 if (!MIMETypes) 207 return NO; 208 } 209 210 NSEnumerator *keyEnumerator = [MIMETypes keyEnumerator]; 211 NSDictionary *MIMEDictionary; 212 NSString *MIME, *description; 213 NSArray *extensions; 214 215 while ((MIME = [keyEnumerator nextObject]) != nil) { 216 MIMEDictionary = [MIMETypes objectForKey:MIME]; 217 218 // FIXME: Consider storing disabled MIME types. 219 NSNumber *isEnabled = [MIMEDictionary objectForKey:WebPluginTypeEnabledKey]; 220 if (isEnabled && [isEnabled boolValue] == NO) 221 continue; 222 223 MimeClassInfo mimeClassInfo; 224 225 extensions = [[MIMEDictionary objectForKey:WebPluginExtensionsKey] _web_lowercaseStrings]; 226 for (NSUInteger i = 0; i < [extensions count]; ++i) { 227 // The DivX plug-in lists multiple extensions in a comma separated string instead of using 228 // multiple array elements in the property list. Work around this here by splitting the 229 // extension string into components. 230 NSArray *extensionComponents = [[extensions objectAtIndex:i] componentsSeparatedByString:@","]; 231 232 for (NSString *extension in extensionComponents) 233 mimeClassInfo.extensions.append(extension); 234 } 235 236 if ([extensions count] == 0) 237 extensions = [NSArray arrayWithObject:@""]; 238 239 mimeClassInfo.type = String(MIME).lower(); 240 241 description = [MIMEDictionary objectForKey:WebPluginTypeDescriptionKey]; 242 mimeClassInfo.desc = description; 243 244 pluginInfo.mimes.append(mimeClassInfo); 245 if (!description) 246 description = @""; 247 } 248 249 NSString *filename = [(NSString *)path lastPathComponent]; 250 pluginInfo.file = filename; 251 252 NSString *theName = [self _objectForInfoDictionaryKey:WebPluginNameKey]; 253 if (!theName) 254 theName = filename; 255 pluginInfo.name = theName; 256 257 description = [self _objectForInfoDictionaryKey:WebPluginDescriptionKey]; 258 if (!description) 259 description = filename; 260 pluginInfo.desc = description; 261 262 return YES; 263 } 264 265 - (BOOL)load 266 { 267 if (cfBundle && !BP_CreatePluginMIMETypesPreferences) 268 BP_CreatePluginMIMETypesPreferences = (BP_CreatePluginMIMETypesPreferencesFuncPtr)CFBundleGetFunctionPointerForName(cfBundle.get(), CFSTR("BP_CreatePluginMIMETypesPreferences")); 269 270 return YES; 271 } 272 273 - (void)dealloc 274 { 275 ASSERT(!pluginDatabases || [pluginDatabases count] == 0); 276 [pluginDatabases release]; 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 [super finalize]; 288 } 289 290 - (const String&)path 291 { 292 return path; 293 } 294 295 - (const PluginInfo&)pluginInfo 296 { 297 return pluginInfo; 298 } 299 300 - (BOOL)supportsExtension:(const String&)extension 301 { 302 ASSERT(extension.lower() == extension); 303 304 for (size_t i = 0; i < pluginInfo.mimes.size(); ++i) { 305 const Vector<String>& extensions = pluginInfo.mimes[i].extensions; 306 307 if (find(extensions.begin(), extensions.end(), extension) != extensions.end()) 308 return YES; 309 } 310 311 return NO; 312 } 313 314 - (BOOL)supportsMIMEType:(const WTF::String&)mimeType 315 { 316 ASSERT(mimeType.lower() == mimeType); 317 318 for (size_t i = 0; i < pluginInfo.mimes.size(); ++i) { 319 if (pluginInfo.mimes[i].type == mimeType) 320 return YES; 321 } 322 323 return NO; 324 } 325 326 - (NSString *)MIMETypeForExtension:(const String&)extension 327 { 328 ASSERT(extension.lower() == extension); 329 330 for (size_t i = 0; i < pluginInfo.mimes.size(); ++i) { 331 const MimeClassInfo& mimeClassInfo = pluginInfo.mimes[i]; 332 const Vector<String>& extensions = mimeClassInfo.extensions; 333 334 if (find(extensions.begin(), extensions.end(), extension) != extensions.end()) 335 return mimeClassInfo.type; 336 } 337 338 return nil; 339 } 340 341 - (BOOL)isQuickTimePlugIn 342 { 343 const String& bundleIdentifier = [self bundleIdentifier]; 344 return bundleIdentifier == QuickTimeCocoaPluginIdentifier || bundleIdentifier == QuickTimeCocoaPluginIdentifier; 345 } 346 347 - (BOOL)isJavaPlugIn 348 { 349 const String& bundleIdentifier = [self bundleIdentifier]; 350 return bundleIdentifier == JavaCocoaPluginIdentifier || bundleIdentifier == JavaCarbonPluginIdentifier || 351 equalIgnoringCase(pluginInfo.file, JavaCFMPluginFilename); 352 } 353 354 static inline void swapIntsInHeader(uint32_t* rawData, size_t length) 355 { 356 for (size_t i = 0; i < length; ++i) 357 rawData[i] = OSSwapInt32(rawData[i]); 358 } 359 360 - (BOOL)isNativeLibraryData:(NSData *)data 361 { 362 NSUInteger sizeInBytes = [data length]; 363 Vector<uint32_t, 128> rawData((sizeInBytes + 3) / 4); 364 memcpy(rawData.data(), [data bytes], sizeInBytes); 365 366 unsigned numArchs = 0; 367 struct fat_arch singleArch = { 0, 0, 0, 0, 0 }; 368 struct fat_arch* archs = 0; 369 370 if (sizeInBytes >= sizeof(struct mach_header_64)) { 371 uint32_t magic = *rawData.data(); 372 373 if (magic == MH_MAGIC || magic == MH_CIGAM) { 374 // We have a 32-bit thin binary 375 struct mach_header* header = (struct mach_header*)rawData.data(); 376 377 // Check if we need to swap the bytes 378 if (magic == MH_CIGAM) 379 swapIntsInHeader(rawData.data(), rawData.size()); 380 381 singleArch.cputype = header->cputype; 382 singleArch.cpusubtype = header->cpusubtype; 383 384 archs = &singleArch; 385 numArchs = 1; 386 } else if (magic == MH_MAGIC_64 || magic == MH_CIGAM_64) { 387 // We have a 64-bit thin binary 388 struct mach_header_64* header = (struct mach_header_64*)rawData.data(); 389 390 // Check if we need to swap the bytes 391 if (magic == MH_CIGAM_64) 392 swapIntsInHeader(rawData.data(), rawData.size()); 393 394 singleArch.cputype = header->cputype; 395 singleArch.cpusubtype = header->cpusubtype; 396 397 archs = &singleArch; 398 numArchs = 1; 399 } else if (magic == FAT_MAGIC || magic == FAT_CIGAM) { 400 // We have a fat (universal) binary 401 402 // Check if we need to swap the bytes 403 if (magic == FAT_CIGAM) 404 swapIntsInHeader(rawData.data(), rawData.size()); 405 406 COMPILE_ASSERT(sizeof(struct fat_header) % sizeof(uint32_t) == 0, struct_fat_header_must_be_integral_size_of_uint32_t); 407 archs = reinterpret_cast<struct fat_arch*>(rawData.data() + sizeof(struct fat_header) / sizeof(uint32_t)); 408 numArchs = reinterpret_cast<struct fat_header*>(rawData.data())->nfat_arch; 409 410 unsigned maxArchs = (sizeInBytes - sizeof(struct fat_header)) / sizeof(struct fat_arch); 411 if (numArchs > maxArchs) 412 numArchs = maxArchs; 413 } 414 } 415 416 if (!archs || !numArchs) 417 return NO; 418 419 const NXArchInfo* localArch = NXGetLocalArchInfo(); 420 if (!localArch) 421 return NO; 422 423 cpu_type_t cputype = localArch->cputype; 424 cpu_subtype_t cpusubtype = localArch->cpusubtype; 425 426 #ifdef __x86_64__ 427 // NXGetLocalArchInfo returns CPU_TYPE_X86 even when running in 64-bit. 428 // See <rdar://problem/4996965> for more information. 429 cputype = CPU_TYPE_X86_64; 430 #endif 431 432 return NXFindBestFatArch(cputype, cpusubtype, archs, numArchs) != 0; 433 } 434 435 - (UInt32)versionNumber 436 { 437 // CFBundleGetVersionNumber doesn't work with all possible versioning schemes, but we think for now it's good enough for us. 438 return CFBundleGetVersionNumber(cfBundle.get()); 439 } 440 441 - (void)wasAddedToPluginDatabase:(WebPluginDatabase *)database 442 { 443 if (!pluginDatabases) 444 pluginDatabases = [[NSMutableSet alloc] init]; 445 446 ASSERT(![pluginDatabases containsObject:database]); 447 [pluginDatabases addObject:database]; 448 } 449 450 - (void)wasRemovedFromPluginDatabase:(WebPluginDatabase *)database 451 { 452 ASSERT(pluginDatabases); 453 ASSERT([pluginDatabases containsObject:database]); 454 455 [pluginDatabases removeObject:database]; 456 } 457 458 - (WTF::String)bundleIdentifier 459 { 460 return CFBundleGetIdentifier(cfBundle.get()); 461 } 462 463 @end 464 465 @implementation NSArray (WebPluginExtensions) 466 467 - (NSArray *)_web_lowercaseStrings 468 { 469 NSMutableArray *lowercaseStrings = [NSMutableArray arrayWithCapacity:[self count]]; 470 NSEnumerator *strings = [self objectEnumerator]; 471 NSString *string; 472 473 while ((string = [strings nextObject]) != nil) { 474 if ([string isKindOfClass:[NSString class]]) 475 [lowercaseStrings addObject:[string lowercaseString]]; 476 } 477 478 return lowercaseStrings; 479 } 480 481 @end 482