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
|
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Configure Syncthing."""
import grp
import os
import pwd
import shutil
import time
import augeas
from plinth import action_utils
from plinth.actions import privileged
DATA_DIR = '/var/lib/syncthing'
# legacy configuration file
CONF_FILE_LEGACY = DATA_DIR + '/.config/syncthing/config.xml'
# configuration file since Debian Trixie if '.config/syncthing' directory
# doesn't exist
CONF_FILE = DATA_DIR + '/.local/state/syncthing/config.xml'
def augeas_load(conf_file):
"""Initialize Augeas."""
aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD +
augeas.Augeas.NO_MODL_AUTOLOAD)
aug.add_transform('Xml.lns', conf_file)
aug.load()
return aug
@privileged
def setup():
"""Perform post-install actions for Syncthing."""
# Create syncthing group if needed.
try:
grp.getgrnam('syncthing')
except KeyError:
action_utils.run(['addgroup', '--system', 'syncthing'], check=True)
# Create syncthing user if needed.
try:
pwd.getpwnam('syncthing')
except KeyError:
action_utils.run([
'adduser', '--system', '--ingroup', 'syncthing', '--home',
DATA_DIR, '--gecos', 'Syncthing file synchronization server',
'syncthing'
], check=True)
if not os.path.exists(DATA_DIR):
os.makedirs(DATA_DIR, mode=0o750)
shutil.chown(DATA_DIR, user='syncthing', group='syncthing')
@privileged
def setup_config():
"""Make configuration changes."""
# wait until the configuration file is created by the syncthing daemon
conf_file_in_use = CONF_FILE
timeout = 300
while timeout > 0:
if os.path.exists(CONF_FILE_LEGACY):
conf_file_in_use = CONF_FILE_LEGACY
break
elif os.path.exists(CONF_FILE):
break
timeout = timeout - 1
time.sleep(1)
aug = augeas_load(conf_file_in_use)
# disable authentication missing notification as FreedomBox itself
# provides authentication
# also make sure no authentication is required on top of FreedomBox's
configs_to_remove = (
'options/unackedNotificationID[#text="authenticationUserAndPassword"]',
'gui/user/#text', 'gui/password/#text')
conf_changed = False
for config in configs_to_remove:
removed = bool(
aug.remove('/files' + conf_file_in_use +
f'/configuration/{config}'))
if removed:
conf_changed = True
configs = {
# disable usage reporting notification by declining reporting
# if the user has not made a choice yet
'options/urAccepted/#text': '-1',
# Set all the values that, misconfigured from the Syncthing UI,
# can make the UI inaccessible. Such misconfigurations can be
# corrected if the user re-runs the setup.
# https://discuss.freedombox.org/t/solved-cant-access-syncthing-administration-panel/2137
'gui/#attribute/tls': 'false',
'gui/#attribute/enabled': 'true',
'gui/address/#text': '127.0.0.1:8384'
}
for key, value in configs.items():
config = f'/configuration/{key}'
if aug.get('/files' + conf_file_in_use + config) != value:
aug.set('/files' + conf_file_in_use + config, value)
conf_changed = True
aug.save()
if conf_changed:
action_utils.service_try_restart('syncthing@syncthing')
@privileged
def uninstall():
"""Remove configuration directory when app is uninstalled."""
# legacy location
shutil.rmtree(DATA_DIR + '/.config', ignore_errors=True)
shutil.rmtree(DATA_DIR + '/.local', ignore_errors=True)
|