29d4f8f459c45f200c744f2fe21dd2b6a11863c8
[WebKit-https.git] / WebKit / Plugins / WebBasePluginPackage.m
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 <JavaScriptCore/Assertions.h>
32 #import <WebKit/WebKitNSStringExtras.h>
33 #import <WebKit/WebNetscapePluginPackage.h>
34 #import <WebKit/WebNSObjectExtras.h>
35 #import <WebKit/WebPluginPackage.h>
36
37 #import <WebKitSystemInterface.h>
38
39 #import <mach-o/arch.h>
40 #import <mach-o/loader.h>
41
42 #define JavaCocoaPluginIdentifier   @"com.apple.JavaPluginCocoa"
43 #define JavaCarbonPluginIdentifier  @"com.apple.JavaAppletPlugin"
44 #define JavaCFMPluginFilename       @"Java Applet Plugin Enabler"
45
46 #define QuickTimeCarbonPluginIdentifier       @"com.apple.QuickTime Plugin.plugin"
47 #define QuickTimeCocoaPluginIdentifier        @"com.apple.quicktime.webplugin"
48
49 @interface NSArray (WebPluginExtensions)
50 - (NSArray *)_web_lowercaseStrings;
51 @end;
52
53 @implementation WebBasePluginPackage
54
55 + (WebBasePluginPackage *)pluginWithPath:(NSString *)pluginPath
56 {
57     WebBasePluginPackage *pluginPackage = [[WebPluginPackage alloc] initWithPath:pluginPath];
58
59     if (!pluginPackage) {
60         pluginPackage = [[WebNetscapePluginPackage alloc] initWithPath:pluginPath];
61     }
62
63     return [pluginPackage autorelease];
64 }
65
66 + (NSString *)preferredLocalizationName
67 {
68     return WebCFAutorelease(WKCopyCFLocalizationPreferredName(NULL));
69 }
70
71 - (NSString *)pathByResolvingSymlinksAndAliasesInPath:(NSString *)thePath
72 {
73     NSString *newPath = [thePath stringByResolvingSymlinksInPath];
74
75     FSRef fref;
76     OSStatus err;
77
78     err = FSPathMakeRef((const UInt8 *)[thePath fileSystemRepresentation], &fref, NULL);
79     if (err != noErr) {
80         return newPath;
81     }
82
83     Boolean targetIsFolder;
84     Boolean wasAliased;
85     err = FSResolveAliasFileWithMountFlags(&fref, TRUE, &targetIsFolder, &wasAliased, kResolveAliasFileNoUI);
86     if (err != noErr) {
87         return newPath;
88     }
89
90     if (wasAliased) {
91         CFURLRef URL = CFURLCreateFromFSRef(kCFAllocatorDefault, &fref);
92         newPath = [(NSURL *)URL path];
93         CFRelease(URL);
94     }
95
96     return newPath;
97 }
98
99 - initWithPath:(NSString *)pluginPath
100 {
101     [super init];
102     extensionToMIME = [[NSMutableDictionary alloc] init];
103     path = [[self pathByResolvingSymlinksAndAliasesInPath:pluginPath] retain];
104     bundle = [[NSBundle alloc] initWithPath:path];
105     cfBundle = CFBundleCreate(NULL, (CFURLRef)[NSURL fileURLWithPath:path]);
106     lastModifiedDate = [[[[NSFileManager defaultManager] fileAttributesAtPath:path traverseLink:YES] objectForKey:NSFileModificationDate] retain];
107     return self;
108 }
109
110 - (BOOL)getPluginInfoFromBundleAndMIMEDictionary:(NSDictionary *)MIMETypes
111 {
112     if (!bundle) {
113         return NO;
114     }
115     
116     if (!MIMETypes) {
117         MIMETypes = [bundle objectForInfoDictionaryKey:WebPluginMIMETypesKey];
118         if (!MIMETypes) {
119             return NO;
120         }
121     }
122
123     NSMutableDictionary *MIMEToExtensionsDictionary = [NSMutableDictionary dictionary];
124     NSMutableDictionary *MIMEToDescriptionDictionary = [NSMutableDictionary dictionary];
125     NSEnumerator *keyEnumerator = [MIMETypes keyEnumerator];
126     NSDictionary *MIMEDictionary;
127     NSString *MIME, *description;
128     NSArray *extensions;
129
130     while ((MIME = [keyEnumerator nextObject]) != nil) {
131         MIMEDictionary = [MIMETypes objectForKey:MIME];
132         
133         // FIXME: Consider storing disabled MIME types.
134         NSNumber *isEnabled = [MIMEDictionary objectForKey:WebPluginTypeEnabledKey];
135         if (isEnabled && [isEnabled boolValue] == NO) {
136             continue;
137         }
138
139         extensions = [[MIMEDictionary objectForKey:WebPluginExtensionsKey] _web_lowercaseStrings];
140         if ([extensions count] == 0) {
141             extensions = [NSArray arrayWithObject:@""];
142         }
143
144         MIME = [MIME lowercaseString];
145
146         [MIMEToExtensionsDictionary setObject:extensions forKey:MIME];
147
148         description = [MIMEDictionary objectForKey:WebPluginTypeDescriptionKey];
149         if (!description) {
150             description = @"";
151         }
152
153         [MIMEToDescriptionDictionary setObject:description forKey:MIME];
154     }
155
156     [self setMIMEToExtensionsDictionary:MIMEToExtensionsDictionary];
157     [self setMIMEToDescriptionDictionary:MIMEToDescriptionDictionary];
158
159     NSString *filename = [self filename];
160
161     NSString *theName = [bundle objectForInfoDictionaryKey:WebPluginNameKey];
162     if (!theName) {
163         theName = filename;
164     }
165     [self setName:theName];
166
167     description = [bundle objectForInfoDictionaryKey:WebPluginDescriptionKey];
168     if (!description) {
169         description = filename;
170     }
171     [self setPluginDescription:description];
172
173     return YES;
174 }
175
176 - (NSDictionary *)pListForPath:(NSString *)pListPath createFile:(BOOL)createFile
177 {
178     if (createFile && [self load] && BP_CreatePluginMIMETypesPreferences) {
179         BP_CreatePluginMIMETypesPreferences();
180     }
181     
182     NSDictionary *pList = nil;
183     NSData *data = [NSData dataWithContentsOfFile:pListPath];
184     if (data) {
185         pList = [NSPropertyListSerialization propertyListFromData:data
186                                                  mutabilityOption:NSPropertyListImmutable
187                                                            format:nil
188                                                  errorDescription:nil];
189     }
190     
191     return pList;
192 }
193
194 - (BOOL)getPluginInfoFromPLists
195 {
196     if (!bundle) {
197         return NO;
198     }
199     
200     NSDictionary *MIMETypes = nil;
201     NSString *pListFilename = [bundle objectForInfoDictionaryKey:WebPluginMIMETypesFilenameKey];
202     
203     // Check if the MIME types are claimed in a plist in the user's preferences directory.
204     if (pListFilename) {
205         NSString *pListPath = [NSString stringWithFormat:@"%@/Library/Preferences/%@", NSHomeDirectory(), pListFilename];
206         NSDictionary *pList = [self pListForPath:pListPath createFile:NO];
207         if (pList) {
208             // If the plist isn't localized, have the plug-in recreate it in the preferred language.
209             NSString *localizationName = [pList objectForKey:WebPluginLocalizationNameKey];
210             if (![localizationName isEqualToString:[[self class] preferredLocalizationName]]) {
211                 pList = [self pListForPath:pListPath createFile:YES];
212             }
213             MIMETypes = [pList objectForKey:WebPluginMIMETypesKey];
214         } else {
215             // Plist doesn't exist, ask the plug-in to create it.
216             MIMETypes = [[self pListForPath:pListPath createFile:YES] objectForKey:WebPluginMIMETypesKey];
217         }
218     }
219     
220     // Pass the MIME dictionary to the superclass to parse it.
221     return [self getPluginInfoFromBundleAndMIMEDictionary:MIMETypes];
222 }
223
224 - (BOOL)isLoaded
225 {
226     return isLoaded;
227 }
228
229 - (BOOL)load
230 {
231     if (isLoaded && bundle != nil && BP_CreatePluginMIMETypesPreferences == NULL) {
232         BP_CreatePluginMIMETypesPreferences = (BP_CreatePluginMIMETypesPreferencesFuncPtr)CFBundleGetFunctionPointerForName(cfBundle, CFSTR("BP_CreatePluginMIMETypesPreferences"));
233     }
234     return isLoaded;
235 }
236
237 - (void)unload
238 {
239 }
240
241 - (void)dealloc
242 {
243     ASSERT(!isLoaded);
244     
245     [name release];
246     [path release];
247     [pluginDescription release];
248
249     [MIMEToDescription release];
250     [MIMEToExtensions release];
251     [extensionToMIME release];
252
253     [bundle release];
254     if (cfBundle)
255         CFRelease(cfBundle);
256
257     [lastModifiedDate release];
258     
259     [super dealloc];
260 }
261
262 - (void)finalize
263 {
264     ASSERT(!isLoaded);
265
266     if (cfBundle)
267         CFRelease(cfBundle);
268
269     [super finalize];
270 }
271
272 - (NSString *)name
273 {
274     return name;
275 }
276
277 - (NSString *)path
278 {
279     return path;
280 }
281
282 - (NSString *)filename
283 {
284     return [path lastPathComponent];
285 }
286
287 - (NSString *)pluginDescription
288 {
289     return pluginDescription;
290 }
291
292 - (NSEnumerator *)extensionEnumerator
293 {
294     return [extensionToMIME keyEnumerator];
295 }
296
297 - (NSEnumerator *)MIMETypeEnumerator
298 {
299     return [MIMEToExtensions keyEnumerator];
300 }
301
302 - (NSString *)descriptionForMIMEType:(NSString *)MIMEType
303 {
304     return [MIMEToDescription objectForKey:MIMEType];
305 }
306
307 - (NSString *)MIMETypeForExtension:(NSString *)extension
308 {
309     return [extensionToMIME objectForKey:extension];
310 }
311
312 - (NSArray *)extensionsForMIMEType:(NSString *)MIMEType
313 {
314     return [MIMEToExtensions objectForKey:MIMEType];
315 }
316
317 - (NSBundle *)bundle
318 {
319     return bundle;
320 }
321
322 - (NSDate *)lastModifiedDate
323 {
324     return lastModifiedDate;
325 }
326
327 - (void)setName:(NSString *)theName
328 {
329     [name release];
330     name = [theName retain];
331 }
332
333 - (void)setPath:(NSString *)thePath
334 {
335     [path release];
336     path = [thePath retain];
337 }
338
339 - (void)setPluginDescription:(NSString *)description
340 {
341     [pluginDescription release];
342     pluginDescription = [description retain];
343 }
344
345 - (void)setMIMEToDescriptionDictionary:(NSDictionary *)MIMEToDescriptionDictionary
346 {
347     [MIMEToDescription release];
348     MIMEToDescription = [MIMEToDescriptionDictionary retain];
349 }
350
351 - (void)setMIMEToExtensionsDictionary:(NSDictionary *)MIMEToExtensionsDictionary
352 {
353     [MIMEToExtensions release];
354     MIMEToExtensions = [MIMEToExtensionsDictionary retain];
355
356     // Reverse the mapping
357     [extensionToMIME removeAllObjects];
358
359     NSEnumerator *MIMEEnumerator = [MIMEToExtensions keyEnumerator], *extensionEnumerator;
360     NSString *MIME, *extension;
361     NSArray *extensions;
362     
363     while ((MIME = [MIMEEnumerator nextObject]) != nil) {
364         extensions = [MIMEToExtensions objectForKey:MIME];
365         extensionEnumerator = [extensions objectEnumerator];
366
367         while ((extension = [extensionEnumerator nextObject]) != nil) {
368             if (![extension isEqualToString:@""]) {
369                 [extensionToMIME setObject:MIME forKey:extension];
370             }
371         }
372     }
373 }
374
375 - (NSString *)description
376 {
377     return [NSString stringWithFormat:@"name: %@\npath: %@\nmimeTypes:\n%@\npluginDescription:%@",
378         name, path, [MIMEToExtensions description], [MIMEToDescription description], pluginDescription];
379 }
380
381 - (BOOL)isEqual:(id)object
382 {
383     return ([object isKindOfClass:[WebBasePluginPackage class]] &&
384             [[object name] isEqualToString:name] &&
385             [[object lastModifiedDate] isEqual:lastModifiedDate]);
386 }
387
388 - (unsigned)hash
389 {
390     return [[name stringByAppendingString:[lastModifiedDate description]] hash];
391 }
392
393 - (BOOL)isQuickTimePlugIn
394 {
395     NSString *bundleIdentifier = [[self bundle] bundleIdentifier];
396     return [bundleIdentifier _webkit_isCaseInsensitiveEqualToString:QuickTimeCarbonPluginIdentifier] || 
397         [bundleIdentifier _webkit_isCaseInsensitiveEqualToString:QuickTimeCocoaPluginIdentifier];
398 }
399
400 - (BOOL)isJavaPlugIn
401 {
402     NSString *bundleIdentifier = [[self bundle] bundleIdentifier];
403     return [bundleIdentifier _webkit_isCaseInsensitiveEqualToString:JavaCocoaPluginIdentifier] || 
404         [bundleIdentifier _webkit_isCaseInsensitiveEqualToString:JavaCarbonPluginIdentifier] ||
405         [[path lastPathComponent] _webkit_isCaseInsensitiveEqualToString:JavaCFMPluginFilename];
406 }
407
408 - (BOOL)isNativeLibraryData:(NSData *)data
409 {  
410     // If we have a 32-bit thin Mach-O file, see if we have an i386 binary.  If not, don't load it.
411     // This is designed to be the safest possible test for now.  We'll only reject files that we
412     // can easily tell are wrong.
413     if ([data length] >= sizeof(struct mach_header)) {
414         const NXArchInfo *localArch = NXGetLocalArchInfo();
415         if (localArch != NULL) {
416             struct mach_header *header = (struct mach_header *)[data bytes];
417             if (header->magic == MH_MAGIC) {
418                 return (header->cputype == localArch->cputype);
419             }
420             if (header->magic == MH_CIGAM) {
421                 return ((cpu_type_t) OSSwapInt32(header->cputype) == localArch->cputype);
422             }
423         }
424     }
425     return YES;
426 }
427
428 @end
429
430 @implementation NSArray (WebPluginExtensions)
431
432 - (NSArray *)_web_lowercaseStrings
433 {
434     NSMutableArray *lowercaseStrings = [NSMutableArray arrayWithCapacity:[self count]];
435     NSEnumerator *strings = [self objectEnumerator];
436     NSString *string;
437
438     while ((string = [strings nextObject]) != nil) {
439         if ([string isKindOfClass:[NSString class]]) {
440             [lowercaseStrings addObject:[string lowercaseString]];
441         }
442     }
443
444     return lowercaseStrings;
445 }
446
447 @end