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