1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
|
#!/usr/bin/python3
# Copyright 2021 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import argparse
import hashlib
import json
import logging
import os
import shutil
import socket
import subprocess
import sys
from pathlib import Path
from typing import Callable
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
HOST_HASH = hashlib.md5(socket.gethostname().encode()).hexdigest()
URL_FORWARDER_DESKTOP_ENTRY = 'crd-url-forwarder.desktop'
# XFCE4's default settings app changes default browser setting on Cinnamon/GNOME
# to xfce4-web-browser, which redirects GNOME apps to use the XFCE default
# browser setting.
XFCE4_WEB_BROWSER_DESKTOP_ENTRY = 'xfce4-web-browser.desktop'
HOST_SETTINGS_PATH = os.path.join(
os.environ['HOME'],
'.config/chrome-remote-desktop/host#{}.settings.json'.format(HOST_HASH))
X_SESSIONS_DIR = '/usr/share/xsessions/'
XDG_SETTING_DEFAULT_WEB_BROWSER = 'default-web-browser'
def env_with_current_desktop(desktop_env: str) -> dict[str, str]:
"""Returns an environment variable dictionary with XDG_CURRENT_DESKTOP set to
|desktop_env|."""
env = os.environ.copy()
env['XDG_CURRENT_DESKTOP'] = desktop_env
return env
def get_default_browser(desktop_env: str) -> str:
"""Returns the XDG default-web-browser setting for the given desktop
environment."""
args = ['xdg-settings', 'get', XDG_SETTING_DEFAULT_WEB_BROWSER]
env = env_with_current_desktop(desktop_env)
try:
return subprocess.check_output(args, env=env).decode('utf-8').strip()
except CalledProcessError as e:
print('Failed to execute', args, ':', e, file=sys.stderr)
return ''
def set_default_browser(desktop_env: str, desktop_entry: str) -> None:
"""Sets the XDG default-web-browser setting for the given desktop
environment."""
args = [
'xdg-settings', 'set', XDG_SETTING_DEFAULT_WEB_BROWSER, desktop_entry]
env = env_with_current_desktop(desktop_env)
subprocess.run(args, env=env)
def set_default_browser_to_url_forwarder(
desktop_env: str,
backup_dict: dict[str, str],
backup_key: str) -> None:
"""Sets default browser on the given desktop environment to the remote URL
forwarder and backs up the previous default browser.
Args:
desktop_env: The desktop environment to be configured.
backup_dict: The dictionary to backup the previous default browser.
backup_key: The key that the previous default browser will be stored in
backup_dict with.
"""
current_entry = get_default_browser(desktop_env)
if not current_entry:
print('Cannot get default browser for', desktop_env, file=sys.stderr)
return
if current_entry == URL_FORWARDER_DESKTOP_ENTRY:
print('Default browser for', desktop_env, 'is already',
URL_FORWARDER_DESKTOP_ENTRY, file=sys.stderr)
return
backup_dict[backup_key] = current_entry
if current_entry == XFCE4_WEB_BROWSER_DESKTOP_ENTRY:
print('Default browser for', desktop_env, 'has been set to',
XFCE4_WEB_BROWSER_DESKTOP_ENTRY, ', which effectively forces',
desktop_env, 'to use XFCE\'s default browser setting.')
# We can't back up XFCE's default browser here for local fallback, since it
# might have been changed to the URL forwarder already.
return
set_default_browser(desktop_env, URL_FORWARDER_DESKTOP_ENTRY)
print('Default browser for', desktop_env, 'has been successfully set to',
URL_FORWARDER_DESKTOP_ENTRY)
def load_host_settings_file() -> dict[str, str]:
"""Loads and returns the host settings JSON."""
if not os.path.isfile(HOST_SETTINGS_PATH):
return {}
with open(HOST_SETTINGS_PATH, 'r') as settings_file:
try:
return json.load(settings_file)
except JSONDecodeError as e:
print('Failed to load JSON file:', e, file=sys.stderr)
return {}
def save_host_settings_file(settings: dict[str, str]) -> None:
"""Saves the host settings JSON to the file."""
with open(HOST_SETTINGS_PATH, 'w') as settings_file:
json.dump(settings, settings_file)
def get_supported_desktop_envs_and_settings_key() -> dict[str, str]:
desktop_envs_and_settings_keys = dict()
for x_session_desktop_path in Path(X_SESSIONS_DIR).glob('*.desktop'):
desktop_name = os.path.basename(x_session_desktop_path)
if desktop_name.startswith('xfce'):
desktop_envs_and_settings_keys['XFCE'] = 'previous_default_browser_xfce'
elif desktop_name.startswith('cinnamon'):
desktop_envs_and_settings_keys['X-Cinnamon'] = \
'previous_default_browser_cinnamon'
elif desktop_name.startswith('gnome'):
desktop_envs_and_settings_keys['GNOME'] = 'previous_default_browser_gnome'
if not desktop_envs_and_settings_keys:
# Add X-Generic for generic fallback.
desktop_envs_and_settings_keys['X-Generic'] = \
'previous_default_browser_generic'
return desktop_envs_and_settings_keys
def setup_url_forwarder() -> None:
settings = load_host_settings_file()
desktop_envs_and_setting_keys = get_supported_desktop_envs_and_settings_key()
for desktop_env, setting_key in desktop_envs_and_setting_keys.items():
set_default_browser_to_url_forwarder(desktop_env, settings, setting_key)
# For previous default browsers that have been set to the XFCE4 web browser,
# replace them with the actual previous default browser configured for XFCE4
# so that the URL forwarder doesn't launch itself recursively in case of local
# fallback.
for desktop_env, setting_key in desktop_envs_and_setting_keys.items():
if (setting_key in settings and
settings[setting_key] == XFCE4_WEB_BROWSER_DESKTOP_ENTRY):
if 'XFCE' not in desktop_envs_and_setting_keys:
print('Default browser for', desktop_env, 'is set to',
XFCE4_WEB_BROWSER_DESKTOP_ENTRY, 'but XFCE is not found',
file=sys.stderr)
break
xfce_setting_key = desktop_envs_and_setting_keys['XFCE']
if xfce_setting_key not in settings:
print('Cannot find', xfce_setting_key, 'in host settings.')
break
settings[setting_key] = settings[xfce_setting_key]
save_host_settings_file(settings)
# There are also x-www-browser and gnome-www-browser in the Debian Alternative
# system. Most apps don't use them directly. xdg-open uses them if the session
# does not have a display (i.e. interactive shell). Configuring them requires
# sudo permission, and we always have a desktop environment, so we are not
# changing them for now.
# There is also a BROWSER environment variable. xdg-open may also use it when
# the session does not have a display. We can't export a environment variable
# back to the parent process anyway, so we don't change it here.
def restore_default_browser(
desktop_env: str,
backup_dict: dict[str, str],
backup_key: str) -> None:
"""Restores XDG default-web-browser to backup_dict[backup_key] on the given
desktop environment.
Args:
desktop_env: The desktop environment to be configured.
backup_dict: The dictionary where the previous configuration can be found.
backup_key: The dictionary key to find the previous configuration.
"""
if (backup_key not in backup_dict) or not backup_dict[backup_key]:
print("No setting to restore from", backup_key, file=sys.stderr)
return
previous_setting = backup_dict[backup_key]
if (previous_setting == URL_FORWARDER_DESKTOP_ENTRY or
previous_setting == XFCE4_WEB_BROWSER_DESKTOP_ENTRY):
print('Setting to restore from', backup_key, 'is', previous_setting,
'. Ignored.', file=sys.stderr)
return
current_entry = get_default_browser(desktop_env)
if current_entry == XFCE4_WEB_BROWSER_DESKTOP_ENTRY:
print('Default browser for', desktop_env, 'is',
XFCE4_WEB_BROWSER_DESKTOP_ENTRY, '. Ignored.', file=sys.stderr)
return
if current_entry != URL_FORWARDER_DESKTOP_ENTRY:
print('Default browser for', desktop_env, 'is no longer',
URL_FORWARDER_DESKTOP_ENTRY,
'. Previously stored setting will not be restored.', file=sys.stderr)
return
set_default_browser(desktop_env, previous_setting)
print('Default browser for', desktop_env, 'has been successfully restored to',
previous_setting)
def restore_previous_settings() -> None:
settings = load_host_settings_file()
for desktop_env, setting_key in (
get_supported_desktop_envs_and_settings_key().items()):
restore_default_browser(desktop_env, settings, setting_key)
def check_default_browser_is_url_forwarder(desktop_env: str) -> None:
"""Checks if the default browser is set to the remote URL forwarder on the
given desktop environment. Exits with 1 if it's not the case."""
if (desktop_env != 'XFCE' and
get_default_browser(desktop_env) == XFCE4_WEB_BROWSER_DESKTOP_ENTRY):
print('Default browser for', desktop_env, 'is',
XFCE4_WEB_BROWSER_DESKTOP_ENTRY,
'. Checking default browser for XFCE instead...')
check_default_browser_is_url_forwarder('XFCE')
return
if get_default_browser(desktop_env) != URL_FORWARDER_DESKTOP_ENTRY:
print('Default browser for', desktop_env, 'is not',
URL_FORWARDER_DESKTOP_ENTRY)
sys.exit(1)
def check_url_forwarder_setup() -> None:
for desktop_env in get_supported_desktop_envs_and_settings_key().keys():
check_default_browser_is_url_forwarder(desktop_env)
print('Chrome Remote Desktop URL forwarder is properly set up.')
sys.exit(0)
def main() -> None:
parser = argparse.ArgumentParser(
usage='Usage: %(prog)s [options]',
description='Set up a URL forwarder so that URLs will be opened on the '
'Chrome Remote Desktop client. This script must be run within the remote '
'desktop\'s session.')
parser.add_argument('--setup', dest='setup', default=False,
action='store_true',
help='Set up the URL forwarder.')
parser.add_argument('--restore', dest='restore', default=False,
action='store_true',
help='Remove the URL forwarder and restore default '
'browser settings.')
parser.add_argument('--check-setup', dest='check_setup', default=False,
action='store_true',
help='Exit with 0 if the URL forwarder is properly set '
'up, or 1 otherwise.')
options = parser.parse_args()
if options.setup:
setup_url_forwarder()
elif options.restore:
restore_previous_settings()
elif options.check_setup:
check_url_forwarder_setup()
else:
parser.print_usage()
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s:%(levelname)s:%(message)s')
sys.exit(main())
|