summaryrefslogtreecommitdiffstats
path: root/dom/plugins/base/nsPluginsDirWin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/plugins/base/nsPluginsDirWin.cpp')
-rw-r--r--dom/plugins/base/nsPluginsDirWin.cpp433
1 files changed, 433 insertions, 0 deletions
diff --git a/dom/plugins/base/nsPluginsDirWin.cpp b/dom/plugins/base/nsPluginsDirWin.cpp
new file mode 100644
index 000000000..8c2d26ca2
--- /dev/null
+++ b/dom/plugins/base/nsPluginsDirWin.cpp
@@ -0,0 +1,433 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ nsPluginsDirWin.cpp
+
+ Windows implementation of the nsPluginsDir/nsPluginsFile classes.
+
+ by Alex Musil
+ */
+
+#include "mozilla/ArrayUtils.h" // ArrayLength
+#include "mozilla/DebugOnly.h"
+
+#include "nsPluginsDir.h"
+#include "prlink.h"
+#include "plstr.h"
+#include "prmem.h"
+#include "prprf.h"
+
+#include "windows.h"
+#include "winbase.h"
+
+#include "nsString.h"
+#include "nsIFile.h"
+#include "nsUnicharUtils.h"
+
+#include <shlwapi.h>
+#define SHOCKWAVE_BASE_FILENAME L"np32dsw"
+/**
+ * Determines whether or not SetDllDirectory should be called for this plugin.
+ *
+ * @param pluginFilePath The full path of the plugin file
+ * @return true if SetDllDirectory can be called for the plugin
+ */
+bool
+ShouldProtectPluginCurrentDirectory(char16ptr_t pluginFilePath)
+{
+ LPCWSTR passedInFilename = PathFindFileName(pluginFilePath);
+ if (!passedInFilename) {
+ return true;
+ }
+
+ // Somewhere in the middle of 11.6 version of Shockwave, naming of the DLL
+ // after its version number is introduced.
+ if (!wcsicmp(passedInFilename, SHOCKWAVE_BASE_FILENAME L".dll")) {
+ return false;
+ }
+
+ // Shockwave versions before 1202122 will break if you call SetDllDirectory
+ const uint64_t kFixedShockwaveVersion = 1202122;
+ uint64_t version;
+ int found = swscanf(passedInFilename, SHOCKWAVE_BASE_FILENAME L"_%llu.dll",
+ &version);
+ if (found && version < kFixedShockwaveVersion) {
+ return false;
+ }
+
+ // We always want to call SetDllDirectory otherwise
+ return true;
+}
+
+using namespace mozilla;
+
+/* Local helper functions */
+
+static char* GetKeyValue(void* verbuf, const WCHAR* key,
+ UINT language, UINT codepage)
+{
+ WCHAR keybuf[64]; // plenty for the template below, with the longest key
+ // we use (currently "FileDescription")
+ const WCHAR keyFormat[] = L"\\StringFileInfo\\%04X%04X\\%ls";
+ WCHAR *buf = nullptr;
+ UINT blen;
+
+ if (_snwprintf_s(keybuf, ArrayLength(keybuf), _TRUNCATE,
+ keyFormat, language, codepage, key) < 0)
+ {
+ NS_NOTREACHED("plugin info key too long for buffer!");
+ return nullptr;
+ }
+
+ if (::VerQueryValueW(verbuf, keybuf, (void **)&buf, &blen) == 0 ||
+ buf == nullptr || blen == 0)
+ {
+ return nullptr;
+ }
+
+ return PL_strdup(NS_ConvertUTF16toUTF8(buf, blen).get());
+}
+
+static char* GetVersion(void* verbuf)
+{
+ VS_FIXEDFILEINFO *fileInfo;
+ UINT fileInfoLen;
+
+ ::VerQueryValueW(verbuf, L"\\", (void **)&fileInfo, &fileInfoLen);
+
+ if (fileInfo) {
+ return PR_smprintf("%ld.%ld.%ld.%ld",
+ HIWORD(fileInfo->dwFileVersionMS),
+ LOWORD(fileInfo->dwFileVersionMS),
+ HIWORD(fileInfo->dwFileVersionLS),
+ LOWORD(fileInfo->dwFileVersionLS));
+ }
+
+ return nullptr;
+}
+
+// Returns a boolean indicating if the key's value contains a string
+// entry equal to "1" or "0". No entry for the key returns false.
+static bool GetBooleanFlag(void* verbuf, const WCHAR* key,
+ UINT language, UINT codepage)
+{
+ char* flagStr = GetKeyValue(verbuf, key, language, codepage);
+ if (!flagStr) {
+ return false;
+ }
+ bool result = (PL_strncmp("1", flagStr, 1) == 0);
+ PL_strfree(flagStr);
+ return result;
+}
+
+static uint32_t CalculateVariantCount(char* mimeTypes)
+{
+ uint32_t variants = 1;
+
+ if (!mimeTypes)
+ return 0;
+
+ char* index = mimeTypes;
+ while (*index) {
+ if (*index == '|')
+ variants++;
+
+ ++index;
+ }
+ return variants;
+}
+
+static char** MakeStringArray(uint32_t variants, char* data)
+{
+ // The number of variants has been calculated based on the mime
+ // type array. Plugins are not explicitely required to match
+ // this number in two other arrays: file extention array and mime
+ // description array, and some of them actually don't.
+ // We should handle such situations gracefully
+
+ if ((variants <= 0) || !data)
+ return nullptr;
+
+ char ** array = (char **)PR_Calloc(variants, sizeof(char *));
+ if (!array)
+ return nullptr;
+
+ char * start = data;
+
+ for (uint32_t i = 0; i < variants; i++) {
+ char * p = PL_strchr(start, '|');
+ if (p)
+ *p = 0;
+
+ array[i] = PL_strdup(start);
+
+ if (!p) {
+ // nothing more to look for, fill everything left
+ // with empty strings and break
+ while(++i < variants)
+ array[i] = PL_strdup("");
+
+ break;
+ }
+
+ start = ++p;
+ }
+ return array;
+}
+
+static void FreeStringArray(uint32_t variants, char ** array)
+{
+ if ((variants == 0) || !array)
+ return;
+
+ for (uint32_t i = 0; i < variants; i++) {
+ if (array[i]) {
+ PL_strfree(array[i]);
+ array[i] = nullptr;
+ }
+ }
+ PR_Free(array);
+}
+
+static bool CanLoadPlugin(char16ptr_t aBinaryPath)
+{
+#if defined(_M_IX86) || defined(_M_X64) || defined(_M_IA64)
+ bool canLoad = false;
+
+ HANDLE file = CreateFileW(aBinaryPath, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+ if (file != INVALID_HANDLE_VALUE) {
+ HANDLE map = CreateFileMappingW(file, nullptr, PAGE_READONLY, 0,
+ GetFileSize(file, nullptr), nullptr);
+ if (map != nullptr) {
+ LPVOID mapView = MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
+ if (mapView != nullptr) {
+ if (((IMAGE_DOS_HEADER*)mapView)->e_magic == IMAGE_DOS_SIGNATURE) {
+ long peImageHeaderStart = (((IMAGE_DOS_HEADER*)mapView)->e_lfanew);
+ if (peImageHeaderStart != 0L) {
+ DWORD arch = (((IMAGE_NT_HEADERS*)((LPBYTE)mapView + peImageHeaderStart))->FileHeader.Machine);
+#ifdef _M_IX86
+ canLoad = (arch == IMAGE_FILE_MACHINE_I386);
+#elif defined(_M_X64)
+ canLoad = (arch == IMAGE_FILE_MACHINE_AMD64);
+#elif defined(_M_IA64)
+ canLoad = (arch == IMAGE_FILE_MACHINE_IA64);
+#endif
+ }
+ }
+ UnmapViewOfFile(mapView);
+ }
+ CloseHandle(map);
+ }
+ CloseHandle(file);
+ }
+
+ return canLoad;
+#else
+ // Assume correct binaries for unhandled cases.
+ return true;
+#endif
+}
+
+/* nsPluginsDir implementation */
+
+// The file name must be in the form "np*.dll"
+bool nsPluginsDir::IsPluginFile(nsIFile* file)
+{
+ nsAutoCString path;
+ if (NS_FAILED(file->GetNativePath(path)))
+ return false;
+
+ const char *cPath = path.get();
+
+ // this is most likely a path, so skip to the filename
+ const char* filename = PL_strrchr(cPath, '\\');
+ if (filename)
+ ++filename;
+ else
+ filename = cPath;
+
+ char* extension = PL_strrchr(filename, '.');
+ if (extension)
+ ++extension;
+
+ uint32_t fullLength = strlen(filename);
+ uint32_t extLength = extension ? strlen(extension) : 0;
+ if (fullLength >= 7 && extLength == 3) {
+ if (!PL_strncasecmp(filename, "np", 2) && !PL_strncasecmp(extension, "dll", 3)) {
+ // don't load OJI-based Java plugins
+ if (!PL_strncasecmp(filename, "npoji", 5) ||
+ !PL_strncasecmp(filename, "npjava", 6))
+ return false;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* nsPluginFile implementation */
+
+nsPluginFile::nsPluginFile(nsIFile* file)
+: mPlugin(file)
+{
+ // nada
+}
+
+nsPluginFile::~nsPluginFile()
+{
+ // nada
+}
+
+/**
+ * Loads the plugin into memory using NSPR's shared-library loading
+ * mechanism. Handles platform differences in loading shared libraries.
+ */
+nsresult nsPluginFile::LoadPlugin(PRLibrary **outLibrary)
+{
+ if (!mPlugin)
+ return NS_ERROR_NULL_POINTER;
+
+ bool protectCurrentDirectory = true;
+
+ nsAutoString pluginFilePath;
+ mPlugin->GetPath(pluginFilePath);
+ protectCurrentDirectory =
+ ShouldProtectPluginCurrentDirectory(pluginFilePath.BeginReading());
+
+ nsAutoString pluginFolderPath = pluginFilePath;
+ int32_t idx = pluginFilePath.RFindChar('\\');
+ pluginFolderPath.SetLength(idx);
+
+ BOOL restoreOrigDir = FALSE;
+ WCHAR aOrigDir[MAX_PATH + 1];
+ DWORD dwCheck = GetCurrentDirectoryW(MAX_PATH, aOrigDir);
+ NS_ASSERTION(dwCheck <= MAX_PATH + 1, "Error in Loading plugin");
+
+ if (dwCheck <= MAX_PATH + 1) {
+ restoreOrigDir = SetCurrentDirectoryW(pluginFolderPath.get());
+ NS_ASSERTION(restoreOrigDir, "Error in Loading plugin");
+ }
+
+ if (protectCurrentDirectory) {
+ SetDllDirectory(nullptr);
+ }
+
+ nsresult rv = mPlugin->Load(outLibrary);
+ if (NS_FAILED(rv))
+ *outLibrary = nullptr;
+
+ if (protectCurrentDirectory) {
+ SetDllDirectory(L"");
+ }
+
+ if (restoreOrigDir) {
+ DebugOnly<BOOL> bCheck = SetCurrentDirectoryW(aOrigDir);
+ NS_ASSERTION(bCheck, "Error in Loading plugin");
+ }
+
+ return rv;
+}
+
+/**
+ * Obtains all of the information currently available for this plugin.
+ */
+nsresult nsPluginFile::GetPluginInfo(nsPluginInfo& info, PRLibrary **outLibrary)
+{
+ *outLibrary = nullptr;
+
+ nsresult rv = NS_OK;
+ DWORD zerome, versionsize;
+ void* verbuf = nullptr;
+
+ if (!mPlugin)
+ return NS_ERROR_NULL_POINTER;
+
+ nsAutoString fullPath;
+ if (NS_FAILED(rv = mPlugin->GetPath(fullPath)))
+ return rv;
+
+ if (!CanLoadPlugin(fullPath.get()))
+ return NS_ERROR_FAILURE;
+
+ nsAutoString fileName;
+ if (NS_FAILED(rv = mPlugin->GetLeafName(fileName)))
+ return rv;
+
+ LPCWSTR lpFilepath = fullPath.get();
+
+ versionsize = ::GetFileVersionInfoSizeW(lpFilepath, &zerome);
+
+ if (versionsize > 0)
+ verbuf = PR_Malloc(versionsize);
+ if (!verbuf)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (::GetFileVersionInfoW(lpFilepath, 0, versionsize, verbuf))
+ {
+ // TODO: get appropriately-localized info from plugin file
+ UINT lang = 1033; // language = English, 0x409
+ UINT cp = 1252; // codepage = Western, 0x4E4
+ info.fName = GetKeyValue(verbuf, L"ProductName", lang, cp);
+ info.fDescription = GetKeyValue(verbuf, L"FileDescription", lang, cp);
+ info.fSupportsAsyncRender = GetBooleanFlag(verbuf, L"AsyncDrawingSupport", lang, cp);
+
+ char *mimeType = GetKeyValue(verbuf, L"MIMEType", lang, cp);
+ char *mimeDescription = GetKeyValue(verbuf, L"FileOpenName", lang, cp);
+ char *extensions = GetKeyValue(verbuf, L"FileExtents", lang, cp);
+
+ info.fVariantCount = CalculateVariantCount(mimeType);
+ info.fMimeTypeArray = MakeStringArray(info.fVariantCount, mimeType);
+ info.fMimeDescriptionArray = MakeStringArray(info.fVariantCount, mimeDescription);
+ info.fExtensionArray = MakeStringArray(info.fVariantCount, extensions);
+ info.fFullPath = PL_strdup(NS_ConvertUTF16toUTF8(fullPath).get());
+ info.fFileName = PL_strdup(NS_ConvertUTF16toUTF8(fileName).get());
+ info.fVersion = GetVersion(verbuf);
+
+ PL_strfree(mimeType);
+ PL_strfree(mimeDescription);
+ PL_strfree(extensions);
+ }
+ else {
+ rv = NS_ERROR_FAILURE;
+ }
+
+ PR_Free(verbuf);
+
+ return rv;
+}
+
+nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info)
+{
+ if (info.fName)
+ PL_strfree(info.fName);
+
+ if (info.fDescription)
+ PL_strfree(info.fDescription);
+
+ if (info.fMimeTypeArray)
+ FreeStringArray(info.fVariantCount, info.fMimeTypeArray);
+
+ if (info.fMimeDescriptionArray)
+ FreeStringArray(info.fVariantCount, info.fMimeDescriptionArray);
+
+ if (info.fExtensionArray)
+ FreeStringArray(info.fVariantCount, info.fExtensionArray);
+
+ if (info.fFullPath)
+ PL_strfree(info.fFullPath);
+
+ if (info.fFileName)
+ PL_strfree(info.fFileName);
+
+ if (info.fVersion)
+ PR_smprintf_free(info.fVersion);
+
+ ZeroMemory((void *)&info, sizeof(info));
+
+ return NS_OK;
+}