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