dom/plugins/base/nsPluginsDirWin.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 /*
michael@0 7 nsPluginsDirWin.cpp
michael@0 8
michael@0 9 Windows implementation of the nsPluginsDir/nsPluginsFile classes.
michael@0 10
michael@0 11 by Alex Musil
michael@0 12 */
michael@0 13
michael@0 14 #include "mozilla/ArrayUtils.h" // ArrayLength
michael@0 15 #include "mozilla/DebugOnly.h"
michael@0 16
michael@0 17 #include "nsPluginsDir.h"
michael@0 18 #include "prlink.h"
michael@0 19 #include "plstr.h"
michael@0 20 #include "prmem.h"
michael@0 21 #include "prprf.h"
michael@0 22
michael@0 23 #include "windows.h"
michael@0 24 #include "winbase.h"
michael@0 25
michael@0 26 #include "nsString.h"
michael@0 27 #include "nsIFile.h"
michael@0 28 #include "nsUnicharUtils.h"
michael@0 29
michael@0 30 #include <shlwapi.h>
michael@0 31 #define SHOCKWAVE_BASE_FILENAME L"np32dsw"
michael@0 32 /**
michael@0 33 * Determines whether or not SetDllDirectory should be called for this plugin.
michael@0 34 *
michael@0 35 * @param pluginFilePath The full path of the plugin file
michael@0 36 * @return true if SetDllDirectory can be called for the plugin
michael@0 37 */
michael@0 38 bool
michael@0 39 ShouldProtectPluginCurrentDirectory(char16ptr_t pluginFilePath)
michael@0 40 {
michael@0 41 LPCWSTR passedInFilename = PathFindFileName(pluginFilePath);
michael@0 42 if (!passedInFilename) {
michael@0 43 return true;
michael@0 44 }
michael@0 45
michael@0 46 // Somewhere in the middle of 11.6 version of Shockwave, naming of the DLL
michael@0 47 // after its version number is introduced.
michael@0 48 if (!wcsicmp(passedInFilename, SHOCKWAVE_BASE_FILENAME L".dll")) {
michael@0 49 return false;
michael@0 50 }
michael@0 51
michael@0 52 // Shockwave versions before 1202122 will break if you call SetDllDirectory
michael@0 53 const uint64_t kFixedShockwaveVersion = 1202122;
michael@0 54 uint64_t version;
michael@0 55 int found = swscanf(passedInFilename, SHOCKWAVE_BASE_FILENAME L"_%llu.dll",
michael@0 56 &version);
michael@0 57 if (found && version < kFixedShockwaveVersion) {
michael@0 58 return false;
michael@0 59 }
michael@0 60
michael@0 61 // We always want to call SetDllDirectory otherwise
michael@0 62 return true;
michael@0 63 }
michael@0 64
michael@0 65 using namespace mozilla;
michael@0 66
michael@0 67 /* Local helper functions */
michael@0 68
michael@0 69 static char* GetKeyValue(void* verbuf, const WCHAR* key,
michael@0 70 UINT language, UINT codepage)
michael@0 71 {
michael@0 72 WCHAR keybuf[64]; // plenty for the template below, with the longest key
michael@0 73 // we use (currently "FileDescription")
michael@0 74 const WCHAR keyFormat[] = L"\\StringFileInfo\\%04X%04X\\%s";
michael@0 75 WCHAR *buf = nullptr;
michael@0 76 UINT blen;
michael@0 77
michael@0 78 if (_snwprintf_s(keybuf, ArrayLength(keybuf), _TRUNCATE,
michael@0 79 keyFormat, language, codepage, key) < 0)
michael@0 80 {
michael@0 81 NS_NOTREACHED("plugin info key too long for buffer!");
michael@0 82 return nullptr;
michael@0 83 }
michael@0 84
michael@0 85 if (::VerQueryValueW(verbuf, keybuf, (void **)&buf, &blen) == 0 ||
michael@0 86 buf == nullptr || blen == 0)
michael@0 87 {
michael@0 88 return nullptr;
michael@0 89 }
michael@0 90
michael@0 91 return PL_strdup(NS_ConvertUTF16toUTF8(buf, blen).get());
michael@0 92 }
michael@0 93
michael@0 94 static char* GetVersion(void* verbuf)
michael@0 95 {
michael@0 96 VS_FIXEDFILEINFO *fileInfo;
michael@0 97 UINT fileInfoLen;
michael@0 98
michael@0 99 ::VerQueryValueW(verbuf, L"\\", (void **)&fileInfo, &fileInfoLen);
michael@0 100
michael@0 101 if (fileInfo) {
michael@0 102 return PR_smprintf("%ld.%ld.%ld.%ld",
michael@0 103 HIWORD(fileInfo->dwFileVersionMS),
michael@0 104 LOWORD(fileInfo->dwFileVersionMS),
michael@0 105 HIWORD(fileInfo->dwFileVersionLS),
michael@0 106 LOWORD(fileInfo->dwFileVersionLS));
michael@0 107 }
michael@0 108
michael@0 109 return nullptr;
michael@0 110 }
michael@0 111
michael@0 112 static uint32_t CalculateVariantCount(char* mimeTypes)
michael@0 113 {
michael@0 114 uint32_t variants = 1;
michael@0 115
michael@0 116 if (!mimeTypes)
michael@0 117 return 0;
michael@0 118
michael@0 119 char* index = mimeTypes;
michael@0 120 while (*index) {
michael@0 121 if (*index == '|')
michael@0 122 variants++;
michael@0 123
michael@0 124 ++index;
michael@0 125 }
michael@0 126 return variants;
michael@0 127 }
michael@0 128
michael@0 129 static char** MakeStringArray(uint32_t variants, char* data)
michael@0 130 {
michael@0 131 // The number of variants has been calculated based on the mime
michael@0 132 // type array. Plugins are not explicitely required to match
michael@0 133 // this number in two other arrays: file extention array and mime
michael@0 134 // description array, and some of them actually don't.
michael@0 135 // We should handle such situations gracefully
michael@0 136
michael@0 137 if ((variants <= 0) || !data)
michael@0 138 return nullptr;
michael@0 139
michael@0 140 char ** array = (char **)PR_Calloc(variants, sizeof(char *));
michael@0 141 if (!array)
michael@0 142 return nullptr;
michael@0 143
michael@0 144 char * start = data;
michael@0 145
michael@0 146 for (uint32_t i = 0; i < variants; i++) {
michael@0 147 char * p = PL_strchr(start, '|');
michael@0 148 if (p)
michael@0 149 *p = 0;
michael@0 150
michael@0 151 array[i] = PL_strdup(start);
michael@0 152
michael@0 153 if (!p) {
michael@0 154 // nothing more to look for, fill everything left
michael@0 155 // with empty strings and break
michael@0 156 while(++i < variants)
michael@0 157 array[i] = PL_strdup("");
michael@0 158
michael@0 159 break;
michael@0 160 }
michael@0 161
michael@0 162 start = ++p;
michael@0 163 }
michael@0 164 return array;
michael@0 165 }
michael@0 166
michael@0 167 static void FreeStringArray(uint32_t variants, char ** array)
michael@0 168 {
michael@0 169 if ((variants == 0) || !array)
michael@0 170 return;
michael@0 171
michael@0 172 for (uint32_t i = 0; i < variants; i++) {
michael@0 173 if (array[i]) {
michael@0 174 PL_strfree(array[i]);
michael@0 175 array[i] = nullptr;
michael@0 176 }
michael@0 177 }
michael@0 178 PR_Free(array);
michael@0 179 }
michael@0 180
michael@0 181 static bool CanLoadPlugin(char16ptr_t aBinaryPath)
michael@0 182 {
michael@0 183 #if defined(_M_IX86) || defined(_M_X64) || defined(_M_IA64)
michael@0 184 bool canLoad = false;
michael@0 185
michael@0 186 HANDLE file = CreateFileW(aBinaryPath, GENERIC_READ,
michael@0 187 FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
michael@0 188 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
michael@0 189 if (file != INVALID_HANDLE_VALUE) {
michael@0 190 HANDLE map = CreateFileMappingW(file, nullptr, PAGE_READONLY, 0,
michael@0 191 GetFileSize(file, nullptr), nullptr);
michael@0 192 if (map != nullptr) {
michael@0 193 LPVOID mapView = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
michael@0 194 if (mapView != nullptr) {
michael@0 195 if (((IMAGE_DOS_HEADER*)mapView)->e_magic == IMAGE_DOS_SIGNATURE) {
michael@0 196 long peImageHeaderStart = (((IMAGE_DOS_HEADER*)mapView)->e_lfanew);
michael@0 197 if (peImageHeaderStart != 0L) {
michael@0 198 DWORD arch = (((IMAGE_NT_HEADERS*)((LPBYTE)mapView + peImageHeaderStart))->FileHeader.Machine);
michael@0 199 #ifdef _M_IX86
michael@0 200 canLoad = (arch == IMAGE_FILE_MACHINE_I386);
michael@0 201 #elif defined(_M_X64)
michael@0 202 canLoad = (arch == IMAGE_FILE_MACHINE_AMD64);
michael@0 203 #elif defined(_M_IA64)
michael@0 204 canLoad = (arch == IMAGE_FILE_MACHINE_IA64);
michael@0 205 #endif
michael@0 206 }
michael@0 207 }
michael@0 208 UnmapViewOfFile(mapView);
michael@0 209 }
michael@0 210 CloseHandle(map);
michael@0 211 }
michael@0 212 CloseHandle(file);
michael@0 213 }
michael@0 214
michael@0 215 return canLoad;
michael@0 216 #else
michael@0 217 // Assume correct binaries for unhandled cases.
michael@0 218 return true;
michael@0 219 #endif
michael@0 220 }
michael@0 221
michael@0 222 /* nsPluginsDir implementation */
michael@0 223
michael@0 224 // The file name must be in the form "np*.dll"
michael@0 225 bool nsPluginsDir::IsPluginFile(nsIFile* file)
michael@0 226 {
michael@0 227 nsAutoCString path;
michael@0 228 if (NS_FAILED(file->GetNativePath(path)))
michael@0 229 return false;
michael@0 230
michael@0 231 const char *cPath = path.get();
michael@0 232
michael@0 233 // this is most likely a path, so skip to the filename
michael@0 234 const char* filename = PL_strrchr(cPath, '\\');
michael@0 235 if (filename)
michael@0 236 ++filename;
michael@0 237 else
michael@0 238 filename = cPath;
michael@0 239
michael@0 240 char* extension = PL_strrchr(filename, '.');
michael@0 241 if (extension)
michael@0 242 ++extension;
michael@0 243
michael@0 244 uint32_t fullLength = strlen(filename);
michael@0 245 uint32_t extLength = extension ? strlen(extension) : 0;
michael@0 246 if (fullLength >= 7 && extLength == 3) {
michael@0 247 if (!PL_strncasecmp(filename, "np", 2) && !PL_strncasecmp(extension, "dll", 3)) {
michael@0 248 // don't load OJI-based Java plugins
michael@0 249 if (!PL_strncasecmp(filename, "npoji", 5) ||
michael@0 250 !PL_strncasecmp(filename, "npjava", 6))
michael@0 251 return false;
michael@0 252 return true;
michael@0 253 }
michael@0 254 }
michael@0 255
michael@0 256 return false;
michael@0 257 }
michael@0 258
michael@0 259 /* nsPluginFile implementation */
michael@0 260
michael@0 261 nsPluginFile::nsPluginFile(nsIFile* file)
michael@0 262 : mPlugin(file)
michael@0 263 {
michael@0 264 // nada
michael@0 265 }
michael@0 266
michael@0 267 nsPluginFile::~nsPluginFile()
michael@0 268 {
michael@0 269 // nada
michael@0 270 }
michael@0 271
michael@0 272 /**
michael@0 273 * Loads the plugin into memory using NSPR's shared-library loading
michael@0 274 * mechanism. Handles platform differences in loading shared libraries.
michael@0 275 */
michael@0 276 nsresult nsPluginFile::LoadPlugin(PRLibrary **outLibrary)
michael@0 277 {
michael@0 278 if (!mPlugin)
michael@0 279 return NS_ERROR_NULL_POINTER;
michael@0 280
michael@0 281 bool protectCurrentDirectory = true;
michael@0 282
michael@0 283 nsAutoString pluginFilePath;
michael@0 284 mPlugin->GetPath(pluginFilePath);
michael@0 285 protectCurrentDirectory =
michael@0 286 ShouldProtectPluginCurrentDirectory(pluginFilePath.BeginReading());
michael@0 287
michael@0 288 nsAutoString pluginFolderPath = pluginFilePath;
michael@0 289 int32_t idx = pluginFilePath.RFindChar('\\');
michael@0 290 pluginFolderPath.SetLength(idx);
michael@0 291
michael@0 292 BOOL restoreOrigDir = FALSE;
michael@0 293 WCHAR aOrigDir[MAX_PATH + 1];
michael@0 294 DWORD dwCheck = GetCurrentDirectoryW(MAX_PATH, aOrigDir);
michael@0 295 NS_ASSERTION(dwCheck <= MAX_PATH + 1, "Error in Loading plugin");
michael@0 296
michael@0 297 if (dwCheck <= MAX_PATH + 1) {
michael@0 298 restoreOrigDir = SetCurrentDirectoryW(pluginFolderPath.get());
michael@0 299 NS_ASSERTION(restoreOrigDir, "Error in Loading plugin");
michael@0 300 }
michael@0 301
michael@0 302 if (protectCurrentDirectory) {
michael@0 303 SetDllDirectory(nullptr);
michael@0 304 }
michael@0 305
michael@0 306 nsresult rv = mPlugin->Load(outLibrary);
michael@0 307 if (NS_FAILED(rv))
michael@0 308 *outLibrary = nullptr;
michael@0 309
michael@0 310 if (protectCurrentDirectory) {
michael@0 311 SetDllDirectory(L"");
michael@0 312 }
michael@0 313
michael@0 314 if (restoreOrigDir) {
michael@0 315 DebugOnly<BOOL> bCheck = SetCurrentDirectoryW(aOrigDir);
michael@0 316 NS_ASSERTION(bCheck, "Error in Loading plugin");
michael@0 317 }
michael@0 318
michael@0 319 return rv;
michael@0 320 }
michael@0 321
michael@0 322 /**
michael@0 323 * Obtains all of the information currently available for this plugin.
michael@0 324 */
michael@0 325 nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info, PRLibrary **outLibrary)
michael@0 326 {
michael@0 327 *outLibrary = nullptr;
michael@0 328
michael@0 329 nsresult rv = NS_OK;
michael@0 330 DWORD zerome, versionsize;
michael@0 331 void* verbuf = nullptr;
michael@0 332
michael@0 333 if (!mPlugin)
michael@0 334 return NS_ERROR_NULL_POINTER;
michael@0 335
michael@0 336 nsAutoString fullPath;
michael@0 337 if (NS_FAILED(rv = mPlugin->GetPath(fullPath)))
michael@0 338 return rv;
michael@0 339
michael@0 340 if (!CanLoadPlugin(fullPath.get()))
michael@0 341 return NS_ERROR_FAILURE;
michael@0 342
michael@0 343 nsAutoString fileName;
michael@0 344 if (NS_FAILED(rv = mPlugin->GetLeafName(fileName)))
michael@0 345 return rv;
michael@0 346
michael@0 347 LPCWSTR lpFilepath = fullPath.get();
michael@0 348
michael@0 349 versionsize = ::GetFileVersionInfoSizeW(lpFilepath, &zerome);
michael@0 350
michael@0 351 if (versionsize > 0)
michael@0 352 verbuf = PR_Malloc(versionsize);
michael@0 353 if (!verbuf)
michael@0 354 return NS_ERROR_OUT_OF_MEMORY;
michael@0 355
michael@0 356 if (::GetFileVersionInfoW(lpFilepath, 0, versionsize, verbuf))
michael@0 357 {
michael@0 358 // TODO: get appropriately-localized info from plugin file
michael@0 359 UINT lang = 1033; // language = English
michael@0 360 UINT cp = 1252; // codepage = Western
michael@0 361 info.fName = GetKeyValue(verbuf, L"ProductName", lang, cp);
michael@0 362 info.fDescription = GetKeyValue(verbuf, L"FileDescription", lang, cp);
michael@0 363
michael@0 364 char *mimeType = GetKeyValue(verbuf, L"MIMEType", lang, cp);
michael@0 365 char *mimeDescription = GetKeyValue(verbuf, L"FileOpenName", lang, cp);
michael@0 366 char *extensions = GetKeyValue(verbuf, L"FileExtents", lang, cp);
michael@0 367
michael@0 368 info.fVariantCount = CalculateVariantCount(mimeType);
michael@0 369 info.fMimeTypeArray = MakeStringArray(info.fVariantCount, mimeType);
michael@0 370 info.fMimeDescriptionArray = MakeStringArray(info.fVariantCount, mimeDescription);
michael@0 371 info.fExtensionArray = MakeStringArray(info.fVariantCount, extensions);
michael@0 372 info.fFullPath = PL_strdup(NS_ConvertUTF16toUTF8(fullPath).get());
michael@0 373 info.fFileName = PL_strdup(NS_ConvertUTF16toUTF8(fileName).get());
michael@0 374 info.fVersion = GetVersion(verbuf);
michael@0 375
michael@0 376 PL_strfree(mimeType);
michael@0 377 PL_strfree(mimeDescription);
michael@0 378 PL_strfree(extensions);
michael@0 379 }
michael@0 380 else {
michael@0 381 rv = NS_ERROR_FAILURE;
michael@0 382 }
michael@0 383
michael@0 384 PR_Free(verbuf);
michael@0 385
michael@0 386 return rv;
michael@0 387 }
michael@0 388
michael@0 389 nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info)
michael@0 390 {
michael@0 391 if (info.fName)
michael@0 392 PL_strfree(info.fName);
michael@0 393
michael@0 394 if (info.fDescription)
michael@0 395 PL_strfree(info.fDescription);
michael@0 396
michael@0 397 if (info.fMimeTypeArray)
michael@0 398 FreeStringArray(info.fVariantCount, info.fMimeTypeArray);
michael@0 399
michael@0 400 if (info.fMimeDescriptionArray)
michael@0 401 FreeStringArray(info.fVariantCount, info.fMimeDescriptionArray);
michael@0 402
michael@0 403 if (info.fExtensionArray)
michael@0 404 FreeStringArray(info.fVariantCount, info.fExtensionArray);
michael@0 405
michael@0 406 if (info.fFullPath)
michael@0 407 PL_strfree(info.fFullPath);
michael@0 408
michael@0 409 if (info.fFileName)
michael@0 410 PL_strfree(info.fFileName);
michael@0 411
michael@0 412 if (info.fVersion)
michael@0 413 PR_smprintf_free(info.fVersion);
michael@0 414
michael@0 415 ZeroMemory((void *)&info, sizeof(info));
michael@0 416
michael@0 417 return NS_OK;
michael@0 418 }

mercurial