uriloader/exthandler/win/nsMIMEInfoWin.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial