1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/plugins/base/nsPluginsDirWin.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,418 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 + nsPluginsDirWin.cpp 1.11 + 1.12 + Windows implementation of the nsPluginsDir/nsPluginsFile classes. 1.13 + 1.14 + by Alex Musil 1.15 + */ 1.16 + 1.17 +#include "mozilla/ArrayUtils.h" // ArrayLength 1.18 +#include "mozilla/DebugOnly.h" 1.19 + 1.20 +#include "nsPluginsDir.h" 1.21 +#include "prlink.h" 1.22 +#include "plstr.h" 1.23 +#include "prmem.h" 1.24 +#include "prprf.h" 1.25 + 1.26 +#include "windows.h" 1.27 +#include "winbase.h" 1.28 + 1.29 +#include "nsString.h" 1.30 +#include "nsIFile.h" 1.31 +#include "nsUnicharUtils.h" 1.32 + 1.33 +#include <shlwapi.h> 1.34 +#define SHOCKWAVE_BASE_FILENAME L"np32dsw" 1.35 +/** 1.36 + * Determines whether or not SetDllDirectory should be called for this plugin. 1.37 + * 1.38 + * @param pluginFilePath The full path of the plugin file 1.39 + * @return true if SetDllDirectory can be called for the plugin 1.40 + */ 1.41 +bool 1.42 +ShouldProtectPluginCurrentDirectory(char16ptr_t pluginFilePath) 1.43 +{ 1.44 + LPCWSTR passedInFilename = PathFindFileName(pluginFilePath); 1.45 + if (!passedInFilename) { 1.46 + return true; 1.47 + } 1.48 + 1.49 + // Somewhere in the middle of 11.6 version of Shockwave, naming of the DLL 1.50 + // after its version number is introduced. 1.51 + if (!wcsicmp(passedInFilename, SHOCKWAVE_BASE_FILENAME L".dll")) { 1.52 + return false; 1.53 + } 1.54 + 1.55 + // Shockwave versions before 1202122 will break if you call SetDllDirectory 1.56 + const uint64_t kFixedShockwaveVersion = 1202122; 1.57 + uint64_t version; 1.58 + int found = swscanf(passedInFilename, SHOCKWAVE_BASE_FILENAME L"_%llu.dll", 1.59 + &version); 1.60 + if (found && version < kFixedShockwaveVersion) { 1.61 + return false; 1.62 + } 1.63 + 1.64 + // We always want to call SetDllDirectory otherwise 1.65 + return true; 1.66 +} 1.67 + 1.68 +using namespace mozilla; 1.69 + 1.70 +/* Local helper functions */ 1.71 + 1.72 +static char* GetKeyValue(void* verbuf, const WCHAR* key, 1.73 + UINT language, UINT codepage) 1.74 +{ 1.75 + WCHAR keybuf[64]; // plenty for the template below, with the longest key 1.76 + // we use (currently "FileDescription") 1.77 + const WCHAR keyFormat[] = L"\\StringFileInfo\\%04X%04X\\%s"; 1.78 + WCHAR *buf = nullptr; 1.79 + UINT blen; 1.80 + 1.81 + if (_snwprintf_s(keybuf, ArrayLength(keybuf), _TRUNCATE, 1.82 + keyFormat, language, codepage, key) < 0) 1.83 + { 1.84 + NS_NOTREACHED("plugin info key too long for buffer!"); 1.85 + return nullptr; 1.86 + } 1.87 + 1.88 + if (::VerQueryValueW(verbuf, keybuf, (void **)&buf, &blen) == 0 || 1.89 + buf == nullptr || blen == 0) 1.90 + { 1.91 + return nullptr; 1.92 + } 1.93 + 1.94 + return PL_strdup(NS_ConvertUTF16toUTF8(buf, blen).get()); 1.95 +} 1.96 + 1.97 +static char* GetVersion(void* verbuf) 1.98 +{ 1.99 + VS_FIXEDFILEINFO *fileInfo; 1.100 + UINT fileInfoLen; 1.101 + 1.102 + ::VerQueryValueW(verbuf, L"\\", (void **)&fileInfo, &fileInfoLen); 1.103 + 1.104 + if (fileInfo) { 1.105 + return PR_smprintf("%ld.%ld.%ld.%ld", 1.106 + HIWORD(fileInfo->dwFileVersionMS), 1.107 + LOWORD(fileInfo->dwFileVersionMS), 1.108 + HIWORD(fileInfo->dwFileVersionLS), 1.109 + LOWORD(fileInfo->dwFileVersionLS)); 1.110 + } 1.111 + 1.112 + return nullptr; 1.113 +} 1.114 + 1.115 +static uint32_t CalculateVariantCount(char* mimeTypes) 1.116 +{ 1.117 + uint32_t variants = 1; 1.118 + 1.119 + if (!mimeTypes) 1.120 + return 0; 1.121 + 1.122 + char* index = mimeTypes; 1.123 + while (*index) { 1.124 + if (*index == '|') 1.125 + variants++; 1.126 + 1.127 + ++index; 1.128 + } 1.129 + return variants; 1.130 +} 1.131 + 1.132 +static char** MakeStringArray(uint32_t variants, char* data) 1.133 +{ 1.134 + // The number of variants has been calculated based on the mime 1.135 + // type array. Plugins are not explicitely required to match 1.136 + // this number in two other arrays: file extention array and mime 1.137 + // description array, and some of them actually don't. 1.138 + // We should handle such situations gracefully 1.139 + 1.140 + if ((variants <= 0) || !data) 1.141 + return nullptr; 1.142 + 1.143 + char ** array = (char **)PR_Calloc(variants, sizeof(char *)); 1.144 + if (!array) 1.145 + return nullptr; 1.146 + 1.147 + char * start = data; 1.148 + 1.149 + for (uint32_t i = 0; i < variants; i++) { 1.150 + char * p = PL_strchr(start, '|'); 1.151 + if (p) 1.152 + *p = 0; 1.153 + 1.154 + array[i] = PL_strdup(start); 1.155 + 1.156 + if (!p) { 1.157 + // nothing more to look for, fill everything left 1.158 + // with empty strings and break 1.159 + while(++i < variants) 1.160 + array[i] = PL_strdup(""); 1.161 + 1.162 + break; 1.163 + } 1.164 + 1.165 + start = ++p; 1.166 + } 1.167 + return array; 1.168 +} 1.169 + 1.170 +static void FreeStringArray(uint32_t variants, char ** array) 1.171 +{ 1.172 + if ((variants == 0) || !array) 1.173 + return; 1.174 + 1.175 + for (uint32_t i = 0; i < variants; i++) { 1.176 + if (array[i]) { 1.177 + PL_strfree(array[i]); 1.178 + array[i] = nullptr; 1.179 + } 1.180 + } 1.181 + PR_Free(array); 1.182 +} 1.183 + 1.184 +static bool CanLoadPlugin(char16ptr_t aBinaryPath) 1.185 +{ 1.186 +#if defined(_M_IX86) || defined(_M_X64) || defined(_M_IA64) 1.187 + bool canLoad = false; 1.188 + 1.189 + HANDLE file = CreateFileW(aBinaryPath, GENERIC_READ, 1.190 + FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, 1.191 + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); 1.192 + if (file != INVALID_HANDLE_VALUE) { 1.193 + HANDLE map = CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 1.194 + GetFileSize(file, nullptr), nullptr); 1.195 + if (map != nullptr) { 1.196 + LPVOID mapView = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); 1.197 + if (mapView != nullptr) { 1.198 + if (((IMAGE_DOS_HEADER*)mapView)->e_magic == IMAGE_DOS_SIGNATURE) { 1.199 + long peImageHeaderStart = (((IMAGE_DOS_HEADER*)mapView)->e_lfanew); 1.200 + if (peImageHeaderStart != 0L) { 1.201 + DWORD arch = (((IMAGE_NT_HEADERS*)((LPBYTE)mapView + peImageHeaderStart))->FileHeader.Machine); 1.202 +#ifdef _M_IX86 1.203 + canLoad = (arch == IMAGE_FILE_MACHINE_I386); 1.204 +#elif defined(_M_X64) 1.205 + canLoad = (arch == IMAGE_FILE_MACHINE_AMD64); 1.206 +#elif defined(_M_IA64) 1.207 + canLoad = (arch == IMAGE_FILE_MACHINE_IA64); 1.208 +#endif 1.209 + } 1.210 + } 1.211 + UnmapViewOfFile(mapView); 1.212 + } 1.213 + CloseHandle(map); 1.214 + } 1.215 + CloseHandle(file); 1.216 + } 1.217 + 1.218 + return canLoad; 1.219 +#else 1.220 + // Assume correct binaries for unhandled cases. 1.221 + return true; 1.222 +#endif 1.223 +} 1.224 + 1.225 +/* nsPluginsDir implementation */ 1.226 + 1.227 +// The file name must be in the form "np*.dll" 1.228 +bool nsPluginsDir::IsPluginFile(nsIFile* file) 1.229 +{ 1.230 + nsAutoCString path; 1.231 + if (NS_FAILED(file->GetNativePath(path))) 1.232 + return false; 1.233 + 1.234 + const char *cPath = path.get(); 1.235 + 1.236 + // this is most likely a path, so skip to the filename 1.237 + const char* filename = PL_strrchr(cPath, '\\'); 1.238 + if (filename) 1.239 + ++filename; 1.240 + else 1.241 + filename = cPath; 1.242 + 1.243 + char* extension = PL_strrchr(filename, '.'); 1.244 + if (extension) 1.245 + ++extension; 1.246 + 1.247 + uint32_t fullLength = strlen(filename); 1.248 + uint32_t extLength = extension ? strlen(extension) : 0; 1.249 + if (fullLength >= 7 && extLength == 3) { 1.250 + if (!PL_strncasecmp(filename, "np", 2) && !PL_strncasecmp(extension, "dll", 3)) { 1.251 + // don't load OJI-based Java plugins 1.252 + if (!PL_strncasecmp(filename, "npoji", 5) || 1.253 + !PL_strncasecmp(filename, "npjava", 6)) 1.254 + return false; 1.255 + return true; 1.256 + } 1.257 + } 1.258 + 1.259 + return false; 1.260 +} 1.261 + 1.262 +/* nsPluginFile implementation */ 1.263 + 1.264 +nsPluginFile::nsPluginFile(nsIFile* file) 1.265 +: mPlugin(file) 1.266 +{ 1.267 + // nada 1.268 +} 1.269 + 1.270 +nsPluginFile::~nsPluginFile() 1.271 +{ 1.272 + // nada 1.273 +} 1.274 + 1.275 +/** 1.276 + * Loads the plugin into memory using NSPR's shared-library loading 1.277 + * mechanism. Handles platform differences in loading shared libraries. 1.278 + */ 1.279 +nsresult nsPluginFile::LoadPlugin(PRLibrary **outLibrary) 1.280 +{ 1.281 + if (!mPlugin) 1.282 + return NS_ERROR_NULL_POINTER; 1.283 + 1.284 + bool protectCurrentDirectory = true; 1.285 + 1.286 + nsAutoString pluginFilePath; 1.287 + mPlugin->GetPath(pluginFilePath); 1.288 + protectCurrentDirectory = 1.289 + ShouldProtectPluginCurrentDirectory(pluginFilePath.BeginReading()); 1.290 + 1.291 + nsAutoString pluginFolderPath = pluginFilePath; 1.292 + int32_t idx = pluginFilePath.RFindChar('\\'); 1.293 + pluginFolderPath.SetLength(idx); 1.294 + 1.295 + BOOL restoreOrigDir = FALSE; 1.296 + WCHAR aOrigDir[MAX_PATH + 1]; 1.297 + DWORD dwCheck = GetCurrentDirectoryW(MAX_PATH, aOrigDir); 1.298 + NS_ASSERTION(dwCheck <= MAX_PATH + 1, "Error in Loading plugin"); 1.299 + 1.300 + if (dwCheck <= MAX_PATH + 1) { 1.301 + restoreOrigDir = SetCurrentDirectoryW(pluginFolderPath.get()); 1.302 + NS_ASSERTION(restoreOrigDir, "Error in Loading plugin"); 1.303 + } 1.304 + 1.305 + if (protectCurrentDirectory) { 1.306 + SetDllDirectory(nullptr); 1.307 + } 1.308 + 1.309 + nsresult rv = mPlugin->Load(outLibrary); 1.310 + if (NS_FAILED(rv)) 1.311 + *outLibrary = nullptr; 1.312 + 1.313 + if (protectCurrentDirectory) { 1.314 + SetDllDirectory(L""); 1.315 + } 1.316 + 1.317 + if (restoreOrigDir) { 1.318 + DebugOnly<BOOL> bCheck = SetCurrentDirectoryW(aOrigDir); 1.319 + NS_ASSERTION(bCheck, "Error in Loading plugin"); 1.320 + } 1.321 + 1.322 + return rv; 1.323 +} 1.324 + 1.325 +/** 1.326 + * Obtains all of the information currently available for this plugin. 1.327 + */ 1.328 +nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info, PRLibrary **outLibrary) 1.329 +{ 1.330 + *outLibrary = nullptr; 1.331 + 1.332 + nsresult rv = NS_OK; 1.333 + DWORD zerome, versionsize; 1.334 + void* verbuf = nullptr; 1.335 + 1.336 + if (!mPlugin) 1.337 + return NS_ERROR_NULL_POINTER; 1.338 + 1.339 + nsAutoString fullPath; 1.340 + if (NS_FAILED(rv = mPlugin->GetPath(fullPath))) 1.341 + return rv; 1.342 + 1.343 + if (!CanLoadPlugin(fullPath.get())) 1.344 + return NS_ERROR_FAILURE; 1.345 + 1.346 + nsAutoString fileName; 1.347 + if (NS_FAILED(rv = mPlugin->GetLeafName(fileName))) 1.348 + return rv; 1.349 + 1.350 + LPCWSTR lpFilepath = fullPath.get(); 1.351 + 1.352 + versionsize = ::GetFileVersionInfoSizeW(lpFilepath, &zerome); 1.353 + 1.354 + if (versionsize > 0) 1.355 + verbuf = PR_Malloc(versionsize); 1.356 + if (!verbuf) 1.357 + return NS_ERROR_OUT_OF_MEMORY; 1.358 + 1.359 + if (::GetFileVersionInfoW(lpFilepath, 0, versionsize, verbuf)) 1.360 + { 1.361 + // TODO: get appropriately-localized info from plugin file 1.362 + UINT lang = 1033; // language = English 1.363 + UINT cp = 1252; // codepage = Western 1.364 + info.fName = GetKeyValue(verbuf, L"ProductName", lang, cp); 1.365 + info.fDescription = GetKeyValue(verbuf, L"FileDescription", lang, cp); 1.366 + 1.367 + char *mimeType = GetKeyValue(verbuf, L"MIMEType", lang, cp); 1.368 + char *mimeDescription = GetKeyValue(verbuf, L"FileOpenName", lang, cp); 1.369 + char *extensions = GetKeyValue(verbuf, L"FileExtents", lang, cp); 1.370 + 1.371 + info.fVariantCount = CalculateVariantCount(mimeType); 1.372 + info.fMimeTypeArray = MakeStringArray(info.fVariantCount, mimeType); 1.373 + info.fMimeDescriptionArray = MakeStringArray(info.fVariantCount, mimeDescription); 1.374 + info.fExtensionArray = MakeStringArray(info.fVariantCount, extensions); 1.375 + info.fFullPath = PL_strdup(NS_ConvertUTF16toUTF8(fullPath).get()); 1.376 + info.fFileName = PL_strdup(NS_ConvertUTF16toUTF8(fileName).get()); 1.377 + info.fVersion = GetVersion(verbuf); 1.378 + 1.379 + PL_strfree(mimeType); 1.380 + PL_strfree(mimeDescription); 1.381 + PL_strfree(extensions); 1.382 + } 1.383 + else { 1.384 + rv = NS_ERROR_FAILURE; 1.385 + } 1.386 + 1.387 + PR_Free(verbuf); 1.388 + 1.389 + return rv; 1.390 +} 1.391 + 1.392 +nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info) 1.393 +{ 1.394 + if (info.fName) 1.395 + PL_strfree(info.fName); 1.396 + 1.397 + if (info.fDescription) 1.398 + PL_strfree(info.fDescription); 1.399 + 1.400 + if (info.fMimeTypeArray) 1.401 + FreeStringArray(info.fVariantCount, info.fMimeTypeArray); 1.402 + 1.403 + if (info.fMimeDescriptionArray) 1.404 + FreeStringArray(info.fVariantCount, info.fMimeDescriptionArray); 1.405 + 1.406 + if (info.fExtensionArray) 1.407 + FreeStringArray(info.fVariantCount, info.fExtensionArray); 1.408 + 1.409 + if (info.fFullPath) 1.410 + PL_strfree(info.fFullPath); 1.411 + 1.412 + if (info.fFileName) 1.413 + PL_strfree(info.fFileName); 1.414 + 1.415 + if (info.fVersion) 1.416 + PR_smprintf_free(info.fVersion); 1.417 + 1.418 + ZeroMemory((void *)&info, sizeof(info)); 1.419 + 1.420 + return NS_OK; 1.421 +}