blob: eaecd27b6d9a113d4a22f340734a4e13031dc803 [file] [log] [blame]
#!/usr/bin/python3
# Copyright 2021 The Chromium Authors. All rights reserved.
# 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())