1/*
2 * Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
3 * Copyright (C) 2008 Collabora Ltd. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "PluginPackage.h"
29
30#include "MIMETypeRegistry.h"
31#include "PluginDatabase.h"
32#include "PluginDebug.h"
33#include "npruntime_impl.h"
34#include <QFileInfo>
35#include <wtf/text/CString.h>
36
37namespace WebCore {
38
39bool PluginPackage::fetchInfo()
40{
41    if (!m_module) {
42        if (isPluginBlacklisted())
43            return false;
44        m_module = new QLibrary((QString)m_path);
45        m_module->setLoadHints(QLibrary::ResolveAllSymbolsHint);
46        if (!m_module->load()) {
47            LOG(Plugins, "%s not loaded (%s)", m_path.utf8().data(),
48                m_module->errorString().toLatin1().constData());
49            return false;
50        }
51        // This is technically wrong (not matched by a decrement), but
52        // it matches the previous behavior (fetchInfo calling load) and
53        // prevents crashes in flash due to unload+load.
54        m_loadCount++;
55    }
56
57    NPP_GetValueProcPtr gv = (NPP_GetValueProcPtr)m_module->resolve("NP_GetValue");
58    NP_GetMIMEDescriptionFuncPtr gm =
59        (NP_GetMIMEDescriptionFuncPtr)m_module->resolve("NP_GetMIMEDescription");
60    if (!gm || !gv)
61        return false;
62
63    char *buf = 0;
64    NPError err = gv(0, NPPVpluginNameString, (void*) &buf);
65    if (err != NPERR_NO_ERROR)
66        return false;
67
68    m_name = String::fromUTF8(buf);
69    err = gv(0, NPPVpluginDescriptionString, (void*) &buf);
70    if (err != NPERR_NO_ERROR)
71        return false;
72
73    m_description = String::fromUTF8(buf);
74    determineModuleVersionFromDescription();
75
76    setMIMEDescription(String::fromUTF8(gm()));
77
78    return true;
79}
80
81void PluginPackage::setMIMEDescription(const String& mimeDescription)
82{
83    m_fullMIMEDescription = mimeDescription.lower();
84
85    Vector<String> types;
86    mimeDescription.lower().split(UChar(';'), false, types);
87    for (unsigned i = 0; i < types.size(); ++i) {
88        Vector<String> mime;
89        types[i].split(UChar(':'), true, mime);
90        if (mime.size() > 0) {
91            Vector<String> exts;
92            if (mime.size() > 1)
93                mime[1].split(UChar(','), false, exts);
94            determineQuirks(mime[0]);
95            m_mimeToExtensions.add(mime[0], exts);
96            if (mime.size() > 2)
97                m_mimeToDescriptions.add(mime[0], mime[2]);
98        }
99    }
100}
101
102static NPError staticPluginQuirkRequiresGtkToolKit_NPN_GetValue(NPP instance, NPNVariable variable, void* value)
103{
104    if (variable == NPNVToolkit) {
105        *static_cast<uint32_t*>(value) = 2;
106        return NPERR_NO_ERROR;
107    }
108
109    return NPN_GetValue(instance, variable, value);
110}
111
112static void initializeGtk(QLibrary* module = 0)
113{
114    // Ensures missing Gtk initialization in some versions of Adobe's flash player
115    // plugin do not cause crashes. See BR# 40567, 44324, and 44405 for details.
116    if (module) {
117        typedef void *(*gtk_init_ptr)(int*, char***);
118        gtk_init_ptr gtkInit = (gtk_init_ptr)module->resolve("gtk_init");
119        if (gtkInit) {
120            // Prevent gtk_init() from replacing the X error handlers, since the Gtk
121            // handlers abort when they receive an X error, thus killing the viewer.
122#ifdef Q_WS_X11
123            int (*old_error_handler)(Display*, XErrorEvent*) = XSetErrorHandler(0);
124            int (*old_io_error_handler)(Display*) = XSetIOErrorHandler(0);
125#endif
126            gtkInit(0, 0);
127#ifdef Q_WS_X11
128            XSetErrorHandler(old_error_handler);
129            XSetIOErrorHandler(old_io_error_handler);
130#endif
131            return;
132        }
133    }
134
135    QLibrary library(QLatin1String("libgtk-x11-2.0"), 0);
136    if (library.load()) {
137        typedef void *(*gtk_init_check_ptr)(int*, char***);
138        gtk_init_check_ptr gtkInitCheck = (gtk_init_check_ptr)library.resolve("gtk_init_check");
139        // NOTE: We're using gtk_init_check() since gtk_init() calls exit() on failure.
140        if (gtkInitCheck)
141            (void) gtkInitCheck(0, 0);
142    }
143}
144
145bool PluginPackage::isPluginBlacklisted()
146{
147    // TODO: enumerate all plugins that are incompatible with Qt5.
148    const QLatin1String pluginBlacklist[] = {
149        QLatin1String("skypebuttons")
150    };
151
152    QString baseName = QFileInfo(static_cast<QString>(m_path)).baseName();
153    for (unsigned i = 0; i < sizeof(pluginBlacklist) / sizeof(QLatin1String); ++i) {
154        if (baseName == pluginBlacklist[i])
155            return true;
156    }
157    return false;
158}
159
160bool PluginPackage::load()
161{
162    if (m_isLoaded) {
163        m_loadCount++;
164        return true;
165    }
166
167    if (isPluginBlacklisted())
168        return false;
169
170    if (!m_module) {
171        m_module = new QLibrary((QString)m_path);
172        m_module->setLoadHints(QLibrary::ResolveAllSymbolsHint);
173        if (!m_module->load()) {
174            LOG(Plugins, "%s not loaded (%s)", m_path.utf8().data(),
175                m_module->errorString().toLatin1().constData());
176            return false;
177        }
178    }
179
180    m_isLoaded = true;
181
182    NP_InitializeFuncPtr NP_Initialize;
183    NPError npErr;
184
185    NP_Initialize = (NP_InitializeFuncPtr)m_module->resolve("NP_Initialize");
186    m_NPP_Shutdown = (NPP_ShutdownProcPtr)m_module->resolve("NP_Shutdown");
187
188    if (!NP_Initialize || !m_NPP_Shutdown)
189        goto abort;
190
191    memset(&m_pluginFuncs, 0, sizeof(m_pluginFuncs));
192    m_pluginFuncs.size = sizeof(m_pluginFuncs);
193
194    initializeBrowserFuncs();
195
196    if (m_path.contains("npwrapper.") || m_path.contains("gnash")) {
197        // nspluginwrapper relies on the toolkit value to know if glib is available
198        // It does so in NP_Initialize with a null instance, therefore it is done this way:
199        m_browserFuncs.getvalue = staticPluginQuirkRequiresGtkToolKit_NPN_GetValue;
200        // Workaround Adobe's failure to properly initialize Gtk in some versions
201        // of their flash player plugin.
202        initializeGtk();
203    } else if (m_path.contains("flashplayer")) {
204        // Workaround Adobe's failure to properly initialize Gtk in some versions
205        // of their flash player plugin.
206        initializeGtk(m_module);
207    }
208
209#if defined(XP_UNIX)
210    npErr = NP_Initialize(&m_browserFuncs, &m_pluginFuncs);
211#else
212    npErr = NP_Initialize(&m_browserFuncs);
213#endif
214    if (npErr != NPERR_NO_ERROR)
215        goto abort;
216
217    m_loadCount++;
218    return true;
219
220abort:
221    unloadWithoutShutdown();
222    return false;
223}
224
225uint16_t PluginPackage::NPVersion() const
226{
227    return NP_VERSION_MINOR;
228}
229
230}
231