Home | History | Annotate | Download | only in win
      1 /*
      2  * Copyright (C) 2010 Apple 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  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
     14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
     17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     23  * THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "PluginInfoStore.h"
     28 
     29 #include "NetscapePluginModule.h"
     30 #include <WebCore/FileSystem.h>
     31 #include <WebCore/PathWalker.h>
     32 #include <shlwapi.h>
     33 
     34 using namespace WebCore;
     35 
     36 namespace WebKit {
     37 
     38 static inline Vector<int> parseVersionString(const String& versionString)
     39 {
     40     Vector<int> version;
     41 
     42     unsigned startPos = 0;
     43     unsigned endPos;
     44 
     45     while (startPos < versionString.length()) {
     46         for (endPos = startPos; endPos < versionString.length(); ++endPos)
     47             if (versionString[endPos] == '.' || versionString[endPos] == '_')
     48                 break;
     49 
     50         int versionComponent = versionString.substring(startPos, endPos - startPos).toInt();
     51         version.append(versionComponent);
     52 
     53         startPos = endPos + 1;
     54     }
     55 
     56     return version;
     57 }
     58 
     59 // This returns whether versionA is higher than versionB
     60 static inline bool compareVersions(const Vector<int>& versionA, const Vector<int>& versionB)
     61 {
     62     for (unsigned i = 0; i < versionA.size(); i++) {
     63         if (i >= versionB.size())
     64             return true;
     65 
     66         if (versionA[i] > versionB[i])
     67             return true;
     68         else if (versionA[i] < versionB[i])
     69             return false;
     70     }
     71 
     72     // If we come here, the versions are either the same or versionB has an extra component, just return false
     73     return false;
     74 }
     75 
     76 static inline String safariPluginsDirectory()
     77 {
     78     static String pluginsDirectory;
     79     static bool cachedPluginDirectory = false;
     80 
     81     if (!cachedPluginDirectory) {
     82         cachedPluginDirectory = true;
     83 
     84         WCHAR moduleFileNameStr[MAX_PATH];
     85         int moduleFileNameLen = ::GetModuleFileNameW(0, moduleFileNameStr, WTF_ARRAY_LENGTH(moduleFileNameStr));
     86 
     87         if (!moduleFileNameLen || moduleFileNameLen == WTF_ARRAY_LENGTH(moduleFileNameStr))
     88             return pluginsDirectory;
     89 
     90         if (!::PathRemoveFileSpecW(moduleFileNameStr))
     91             return pluginsDirectory;
     92 
     93         pluginsDirectory = String(moduleFileNameStr) + "\\Plugins";
     94     }
     95 
     96     return pluginsDirectory;
     97 }
     98 
     99 static inline void addMozillaPluginDirectories(Vector<String>& directories)
    100 {
    101     // Enumerate all Mozilla plugin directories in the registry
    102     HKEY key;
    103     LONG result = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Mozilla", 0, KEY_READ, &key);
    104     if (result != ERROR_SUCCESS)
    105         return;
    106 
    107     WCHAR name[128];
    108     FILETIME lastModified;
    109 
    110     // Enumerate subkeys
    111     for (int i = 0;; i++) {
    112         DWORD nameLen = WTF_ARRAY_LENGTH(name);
    113         result = ::RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified);
    114 
    115         if (result != ERROR_SUCCESS)
    116             break;
    117 
    118         String extensionsPath = String(name, nameLen) + "\\Extensions";
    119         HKEY extensionsKey;
    120 
    121         // Try opening the key
    122         result = ::RegOpenKeyExW(key, extensionsPath.charactersWithNullTermination(), 0, KEY_READ, &extensionsKey);
    123 
    124         if (result == ERROR_SUCCESS) {
    125             // Now get the plugins directory
    126             WCHAR pluginsDirectoryStr[MAX_PATH];
    127             DWORD pluginsDirectorySize = sizeof(pluginsDirectoryStr);
    128             DWORD type;
    129 
    130             result = ::RegQueryValueExW(extensionsKey, L"Plugins", 0, &type, reinterpret_cast<LPBYTE>(&pluginsDirectoryStr), &pluginsDirectorySize);
    131 
    132             if (result == ERROR_SUCCESS && type == REG_SZ)
    133                 directories.append(String(pluginsDirectoryStr, pluginsDirectorySize / sizeof(WCHAR) - 1));
    134 
    135             ::RegCloseKey(extensionsKey);
    136         }
    137     }
    138 
    139     ::RegCloseKey(key);
    140 }
    141 
    142 static inline void addWindowsMediaPlayerPluginDirectory(Vector<String>& directories)
    143 {
    144     // The new WMP Firefox plugin is installed in \PFiles\Plugins if it can't find any Firefox installs
    145     WCHAR pluginDirectoryStr[MAX_PATH + 1];
    146     DWORD pluginDirectorySize = ::ExpandEnvironmentStringsW(L"%SYSTEMDRIVE%\\PFiles\\Plugins", pluginDirectoryStr, WTF_ARRAY_LENGTH(pluginDirectoryStr));
    147 
    148     if (pluginDirectorySize > 0 && pluginDirectorySize <= WTF_ARRAY_LENGTH(pluginDirectoryStr))
    149         directories.append(String(pluginDirectoryStr, pluginDirectorySize - 1));
    150 
    151     DWORD type;
    152     WCHAR installationDirectoryStr[MAX_PATH];
    153     DWORD installationDirectorySize = sizeof(installationDirectoryStr);
    154 
    155     HRESULT result = ::SHGetValueW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\MediaPlayer", L"Installation Directory", &type, reinterpret_cast<LPBYTE>(&installationDirectoryStr), &installationDirectorySize);
    156 
    157     if (result == ERROR_SUCCESS && type == REG_SZ)
    158         directories.append(String(installationDirectoryStr, installationDirectorySize / sizeof(WCHAR) - 1));
    159 }
    160 
    161 static inline void addQuickTimePluginDirectory(Vector<String>& directories)
    162 {
    163     DWORD type;
    164     WCHAR installationDirectoryStr[MAX_PATH];
    165     DWORD installationDirectorySize = sizeof(installationDirectoryStr);
    166 
    167     HRESULT result = ::SHGetValueW(HKEY_LOCAL_MACHINE, L"Software\\Apple Computer, Inc.\\QuickTime", L"InstallDir", &type, reinterpret_cast<LPBYTE>(&installationDirectoryStr), &installationDirectorySize);
    168 
    169     if (result == ERROR_SUCCESS && type == REG_SZ) {
    170         String pluginDir = String(installationDirectoryStr, installationDirectorySize / sizeof(WCHAR) - 1) + "\\plugins";
    171         directories.append(pluginDir);
    172     }
    173 }
    174 
    175 static inline void addAdobeAcrobatPluginDirectory(Vector<String>& directories)
    176 {
    177     HKEY key;
    178     HRESULT result = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Adobe\\Acrobat Reader", 0, KEY_READ, &key);
    179     if (result != ERROR_SUCCESS)
    180         return;
    181 
    182     WCHAR name[128];
    183     FILETIME lastModified;
    184 
    185     Vector<int> latestAcrobatVersion;
    186     String latestAcrobatVersionString;
    187 
    188     // Enumerate subkeys
    189     for (int i = 0;; i++) {
    190         DWORD nameLen = WTF_ARRAY_LENGTH(name);
    191         result = ::RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified);
    192 
    193         if (result != ERROR_SUCCESS)
    194             break;
    195 
    196         Vector<int> acrobatVersion = parseVersionString(String(name, nameLen));
    197         if (compareVersions(acrobatVersion, latestAcrobatVersion)) {
    198             latestAcrobatVersion = acrobatVersion;
    199             latestAcrobatVersionString = String(name, nameLen);
    200         }
    201     }
    202 
    203     if (!latestAcrobatVersionString.isNull()) {
    204         DWORD type;
    205         WCHAR acrobatInstallPathStr[MAX_PATH];
    206         DWORD acrobatInstallPathSize = sizeof(acrobatInstallPathStr);
    207 
    208         String acrobatPluginKeyPath = "Software\\Adobe\\Acrobat Reader\\" + latestAcrobatVersionString + "\\InstallPath";
    209         result = ::SHGetValueW(HKEY_LOCAL_MACHINE, acrobatPluginKeyPath.charactersWithNullTermination(), 0, &type, reinterpret_cast<LPBYTE>(acrobatInstallPathStr), &acrobatInstallPathSize);
    210 
    211         if (result == ERROR_SUCCESS) {
    212             String acrobatPluginDirectory = String(acrobatInstallPathStr, acrobatInstallPathSize / sizeof(WCHAR) - 1) + "\\browser";
    213             directories.append(acrobatPluginDirectory);
    214         }
    215     }
    216 
    217     ::RegCloseKey(key);
    218 }
    219 
    220 static inline void addMacromediaPluginDirectories(Vector<String>& directories)
    221 {
    222 #if !OS(WINCE)
    223     WCHAR systemDirectoryStr[MAX_PATH];
    224 
    225     if (!::GetSystemDirectoryW(systemDirectoryStr, WTF_ARRAY_LENGTH(systemDirectoryStr)))
    226         return;
    227 
    228     WCHAR macromediaDirectoryStr[MAX_PATH];
    229 
    230     if (!::PathCombineW(macromediaDirectoryStr, systemDirectoryStr, L"macromed\\Flash"))
    231         return;
    232 
    233     directories.append(macromediaDirectoryStr);
    234 
    235     if (!::PathCombineW(macromediaDirectoryStr, systemDirectoryStr, L"macromed\\Shockwave 10"))
    236         return;
    237 
    238     directories.append(macromediaDirectoryStr);
    239 #endif
    240 }
    241 
    242 Vector<String> PluginInfoStore::pluginsDirectories()
    243 {
    244     Vector<String> directories;
    245 
    246     String ourDirectory = safariPluginsDirectory();
    247     if (!ourDirectory.isNull())
    248         directories.append(ourDirectory);
    249 
    250     addQuickTimePluginDirectory(directories);
    251     addAdobeAcrobatPluginDirectory(directories);
    252     addMozillaPluginDirectories(directories);
    253     addWindowsMediaPlayerPluginDirectory(directories);
    254     addMacromediaPluginDirectories(directories);
    255 
    256     return directories;
    257 }
    258 
    259 Vector<String> PluginInfoStore::pluginPathsInDirectory(const String& directory)
    260 {
    261     Vector<String> paths;
    262 
    263     PathWalker walker(directory, "*");
    264     if (!walker.isValid())
    265         return paths;
    266 
    267     do {
    268         if (walker.data().dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    269             continue;
    270 
    271         String filename = walker.data().cFileName;
    272         if ((!filename.startsWith("np", false) || !filename.endsWith("dll", false)) && (!equalIgnoringCase(filename, "Plugin.dll") || !directory.endsWith("Shockwave 10", false)))
    273             continue;
    274 
    275         paths.append(directory + "\\" + filename);
    276     } while (walker.step());
    277 
    278     return paths;
    279 }
    280 
    281 static void addPluginPathsFromRegistry(HKEY rootKey, Vector<String>& paths)
    282 {
    283     HKEY key;
    284     if (::RegOpenKeyExW(rootKey, L"Software\\MozillaPlugins", 0, KEY_ENUMERATE_SUB_KEYS, &key) != ERROR_SUCCESS)
    285         return;
    286 
    287     for (size_t i = 0; ; ++i) {
    288         // MSDN says that key names have a maximum length of 255 characters.
    289         wchar_t name[256];
    290         DWORD nameLen = WTF_ARRAY_LENGTH(name);
    291         if (::RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, 0) != ERROR_SUCCESS)
    292             break;
    293 
    294         wchar_t path[MAX_PATH];
    295         DWORD pathSizeInBytes = sizeof(path);
    296         DWORD type;
    297         if (::SHGetValueW(key, name, L"Path", &type, path, &pathSizeInBytes) != ERROR_SUCCESS)
    298             continue;
    299         if (type != REG_SZ)
    300             continue;
    301 
    302         paths.append(path);
    303     }
    304 
    305     ::RegCloseKey(key);
    306 }
    307 
    308 Vector<String> PluginInfoStore::individualPluginPaths()
    309 {
    310     Vector<String> paths;
    311 
    312     addPluginPathsFromRegistry(HKEY_LOCAL_MACHINE, paths);
    313     addPluginPathsFromRegistry(HKEY_CURRENT_USER, paths);
    314 
    315     return paths;
    316 }
    317 
    318 static uint64_t fileVersion(DWORD leastSignificant, DWORD mostSignificant)
    319 {
    320     ULARGE_INTEGER version;
    321     version.LowPart = leastSignificant;
    322     version.HighPart = mostSignificant;
    323     return version.QuadPart;
    324 }
    325 
    326 bool PluginInfoStore::getPluginInfo(const String& pluginPath, Plugin& plugin)
    327 {
    328     return NetscapePluginModule::getPluginInfo(pluginPath, plugin);
    329 }
    330 
    331 static bool isOldWindowsMediaPlayerPlugin(const PluginInfoStore::Plugin& plugin)
    332 {
    333     return equalIgnoringCase(plugin.info.file, "npdsplay.dll");
    334 }
    335 
    336 static bool isNewWindowsMediaPlayerPlugin(const PluginInfoStore::Plugin& plugin)
    337 {
    338     return equalIgnoringCase(plugin.info.file, "np-mswmp.dll");
    339 }
    340 
    341 bool PluginInfoStore::shouldUsePlugin(const Plugin& plugin)
    342 {
    343     if (plugin.info.name == "Citrix ICA Client") {
    344         // The Citrix ICA Client plug-in requires a Mozilla-based browser; see <rdar://6418681>.
    345         return false;
    346     }
    347 
    348     if (plugin.info.name == "Silverlight Plug-In") {
    349         // workaround for <rdar://5557379> Crash in Silverlight when opening microsoft.com.
    350         // the latest 1.0 version of Silverlight does not reproduce this crash, so allow it
    351         // and any newer versions
    352         static const uint64_t minimumRequiredVersion = fileVersion(0x51BE0000, 0x00010000);
    353         return plugin.fileVersion >= minimumRequiredVersion;
    354     }
    355 
    356     if (equalIgnoringCase(plugin.info.file, "npmozax.dll")) {
    357         // Bug 15217: Mozilla ActiveX control complains about missing xpcom_core.dll
    358         return false;
    359     }
    360 
    361     if (equalIgnoringCase(plugin.info.file, "npwpf.dll")) {
    362         // Bug 57119: Microsoft Windows Presentation Foundation (WPF) plug-in complains about missing xpcom.dll
    363         return false;
    364     }
    365 
    366     if (plugin.info.name == "Yahoo Application State Plugin") {
    367         // https://bugs.webkit.org/show_bug.cgi?id=26860
    368         // Bug in Yahoo Application State plug-in earlier than 1.0.0.6 leads to heap corruption.
    369         static const uint64_t minimumRequiredVersion = fileVersion(0x00000006, 0x00010000);
    370         return plugin.fileVersion >= minimumRequiredVersion;
    371     }
    372 
    373     if (isOldWindowsMediaPlayerPlugin(plugin)) {
    374         // Don't load the old Windows Media Player plugin if we've already loaded the new Windows
    375         // Media Player plugin.
    376         for (size_t i = 0; i < m_plugins.size(); ++i) {
    377             if (!isNewWindowsMediaPlayerPlugin(m_plugins[i]))
    378                 continue;
    379             return false;
    380         }
    381         return true;
    382     }
    383 
    384     if (isNewWindowsMediaPlayerPlugin(plugin)) {
    385         // Unload the old Windows Media Player plugin if we've already loaded it.
    386         for (size_t i = 0; i < m_plugins.size(); ++i) {
    387             if (!isOldWindowsMediaPlayerPlugin(m_plugins[i]))
    388                 continue;
    389             m_plugins.remove(i);
    390         }
    391         return true;
    392     }
    393 
    394     // FIXME: We should prefer a newer version of a plugin to an older version, rather than loading
    395     // only the first. <http://webkit.org/b/58469>
    396     String pluginFileName = pathGetFileName(plugin.path);
    397     for (size_t i = 0; i < m_plugins.size(); ++i) {
    398         Plugin& loadedPlugin = m_plugins[i];
    399 
    400         // If a plug-in with the same filename already exists, we don't want to load it.
    401         if (equalIgnoringCase(pluginFileName, pathGetFileName(loadedPlugin.path)))
    402             return false;
    403     }
    404 
    405     return true;
    406 }
    407 
    408 } // namespace WebKit
    409