tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WindowsDefaultBrowser.cpp (6794B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 /**
      7 * This file exists so that LaunchModernSettingsDialogDefaultApps can be called
      8 * without linking to libxul.
      9 */
     10 #include "WindowsDefaultBrowser.h"
     11 
     12 #include "city.h"
     13 #include "mozilla/UniquePtr.h"
     14 #include "mozilla/WindowsVersion.h"
     15 #include "mozilla/WinHeaderOnlyUtils.h"
     16 
     17 // This must be before any other includes that might include shlobj.h
     18 #define INITGUID
     19 #include <shlobj.h>
     20 
     21 #include <lm.h>
     22 #include <shellapi.h>
     23 #include <shlwapi.h>
     24 #include <wchar.h>
     25 #include <windows.h>
     26 
     27 #define APP_REG_NAME_BASE L"Firefox-"
     28 
     29 static bool IsWindowsLogonConnected() {
     30  WCHAR userName[UNLEN + 1];
     31  DWORD size = std::size(userName);
     32  if (!GetUserNameW(userName, &size)) {
     33    return false;
     34  }
     35 
     36  LPUSER_INFO_24 info;
     37  if (NetUserGetInfo(nullptr, userName, 24, (LPBYTE*)&info) != NERR_Success) {
     38    return false;
     39  }
     40  bool connected = info->usri24_internet_identity;
     41  NetApiBufferFree(info);
     42 
     43  return connected;
     44 }
     45 
     46 static bool SettingsAppBelievesConnected() {
     47  const wchar_t* keyPath = L"SOFTWARE\\Microsoft\\Windows\\Shell\\Associations";
     48  const wchar_t* valueName = L"IsConnectedAtLogon";
     49 
     50  uint32_t value = 0;
     51  DWORD size = sizeof(uint32_t);
     52  LSTATUS ls = RegGetValueW(HKEY_CURRENT_USER, keyPath, valueName, RRF_RT_ANY,
     53                            nullptr, &value, &size);
     54  if (ls != ERROR_SUCCESS) {
     55    return false;
     56  }
     57 
     58  return !!value;
     59 }
     60 
     61 bool GetAppRegName(mozilla::UniquePtr<wchar_t[]>& aAppRegName) {
     62  mozilla::UniquePtr<wchar_t[]> appDirStr;
     63  bool success = mozilla::GetInstallDirectory(appDirStr);
     64  if (!success) {
     65    return success;
     66  }
     67 
     68  uint64_t hash = CityHash64(reinterpret_cast<char*>(appDirStr.get()),
     69                             wcslen(appDirStr.get()) * sizeof(wchar_t));
     70 
     71  const wchar_t* format = L"%s%I64X";
     72  int bufferSize = _scwprintf(format, APP_REG_NAME_BASE, hash);
     73  ++bufferSize;  // Extra byte for terminating null
     74  aAppRegName = mozilla::MakeUnique<wchar_t[]>(bufferSize);
     75 
     76  _snwprintf_s(aAppRegName.get(), bufferSize, _TRUNCATE, format,
     77               APP_REG_NAME_BASE, hash);
     78 
     79  return success;
     80 }
     81 
     82 bool LaunchControlPanelDefaultPrograms() {
     83  // Build the path control.exe path safely
     84  WCHAR controlEXEPath[MAX_PATH + 1] = {'\0'};
     85  if (!GetSystemDirectoryW(controlEXEPath, MAX_PATH)) {
     86    return false;
     87  }
     88  LPCWSTR controlEXE = L"control.exe";
     89  if (wcslen(controlEXEPath) + wcslen(controlEXE) >= MAX_PATH) {
     90    return false;
     91  }
     92  if (!PathAppendW(controlEXEPath, controlEXE)) {
     93    return false;
     94  }
     95 
     96  const wchar_t* paramFormat =
     97      L"control.exe /name Microsoft.DefaultPrograms "
     98      L"/page pageDefaultProgram\\pageAdvancedSettings?pszAppName=%s";
     99  mozilla::UniquePtr<wchar_t[]> appRegName;
    100  GetAppRegName(appRegName);
    101  int bufferSize = _scwprintf(paramFormat, appRegName.get());
    102  ++bufferSize;  // Extra byte for terminating null
    103  mozilla::UniquePtr<wchar_t[]> params =
    104      mozilla::MakeUnique<wchar_t[]>(bufferSize);
    105  _snwprintf_s(params.get(), bufferSize, _TRUNCATE, paramFormat,
    106               appRegName.get());
    107 
    108  STARTUPINFOW si = {sizeof(si), 0};
    109  si.dwFlags = STARTF_USESHOWWINDOW;
    110  si.wShowWindow = SW_SHOWDEFAULT;
    111  PROCESS_INFORMATION pi = {0};
    112  if (!CreateProcessW(controlEXEPath, params.get(), nullptr, nullptr, FALSE, 0,
    113                      nullptr, nullptr, &si, &pi)) {
    114    return false;
    115  }
    116  CloseHandle(pi.hProcess);
    117  CloseHandle(pi.hThread);
    118 
    119  return true;
    120 }
    121 
    122 static bool IsAppRegistered(HKEY rootKey, const wchar_t* appRegName) {
    123  const wchar_t* keyPath = L"Software\\RegisteredApplications";
    124 
    125  DWORD size = sizeof(uint32_t);
    126  LSTATUS ls = RegGetValueW(rootKey, keyPath, appRegName, RRF_RT_ANY, nullptr,
    127                            nullptr, &size);
    128  return ls == ERROR_SUCCESS;
    129 }
    130 
    131 static bool LaunchMsSettingsProtocol() {
    132  mozilla::UniquePtr<wchar_t[]> params = nullptr;
    133  if (mozilla::HasPackageIdentity()) {
    134    mozilla::UniquePtr<wchar_t[]> packageFamilyName =
    135        mozilla::GetPackageFamilyName();
    136    if (packageFamilyName) {
    137      const wchar_t* paramFormat =
    138          L"ms-settings:defaultapps?registeredAUMID=%s!App";
    139      int bufferSize = _scwprintf(paramFormat, packageFamilyName.get());
    140      ++bufferSize;  // Extra byte for terminating null
    141      params = mozilla::MakeUnique<wchar_t[]>(bufferSize);
    142      _snwprintf_s(params.get(), bufferSize, _TRUNCATE, paramFormat,
    143                   packageFamilyName.get());
    144    }
    145  }
    146  if (!params) {
    147    mozilla::UniquePtr<wchar_t[]> appRegName;
    148    GetAppRegName(appRegName);
    149    const wchar_t* paramFormat =
    150        IsAppRegistered(HKEY_CURRENT_USER, appRegName.get()) ||
    151                !IsAppRegistered(HKEY_LOCAL_MACHINE, appRegName.get())
    152            ? L"ms-settings:defaultapps?registeredAppUser=%s"
    153            : L"ms-settings:defaultapps?registeredAppMachine=%s";
    154    int bufferSize = _scwprintf(paramFormat, appRegName.get());
    155    ++bufferSize;  // Extra byte for terminating null
    156    params = mozilla::MakeUnique<wchar_t[]>(bufferSize);
    157    _snwprintf_s(params.get(), bufferSize, _TRUNCATE, paramFormat,
    158                 appRegName.get());
    159  }
    160 
    161  SHELLEXECUTEINFOW seinfo = {sizeof(seinfo)};
    162  seinfo.lpFile = params.get();
    163  seinfo.nShow = SW_SHOWNORMAL;
    164  return ShellExecuteExW(&seinfo);
    165 }
    166 
    167 bool LaunchModernSettingsDialogDefaultApps() {
    168  if (mozilla::IsWin11OrLater()) {
    169    return LaunchMsSettingsProtocol();
    170  }
    171 
    172  if (!mozilla::IsWindows10BuildOrLater(14965) && !IsWindowsLogonConnected() &&
    173      SettingsAppBelievesConnected()) {
    174    // Use the classic Control Panel to work around a bug of older
    175    // builds of Windows 10.
    176    return LaunchControlPanelDefaultPrograms();
    177  }
    178 
    179  IApplicationActivationManager* pActivator;
    180  HRESULT hr = CoCreateInstance(
    181      CLSID_ApplicationActivationManager, nullptr, CLSCTX_INPROC,
    182      IID_IApplicationActivationManager, (void**)&pActivator);
    183 
    184  if (SUCCEEDED(hr)) {
    185    DWORD pid;
    186    hr = pActivator->ActivateApplication(
    187        L"windows.immersivecontrolpanel_cw5n1h2txyewy"
    188        L"!microsoft.windows.immersivecontrolpanel",
    189        L"page=SettingsPageAppsDefaults", AO_NONE, &pid);
    190    if (SUCCEEDED(hr)) {
    191      // Do not check error because we could at least open
    192      // the "Default apps" setting.
    193      pActivator->ActivateApplication(
    194          L"windows.immersivecontrolpanel_cw5n1h2txyewy"
    195          L"!microsoft.windows.immersivecontrolpanel",
    196          L"page=SettingsPageAppsDefaults"
    197          L"&target=SystemSettings_DefaultApps_Browser",
    198          AO_NONE, &pid);
    199    }
    200    pActivator->Release();
    201    return SUCCEEDED(hr);
    202  }
    203  return true;
    204 }