1/*
2 * Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
3 * Copyright (C) 2008 Collabora Ltd. All rights reserved.
4 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "config.h"
29#include "PluginPackage.h"
30
31#include <wtf/RetainPtr.h>
32#include "MIMETypeRegistry.h"
33#include "npruntime_impl.h"
34#include "PluginDatabase.h"
35#include "PluginDebug.h"
36#include "WebCoreNSStringExtras.h"
37#include <wtf/text/CString.h>
38
39#include <CoreFoundation/CoreFoundation.h>
40
41#define PluginNameOrDescriptionStringNumber     126
42#define MIMEDescriptionStringNumber             127
43#define MIMEListStringStringNumber              128
44
45namespace WebCore {
46
47void PluginPackage::determineQuirks(const String& mimeType)
48{
49    if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType)) {
50        // Because a single process cannot create multiple VMs, and we cannot reliably unload a
51        // Java VM, we cannot unload the Java Plugin, or we'll lose reference to our only VM
52        m_quirks.add(PluginQuirkDontUnloadPlugin);
53
54        // Setting the window region to an empty region causes bad scrolling repaint problems
55        // with the Java plug-in.
56        m_quirks.add(PluginQuirkDontClipToZeroRectWhenScrolling);
57    }
58
59    if (mimeType == "application/x-shockwave-flash") {
60        // The flash plugin only requests windowless plugins if we return a mozilla user agent
61        m_quirks.add(PluginQuirkWantsMozillaUserAgent);
62        m_quirks.add(PluginQuirkThrottleInvalidate);
63        m_quirks.add(PluginQuirkThrottleWMUserPlusOneMessages);
64        m_quirks.add(PluginQuirkFlashURLNotifyBug);
65    }
66
67}
68
69typedef void (*BP_CreatePluginMIMETypesPreferencesFuncPtr)(void);
70
71static WTF::RetainPtr<CFDictionaryRef> readPListFile(CFStringRef fileName, bool createFile, CFBundleRef bundle)
72{
73    if (createFile) {
74        BP_CreatePluginMIMETypesPreferencesFuncPtr funcPtr =
75            (BP_CreatePluginMIMETypesPreferencesFuncPtr)CFBundleGetFunctionPointerForName(bundle, CFSTR("BP_CreatePluginMIMETypesPreferences"));
76        if (funcPtr)
77            funcPtr();
78    }
79
80    WTF::RetainPtr<CFDictionaryRef> map;
81    WTF::RetainPtr<CFURLRef> url =
82        CFURLCreateWithFileSystemPath(kCFAllocatorDefault, fileName, kCFURLPOSIXPathStyle, false);
83
84    CFDataRef resource = 0;
85    SInt32 code;
86    if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, url.get(), &resource, 0, 0, &code))
87        return map;
88
89    WTF::RetainPtr<CFPropertyListRef> propertyList =
90            CFPropertyListCreateFromXMLData(kCFAllocatorDefault, resource, kCFPropertyListImmutable, 0);
91
92    CFRelease(resource);
93
94    if (!propertyList)
95        return map;
96
97    if (CFGetTypeID(propertyList.get()) != CFDictionaryGetTypeID())
98        return map;
99
100    map = static_cast<CFDictionaryRef>(static_cast<CFPropertyListRef>(propertyList.get()));
101    return map;
102}
103
104static Vector<String> stringListFromResourceId(SInt16 id)
105{
106    Vector<String> list;
107
108    Handle handle = Get1Resource('STR#', id);
109    if (!handle)
110        return list;
111
112    CFStringEncoding encoding = stringEncodingForResource(handle);
113
114    unsigned char* p = (unsigned char*)*handle;
115    if (!p)
116        return list;
117
118    SInt16 count = *(SInt16*)p;
119    p += sizeof(SInt16);
120
121    for (SInt16 i = 0; i < count; ++i) {
122        unsigned char length = *p;
123        WTF::RetainPtr<CFStringRef> str = CFStringCreateWithPascalString(0, p, encoding);
124        list.append(str.get());
125        p += 1 + length;
126    }
127
128    return list;
129}
130
131bool PluginPackage::fetchInfo()
132{
133    if (!load())
134        return false;
135
136    WTF::RetainPtr<CFDictionaryRef> mimeDict;
137
138    WTF::RetainPtr<CFTypeRef> mimeTypesFileName = CFBundleGetValueForInfoDictionaryKey(m_module, CFSTR("WebPluginMIMETypesFilename"));
139    if (mimeTypesFileName && CFGetTypeID(mimeTypesFileName.get()) == CFStringGetTypeID()) {
140
141        WTF::RetainPtr<CFStringRef> fileName = (CFStringRef)mimeTypesFileName.get();
142        WTF::RetainPtr<CFStringRef> homeDir = homeDirectoryPath().createCFString();
143        WTF::RetainPtr<CFStringRef> path = CFStringCreateWithFormat(0, 0, CFSTR("%@/Library/Preferences/%@"), homeDir.get(), fileName.get());
144
145        WTF::RetainPtr<CFDictionaryRef> plist = readPListFile(path.get(), /*createFile*/ false, m_module);
146        if (plist) {
147            // If the plist isn't localized, have the plug-in recreate it in the preferred language.
148            WTF::RetainPtr<CFStringRef> localizationName =
149                (CFStringRef)CFDictionaryGetValue(plist.get(), CFSTR("WebPluginLocalizationName"));
150            CFLocaleRef locale = CFLocaleCopyCurrent();
151            if (localizationName != CFLocaleGetIdentifier(locale))
152                plist = readPListFile(path.get(), /*createFile*/ true, m_module);
153
154            CFRelease(locale);
155        } else {
156            // Plist doesn't exist, ask the plug-in to create it.
157            plist = readPListFile(path.get(), /*createFile*/ true, m_module);
158        }
159
160        if (plist)
161            mimeDict = (CFDictionaryRef)CFDictionaryGetValue(plist.get(), CFSTR("WebPluginMIMETypes"));
162    }
163
164    if (!mimeDict)
165        mimeDict = (CFDictionaryRef)CFBundleGetValueForInfoDictionaryKey(m_module, CFSTR("WebPluginMIMETypes"));
166
167    if (mimeDict) {
168        CFIndex propCount = CFDictionaryGetCount(mimeDict.get());
169        Vector<const void*, 128> keys(propCount);
170        Vector<const void*, 128> values(propCount);
171        CFDictionaryGetKeysAndValues(mimeDict.get(), keys.data(), values.data());
172        for (int i = 0; i < propCount; ++i) {
173            String mimeType = (CFStringRef)keys[i];
174            mimeType = mimeType.lower();
175
176            WTF::RetainPtr<CFDictionaryRef> extensionsDict = (CFDictionaryRef)values[i];
177
178            WTF::RetainPtr<CFNumberRef> enabled = (CFNumberRef)CFDictionaryGetValue(extensionsDict.get(), CFSTR("WebPluginTypeEnabled"));
179            if (enabled) {
180                int enabledValue = 0;
181                if (CFNumberGetValue(enabled.get(), kCFNumberIntType, &enabledValue) && enabledValue == 0)
182                    continue;
183            }
184
185            Vector<String> mimeExtensions;
186            WTF::RetainPtr<CFArrayRef> extensions = (CFArrayRef)CFDictionaryGetValue(extensionsDict.get(), CFSTR("WebPluginExtensions"));
187            if (extensions) {
188                CFIndex extensionCount = CFArrayGetCount(extensions.get());
189                for (CFIndex i = 0; i < extensionCount; ++i) {
190                    String extension =(CFStringRef)CFArrayGetValueAtIndex(extensions.get(), i);
191                    extension = extension.lower();
192                    mimeExtensions.append(extension);
193                }
194            }
195            m_mimeToExtensions.set(mimeType, mimeExtensions);
196
197            String description = (CFStringRef)CFDictionaryGetValue(extensionsDict.get(), CFSTR("WebPluginTypeDescription"));
198            m_mimeToDescriptions.set(mimeType, description);
199        }
200
201        m_name = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(m_module, CFSTR("WebPluginName"));
202        m_description = (CFStringRef)CFBundleGetValueForInfoDictionaryKey(m_module, CFSTR("WebPluginDescription"));
203
204    } else {
205        int resFile = CFBundleOpenBundleResourceMap(m_module);
206
207        UseResFile(resFile);
208
209        Vector<String> mimes = stringListFromResourceId(MIMEListStringStringNumber);
210
211        if (mimes.size() % 2 != 0)
212            return false;
213
214        Vector<String> descriptions = stringListFromResourceId(MIMEDescriptionStringNumber);
215        if (descriptions.size() != mimes.size() / 2)
216            return false;
217
218        for (size_t i = 0;  i < mimes.size(); i += 2) {
219            String mime = mimes[i].lower();
220            Vector<String> extensions;
221            mimes[i + 1].lower().split(UChar(','), extensions);
222
223            m_mimeToExtensions.set(mime, extensions);
224
225            m_mimeToDescriptions.set(mime, descriptions[i / 2]);
226        }
227
228        Vector<String> names = stringListFromResourceId(PluginNameOrDescriptionStringNumber);
229        if (names.size() == 2) {
230            m_description = names[0];
231            m_name = names[1];
232        }
233
234        CFBundleCloseBundleResourceMap(m_module, resFile);
235    }
236
237    LOG(Plugins, "PluginPackage::fetchInfo(): Found plug-in '%s'", m_name.utf8().data());
238    if (isPluginBlacklisted()) {
239        LOG(Plugins, "\tPlug-in is blacklisted!");
240        return false;
241    }
242
243    return true;
244}
245
246bool PluginPackage::isPluginBlacklisted()
247{
248    return false;
249}
250
251bool PluginPackage::load()
252{
253    if (m_isLoaded) {
254        m_loadCount++;
255        return true;
256    }
257
258    WTF::RetainPtr<CFStringRef> path(AdoptCF, m_path.createCFString());
259    WTF::RetainPtr<CFURLRef> url(AdoptCF, CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path.get(),
260                                                                        kCFURLPOSIXPathStyle, false));
261    m_module = CFBundleCreate(NULL, url.get());
262    if (!m_module || !CFBundleLoadExecutable(m_module)) {
263        LOG(Plugins, "%s not loaded", m_path.utf8().data());
264        return false;
265    }
266
267    m_isLoaded = true;
268
269    NP_GetEntryPointsFuncPtr NP_GetEntryPoints = 0;
270    NP_InitializeFuncPtr NP_Initialize;
271    NPError npErr;
272
273    NP_Initialize = (NP_InitializeFuncPtr)CFBundleGetFunctionPointerForName(m_module, CFSTR("NP_Initialize"));
274    NP_GetEntryPoints = (NP_GetEntryPointsFuncPtr)CFBundleGetFunctionPointerForName(m_module, CFSTR("NP_GetEntryPoints"));
275    m_NPP_Shutdown = (NPP_ShutdownProcPtr)CFBundleGetFunctionPointerForName(m_module, CFSTR("NP_Shutdown"));
276
277    if (!NP_Initialize || !NP_GetEntryPoints || !m_NPP_Shutdown)
278        goto abort;
279
280    memset(&m_pluginFuncs, 0, sizeof(m_pluginFuncs));
281    m_pluginFuncs.size = sizeof(m_pluginFuncs);
282
283    initializeBrowserFuncs();
284
285    npErr = NP_Initialize(&m_browserFuncs);
286    LOG_NPERROR(npErr);
287    if (npErr != NPERR_NO_ERROR)
288        goto abort;
289
290    npErr = NP_GetEntryPoints(&m_pluginFuncs);
291    LOG_NPERROR(npErr);
292    if (npErr != NPERR_NO_ERROR)
293        goto abort;
294
295    m_loadCount++;
296    return true;
297
298abort:
299    unloadWithoutShutdown();
300    return false;
301}
302
303uint16_t PluginPackage::NPVersion() const
304{
305    return NP_VERSION_MINOR;
306}
307} // namespace WebCore
308