|
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 } |