dom/plugins/base/nsPluginsDirWin.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:5c5f98669903
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/. */
5
6 /*
7 nsPluginsDirWin.cpp
8
9 Windows implementation of the nsPluginsDir/nsPluginsFile classes.
10
11 by Alex Musil
12 */
13
14 #include "mozilla/ArrayUtils.h" // ArrayLength
15 #include "mozilla/DebugOnly.h"
16
17 #include "nsPluginsDir.h"
18 #include "prlink.h"
19 #include "plstr.h"
20 #include "prmem.h"
21 #include "prprf.h"
22
23 #include "windows.h"
24 #include "winbase.h"
25
26 #include "nsString.h"
27 #include "nsIFile.h"
28 #include "nsUnicharUtils.h"
29
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 }
45
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 }
51
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 }
60
61 // We always want to call SetDllDirectory otherwise
62 return true;
63 }
64
65 using namespace mozilla;
66
67 /* Local helper functions */
68
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;
77
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 }
84
85 if (::VerQueryValueW(verbuf, keybuf, (void **)&buf, &blen) == 0 ||
86 buf == nullptr || blen == 0)
87 {
88 return nullptr;
89 }
90
91 return PL_strdup(NS_ConvertUTF16toUTF8(buf, blen).get());
92 }
93
94 static char* GetVersion(void* verbuf)
95 {
96 VS_FIXEDFILEINFO *fileInfo;
97 UINT fileInfoLen;
98
99 ::VerQueryValueW(verbuf, L"\\", (void **)&fileInfo, &fileInfoLen);
100
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 }
108
109 return nullptr;
110 }
111
112 static uint32_t CalculateVariantCount(char* mimeTypes)
113 {
114 uint32_t variants = 1;
115
116 if (!mimeTypes)
117 return 0;
118
119 char* index = mimeTypes;
120 while (*index) {
121 if (*index == '|')
122 variants++;
123
124 ++index;
125 }
126 return variants;
127 }
128
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
136
137 if ((variants <= 0) || !data)
138 return nullptr;
139
140 char ** array = (char **)PR_Calloc(variants, sizeof(char *));
141 if (!array)
142 return nullptr;
143
144 char * start = data;
145
146 for (uint32_t i = 0; i < variants; i++) {
147 char * p = PL_strchr(start, '|');
148 if (p)
149 *p = 0;
150
151 array[i] = PL_strdup(start);
152
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("");
158
159 break;
160 }
161
162 start = ++p;
163 }
164 return array;
165 }
166
167 static void FreeStringArray(uint32_t variants, char ** array)
168 {
169 if ((variants == 0) || !array)
170 return;
171
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 }
180
181 static bool CanLoadPlugin(char16ptr_t aBinaryPath)
182 {
183 #if defined(_M_IX86) || defined(_M_X64) || defined(_M_IA64)
184 bool canLoad = false;
185
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 }
214
215 return canLoad;
216 #else
217 // Assume correct binaries for unhandled cases.
218 return true;
219 #endif
220 }
221
222 /* nsPluginsDir implementation */
223
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;
230
231 const char *cPath = path.get();
232
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;
239
240 char* extension = PL_strrchr(filename, '.');
241 if (extension)
242 ++extension;
243
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 }
255
256 return false;
257 }
258
259 /* nsPluginFile implementation */
260
261 nsPluginFile::nsPluginFile(nsIFile* file)
262 : mPlugin(file)
263 {
264 // nada
265 }
266
267 nsPluginFile::~nsPluginFile()
268 {
269 // nada
270 }
271
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;
280
281 bool protectCurrentDirectory = true;
282
283 nsAutoString pluginFilePath;
284 mPlugin->GetPath(pluginFilePath);
285 protectCurrentDirectory =
286 ShouldProtectPluginCurrentDirectory(pluginFilePath.BeginReading());
287
288 nsAutoString pluginFolderPath = pluginFilePath;
289 int32_t idx = pluginFilePath.RFindChar('\\');
290 pluginFolderPath.SetLength(idx);
291
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");
296
297 if (dwCheck <= MAX_PATH + 1) {
298 restoreOrigDir = SetCurrentDirectoryW(pluginFolderPath.get());
299 NS_ASSERTION(restoreOrigDir, "Error in Loading plugin");
300 }
301
302 if (protectCurrentDirectory) {
303 SetDllDirectory(nullptr);
304 }
305
306 nsresult rv = mPlugin->Load(outLibrary);
307 if (NS_FAILED(rv))
308 *outLibrary = nullptr;
309
310 if (protectCurrentDirectory) {
311 SetDllDirectory(L"");
312 }
313
314 if (restoreOrigDir) {
315 DebugOnly<BOOL> bCheck = SetCurrentDirectoryW(aOrigDir);
316 NS_ASSERTION(bCheck, "Error in Loading plugin");
317 }
318
319 return rv;
320 }
321
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;
328
329 nsresult rv = NS_OK;
330 DWORD zerome, versionsize;
331 void* verbuf = nullptr;
332
333 if (!mPlugin)
334 return NS_ERROR_NULL_POINTER;
335
336 nsAutoString fullPath;
337 if (NS_FAILED(rv = mPlugin->GetPath(fullPath)))
338 return rv;
339
340 if (!CanLoadPlugin(fullPath.get()))
341 return NS_ERROR_FAILURE;
342
343 nsAutoString fileName;
344 if (NS_FAILED(rv = mPlugin->GetLeafName(fileName)))
345 return rv;
346
347 LPCWSTR lpFilepath = fullPath.get();
348
349 versionsize = ::GetFileVersionInfoSizeW(lpFilepath, &zerome);
350
351 if (versionsize > 0)
352 verbuf = PR_Malloc(versionsize);
353 if (!verbuf)
354 return NS_ERROR_OUT_OF_MEMORY;
355
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);
363
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);
367
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);
375
376 PL_strfree(mimeType);
377 PL_strfree(mimeDescription);
378 PL_strfree(extensions);
379 }
380 else {
381 rv = NS_ERROR_FAILURE;
382 }
383
384 PR_Free(verbuf);
385
386 return rv;
387 }
388
389 nsresult nsPluginFile::FreePluginInfo(nsPluginInfo& info)
390 {
391 if (info.fName)
392 PL_strfree(info.fName);
393
394 if (info.fDescription)
395 PL_strfree(info.fDescription);
396
397 if (info.fMimeTypeArray)
398 FreeStringArray(info.fVariantCount, info.fMimeTypeArray);
399
400 if (info.fMimeDescriptionArray)
401 FreeStringArray(info.fVariantCount, info.fMimeDescriptionArray);
402
403 if (info.fExtensionArray)
404 FreeStringArray(info.fVariantCount, info.fExtensionArray);
405
406 if (info.fFullPath)
407 PL_strfree(info.fFullPath);
408
409 if (info.fFileName)
410 PL_strfree(info.fFileName);
411
412 if (info.fVersion)
413 PR_smprintf_free(info.fVersion);
414
415 ZeroMemory((void *)&info, sizeof(info));
416
417 return NS_OK;
418 }

mercurial