• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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