uriloader/exthandler/win/nsMIMEInfoWin.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/uriloader/exthandler/win/nsMIMEInfoWin.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,884 @@
     1.4 +/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + *
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "nsArrayEnumerator.h"
    1.11 +#include "nsCOMArray.h"
    1.12 +#include "nsIFile.h"
    1.13 +#include "nsIVariant.h"
    1.14 +#include "nsMIMEInfoWin.h"
    1.15 +#include "nsNetUtil.h"
    1.16 +#include <windows.h>
    1.17 +#include <shellapi.h>
    1.18 +#include "nsAutoPtr.h"
    1.19 +#include "nsIMutableArray.h"
    1.20 +#include "nsTArray.h"
    1.21 +#include "shlobj.h"
    1.22 +#include "windows.h"
    1.23 +#include "nsIWindowsRegKey.h"
    1.24 +#include "nsIProcess.h"
    1.25 +#include "nsOSHelperAppService.h"
    1.26 +#include "nsUnicharUtils.h"
    1.27 +#include "nsITextToSubURI.h"
    1.28 +
    1.29 +#define RUNDLL32_EXE L"\\rundll32.exe"
    1.30 +
    1.31 +
    1.32 +NS_IMPL_ISUPPORTS_INHERITED(nsMIMEInfoWin, nsMIMEInfoBase, nsIPropertyBag)
    1.33 +
    1.34 +nsMIMEInfoWin::~nsMIMEInfoWin()
    1.35 +{
    1.36 +}
    1.37 +
    1.38 +nsresult
    1.39 +nsMIMEInfoWin::LaunchDefaultWithFile(nsIFile* aFile)
    1.40 +{
    1.41 +  // Launch the file, unless it is an executable.
    1.42 +  bool executable = true;
    1.43 +  aFile->IsExecutable(&executable);
    1.44 +  if (executable)
    1.45 +    return NS_ERROR_FAILURE;
    1.46 +
    1.47 +  return aFile->Launch();
    1.48 +}
    1.49 +
    1.50 +NS_IMETHODIMP
    1.51 +nsMIMEInfoWin::LaunchWithFile(nsIFile* aFile)
    1.52 +{
    1.53 +  nsresult rv;
    1.54 +
    1.55 +  // it doesn't make any sense to call this on protocol handlers
    1.56 +  NS_ASSERTION(mClass == eMIMEInfo,
    1.57 +               "nsMIMEInfoBase should have mClass == eMIMEInfo");
    1.58 +
    1.59 +  if (mPreferredAction == useSystemDefault) {
    1.60 +    return LaunchDefaultWithFile(aFile);
    1.61 +  }
    1.62 +
    1.63 +  if (mPreferredAction == useHelperApp) {
    1.64 +    if (!mPreferredApplication)
    1.65 +      return NS_ERROR_FILE_NOT_FOUND;
    1.66 +
    1.67 +    // at the moment, we only know how to hand files off to local handlers
    1.68 +    nsCOMPtr<nsILocalHandlerApp> localHandler = 
    1.69 +      do_QueryInterface(mPreferredApplication, &rv);
    1.70 +    NS_ENSURE_SUCCESS(rv, rv);
    1.71 +
    1.72 +    nsCOMPtr<nsIFile> executable;
    1.73 +    rv = localHandler->GetExecutable(getter_AddRefs(executable));
    1.74 +    NS_ENSURE_SUCCESS(rv, rv);
    1.75 +
    1.76 +    nsAutoString path;
    1.77 +    aFile->GetPath(path);
    1.78 +
    1.79 +    // Deal with local dll based handlers
    1.80 +    nsCString filename;
    1.81 +    executable->GetNativeLeafName(filename);
    1.82 +    if (filename.Length() > 4) {
    1.83 +      nsCString extension(Substring(filename, filename.Length() - 4, 4));
    1.84 +
    1.85 +      if (extension.LowerCaseEqualsLiteral(".dll")) {
    1.86 +        nsAutoString args;
    1.87 +
    1.88 +        // executable is rundll32, everything else is a list of parameters, 
    1.89 +        // including the dll handler.
    1.90 +        if (!GetDllLaunchInfo(executable, aFile, args, false))
    1.91 +          return NS_ERROR_INVALID_ARG;
    1.92 +
    1.93 +        WCHAR rundll32Path[MAX_PATH + sizeof(RUNDLL32_EXE) / sizeof(WCHAR) + 1] = {L'\0'};
    1.94 +        if (!GetSystemDirectoryW(rundll32Path, MAX_PATH)) {
    1.95 +          return NS_ERROR_FILE_NOT_FOUND;
    1.96 +        }
    1.97 +        lstrcatW(rundll32Path, RUNDLL32_EXE);
    1.98 +
    1.99 +        SHELLEXECUTEINFOW seinfo;
   1.100 +        memset(&seinfo, 0, sizeof(seinfo));
   1.101 +        seinfo.cbSize = sizeof(SHELLEXECUTEINFOW);
   1.102 +        seinfo.fMask  = 0;
   1.103 +        seinfo.hwnd   = nullptr;
   1.104 +        seinfo.lpVerb = nullptr;
   1.105 +        seinfo.lpFile = rundll32Path;
   1.106 +        seinfo.lpParameters =  args.get();
   1.107 +        seinfo.lpDirectory  = nullptr;
   1.108 +        seinfo.nShow  = SW_SHOWNORMAL;
   1.109 +        if (ShellExecuteExW(&seinfo))
   1.110 +          return NS_OK;
   1.111 +
   1.112 +        switch ((LONG_PTR)seinfo.hInstApp) {
   1.113 +          case 0:
   1.114 +          case SE_ERR_OOM:
   1.115 +            return NS_ERROR_OUT_OF_MEMORY;
   1.116 +          case SE_ERR_ACCESSDENIED:
   1.117 +            return NS_ERROR_FILE_ACCESS_DENIED;
   1.118 +          case SE_ERR_ASSOCINCOMPLETE:
   1.119 +          case SE_ERR_NOASSOC:
   1.120 +            return NS_ERROR_UNEXPECTED;
   1.121 +          case SE_ERR_DDEBUSY:
   1.122 +          case SE_ERR_DDEFAIL:
   1.123 +          case SE_ERR_DDETIMEOUT:
   1.124 +            return NS_ERROR_NOT_AVAILABLE;
   1.125 +          case SE_ERR_DLLNOTFOUND:
   1.126 +            return NS_ERROR_FAILURE;
   1.127 +          case SE_ERR_SHARE:
   1.128 +            return NS_ERROR_FILE_IS_LOCKED;
   1.129 +          default:
   1.130 +            switch(GetLastError()) {
   1.131 +              case ERROR_FILE_NOT_FOUND:
   1.132 +                return NS_ERROR_FILE_NOT_FOUND;
   1.133 +              case ERROR_PATH_NOT_FOUND:
   1.134 +                return NS_ERROR_FILE_UNRECOGNIZED_PATH;
   1.135 +              case ERROR_BAD_FORMAT:
   1.136 +                return NS_ERROR_FILE_CORRUPTED;
   1.137 +            }
   1.138 +
   1.139 +        }
   1.140 +        return NS_ERROR_FILE_EXECUTION_FAILED;
   1.141 +      }
   1.142 +    }
   1.143 +    return LaunchWithIProcess(executable, path);
   1.144 +  }
   1.145 +
   1.146 +  return NS_ERROR_INVALID_ARG;
   1.147 +}
   1.148 +
   1.149 +NS_IMETHODIMP
   1.150 +nsMIMEInfoWin::GetHasDefaultHandler(bool * _retval)
   1.151 +{
   1.152 +  // We have a default application if we have a description
   1.153 +  // We can ShellExecute anything; however, callers are probably interested if
   1.154 +  // there is really an application associated with this type of file
   1.155 +  *_retval = !mDefaultAppDescription.IsEmpty();
   1.156 +  return NS_OK;
   1.157 +}
   1.158 +
   1.159 +NS_IMETHODIMP
   1.160 +nsMIMEInfoWin::GetEnumerator(nsISimpleEnumerator* *_retval)
   1.161 +{
   1.162 +  nsCOMArray<nsIVariant> properties;
   1.163 +
   1.164 +  nsCOMPtr<nsIVariant> variant;
   1.165 +  GetProperty(NS_LITERAL_STRING("defaultApplicationIconURL"), getter_AddRefs(variant));
   1.166 +  if (variant)
   1.167 +    properties.AppendObject(variant);
   1.168 +
   1.169 +  GetProperty(NS_LITERAL_STRING("customApplicationIconURL"), getter_AddRefs(variant));
   1.170 +  if (variant)
   1.171 +    properties.AppendObject(variant);
   1.172 +
   1.173 +  return NS_NewArrayEnumerator(_retval, properties);
   1.174 +}
   1.175 +
   1.176 +static nsresult GetIconURLVariant(nsIFile* aApplication, nsIVariant* *_retval)
   1.177 +{
   1.178 +  nsresult rv = CallCreateInstance("@mozilla.org/variant;1", _retval);
   1.179 +  if (NS_FAILED(rv))
   1.180 +    return rv;
   1.181 +  nsAutoCString fileURLSpec;
   1.182 +  NS_GetURLSpecFromFile(aApplication, fileURLSpec);
   1.183 +  nsAutoCString iconURLSpec; iconURLSpec.AssignLiteral("moz-icon://");
   1.184 +  iconURLSpec += fileURLSpec;
   1.185 +  nsCOMPtr<nsIWritableVariant> writable(do_QueryInterface(*_retval));
   1.186 +  writable->SetAsAUTF8String(iconURLSpec);
   1.187 +  return NS_OK;
   1.188 +}
   1.189 +
   1.190 +NS_IMETHODIMP
   1.191 +nsMIMEInfoWin::GetProperty(const nsAString& aName, nsIVariant* *_retval)
   1.192 +{
   1.193 +  nsresult rv;
   1.194 +  if (mDefaultApplication && aName.EqualsLiteral(PROPERTY_DEFAULT_APP_ICON_URL)) {
   1.195 +    rv = GetIconURLVariant(mDefaultApplication, _retval);
   1.196 +    NS_ENSURE_SUCCESS(rv, rv);    
   1.197 +  } else if (mPreferredApplication && 
   1.198 +             aName.EqualsLiteral(PROPERTY_CUSTOM_APP_ICON_URL)) {
   1.199 +    nsCOMPtr<nsILocalHandlerApp> localHandler =
   1.200 +      do_QueryInterface(mPreferredApplication, &rv);
   1.201 +    NS_ENSURE_SUCCESS(rv, rv);
   1.202 +    
   1.203 +    nsCOMPtr<nsIFile> executable;
   1.204 +    rv = localHandler->GetExecutable(getter_AddRefs(executable));
   1.205 +    NS_ENSURE_SUCCESS(rv, rv);
   1.206 +
   1.207 +    rv = GetIconURLVariant(executable, _retval);
   1.208 +    NS_ENSURE_SUCCESS(rv, rv);
   1.209 +  }
   1.210 +
   1.211 +  return NS_OK;
   1.212 +}
   1.213 +
   1.214 +// this implementation was pretty much copied verbatime from 
   1.215 +// Tony Robinson's code in nsExternalProtocolWin.cpp
   1.216 +nsresult
   1.217 +nsMIMEInfoWin::LoadUriInternal(nsIURI * aURL)
   1.218 +{
   1.219 +  nsresult rv = NS_OK;
   1.220 +
   1.221 +  // 1. Find the default app for this protocol
   1.222 +  // 2. Set up the command line
   1.223 +  // 3. Launch the app.
   1.224 +
   1.225 +  // For now, we'll just cheat essentially, check for the command line
   1.226 +  // then just call ShellExecute()!
   1.227 +
   1.228 +  if (aURL)
   1.229 +  {
   1.230 +    // extract the url spec from the url
   1.231 +    nsAutoCString urlSpec;
   1.232 +    aURL->GetAsciiSpec(urlSpec);
   1.233 + 
   1.234 +    // Unescape non-ASCII characters in the URL
   1.235 +    nsAutoCString urlCharset;
   1.236 +    nsAutoString utf16Spec;
   1.237 +    rv = aURL->GetOriginCharset(urlCharset);
   1.238 +    NS_ENSURE_SUCCESS(rv, rv);
   1.239 +
   1.240 +    nsCOMPtr<nsITextToSubURI> textToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
   1.241 +    NS_ENSURE_SUCCESS(rv, rv);
   1.242 +
   1.243 +    rv = textToSubURI->UnEscapeNonAsciiURI(urlCharset, urlSpec, utf16Spec);
   1.244 +    NS_ENSURE_SUCCESS(rv, rv);
   1.245 +
   1.246 +    static const wchar_t cmdVerb[] = L"open";
   1.247 +    SHELLEXECUTEINFOW sinfo;
   1.248 +    memset(&sinfo, 0, sizeof(sinfo));
   1.249 +    sinfo.cbSize   = sizeof(sinfo);
   1.250 +    sinfo.fMask    = SEE_MASK_FLAG_DDEWAIT |
   1.251 +      SEE_MASK_FLAG_NO_UI;
   1.252 +    sinfo.hwnd     = nullptr;
   1.253 +    sinfo.lpVerb   = (LPWSTR)&cmdVerb;
   1.254 +    sinfo.nShow    = SW_SHOWNORMAL;
   1.255 +    
   1.256 +    LPITEMIDLIST pidl = nullptr;
   1.257 +    SFGAOF sfgao;
   1.258 +    
   1.259 +    // Bug 394974
   1.260 +    if (SUCCEEDED(SHParseDisplayName(utf16Spec.get(), nullptr,
   1.261 +                                     &pidl, 0, &sfgao))) {
   1.262 +      sinfo.lpIDList = pidl;
   1.263 +      sinfo.fMask |= SEE_MASK_INVOKEIDLIST;
   1.264 +    } else {
   1.265 +      // SHParseDisplayName failed. Bailing out as work around for
   1.266 +      // Microsoft Security Bulletin MS07-061
   1.267 +      rv = NS_ERROR_FAILURE;
   1.268 +    }
   1.269 +    if (NS_SUCCEEDED(rv)) {
   1.270 +      BOOL result = ShellExecuteExW(&sinfo);
   1.271 +      if (!result || ((LONG_PTR)sinfo.hInstApp) < 32)
   1.272 +        rv = NS_ERROR_FAILURE;
   1.273 +    }
   1.274 +    if (pidl)
   1.275 +      CoTaskMemFree(pidl);
   1.276 +  }
   1.277 +  
   1.278 +  return rv;
   1.279 +}
   1.280 +
   1.281 +// Given a path to a local file, return its nsILocalHandlerApp instance.
   1.282 +bool nsMIMEInfoWin::GetLocalHandlerApp(const nsAString& aCommandHandler,
   1.283 +                                         nsCOMPtr<nsILocalHandlerApp>& aApp)
   1.284 +{
   1.285 +  nsCOMPtr<nsIFile> locfile;
   1.286 +  nsresult rv = 
   1.287 +    NS_NewLocalFile(aCommandHandler, true, getter_AddRefs(locfile));
   1.288 +  if (NS_FAILED(rv))
   1.289 +    return false;
   1.290 +
   1.291 +  aApp = do_CreateInstance("@mozilla.org/uriloader/local-handler-app;1");
   1.292 +  if (!aApp) 
   1.293 +    return false;
   1.294 +
   1.295 +  aApp->SetExecutable(locfile);
   1.296 +  return true;
   1.297 +}
   1.298 +
   1.299 +// Return the cleaned up file path associated with a command verb 
   1.300 +// located in root/Applications.
   1.301 +bool nsMIMEInfoWin::GetAppsVerbCommandHandler(const nsAString& appExeName,
   1.302 +                                                nsAString& applicationPath,
   1.303 +                                                bool edit)
   1.304 +{
   1.305 +  nsCOMPtr<nsIWindowsRegKey> appKey = 
   1.306 +    do_CreateInstance("@mozilla.org/windows-registry-key;1");
   1.307 +  if (!appKey) 
   1.308 +    return false; 
   1.309 +
   1.310 +  // HKEY_CLASSES_ROOT\Applications\iexplore.exe
   1.311 +  nsAutoString applicationsPath;
   1.312 +  applicationsPath.AppendLiteral("Applications\\");
   1.313 +  applicationsPath.Append(appExeName);
   1.314 +
   1.315 +  nsresult rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
   1.316 +                             applicationsPath,
   1.317 +                             nsIWindowsRegKey::ACCESS_QUERY_VALUE);
   1.318 +  if (NS_FAILED(rv)) 
   1.319 +    return false;
   1.320 +
   1.321 +  // Check for the NoOpenWith flag, if it exists
   1.322 +  uint32_t value;
   1.323 +  if (NS_SUCCEEDED(appKey->ReadIntValue(
   1.324 +      NS_LITERAL_STRING("NoOpenWith"), &value)) &&
   1.325 +      value == 1)
   1.326 +    return false;
   1.327 +
   1.328 +  nsAutoString dummy;
   1.329 +  if (NS_SUCCEEDED(appKey->ReadStringValue(
   1.330 +        NS_LITERAL_STRING("NoOpenWith"), dummy)))
   1.331 +    return false;
   1.332 +
   1.333 +  appKey->Close();
   1.334 +
   1.335 +  // HKEY_CLASSES_ROOT\Applications\iexplore.exe\shell\open\command
   1.336 +  applicationsPath.AssignLiteral("Applications\\");
   1.337 +  applicationsPath.Append(appExeName);
   1.338 +  if (!edit)
   1.339 +    applicationsPath.AppendLiteral("\\shell\\open\\command");
   1.340 +  else
   1.341 +    applicationsPath.AppendLiteral("\\shell\\edit\\command");
   1.342 +
   1.343 +
   1.344 +  rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
   1.345 +                    applicationsPath,
   1.346 +                    nsIWindowsRegKey::ACCESS_QUERY_VALUE);
   1.347 +  if (NS_FAILED(rv)) 
   1.348 +    return false;
   1.349 +
   1.350 +  nsAutoString appFilesystemCommand;
   1.351 +  if (NS_SUCCEEDED(appKey->ReadStringValue(EmptyString(), 
   1.352 +                                           appFilesystemCommand))) {
   1.353 +    
   1.354 +    // Expand environment vars, clean up any misc.
   1.355 +    if (!nsOSHelperAppService::CleanupCmdHandlerPath(appFilesystemCommand))
   1.356 +      return false;
   1.357 +    
   1.358 +    applicationPath = appFilesystemCommand;
   1.359 +    return true;
   1.360 +  }
   1.361 +  return false;
   1.362 +}
   1.363 +
   1.364 +// Return a fully populated command string based on
   1.365 +// passing information. Used in launchWithFile to trace
   1.366 +// back to the full handler path based on the dll.
   1.367 +// (dll, targetfile, return args, open/edit)
   1.368 +bool nsMIMEInfoWin::GetDllLaunchInfo(nsIFile * aDll,
   1.369 +                                       nsIFile * aFile,
   1.370 +                                       nsAString& args,
   1.371 +                                       bool edit)
   1.372 +{
   1.373 +  if (!aDll || !aFile) 
   1.374 +    return false;
   1.375 +
   1.376 +  nsString appExeName;
   1.377 +  aDll->GetLeafName(appExeName);
   1.378 +
   1.379 +  nsCOMPtr<nsIWindowsRegKey> appKey = 
   1.380 +    do_CreateInstance("@mozilla.org/windows-registry-key;1");
   1.381 +  if (!appKey) 
   1.382 +    return false; 
   1.383 +
   1.384 +  // HKEY_CLASSES_ROOT\Applications\iexplore.exe
   1.385 +  nsAutoString applicationsPath;
   1.386 +  applicationsPath.AppendLiteral("Applications\\");
   1.387 +  applicationsPath.Append(appExeName);
   1.388 +
   1.389 +  nsresult rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
   1.390 +                             applicationsPath,
   1.391 +                             nsIWindowsRegKey::ACCESS_QUERY_VALUE);
   1.392 +  if (NS_FAILED(rv))
   1.393 +    return false;
   1.394 +
   1.395 +  // Check for the NoOpenWith flag, if it exists
   1.396 +  uint32_t value;
   1.397 +  rv = appKey->ReadIntValue(NS_LITERAL_STRING("NoOpenWith"), &value);
   1.398 +  if (NS_SUCCEEDED(rv) && value == 1)
   1.399 +    return false;
   1.400 +
   1.401 +  nsAutoString dummy;
   1.402 +  if (NS_SUCCEEDED(appKey->ReadStringValue(NS_LITERAL_STRING("NoOpenWith"), 
   1.403 +                                           dummy)))
   1.404 +    return false;
   1.405 +
   1.406 +  appKey->Close();
   1.407 +
   1.408 +  // HKEY_CLASSES_ROOT\Applications\iexplore.exe\shell\open\command
   1.409 +  applicationsPath.AssignLiteral("Applications\\");
   1.410 +  applicationsPath.Append(appExeName);
   1.411 +  if (!edit)
   1.412 +    applicationsPath.AppendLiteral("\\shell\\open\\command");
   1.413 +  else
   1.414 +    applicationsPath.AppendLiteral("\\shell\\edit\\command");
   1.415 +
   1.416 +  rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
   1.417 +                    applicationsPath,
   1.418 +                    nsIWindowsRegKey::ACCESS_QUERY_VALUE);
   1.419 +  if (NS_FAILED(rv))
   1.420 +    return false;
   1.421 +
   1.422 +  nsAutoString appFilesystemCommand;
   1.423 +  if (NS_SUCCEEDED(appKey->ReadStringValue(EmptyString(),
   1.424 +                                           appFilesystemCommand))) {
   1.425 +    // Replace embedded environment variables.
   1.426 +    uint32_t bufLength = 
   1.427 +      ::ExpandEnvironmentStringsW(appFilesystemCommand.get(),
   1.428 +                                  L"", 0);
   1.429 +    if (bufLength == 0) // Error
   1.430 +      return false;
   1.431 +
   1.432 +    nsAutoArrayPtr<wchar_t> destination(new wchar_t[bufLength]);
   1.433 +    if (!destination)
   1.434 +      return false;
   1.435 +    if (!::ExpandEnvironmentStringsW(appFilesystemCommand.get(),
   1.436 +                                     destination,
   1.437 +                                     bufLength))
   1.438 +      return false;
   1.439 +
   1.440 +    appFilesystemCommand = static_cast<const wchar_t*>(destination);
   1.441 +
   1.442 +    // C:\Windows\System32\rundll32.exe "C:\Program Files\Windows 
   1.443 +    // Photo Gallery\PhotoViewer.dll", ImageView_Fullscreen %1
   1.444 +    nsAutoString params;
   1.445 +    NS_NAMED_LITERAL_STRING(rundllSegment, "rundll32.exe ");
   1.446 +    int32_t index = appFilesystemCommand.Find(rundllSegment);
   1.447 +    if (index > kNotFound) {
   1.448 +      params.Append(Substring(appFilesystemCommand,
   1.449 +                    index + rundllSegment.Length()));
   1.450 +    } else {
   1.451 +      params.Append(appFilesystemCommand);
   1.452 +    }
   1.453 +
   1.454 +    // check to make sure we have a %1 and fill it
   1.455 +    NS_NAMED_LITERAL_STRING(percentOneParam, "%1");
   1.456 +    index = params.Find(percentOneParam);
   1.457 +    if (index == kNotFound) // no parameter
   1.458 +      return false;
   1.459 +
   1.460 +    nsString target;
   1.461 +    aFile->GetTarget(target);
   1.462 +    params.Replace(index, 2, target);
   1.463 +
   1.464 +    args = params;
   1.465 +
   1.466 +    return true;
   1.467 +  }
   1.468 +  return false;
   1.469 +}
   1.470 +
   1.471 +// Return the cleaned up file path associated with a progid command 
   1.472 +// verb located in root.
   1.473 +bool nsMIMEInfoWin::GetProgIDVerbCommandHandler(const nsAString& appProgIDName,
   1.474 +                                                  nsAString& applicationPath,
   1.475 +                                                  bool edit)
   1.476 +{
   1.477 +  nsCOMPtr<nsIWindowsRegKey> appKey =
   1.478 +    do_CreateInstance("@mozilla.org/windows-registry-key;1");
   1.479 +  if (!appKey) 
   1.480 +    return false; 
   1.481 +
   1.482 +  nsAutoString appProgId(appProgIDName);
   1.483 +
   1.484 +  // HKEY_CLASSES_ROOT\Windows.XPSReachViewer\shell\open\command
   1.485 +  if (!edit)
   1.486 +    appProgId.AppendLiteral("\\shell\\open\\command");
   1.487 +  else
   1.488 +    appProgId.AppendLiteral("\\shell\\edit\\command");
   1.489 +
   1.490 +  nsresult rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
   1.491 +                             appProgId,
   1.492 +                             nsIWindowsRegKey::ACCESS_QUERY_VALUE);
   1.493 +  if (NS_FAILED(rv))
   1.494 +    return false;
   1.495 +
   1.496 +  nsAutoString appFilesystemCommand;
   1.497 +  if (NS_SUCCEEDED(appKey->ReadStringValue(EmptyString(), appFilesystemCommand))) {
   1.498 +    
   1.499 +    // Expand environment vars, clean up any misc.
   1.500 +    if (!nsOSHelperAppService::CleanupCmdHandlerPath(appFilesystemCommand))
   1.501 +      return false;
   1.502 +    
   1.503 +    applicationPath = appFilesystemCommand;
   1.504 +    return true;
   1.505 +  }
   1.506 +  return false;
   1.507 +}
   1.508 +
   1.509 +// Helper routine used in tracking app lists. Converts path
   1.510 +// entries to lower case and stores them in the trackList array.
   1.511 +void nsMIMEInfoWin::ProcessPath(nsCOMPtr<nsIMutableArray>& appList,
   1.512 +                                nsTArray<nsString>& trackList,
   1.513 +                                const nsAString& appFilesystemCommand)
   1.514 +{
   1.515 +  nsAutoString lower(appFilesystemCommand);
   1.516 +  ToLowerCase(lower);
   1.517 +
   1.518 +  // Don't include firefox.exe in the list
   1.519 +  WCHAR exe[MAX_PATH+1];
   1.520 +  uint32_t len = GetModuleFileNameW(nullptr, exe, MAX_PATH);
   1.521 +  if (len < MAX_PATH && len != 0) {
   1.522 +    uint32_t index = lower.Find(exe);
   1.523 +    if (index != -1)
   1.524 +      return;
   1.525 +  }
   1.526 +
   1.527 +  nsCOMPtr<nsILocalHandlerApp> aApp;
   1.528 +  if (!GetLocalHandlerApp(appFilesystemCommand, aApp))
   1.529 +    return;
   1.530 +
   1.531 +  // Save in our main tracking arrays
   1.532 +  appList->AppendElement(aApp, false);
   1.533 +  trackList.AppendElement(lower);
   1.534 +}
   1.535 +
   1.536 +// Helper routine that handles a compare between a path
   1.537 +// and an array of paths.
   1.538 +static bool IsPathInList(nsAString& appPath,
   1.539 +                           nsTArray<nsString>& trackList)
   1.540 +{
   1.541 +  // trackList data is always lowercase, see ProcessPath
   1.542 +  // above.
   1.543 +  nsAutoString tmp(appPath);
   1.544 +  ToLowerCase(tmp);
   1.545 +
   1.546 +  for (uint32_t i = 0; i < trackList.Length(); i++) {
   1.547 +    if (tmp.Equals(trackList[i]))
   1.548 +      return true;
   1.549 +  }
   1.550 +  return false;
   1.551 +}
   1.552 +
   1.553 +/** 
   1.554 + * Returns a list of nsILocalHandlerApp objects containing local 
   1.555 + * handlers associated with this mimeinfo. Implemented per 
   1.556 + * platform using information in this object to generate the
   1.557 + * best list. Typically used for an "open with" style user 
   1.558 + * option.
   1.559 + * 
   1.560 + * @return nsIArray of nsILocalHandlerApp
   1.561 + */
   1.562 +NS_IMETHODIMP
   1.563 +nsMIMEInfoWin::GetPossibleLocalHandlers(nsIArray **_retval)
   1.564 +{
   1.565 +  nsresult rv;
   1.566 +
   1.567 +  *_retval = nullptr;
   1.568 +
   1.569 +  nsCOMPtr<nsIMutableArray> appList =
   1.570 +    do_CreateInstance("@mozilla.org/array;1");
   1.571 +
   1.572 +  if (!appList)
   1.573 +    return NS_ERROR_FAILURE;
   1.574 +
   1.575 +  nsTArray<nsString> trackList;
   1.576 +
   1.577 +  nsAutoCString fileExt;
   1.578 +  GetPrimaryExtension(fileExt);
   1.579 +
   1.580 +  nsCOMPtr<nsIWindowsRegKey> regKey =
   1.581 +    do_CreateInstance("@mozilla.org/windows-registry-key;1");
   1.582 +  if (!regKey) 
   1.583 +    return NS_ERROR_FAILURE; 
   1.584 +  nsCOMPtr<nsIWindowsRegKey> appKey =
   1.585 +    do_CreateInstance("@mozilla.org/windows-registry-key;1");
   1.586 +  if (!appKey) 
   1.587 +    return NS_ERROR_FAILURE; 
   1.588 +
   1.589 +  nsAutoString workingRegistryPath;
   1.590 +
   1.591 +  bool extKnown = false;
   1.592 +  if (fileExt.IsEmpty()) {
   1.593 +    extKnown = true;
   1.594 +    // Mime type discovery is possible in some cases, through 
   1.595 +    // HKEY_CLASSES_ROOT\MIME\Database\Content Type, however, a number
   1.596 +    // of file extensions related to mime type are simply not defined,
   1.597 +    // (application/rss+xml & application/atom+xml are good examples)
   1.598 +    // in which case we can only provide a generic list.
   1.599 +    nsAutoCString mimeType;
   1.600 +    GetMIMEType(mimeType);
   1.601 +    if (!mimeType.IsEmpty()) {
   1.602 +      workingRegistryPath.AppendLiteral("MIME\\Database\\Content Type\\");
   1.603 +      workingRegistryPath.Append(NS_ConvertASCIItoUTF16(mimeType));
   1.604 +            
   1.605 +      rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
   1.606 +                        workingRegistryPath,
   1.607 +                        nsIWindowsRegKey::ACCESS_QUERY_VALUE);
   1.608 +      if(NS_SUCCEEDED(rv)) {
   1.609 +        nsAutoString mimeFileExt;
   1.610 +        if (NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(), mimeFileExt))) {
   1.611 +          CopyUTF16toUTF8(mimeFileExt, fileExt);
   1.612 +          extKnown = false;
   1.613 +        }
   1.614 +      }
   1.615 +    }
   1.616 +  }
   1.617 +
   1.618 +  nsAutoString fileExtToUse;
   1.619 +  if (fileExt.First() != '.')
   1.620 +    fileExtToUse = char16_t('.');
   1.621 +  fileExtToUse.Append(NS_ConvertUTF8toUTF16(fileExt));
   1.622 +
   1.623 +  // Note, the order in which these occur has an effect on the 
   1.624 +  // validity of the resulting display list.
   1.625 +
   1.626 +  if (!extKnown) {
   1.627 +    // 1) Get the default handler if it exists
   1.628 +    workingRegistryPath = fileExtToUse;
   1.629 +
   1.630 +    rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
   1.631 +                      workingRegistryPath,
   1.632 +                      nsIWindowsRegKey::ACCESS_QUERY_VALUE);
   1.633 +    if (NS_SUCCEEDED(rv)) {
   1.634 +      nsAutoString appProgId;
   1.635 +      if (NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(), appProgId))) {
   1.636 +        // Bug 358297 - ignore the embedded internet explorer handler
   1.637 +        if (appProgId != NS_LITERAL_STRING("XPSViewer.Document")) {
   1.638 +          nsAutoString appFilesystemCommand;
   1.639 +          if (GetProgIDVerbCommandHandler(appProgId,
   1.640 +                                          appFilesystemCommand,
   1.641 +                                          false) &&
   1.642 +              !IsPathInList(appFilesystemCommand, trackList)) {
   1.643 +            ProcessPath(appList, trackList, appFilesystemCommand);
   1.644 +          }
   1.645 +        }
   1.646 +      }
   1.647 +      regKey->Close();
   1.648 +    }
   1.649 +
   1.650 +
   1.651 +    // 2) list HKEY_CLASSES_ROOT\.ext\OpenWithList
   1.652 +    
   1.653 +    workingRegistryPath = fileExtToUse;
   1.654 +    workingRegistryPath.AppendLiteral("\\OpenWithList");
   1.655 +
   1.656 +    rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
   1.657 +                      workingRegistryPath,
   1.658 +                      nsIWindowsRegKey::ACCESS_QUERY_VALUE);
   1.659 +    if (NS_SUCCEEDED(rv)) {
   1.660 +      uint32_t count = 0;
   1.661 +      if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) {
   1.662 +        for (uint32_t index = 0; index < count; index++) {
   1.663 +          nsAutoString appName;
   1.664 +          if (NS_FAILED(regKey->GetValueName(index, appName)))
   1.665 +            continue;
   1.666 +
   1.667 +          // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params"
   1.668 +          nsAutoString appFilesystemCommand;
   1.669 +          if (!GetAppsVerbCommandHandler(appName,
   1.670 +                                         appFilesystemCommand,
   1.671 +                                         false) ||
   1.672 +              IsPathInList(appFilesystemCommand, trackList))
   1.673 +            continue;
   1.674 +          ProcessPath(appList, trackList, appFilesystemCommand);
   1.675 +        }
   1.676 +      }
   1.677 +      regKey->Close();
   1.678 +    }
   1.679 +
   1.680 +
   1.681 +    // 3) List HKEY_CLASSES_ROOT\.ext\OpenWithProgids, with the
   1.682 +    // different step of resolving the progids for the command handler.
   1.683 +
   1.684 +    workingRegistryPath = fileExtToUse;
   1.685 +    workingRegistryPath.AppendLiteral("\\OpenWithProgids");
   1.686 +
   1.687 +    rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
   1.688 +                      workingRegistryPath,
   1.689 +                      nsIWindowsRegKey::ACCESS_QUERY_VALUE);
   1.690 +    if (NS_SUCCEEDED(rv)) {
   1.691 +      uint32_t count = 0;
   1.692 +      if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) {
   1.693 +        for (uint32_t index = 0; index < count; index++) {
   1.694 +          // HKEY_CLASSES_ROOT\.ext\OpenWithProgids\Windows.XPSReachViewer
   1.695 +          nsAutoString appProgId;
   1.696 +          if (NS_FAILED(regKey->GetValueName(index, appProgId)))
   1.697 +            continue;
   1.698 +
   1.699 +          nsAutoString appFilesystemCommand;
   1.700 +          if (!GetProgIDVerbCommandHandler(appProgId,
   1.701 +                                           appFilesystemCommand,
   1.702 +                                           false) ||
   1.703 +              IsPathInList(appFilesystemCommand, trackList))
   1.704 +            continue;
   1.705 +          ProcessPath(appList, trackList, appFilesystemCommand);
   1.706 +        }
   1.707 +      }
   1.708 +      regKey->Close();
   1.709 +    }
   1.710 +
   1.711 +
   1.712 +    // 4) Add any non configured applications located in the MRU list
   1.713 +
   1.714 +    // HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion
   1.715 +    // \Explorer\FileExts\.ext\OpenWithList
   1.716 +    workingRegistryPath =
   1.717 +      NS_LITERAL_STRING("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\");
   1.718 +    workingRegistryPath += fileExtToUse;
   1.719 +    workingRegistryPath.AppendLiteral("\\OpenWithList");
   1.720 +
   1.721 +    rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
   1.722 +                      workingRegistryPath,
   1.723 +                      nsIWindowsRegKey::ACCESS_QUERY_VALUE);
   1.724 +    if (NS_SUCCEEDED(rv)) {
   1.725 +      uint32_t count = 0;
   1.726 +      if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) {
   1.727 +        for (uint32_t index = 0; index < count; index++) {
   1.728 +          nsAutoString appName, appValue;
   1.729 +          if (NS_FAILED(regKey->GetValueName(index, appName)))
   1.730 +            continue;
   1.731 +          if (appName.EqualsLiteral("MRUList"))
   1.732 +            continue;
   1.733 +          if (NS_FAILED(regKey->ReadStringValue(appName, appValue)))
   1.734 +            continue;
   1.735 +          
   1.736 +          // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params"
   1.737 +          nsAutoString appFilesystemCommand;
   1.738 +          if (!GetAppsVerbCommandHandler(appValue,
   1.739 +                                         appFilesystemCommand,
   1.740 +                                         false) ||
   1.741 +              IsPathInList(appFilesystemCommand, trackList))
   1.742 +            continue;
   1.743 +          ProcessPath(appList, trackList, appFilesystemCommand);
   1.744 +        }
   1.745 +      }
   1.746 +    }
   1.747 +    
   1.748 +
   1.749 +    // 5) Add any non configured progids in the MRU list, with the
   1.750 +    // different step of resolving the progids for the command handler.
   1.751 +    
   1.752 +    // HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion
   1.753 +    // \Explorer\FileExts\.ext\OpenWithProgids
   1.754 +    workingRegistryPath =
   1.755 +      NS_LITERAL_STRING("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\");
   1.756 +    workingRegistryPath += fileExtToUse;
   1.757 +    workingRegistryPath.AppendLiteral("\\OpenWithProgids");
   1.758 +
   1.759 +    regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
   1.760 +                 workingRegistryPath,
   1.761 +                 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
   1.762 +    if (NS_SUCCEEDED(rv)) {
   1.763 +      uint32_t count = 0;
   1.764 +      if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) {
   1.765 +        for (uint32_t index = 0; index < count; index++) {
   1.766 +          nsAutoString appIndex, appProgId;
   1.767 +          if (NS_FAILED(regKey->GetValueName(index, appProgId)))
   1.768 +            continue;
   1.769 +
   1.770 +          nsAutoString appFilesystemCommand;
   1.771 +          if (!GetProgIDVerbCommandHandler(appProgId,
   1.772 +                                           appFilesystemCommand,
   1.773 +                                           false) ||
   1.774 +              IsPathInList(appFilesystemCommand, trackList))
   1.775 +            continue;
   1.776 +          ProcessPath(appList, trackList, appFilesystemCommand);
   1.777 +        }
   1.778 +      }
   1.779 +      regKey->Close();
   1.780 +    }
   1.781 +
   1.782 +
   1.783 +    // 6) Check the perceived type value, and use this to lookup the perceivedtype
   1.784 +    // open with list.
   1.785 +    // http://msdn2.microsoft.com/en-us/library/aa969373.aspx
   1.786 +
   1.787 +    workingRegistryPath = fileExtToUse;
   1.788 +
   1.789 +    regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
   1.790 +                 workingRegistryPath,
   1.791 +                 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
   1.792 +    if (NS_SUCCEEDED(rv)) {
   1.793 +      nsAutoString perceivedType;
   1.794 +      rv = regKey->ReadStringValue(NS_LITERAL_STRING("PerceivedType"),
   1.795 +                                   perceivedType);
   1.796 +      if (NS_SUCCEEDED(rv)) {
   1.797 +        nsAutoString openWithListPath(NS_LITERAL_STRING("SystemFileAssociations\\"));
   1.798 +        openWithListPath.Append(perceivedType); // no period
   1.799 +        openWithListPath.Append(NS_LITERAL_STRING("\\OpenWithList"));
   1.800 +
   1.801 +        nsresult rv = appKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
   1.802 +                                   openWithListPath,
   1.803 +                                   nsIWindowsRegKey::ACCESS_QUERY_VALUE);
   1.804 +        if (NS_SUCCEEDED(rv)) {
   1.805 +          uint32_t count = 0;
   1.806 +          if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) {
   1.807 +            for (uint32_t index = 0; index < count; index++) {
   1.808 +              nsAutoString appName;
   1.809 +              if (NS_FAILED(regKey->GetValueName(index, appName)))
   1.810 +                continue;
   1.811 +              
   1.812 +              // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params"
   1.813 +              nsAutoString appFilesystemCommand;
   1.814 +              if (!GetAppsVerbCommandHandler(appName, appFilesystemCommand, 
   1.815 +                                             false) ||
   1.816 +                  IsPathInList(appFilesystemCommand, trackList))
   1.817 +                continue;
   1.818 +              ProcessPath(appList, trackList, appFilesystemCommand);
   1.819 +            }
   1.820 +          }
   1.821 +        }
   1.822 +      }
   1.823 +    }
   1.824 +  } // extKnown == false
   1.825 +
   1.826 +
   1.827 +  // 7) list global HKEY_CLASSES_ROOT\*\OpenWithList
   1.828 +  // Listing general purpose handlers, not specific to a mime type or file extension
   1.829 +
   1.830 +  workingRegistryPath = NS_LITERAL_STRING("*\\OpenWithList");
   1.831 +
   1.832 +  rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
   1.833 +                    workingRegistryPath,
   1.834 +                    nsIWindowsRegKey::ACCESS_QUERY_VALUE);
   1.835 +  if (NS_SUCCEEDED(rv)) {
   1.836 +    uint32_t count = 0;
   1.837 +    if (NS_SUCCEEDED(regKey->GetValueCount(&count)) && count > 0) {
   1.838 +      for (uint32_t index = 0; index < count; index++) {
   1.839 +        nsAutoString appName;
   1.840 +        if (NS_FAILED(regKey->GetValueName(index, appName)))
   1.841 +          continue;
   1.842 +
   1.843 +        // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params"
   1.844 +        nsAutoString appFilesystemCommand;
   1.845 +        if (!GetAppsVerbCommandHandler(appName, appFilesystemCommand,
   1.846 +                                       false) ||
   1.847 +            IsPathInList(appFilesystemCommand, trackList))
   1.848 +          continue;
   1.849 +        ProcessPath(appList, trackList, appFilesystemCommand);
   1.850 +      }
   1.851 +    }
   1.852 +    regKey->Close();
   1.853 +  }
   1.854 +
   1.855 +
   1.856 +  // 8) General application's list - not file extension specific on windows
   1.857 +  workingRegistryPath = NS_LITERAL_STRING("Applications");
   1.858 +
   1.859 +  rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
   1.860 +                    workingRegistryPath,
   1.861 +                    nsIWindowsRegKey::ACCESS_ENUMERATE_SUB_KEYS|
   1.862 +                    nsIWindowsRegKey::ACCESS_QUERY_VALUE);
   1.863 +  if (NS_SUCCEEDED(rv)) {
   1.864 +    uint32_t count = 0;
   1.865 +    if (NS_SUCCEEDED(regKey->GetChildCount(&count)) && count > 0) {
   1.866 +      for (uint32_t index = 0; index < count; index++) {
   1.867 +        nsAutoString appName;
   1.868 +        if (NS_FAILED(regKey->GetChildName(index, appName)))
   1.869 +          continue;
   1.870 +
   1.871 +        // HKEY_CLASSES_ROOT\Applications\firefox.exe = "path params"
   1.872 +        nsAutoString appFilesystemCommand;
   1.873 +        if (!GetAppsVerbCommandHandler(appName, appFilesystemCommand,
   1.874 +                                       false) ||
   1.875 +            IsPathInList(appFilesystemCommand, trackList))
   1.876 +          continue;
   1.877 +        ProcessPath(appList, trackList, appFilesystemCommand);
   1.878 +      }
   1.879 +    }
   1.880 +  }
   1.881 +
   1.882 +  // Return to the caller
   1.883 +  *_retval = appList;
   1.884 +  NS_ADDREF(*_retval);
   1.885 +
   1.886 +  return NS_OK;
   1.887 +}

mercurial