|
1 /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
2 * |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "nsArrayEnumerator.h" |
|
8 #include "nsCOMArray.h" |
|
9 #include "nsIFile.h" |
|
10 #include "nsIVariant.h" |
|
11 #include "nsMIMEInfoWin.h" |
|
12 #include "nsNetUtil.h" |
|
13 #include <windows.h> |
|
14 #include <shellapi.h> |
|
15 #include "nsAutoPtr.h" |
|
16 #include "nsIMutableArray.h" |
|
17 #include "nsTArray.h" |
|
18 #include "shlobj.h" |
|
19 #include "windows.h" |
|
20 #include "nsIWindowsRegKey.h" |
|
21 #include "nsIProcess.h" |
|
22 #include "nsOSHelperAppService.h" |
|
23 #include "nsUnicharUtils.h" |
|
24 #include "nsITextToSubURI.h" |
|
25 |
|
26 #define RUNDLL32_EXE L"\\rundll32.exe" |
|
27 |
|
28 |
|
29 NS_IMPL_ISUPPORTS_INHERITED(nsMIMEInfoWin, nsMIMEInfoBase, nsIPropertyBag) |
|
30 |
|
31 nsMIMEInfoWin::~nsMIMEInfoWin() |
|
32 { |
|
33 } |
|
34 |
|
35 nsresult |
|
36 nsMIMEInfoWin::LaunchDefaultWithFile(nsIFile* aFile) |
|
37 { |
|
38 // Launch the file, unless it is an executable. |
|
39 bool executable = true; |
|
40 aFile->IsExecutable(&executable); |
|
41 if (executable) |
|
42 return NS_ERROR_FAILURE; |
|
43 |
|
44 return aFile->Launch(); |
|
45 } |
|
46 |
|
47 NS_IMETHODIMP |
|
48 nsMIMEInfoWin::LaunchWithFile(nsIFile* aFile) |
|
49 { |
|
50 nsresult rv; |
|
51 |
|
52 // it doesn't make any sense to call this on protocol handlers |
|
53 NS_ASSERTION(mClass == eMIMEInfo, |
|
54 "nsMIMEInfoBase should have mClass == eMIMEInfo"); |
|
55 |
|
56 if (mPreferredAction == useSystemDefault) { |
|
57 return LaunchDefaultWithFile(aFile); |
|
58 } |
|
59 |
|
60 if (mPreferredAction == useHelperApp) { |
|
61 if (!mPreferredApplication) |
|
62 return NS_ERROR_FILE_NOT_FOUND; |
|
63 |
|
64 // at the moment, we only know how to hand files off to local handlers |
|
65 nsCOMPtr<nsILocalHandlerApp> localHandler = |
|
66 do_QueryInterface(mPreferredApplication, &rv); |
|
67 NS_ENSURE_SUCCESS(rv, rv); |
|
68 |
|
69 nsCOMPtr<nsIFile> executable; |
|
70 rv = localHandler->GetExecutable(getter_AddRefs(executable)); |
|
71 NS_ENSURE_SUCCESS(rv, rv); |
|
72 |
|
73 nsAutoString path; |
|
74 aFile->GetPath(path); |
|
75 |
|
76 // Deal with local dll based handlers |
|
77 nsCString filename; |
|
78 executable->GetNativeLeafName(filename); |
|
79 if (filename.Length() > 4) { |
|
80 nsCString extension(Substring(filename, filename.Length() - 4, 4)); |
|
81 |
|
82 if (extension.LowerCaseEqualsLiteral(".dll")) { |
|
83 nsAutoString args; |
|
84 |
|
85 // executable is rundll32, everything else is a list of parameters, |
|
86 // including the dll handler. |
|
87 if (!GetDllLaunchInfo(executable, aFile, args, false)) |
|
88 return NS_ERROR_INVALID_ARG; |
|
89 |
|
90 WCHAR rundll32Path[MAX_PATH + sizeof(RUNDLL32_EXE) / sizeof(WCHAR) + 1] = {L'\0'}; |
|
91 if (!GetSystemDirectoryW(rundll32Path, MAX_PATH)) { |
|
92 return NS_ERROR_FILE_NOT_FOUND; |
|
93 } |
|
94 lstrcatW(rundll32Path, RUNDLL32_EXE); |
|
95 |
|
96 SHELLEXECUTEINFOW seinfo; |
|
97 memset(&seinfo, 0, sizeof(seinfo)); |
|
98 seinfo.cbSize = sizeof(SHELLEXECUTEINFOW); |
|
99 seinfo.fMask = 0; |
|
100 seinfo.hwnd = nullptr; |
|
101 seinfo.lpVerb = nullptr; |
|
102 seinfo.lpFile = rundll32Path; |
|
103 seinfo.lpParameters = args.get(); |
|
104 seinfo.lpDirectory = nullptr; |
|
105 seinfo.nShow = SW_SHOWNORMAL; |
|
106 if (ShellExecuteExW(&seinfo)) |
|
107 return NS_OK; |
|
108 |
|
109 switch ((LONG_PTR)seinfo.hInstApp) { |
|
110 case 0: |
|
111 case SE_ERR_OOM: |
|
112 return NS_ERROR_OUT_OF_MEMORY; |
|
113 case SE_ERR_ACCESSDENIED: |
|
114 return NS_ERROR_FILE_ACCESS_DENIED; |
|
115 case SE_ERR_ASSOCINCOMPLETE: |
|
116 case SE_ERR_NOASSOC: |
|
117 return NS_ERROR_UNEXPECTED; |
|
118 case SE_ERR_DDEBUSY: |
|
119 case SE_ERR_DDEFAIL: |
|
120 case SE_ERR_DDETIMEOUT: |
|
121 return NS_ERROR_NOT_AVAILABLE; |
|
122 case SE_ERR_DLLNOTFOUND: |
|
123 return NS_ERROR_FAILURE; |
|
124 case SE_ERR_SHARE: |
|
125 return NS_ERROR_FILE_IS_LOCKED; |
|
126 default: |
|
127 switch(GetLastError()) { |
|
128 case ERROR_FILE_NOT_FOUND: |
|
129 return NS_ERROR_FILE_NOT_FOUND; |
|
130 case ERROR_PATH_NOT_FOUND: |
|
131 return NS_ERROR_FILE_UNRECOGNIZED_PATH; |
|
132 case ERROR_BAD_FORMAT: |
|
133 return NS_ERROR_FILE_CORRUPTED; |
|
134 } |
|
135 |
|
136 } |
|
137 return NS_ERROR_FILE_EXECUTION_FAILED; |
|
138 } |
|
139 } |
|
140 return LaunchWithIProcess(executable, path); |
|
141 } |
|
142 |
|
143 return NS_ERROR_INVALID_ARG; |
|
144 } |
|
145 |
|
146 NS_IMETHODIMP |
|
147 nsMIMEInfoWin::GetHasDefaultHandler(bool * _retval) |
|
148 { |
|
149 // We have a default application if we have a description |
|
150 // We can ShellExecute anything; however, callers are probably interested if |
|
151 // there is really an application associated with this type of file |
|
152 *_retval = !mDefaultAppDescription.IsEmpty(); |
|
153 return NS_OK; |
|
154 } |
|
155 |
|
156 NS_IMETHODIMP |
|
157 nsMIMEInfoWin::GetEnumerator(nsISimpleEnumerator* *_retval) |
|
158 { |
|
159 nsCOMArray<nsIVariant> properties; |
|
160 |
|
161 nsCOMPtr<nsIVariant> variant; |
|
162 GetProperty(NS_LITERAL_STRING("defaultApplicationIconURL"), getter_AddRefs(variant)); |
|
163 if (variant) |
|
164 properties.AppendObject(variant); |
|
165 |
|
166 GetProperty(NS_LITERAL_STRING("customApplicationIconURL"), getter_AddRefs(variant)); |
|
167 if (variant) |
|
168 properties.AppendObject(variant); |
|
169 |
|
170 return NS_NewArrayEnumerator(_retval, properties); |
|
171 } |
|
172 |
|
173 static nsresult GetIconURLVariant(nsIFile* aApplication, nsIVariant* *_retval) |
|
174 { |
|
175 nsresult rv = CallCreateInstance("@mozilla.org/variant;1", _retval); |
|
176 if (NS_FAILED(rv)) |
|
177 return rv; |
|
178 nsAutoCString fileURLSpec; |
|
179 NS_GetURLSpecFromFile(aApplication, fileURLSpec); |
|
180 nsAutoCString iconURLSpec; iconURLSpec.AssignLiteral("moz-icon://"); |
|
181 iconURLSpec += fileURLSpec; |
|
182 nsCOMPtr<nsIWritableVariant> writable(do_QueryInterface(*_retval)); |
|
183 writable->SetAsAUTF8String(iconURLSpec); |
|
184 return NS_OK; |
|
185 } |
|
186 |
|
187 NS_IMETHODIMP |
|
188 nsMIMEInfoWin::GetProperty(const nsAString& aName, nsIVariant* *_retval) |
|
189 { |
|
190 nsresult rv; |
|
191 if (mDefaultApplication && aName.EqualsLiteral(PROPERTY_DEFAULT_APP_ICON_URL)) { |
|
192 rv = GetIconURLVariant(mDefaultApplication, _retval); |
|
193 NS_ENSURE_SUCCESS(rv, rv); |
|
194 } else if (mPreferredApplication && |
|
195 aName.EqualsLiteral(PROPERTY_CUSTOM_APP_ICON_URL)) { |
|
196 nsCOMPtr<nsILocalHandlerApp> localHandler = |
|
197 do_QueryInterface(mPreferredApplication, &rv); |
|
198 NS_ENSURE_SUCCESS(rv, rv); |
|
199 |
|
200 nsCOMPtr<nsIFile> executable; |
|
201 rv = localHandler->GetExecutable(getter_AddRefs(executable)); |
|
202 NS_ENSURE_SUCCESS(rv, rv); |
|
203 |
|
204 rv = GetIconURLVariant(executable, _retval); |
|
205 NS_ENSURE_SUCCESS(rv, rv); |
|
206 } |
|
207 |
|
208 return NS_OK; |
|
209 } |
|
210 |
|
211 // this implementation was pretty much copied verbatime from |
|
212 // Tony Robinson's code in nsExternalProtocolWin.cpp |
|
213 nsresult |
|
214 nsMIMEInfoWin::LoadUriInternal(nsIURI * aURL) |
|
215 { |
|
216 nsresult rv = NS_OK; |
|
217 |
|
218 // 1. Find the default app for this protocol |
|
219 // 2. Set up the command line |
|
220 // 3. Launch the app. |
|
221 |
|
222 // For now, we'll just cheat essentially, check for the command line |
|
223 // then just call ShellExecute()! |
|
224 |
|
225 if (aURL) |
|
226 { |
|
227 // extract the url spec from the url |
|
228 nsAutoCString urlSpec; |
|
229 aURL->GetAsciiSpec(urlSpec); |
|
230 |
|
231 // Unescape non-ASCII characters in the URL |
|
232 nsAutoCString urlCharset; |
|
233 nsAutoString utf16Spec; |
|
234 rv = aURL->GetOriginCharset(urlCharset); |
|
235 NS_ENSURE_SUCCESS(rv, rv); |
|
236 |
|
237 nsCOMPtr<nsITextToSubURI> textToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv); |
|
238 NS_ENSURE_SUCCESS(rv, rv); |
|
239 |
|
240 rv = textToSubURI->UnEscapeNonAsciiURI(urlCharset, urlSpec, utf16Spec); |
|
241 NS_ENSURE_SUCCESS(rv, rv); |
|
242 |
|
243 static const wchar_t cmdVerb[] = L"open"; |
|
244 SHELLEXECUTEINFOW sinfo; |
|
245 memset(&sinfo, 0, sizeof(sinfo)); |
|
246 sinfo.cbSize = sizeof(sinfo); |
|
247 sinfo.fMask = SEE_MASK_FLAG_DDEWAIT | |
|
248 SEE_MASK_FLAG_NO_UI; |
|
249 sinfo.hwnd = nullptr; |
|
250 sinfo.lpVerb = (LPWSTR)&cmdVerb; |
|
251 sinfo.nShow = SW_SHOWNORMAL; |
|
252 |
|
253 LPITEMIDLIST pidl = nullptr; |
|
254 SFGAOF sfgao; |
|
255 |
|
256 // Bug 394974 |
|
257 if (SUCCEEDED(SHParseDisplayName(utf16Spec.get(), nullptr, |
|
258 &pidl, 0, &sfgao))) { |
|
259 sinfo.lpIDList = pidl; |
|
260 sinfo.fMask |= SEE_MASK_INVOKEIDLIST; |
|
261 } else { |
|
262 // SHParseDisplayName failed. Bailing out as work around for |
|
263 // Microsoft Security Bulletin MS07-061 |
|
264 rv = NS_ERROR_FAILURE; |
|
265 } |
|
266 if (NS_SUCCEEDED(rv)) { |
|
267 BOOL result = ShellExecuteExW(&sinfo); |
|
268 if (!result || ((LONG_PTR)sinfo.hInstApp) < 32) |
|
269 rv = NS_ERROR_FAILURE; |
|
270 } |
|
271 if (pidl) |
|
272 CoTaskMemFree(pidl); |
|
273 } |
|
274 |
|
275 return rv; |
|
276 } |
|
277 |
|
278 // Given a path to a local file, return its nsILocalHandlerApp instance. |
|
279 bool nsMIMEInfoWin::GetLocalHandlerApp(const nsAString& aCommandHandler, |
|
280 nsCOMPtr<nsILocalHandlerApp>& aApp) |
|
281 { |
|
282 nsCOMPtr<nsIFile> locfile; |
|
283 nsresult rv = |
|
284 NS_NewLocalFile(aCommandHandler, true, getter_AddRefs(locfile)); |
|
285 if (NS_FAILED(rv)) |
|
286 return false; |
|
287 |
|
288 aApp = do_CreateInstance("@mozilla.org/uriloader/local-handler-app;1"); |
|
289 if (!aApp) |
|
290 return false; |
|
291 |
|
292 aApp->SetExecutable(locfile); |
|
293 return true; |
|
294 } |
|
295 |
|
296 // Return the cleaned up file path associated with a command verb |
|
297 // located in root/Applications. |
|
298 bool nsMIMEInfoWin::GetAppsVerbCommandHandler(const nsAString& appExeName, |
|
299 nsAString& applicationPath, |
|
300 bool edit) |
|
301 { |
|
302 nsCOMPtr<nsIWindowsRegKey> appKey = |
|
303 do_CreateInstance("@mozilla.org/windows-registry-key;1"); |
|
304 if (!appKey) |
|
305 return false; |
|
306 |
|
307 // HKEY_CLASSES_ROOT\Applications\iexplore.exe |
|
308 nsAutoString applicationsPath; |
|
309 applicationsPath.AppendLiteral("Applications\\"); |
|
310 applicationsPath.Append(appExeName); |
|
311 |
|
312 nsresult rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
313 applicationsPath, |
|
314 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
315 if (NS_FAILED(rv)) |
|
316 return false; |
|
317 |
|
318 // Check for the NoOpenWith flag, if it exists |
|
319 uint32_t value; |
|
320 if (NS_SUCCEEDED(appKey->ReadIntValue( |
|
321 NS_LITERAL_STRING("NoOpenWith"), &value)) && |
|
322 value == 1) |
|
323 return false; |
|
324 |
|
325 nsAutoString dummy; |
|
326 if (NS_SUCCEEDED(appKey->ReadStringValue( |
|
327 NS_LITERAL_STRING("NoOpenWith"), dummy))) |
|
328 return false; |
|
329 |
|
330 appKey->Close(); |
|
331 |
|
332 // HKEY_CLASSES_ROOT\Applications\iexplore.exe\shell\open\command |
|
333 applicationsPath.AssignLiteral("Applications\\"); |
|
334 applicationsPath.Append(appExeName); |
|
335 if (!edit) |
|
336 applicationsPath.AppendLiteral("\\shell\\open\\command"); |
|
337 else |
|
338 applicationsPath.AppendLiteral("\\shell\\edit\\command"); |
|
339 |
|
340 |
|
341 rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
342 applicationsPath, |
|
343 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
344 if (NS_FAILED(rv)) |
|
345 return false; |
|
346 |
|
347 nsAutoString appFilesystemCommand; |
|
348 if (NS_SUCCEEDED(appKey->ReadStringValue(EmptyString(), |
|
349 appFilesystemCommand))) { |
|
350 |
|
351 // Expand environment vars, clean up any misc. |
|
352 if (!nsOSHelperAppService::CleanupCmdHandlerPath(appFilesystemCommand)) |
|
353 return false; |
|
354 |
|
355 applicationPath = appFilesystemCommand; |
|
356 return true; |
|
357 } |
|
358 return false; |
|
359 } |
|
360 |
|
361 // Return a fully populated command string based on |
|
362 // passing information. Used in launchWithFile to trace |
|
363 // back to the full handler path based on the dll. |
|
364 // (dll, targetfile, return args, open/edit) |
|
365 bool nsMIMEInfoWin::GetDllLaunchInfo(nsIFile * aDll, |
|
366 nsIFile * aFile, |
|
367 nsAString& args, |
|
368 bool edit) |
|
369 { |
|
370 if (!aDll || !aFile) |
|
371 return false; |
|
372 |
|
373 nsString appExeName; |
|
374 aDll->GetLeafName(appExeName); |
|
375 |
|
376 nsCOMPtr<nsIWindowsRegKey> appKey = |
|
377 do_CreateInstance("@mozilla.org/windows-registry-key;1"); |
|
378 if (!appKey) |
|
379 return false; |
|
380 |
|
381 // HKEY_CLASSES_ROOT\Applications\iexplore.exe |
|
382 nsAutoString applicationsPath; |
|
383 applicationsPath.AppendLiteral("Applications\\"); |
|
384 applicationsPath.Append(appExeName); |
|
385 |
|
386 nsresult rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
387 applicationsPath, |
|
388 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
389 if (NS_FAILED(rv)) |
|
390 return false; |
|
391 |
|
392 // Check for the NoOpenWith flag, if it exists |
|
393 uint32_t value; |
|
394 rv = appKey->ReadIntValue(NS_LITERAL_STRING("NoOpenWith"), &value); |
|
395 if (NS_SUCCEEDED(rv) && value == 1) |
|
396 return false; |
|
397 |
|
398 nsAutoString dummy; |
|
399 if (NS_SUCCEEDED(appKey->ReadStringValue(NS_LITERAL_STRING("NoOpenWith"), |
|
400 dummy))) |
|
401 return false; |
|
402 |
|
403 appKey->Close(); |
|
404 |
|
405 // HKEY_CLASSES_ROOT\Applications\iexplore.exe\shell\open\command |
|
406 applicationsPath.AssignLiteral("Applications\\"); |
|
407 applicationsPath.Append(appExeName); |
|
408 if (!edit) |
|
409 applicationsPath.AppendLiteral("\\shell\\open\\command"); |
|
410 else |
|
411 applicationsPath.AppendLiteral("\\shell\\edit\\command"); |
|
412 |
|
413 rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
414 applicationsPath, |
|
415 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
416 if (NS_FAILED(rv)) |
|
417 return false; |
|
418 |
|
419 nsAutoString appFilesystemCommand; |
|
420 if (NS_SUCCEEDED(appKey->ReadStringValue(EmptyString(), |
|
421 appFilesystemCommand))) { |
|
422 // Replace embedded environment variables. |
|
423 uint32_t bufLength = |
|
424 ::ExpandEnvironmentStringsW(appFilesystemCommand.get(), |
|
425 L"", 0); |
|
426 if (bufLength == 0) // Error |
|
427 return false; |
|
428 |
|
429 nsAutoArrayPtr<wchar_t> destination(new wchar_t[bufLength]); |
|
430 if (!destination) |
|
431 return false; |
|
432 if (!::ExpandEnvironmentStringsW(appFilesystemCommand.get(), |
|
433 destination, |
|
434 bufLength)) |
|
435 return false; |
|
436 |
|
437 appFilesystemCommand = static_cast<const wchar_t*>(destination); |
|
438 |
|
439 // C:\Windows\System32\rundll32.exe "C:\Program Files\Windows |
|
440 // Photo Gallery\PhotoViewer.dll", ImageView_Fullscreen %1 |
|
441 nsAutoString params; |
|
442 NS_NAMED_LITERAL_STRING(rundllSegment, "rundll32.exe "); |
|
443 int32_t index = appFilesystemCommand.Find(rundllSegment); |
|
444 if (index > kNotFound) { |
|
445 params.Append(Substring(appFilesystemCommand, |
|
446 index + rundllSegment.Length())); |
|
447 } else { |
|
448 params.Append(appFilesystemCommand); |
|
449 } |
|
450 |
|
451 // check to make sure we have a %1 and fill it |
|
452 NS_NAMED_LITERAL_STRING(percentOneParam, "%1"); |
|
453 index = params.Find(percentOneParam); |
|
454 if (index == kNotFound) // no parameter |
|
455 return false; |
|
456 |
|
457 nsString target; |
|
458 aFile->GetTarget(target); |
|
459 params.Replace(index, 2, target); |
|
460 |
|
461 args = params; |
|
462 |
|
463 return true; |
|
464 } |
|
465 return false; |
|
466 } |
|
467 |
|
468 // Return the cleaned up file path associated with a progid command |
|
469 // verb located in root. |
|
470 bool nsMIMEInfoWin::GetProgIDVerbCommandHandler(const nsAString& appProgIDName, |
|
471 nsAString& applicationPath, |
|
472 bool edit) |
|
473 { |
|
474 nsCOMPtr<nsIWindowsRegKey> appKey = |
|
475 do_CreateInstance("@mozilla.org/windows-registry-key;1"); |
|
476 if (!appKey) |
|
477 return false; |
|
478 |
|
479 nsAutoString appProgId(appProgIDName); |
|
480 |
|
481 // HKEY_CLASSES_ROOT\Windows.XPSReachViewer\shell\open\command |
|
482 if (!edit) |
|
483 appProgId.AppendLiteral("\\shell\\open\\command"); |
|
484 else |
|
485 appProgId.AppendLiteral("\\shell\\edit\\command"); |
|
486 |
|
487 nsresult rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
488 appProgId, |
|
489 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
490 if (NS_FAILED(rv)) |
|
491 return false; |
|
492 |
|
493 nsAutoString appFilesystemCommand; |
|
494 if (NS_SUCCEEDED(appKey->ReadStringValue(EmptyString(), appFilesystemCommand))) { |
|
495 |
|
496 // Expand environment vars, clean up any misc. |
|
497 if (!nsOSHelperAppService::CleanupCmdHandlerPath(appFilesystemCommand)) |
|
498 return false; |
|
499 |
|
500 applicationPath = appFilesystemCommand; |
|
501 return true; |
|
502 } |
|
503 return false; |
|
504 } |
|
505 |
|
506 // Helper routine used in tracking app lists. Converts path |
|
507 // entries to lower case and stores them in the trackList array. |
|
508 void nsMIMEInfoWin::ProcessPath(nsCOMPtr<nsIMutableArray>& appList, |
|
509 nsTArray<nsString>& trackList, |
|
510 const nsAString& appFilesystemCommand) |
|
511 { |
|
512 nsAutoString lower(appFilesystemCommand); |
|
513 ToLowerCase(lower); |
|
514 |
|
515 // Don't include firefox.exe in the list |
|
516 WCHAR exe[MAX_PATH+1]; |
|
517 uint32_t len = GetModuleFileNameW(nullptr, exe, MAX_PATH); |
|
518 if (len < MAX_PATH && len != 0) { |
|
519 uint32_t index = lower.Find(exe); |
|
520 if (index != -1) |
|
521 return; |
|
522 } |
|
523 |
|
524 nsCOMPtr<nsILocalHandlerApp> aApp; |
|
525 if (!GetLocalHandlerApp(appFilesystemCommand, aApp)) |
|
526 return; |
|
527 |
|
528 // Save in our main tracking arrays |
|
529 appList->AppendElement(aApp, false); |
|
530 trackList.AppendElement(lower); |
|
531 } |
|
532 |
|
533 // Helper routine that handles a compare between a path |
|
534 // and an array of paths. |
|
535 static bool IsPathInList(nsAString& appPath, |
|
536 nsTArray<nsString>& trackList) |
|
537 { |
|
538 // trackList data is always lowercase, see ProcessPath |
|
539 // above. |
|
540 nsAutoString tmp(appPath); |
|
541 ToLowerCase(tmp); |
|
542 |
|
543 for (uint32_t i = 0; i < trackList.Length(); i++) { |
|
544 if (tmp.Equals(trackList[i])) |
|
545 return true; |
|
546 } |
|
547 return false; |
|
548 } |
|
549 |
|
550 /** |
|
551 * Returns a list of nsILocalHandlerApp objects containing local |
|
552 * handlers associated with this mimeinfo. Implemented per |
|
553 * platform using information in this object to generate the |
|
554 * best list. Typically used for an "open with" style user |
|
555 * option. |
|
556 * |
|
557 * @return nsIArray of nsILocalHandlerApp |
|
558 */ |
|
559 NS_IMETHODIMP |
|
560 nsMIMEInfoWin::GetPossibleLocalHandlers(nsIArray **_retval) |
|
561 { |
|
562 nsresult rv; |
|
563 |
|
564 *_retval = nullptr; |
|
565 |
|
566 nsCOMPtr<nsIMutableArray> appList = |
|
567 do_CreateInstance("@mozilla.org/array;1"); |
|
568 |
|
569 if (!appList) |
|
570 return NS_ERROR_FAILURE; |
|
571 |
|
572 nsTArray<nsString> trackList; |
|
573 |
|
574 nsAutoCString fileExt; |
|
575 GetPrimaryExtension(fileExt); |
|
576 |
|
577 nsCOMPtr<nsIWindowsRegKey> regKey = |
|
578 do_CreateInstance("@mozilla.org/windows-registry-key;1"); |
|
579 if (!regKey) |
|
580 return NS_ERROR_FAILURE; |
|
581 nsCOMPtr<nsIWindowsRegKey> appKey = |
|
582 do_CreateInstance("@mozilla.org/windows-registry-key;1"); |
|
583 if (!appKey) |
|
584 return NS_ERROR_FAILURE; |
|
585 |
|
586 nsAutoString workingRegistryPath; |
|
587 |
|
588 bool extKnown = false; |
|
589 if (fileExt.IsEmpty()) { |
|
590 extKnown = true; |
|
591 // Mime type discovery is possible in some cases, through |
|
592 // HKEY_CLASSES_ROOT\MIME\Database\Content Type, however, a number |
|
593 // of file extensions related to mime type are simply not defined, |
|
594 // (application/rss+xml & application/atom+xml are good examples) |
|
595 // in which case we can only provide a generic list. |
|
596 nsAutoCString mimeType; |
|
597 GetMIMEType(mimeType); |
|
598 if (!mimeType.IsEmpty()) { |
|
599 workingRegistryPath.AppendLiteral("MIME\\Database\\Content Type\\"); |
|
600 workingRegistryPath.Append(NS_ConvertASCIItoUTF16(mimeType)); |
|
601 |
|
602 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
603 workingRegistryPath, |
|
604 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
605 if(NS_SUCCEEDED(rv)) { |
|
606 nsAutoString mimeFileExt; |
|
607 if (NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(), mimeFileExt))) { |
|
608 CopyUTF16toUTF8(mimeFileExt, fileExt); |
|
609 extKnown = false; |
|
610 } |
|
611 } |
|
612 } |
|
613 } |
|
614 |
|
615 nsAutoString fileExtToUse; |
|
616 if (fileExt.First() != '.') |
|
617 fileExtToUse = char16_t('.'); |
|
618 fileExtToUse.Append(NS_ConvertUTF8toUTF16(fileExt)); |
|
619 |
|
620 // Note, the order in which these occur has an effect on the |
|
621 // validity of the resulting display list. |
|
622 |
|
623 if (!extKnown) { |
|
624 // 1) Get the default handler if it exists |
|
625 workingRegistryPath = fileExtToUse; |
|
626 |
|
627 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
628 workingRegistryPath, |
|
629 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
630 if (NS_SUCCEEDED(rv)) { |
|
631 nsAutoString appProgId; |
|
632 if (NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(), appProgId))) { |
|
633 // Bug 358297 - ignore the embedded internet explorer handler |
|
634 if (appProgId != NS_LITERAL_STRING("XPSViewer.Document")) { |
|
635 nsAutoString appFilesystemCommand; |
|
636 if (GetProgIDVerbCommandHandler(appProgId, |
|
637 appFilesystemCommand, |
|
638 false) && |
|
639 !IsPathInList(appFilesystemCommand, trackList)) { |
|
640 ProcessPath(appList, trackList, appFilesystemCommand); |
|
641 } |
|
642 } |
|
643 } |
|
644 regKey->Close(); |
|
645 } |
|
646 |
|
647 |
|
648 // 2) list HKEY_CLASSES_ROOT\.ext\OpenWithList |
|
649 |
|
650 workingRegistryPath = fileExtToUse; |
|
651 workingRegistryPath.AppendLiteral("\\OpenWithList"); |
|
652 |
|
653 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
654 workingRegistryPath, |
|
655 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
656 if (NS_SUCCEEDED(rv)) { |
|
657 uint32_t count = 0; |
|
658 if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) { |
|
659 for (uint32_t index = 0; index < count; index++) { |
|
660 nsAutoString appName; |
|
661 if (NS_FAILED(regKey->GetValueName(index, appName))) |
|
662 continue; |
|
663 |
|
664 // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params" |
|
665 nsAutoString appFilesystemCommand; |
|
666 if (!GetAppsVerbCommandHandler(appName, |
|
667 appFilesystemCommand, |
|
668 false) || |
|
669 IsPathInList(appFilesystemCommand, trackList)) |
|
670 continue; |
|
671 ProcessPath(appList, trackList, appFilesystemCommand); |
|
672 } |
|
673 } |
|
674 regKey->Close(); |
|
675 } |
|
676 |
|
677 |
|
678 // 3) List HKEY_CLASSES_ROOT\.ext\OpenWithProgids, with the |
|
679 // different step of resolving the progids for the command handler. |
|
680 |
|
681 workingRegistryPath = fileExtToUse; |
|
682 workingRegistryPath.AppendLiteral("\\OpenWithProgids"); |
|
683 |
|
684 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
685 workingRegistryPath, |
|
686 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
687 if (NS_SUCCEEDED(rv)) { |
|
688 uint32_t count = 0; |
|
689 if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) { |
|
690 for (uint32_t index = 0; index < count; index++) { |
|
691 // HKEY_CLASSES_ROOT\.ext\OpenWithProgids\Windows.XPSReachViewer |
|
692 nsAutoString appProgId; |
|
693 if (NS_FAILED(regKey->GetValueName(index, appProgId))) |
|
694 continue; |
|
695 |
|
696 nsAutoString appFilesystemCommand; |
|
697 if (!GetProgIDVerbCommandHandler(appProgId, |
|
698 appFilesystemCommand, |
|
699 false) || |
|
700 IsPathInList(appFilesystemCommand, trackList)) |
|
701 continue; |
|
702 ProcessPath(appList, trackList, appFilesystemCommand); |
|
703 } |
|
704 } |
|
705 regKey->Close(); |
|
706 } |
|
707 |
|
708 |
|
709 // 4) Add any non configured applications located in the MRU list |
|
710 |
|
711 // HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion |
|
712 // \Explorer\FileExts\.ext\OpenWithList |
|
713 workingRegistryPath = |
|
714 NS_LITERAL_STRING("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\"); |
|
715 workingRegistryPath += fileExtToUse; |
|
716 workingRegistryPath.AppendLiteral("\\OpenWithList"); |
|
717 |
|
718 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, |
|
719 workingRegistryPath, |
|
720 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
721 if (NS_SUCCEEDED(rv)) { |
|
722 uint32_t count = 0; |
|
723 if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) { |
|
724 for (uint32_t index = 0; index < count; index++) { |
|
725 nsAutoString appName, appValue; |
|
726 if (NS_FAILED(regKey->GetValueName(index, appName))) |
|
727 continue; |
|
728 if (appName.EqualsLiteral("MRUList")) |
|
729 continue; |
|
730 if (NS_FAILED(regKey->ReadStringValue(appName, appValue))) |
|
731 continue; |
|
732 |
|
733 // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params" |
|
734 nsAutoString appFilesystemCommand; |
|
735 if (!GetAppsVerbCommandHandler(appValue, |
|
736 appFilesystemCommand, |
|
737 false) || |
|
738 IsPathInList(appFilesystemCommand, trackList)) |
|
739 continue; |
|
740 ProcessPath(appList, trackList, appFilesystemCommand); |
|
741 } |
|
742 } |
|
743 } |
|
744 |
|
745 |
|
746 // 5) Add any non configured progids in the MRU list, with the |
|
747 // different step of resolving the progids for the command handler. |
|
748 |
|
749 // HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion |
|
750 // \Explorer\FileExts\.ext\OpenWithProgids |
|
751 workingRegistryPath = |
|
752 NS_LITERAL_STRING("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\"); |
|
753 workingRegistryPath += fileExtToUse; |
|
754 workingRegistryPath.AppendLiteral("\\OpenWithProgids"); |
|
755 |
|
756 regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, |
|
757 workingRegistryPath, |
|
758 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
759 if (NS_SUCCEEDED(rv)) { |
|
760 uint32_t count = 0; |
|
761 if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) { |
|
762 for (uint32_t index = 0; index < count; index++) { |
|
763 nsAutoString appIndex, appProgId; |
|
764 if (NS_FAILED(regKey->GetValueName(index, appProgId))) |
|
765 continue; |
|
766 |
|
767 nsAutoString appFilesystemCommand; |
|
768 if (!GetProgIDVerbCommandHandler(appProgId, |
|
769 appFilesystemCommand, |
|
770 false) || |
|
771 IsPathInList(appFilesystemCommand, trackList)) |
|
772 continue; |
|
773 ProcessPath(appList, trackList, appFilesystemCommand); |
|
774 } |
|
775 } |
|
776 regKey->Close(); |
|
777 } |
|
778 |
|
779 |
|
780 // 6) Check the perceived type value, and use this to lookup the perceivedtype |
|
781 // open with list. |
|
782 // http://msdn2.microsoft.com/en-us/library/aa969373.aspx |
|
783 |
|
784 workingRegistryPath = fileExtToUse; |
|
785 |
|
786 regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
787 workingRegistryPath, |
|
788 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
789 if (NS_SUCCEEDED(rv)) { |
|
790 nsAutoString perceivedType; |
|
791 rv = regKey->ReadStringValue(NS_LITERAL_STRING("PerceivedType"), |
|
792 perceivedType); |
|
793 if (NS_SUCCEEDED(rv)) { |
|
794 nsAutoString openWithListPath(NS_LITERAL_STRING("SystemFileAssociations\\")); |
|
795 openWithListPath.Append(perceivedType); // no period |
|
796 openWithListPath.Append(NS_LITERAL_STRING("\\OpenWithList")); |
|
797 |
|
798 nsresult rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
799 openWithListPath, |
|
800 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
801 if (NS_SUCCEEDED(rv)) { |
|
802 uint32_t count = 0; |
|
803 if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) { |
|
804 for (uint32_t index = 0; index < count; index++) { |
|
805 nsAutoString appName; |
|
806 if (NS_FAILED(regKey->GetValueName(index, appName))) |
|
807 continue; |
|
808 |
|
809 // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params" |
|
810 nsAutoString appFilesystemCommand; |
|
811 if (!GetAppsVerbCommandHandler(appName, appFilesystemCommand, |
|
812 false) || |
|
813 IsPathInList(appFilesystemCommand, trackList)) |
|
814 continue; |
|
815 ProcessPath(appList, trackList, appFilesystemCommand); |
|
816 } |
|
817 } |
|
818 } |
|
819 } |
|
820 } |
|
821 } // extKnown == false |
|
822 |
|
823 |
|
824 // 7) list global HKEY_CLASSES_ROOT\*\OpenWithList |
|
825 // Listing general purpose handlers, not specific to a mime type or file extension |
|
826 |
|
827 workingRegistryPath = NS_LITERAL_STRING("*\\OpenWithList"); |
|
828 |
|
829 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
830 workingRegistryPath, |
|
831 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
832 if (NS_SUCCEEDED(rv)) { |
|
833 uint32_t count = 0; |
|
834 if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) { |
|
835 for (uint32_t index = 0; index < count; index++) { |
|
836 nsAutoString appName; |
|
837 if (NS_FAILED(regKey->GetValueName(index, appName))) |
|
838 continue; |
|
839 |
|
840 // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params" |
|
841 nsAutoString appFilesystemCommand; |
|
842 if (!GetAppsVerbCommandHandler(appName, appFilesystemCommand, |
|
843 false) || |
|
844 IsPathInList(appFilesystemCommand, trackList)) |
|
845 continue; |
|
846 ProcessPath(appList, trackList, appFilesystemCommand); |
|
847 } |
|
848 } |
|
849 regKey->Close(); |
|
850 } |
|
851 |
|
852 |
|
853 // 8) General application's list - not file extension specific on windows |
|
854 workingRegistryPath = NS_LITERAL_STRING("Applications"); |
|
855 |
|
856 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, |
|
857 workingRegistryPath, |
|
858 nsIWindowsRegKey::ACCESS_ENUMERATE_SUB_KEYS| |
|
859 nsIWindowsRegKey::ACCESS_QUERY_VALUE); |
|
860 if (NS_SUCCEEDED(rv)) { |
|
861 uint32_t count = 0; |
|
862 if (NS_SUCCEEDED(regKey->GetChildCount(&count)) && count > 0) { |
|
863 for (uint32_t index = 0; index < count; index++) { |
|
864 nsAutoString appName; |
|
865 if (NS_FAILED(regKey->GetChildName(index, appName))) |
|
866 continue; |
|
867 |
|
868 // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params" |
|
869 nsAutoString appFilesystemCommand; |
|
870 if (!GetAppsVerbCommandHandler(appName, appFilesystemCommand, |
|
871 false) || |
|
872 IsPathInList(appFilesystemCommand, trackList)) |
|
873 continue; |
|
874 ProcessPath(appList, trackList, appFilesystemCommand); |
|
875 } |
|
876 } |
|
877 } |
|
878 |
|
879 // Return to the caller |
|
880 *_retval = appList; |
|
881 NS_ADDREF(*_retval); |
|
882 |
|
883 return NS_OK; |
|
884 } |