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_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.')
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:
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 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)
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())