Home | History | Annotate | Download | only in Plugins
      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