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

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

mercurial