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