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 63using namespace std; 64using 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 98static 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 354static 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