1/*
2 * Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
3 * Copyright (C) 2008 Collabora, Ltd.  All rights reserved.
4 * Copyright (C) 2008-2009 Torch Mobile, Inc.  All rights reserved.
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 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 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 "PluginDatabase.h"
30
31#include "Frame.h"
32#include "URL.h"
33#include "PluginPackage.h"
34#include <wtf/WindowsExtras.h>
35
36#if OS(WINCE)
37// WINCE doesn't support Registry Key Access Rights. The parameter should always be 0
38#ifndef KEY_ENUMERATE_SUB_KEYS
39#define KEY_ENUMERATE_SUB_KEYS 0
40#endif
41
42BOOL PathRemoveFileSpec(LPWSTR moduleFileNameStr)
43{
44    if (!*moduleFileNameStr)
45        return FALSE;
46
47    LPWSTR lastPos = 0;
48    LPWSTR curPos = moduleFileNameStr;
49    do {
50        if (*curPos == L'/' || *curPos == L'\\')
51            lastPos = curPos;
52    } while (*++curPos);
53
54    if (lastPos == curPos - 1)
55        return FALSE;
56
57    if (lastPos)
58        *lastPos = 0;
59    else {
60        moduleFileNameStr[0] = L'\\';
61        moduleFileNameStr[1] = 0;
62    }
63
64    return TRUE;
65}
66#endif
67
68namespace WebCore {
69
70static inline void addPluginPathsFromRegistry(HKEY rootKey, HashSet<String>& paths)
71{
72    HKEY key;
73    HRESULT result = RegOpenKeyExW(rootKey, L"Software\\MozillaPlugins", 0, KEY_ENUMERATE_SUB_KEYS, &key);
74
75    if (result != ERROR_SUCCESS)
76        return;
77
78    wchar_t name[128];
79    FILETIME lastModified;
80
81    // Enumerate subkeys
82    for (int i = 0;; i++) {
83        DWORD nameLen = WTF_ARRAY_LENGTH(name);
84        result = RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified);
85
86        if (result != ERROR_SUCCESS)
87            break;
88
89        WCHAR pathStr[_MAX_PATH];
90        DWORD pathStrSize = sizeof(pathStr);
91        DWORD type;
92
93        result = getRegistryValue(key, name, L"Path", &type, pathStr, &pathStrSize);
94        if (result != ERROR_SUCCESS || type != REG_SZ)
95            continue;
96
97        paths.add(String(pathStr, pathStrSize / sizeof(WCHAR) - 1));
98    }
99
100    RegCloseKey(key);
101}
102
103void PluginDatabase::getPluginPathsInDirectories(HashSet<String>& paths) const
104{
105    // FIXME: This should be a case insensitive set.
106    HashSet<String> uniqueFilenames;
107
108    HANDLE hFind = INVALID_HANDLE_VALUE;
109    WIN32_FIND_DATAW findFileData;
110
111    String oldWMPPluginPath;
112    String newWMPPluginPath;
113
114    Vector<String>::const_iterator end = m_pluginDirectories.end();
115    for (Vector<String>::const_iterator it = m_pluginDirectories.begin(); it != end; ++it) {
116        String pattern = *it + "\\*";
117
118        hFind = FindFirstFileW(pattern.charactersWithNullTermination().data(), &findFileData);
119
120        if (hFind == INVALID_HANDLE_VALUE)
121            continue;
122
123        do {
124            if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
125                continue;
126
127            String filename = String(findFileData.cFileName, wcslen(findFileData.cFileName));
128            if ((!filename.startsWith("np", false) || !filename.endsWith("dll", false)) &&
129                (!equalIgnoringCase(filename, "Plugin.dll") || !it->endsWith("Shockwave 10", false)))
130                continue;
131
132            String fullPath = *it + "\\" + filename;
133            if (!uniqueFilenames.add(fullPath).isNewEntry)
134                continue;
135
136            paths.add(fullPath);
137
138            if (equalIgnoringCase(filename, "npdsplay.dll"))
139                oldWMPPluginPath = fullPath;
140            else if (equalIgnoringCase(filename, "np-mswmp.dll"))
141                newWMPPluginPath = fullPath;
142
143        } while (FindNextFileW(hFind, &findFileData) != 0);
144
145        FindClose(hFind);
146    }
147
148    addPluginPathsFromRegistry(HKEY_LOCAL_MACHINE, paths);
149    addPluginPathsFromRegistry(HKEY_CURRENT_USER, paths);
150
151    // If both the old and new WMP plugin are present in the plugins set,
152    // we remove the old one so we don't end up choosing the old one.
153    if (!oldWMPPluginPath.isEmpty() && !newWMPPluginPath.isEmpty())
154        paths.remove(oldWMPPluginPath);
155}
156
157static inline Vector<int> parseVersionString(const String& versionString)
158{
159    Vector<int> version;
160
161    unsigned startPos = 0;
162    unsigned endPos;
163
164    while (startPos < versionString.length()) {
165        for (endPos = startPos; endPos < versionString.length(); ++endPos)
166            if (versionString[endPos] == '.' || versionString[endPos] == '_')
167                break;
168
169        int versionComponent = versionString.substring(startPos, endPos - startPos).toInt();
170        version.append(versionComponent);
171
172        startPos = endPos + 1;
173    }
174
175    return version;
176}
177
178// This returns whether versionA is higher than versionB
179static inline bool compareVersions(const Vector<int>& versionA, const Vector<int>& versionB)
180{
181    for (unsigned i = 0; i < versionA.size(); i++) {
182        if (i >= versionB.size())
183            return true;
184
185        if (versionA[i] > versionB[i])
186            return true;
187        else if (versionA[i] < versionB[i])
188            return false;
189    }
190
191    // If we come here, the versions are either the same or versionB has an extra component, just return false
192    return false;
193}
194
195static inline void addMozillaPluginDirectories(Vector<String>& directories)
196{
197    // Enumerate all Mozilla plugin directories in the registry
198    HKEY key;
199    LONG result;
200
201    result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Mozilla"), 0, KEY_READ, &key);
202    if (result == ERROR_SUCCESS) {
203        WCHAR name[128];
204        FILETIME lastModified;
205
206        // Enumerate subkeys
207        for (int i = 0;; i++) {
208            DWORD nameLen = sizeof(name) / sizeof(WCHAR);
209            result = RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified);
210
211            if (result != ERROR_SUCCESS)
212                break;
213
214            String extensionsPath = String(name, nameLen) + "\\Extensions";
215            HKEY extensionsKey;
216
217            // Try opening the key
218            result = RegOpenKeyEx(key, extensionsPath.charactersWithNullTermination().data(), 0, KEY_READ, &extensionsKey);
219
220            if (result == ERROR_SUCCESS) {
221                // Now get the plugins directory
222                WCHAR pluginsDirectoryStr[_MAX_PATH];
223                DWORD pluginsDirectorySize = sizeof(pluginsDirectoryStr);
224                DWORD type;
225
226                result = RegQueryValueEx(extensionsKey, TEXT("Plugins"), 0, &type, (LPBYTE)&pluginsDirectoryStr, &pluginsDirectorySize);
227
228                if (result == ERROR_SUCCESS && type == REG_SZ)
229                    directories.append(String(pluginsDirectoryStr, pluginsDirectorySize / sizeof(WCHAR) - 1));
230
231                RegCloseKey(extensionsKey);
232            }
233        }
234
235        RegCloseKey(key);
236    }
237}
238
239static inline void addWindowsMediaPlayerPluginDirectory(Vector<String>& directories)
240{
241#if !OS(WINCE)
242    // The new WMP Firefox plugin is installed in \PFiles\Plugins if it can't find any Firefox installs
243    WCHAR pluginDirectoryStr[_MAX_PATH + 1];
244    DWORD pluginDirectorySize = ::ExpandEnvironmentStringsW(TEXT("%SYSTEMDRIVE%\\PFiles\\Plugins"), pluginDirectoryStr, WTF_ARRAY_LENGTH(pluginDirectoryStr));
245
246    if (pluginDirectorySize > 0 && pluginDirectorySize <= WTF_ARRAY_LENGTH(pluginDirectoryStr))
247        directories.append(String(pluginDirectoryStr, pluginDirectorySize - 1));
248#endif
249
250    DWORD type;
251    WCHAR installationDirectoryStr[_MAX_PATH];
252    DWORD installationDirectorySize = sizeof(installationDirectoryStr);
253
254    HRESULT result = getRegistryValue(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\MediaPlayer", L"Installation Directory", &type, &installationDirectoryStr, &installationDirectorySize);
255
256    if (result == ERROR_SUCCESS && type == REG_SZ)
257        directories.append(String(installationDirectoryStr, installationDirectorySize / sizeof(WCHAR) - 1));
258}
259
260static inline void addAdobeAcrobatPluginDirectory(Vector<String>& directories)
261{
262    HKEY key;
263    HRESULT result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Adobe\\Acrobat Reader"), 0, KEY_READ, &key);
264    if (result != ERROR_SUCCESS)
265        return;
266
267    WCHAR name[128];
268    FILETIME lastModified;
269
270    Vector<int> latestAcrobatVersion;
271    String latestAcrobatVersionString;
272
273    // Enumerate subkeys
274    for (int i = 0;; i++) {
275        DWORD nameLen = sizeof(name) / sizeof(WCHAR);
276        result = RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified);
277
278        if (result != ERROR_SUCCESS)
279            break;
280
281        Vector<int> acrobatVersion = parseVersionString(String(name, nameLen));
282        if (compareVersions(acrobatVersion, latestAcrobatVersion)) {
283            latestAcrobatVersion = acrobatVersion;
284            latestAcrobatVersionString = String(name, nameLen);
285        }
286    }
287
288    if (!latestAcrobatVersionString.isNull()) {
289        DWORD type;
290        WCHAR acrobatInstallPathStr[_MAX_PATH];
291        DWORD acrobatInstallPathSize = sizeof(acrobatInstallPathStr);
292
293        String acrobatPluginKeyPath = "Software\\Adobe\\Acrobat Reader\\" + latestAcrobatVersionString + "\\InstallPath";
294        result = getRegistryValue(HKEY_LOCAL_MACHINE, acrobatPluginKeyPath.charactersWithNullTermination().data(), 0, &type, acrobatInstallPathStr, &acrobatInstallPathSize);
295
296        if (result == ERROR_SUCCESS) {
297            String acrobatPluginDirectory = String(acrobatInstallPathStr, acrobatInstallPathSize / sizeof(WCHAR) - 1) + "\\browser";
298            directories.append(acrobatPluginDirectory);
299        }
300    }
301
302    RegCloseKey(key);
303}
304
305static inline void addJavaPluginDirectory(Vector<String>& directories)
306{
307    HKEY key;
308    HRESULT result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\JavaSoft\\Java Plug-in"), 0, KEY_READ, &key);
309    if (result != ERROR_SUCCESS)
310        return;
311
312    WCHAR name[128];
313    FILETIME lastModified;
314
315    Vector<int> latestJavaVersion;
316    String latestJavaVersionString;
317
318    // Enumerate subkeys
319    for (int i = 0;; i++) {
320        DWORD nameLen = sizeof(name) / sizeof(WCHAR);
321        result = RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified);
322
323        if (result != ERROR_SUCCESS)
324            break;
325
326        Vector<int> javaVersion = parseVersionString(String(name, nameLen));
327        if (compareVersions(javaVersion, latestJavaVersion)) {
328            latestJavaVersion = javaVersion;
329            latestJavaVersionString = String(name, nameLen);
330        }
331    }
332
333    if (!latestJavaVersionString.isEmpty()) {
334        DWORD type;
335        WCHAR javaInstallPathStr[_MAX_PATH];
336        DWORD javaInstallPathSize = sizeof(javaInstallPathStr);
337        DWORD useNewPluginValue;
338        DWORD useNewPluginSize;
339
340        String javaPluginKeyPath = "Software\\JavaSoft\\Java Plug-in\\" + latestJavaVersionString;
341        result = getRegistryValue(HKEY_LOCAL_MACHINE, javaPluginKeyPath.charactersWithNullTermination().data(), L"UseNewJavaPlugin", &type, &useNewPluginValue, &useNewPluginSize);
342
343        if (result == ERROR_SUCCESS && useNewPluginValue == 1) {
344            result = getRegistryValue(HKEY_LOCAL_MACHINE, javaPluginKeyPath.charactersWithNullTermination().data(), L"JavaHome", &type, javaInstallPathStr, &javaInstallPathSize);
345            if (result == ERROR_SUCCESS) {
346                String javaPluginDirectory = String(javaInstallPathStr, javaInstallPathSize / sizeof(WCHAR) - 1) + "\\bin\\new_plugin";
347                directories.append(javaPluginDirectory);
348            }
349        }
350    }
351
352    RegCloseKey(key);
353}
354
355static inline String safariPluginsDirectory()
356{
357    WCHAR moduleFileNameStr[_MAX_PATH];
358    static String pluginsDirectory;
359    static bool cachedPluginDirectory = false;
360
361    if (!cachedPluginDirectory) {
362        cachedPluginDirectory = true;
363
364        int moduleFileNameLen = GetModuleFileName(0, moduleFileNameStr, _MAX_PATH);
365
366        if (!moduleFileNameLen || moduleFileNameLen == _MAX_PATH)
367            goto exit;
368
369        if (!PathRemoveFileSpec(moduleFileNameStr))
370            goto exit;
371
372        pluginsDirectory = String(moduleFileNameStr) + "\\Plugins";
373    }
374exit:
375    return pluginsDirectory;
376}
377
378static inline void addMacromediaPluginDirectories(Vector<String>& directories)
379{
380#if !OS(WINCE)
381    WCHAR systemDirectoryStr[MAX_PATH];
382
383    if (!GetSystemDirectory(systemDirectoryStr, WTF_ARRAY_LENGTH(systemDirectoryStr)))
384        return;
385
386    WCHAR macromediaDirectoryStr[MAX_PATH];
387
388    PathCombine(macromediaDirectoryStr, systemDirectoryStr, TEXT("macromed\\Flash"));
389    directories.append(macromediaDirectoryStr);
390
391    PathCombine(macromediaDirectoryStr, systemDirectoryStr, TEXT("macromed\\Shockwave 10"));
392    directories.append(macromediaDirectoryStr);
393#endif
394}
395
396Vector<String> PluginDatabase::defaultPluginDirectories()
397{
398    Vector<String> directories;
399    String ourDirectory = safariPluginsDirectory();
400
401    if (!ourDirectory.isNull())
402        directories.append(ourDirectory);
403    addAdobeAcrobatPluginDirectory(directories);
404    addMozillaPluginDirectories(directories);
405    addWindowsMediaPlayerPluginDirectory(directories);
406    addMacromediaPluginDirectories(directories);
407
408    return directories;
409}
410
411bool PluginDatabase::isPreferredPluginDirectory(const String& directory)
412{
413    String ourDirectory = safariPluginsDirectory();
414
415    if (!ourDirectory.isNull() && !directory.isNull())
416        return ourDirectory == directory;
417
418    return false;
419}
420
421}
422