#!/usr/bin/python3 # SPDX-License-Identifier: AGPL-3.0-or-later """ Configuration helper for Zoph server. """ import argparse import configparser import json import os import re import subprocess from plinth import action_utils APACHE_CONF = '/etc/apache2/conf-available/zoph.conf' DB_BACKUP_FILE = '/var/lib/plinth/backups-data/zoph-database.sql' def parse_arguments(): """Return parsed command line arguments as dictionary.""" parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') subparsers.add_parser('pre-install', help='Perform Zoph pre-install configuration') subparser = subparsers.add_parser('setup', help='Perform Zoph configuration setup') subparsers.add_parser('get-configuration', help='Return the current configuration') subparser = subparsers.add_parser('set-configuration', help='Configure zoph') subparser.add_argument('--admin-user', help='Name of the admin user') subparser.add_argument('--enable-osm', help='Enable OpenSteetMap maps') subparsers.add_parser('is-configured', help='return true if configured') subparsers.add_parser('dump-database', help='Dump database to file') subparsers.add_parser('restore-database', help='Restore database from file') subparsers.required = True return parser.parse_args() def subcommand_pre_install(_): """Preseed debconf values before packages are installed.""" action_utils.debconf_set_selections([ 'zoph zoph/dbconfig-install boolean true', 'zoph zoph/mysql/admin-user string root' ]) def subcommand_get_configuration(_): """Return the current configuration.""" configuration = {} process = subprocess.run(['zoph', '--dump-config'], stdout=subprocess.PIPE, check=True) for line in process.stdout.decode().splitlines(): name, value = line.partition(':')[::2] configuration[name.strip()] = value[1:] print(json.dumps(configuration)) def _zoph_configure(key, value): """Set a configure value in Zoph.""" subprocess.run(['zoph', '--config', key, value], check=True) def subcommand_setup(_): """Setup Zoph configuration.""" _zoph_configure('import.enable', 'true') _zoph_configure('import.upload', 'true') _zoph_configure('import.rotate', 'true') _zoph_configure('path.unzip', 'unzip') _zoph_configure('path.untar', 'tar xvf') _zoph_configure('path.ungz', 'gunzip') # Maps using OpenStreetMap is enabled by default. _zoph_configure('maps.provider', 'osm') def _get_db_name(): """Return the name of the database configured by dbconfig.""" config = configparser.ConfigParser() config.read_file(open('/etc/zoph.ini', 'r')) return config['zoph']['db_name'].strip('"') def subcommand_set_configuration(arguments): """Setup Zoph Apache configuration.""" _zoph_configure('interface.user.remote', 'true') # Note that using OpenSteetmap as a mapping provider is a very nice # feature, but some people may regard its use as a privacy issue if arguments.enable_osm: value = 'osm' if arguments.enable_osm == 'True' else '' _zoph_configure('maps.provider', value) if arguments.admin_user: # Edit the database to rename the admin user to FreedomBox admin user. admin_user = arguments.admin_user if not re.match(r'^[\w.@][\w.@-]+\Z', admin_user, flags=re.ASCII): # Check to avoid SQL injection raise ValueError('Invalid username') query = f"UPDATE zoph_users SET user_name='{admin_user}' \ WHERE user_name='admin';" subprocess.run(['mysql', _get_db_name()], input=query.encode(), check=True) def subcommand_is_configured(_): """Print whether zoph app is configured.""" subprocess.run(['zoph', '--get-config', 'interface.user.remote'], check=True) def subcommand_dump_database(_): """Dump database to file.""" db_name = _get_db_name() os.makedirs(os.path.dirname(DB_BACKUP_FILE), exist_ok=True) with open(DB_BACKUP_FILE, 'w') as db_backup_file: subprocess.run(['mysqldump', db_name], stdout=db_backup_file, check=True) def subcommand_restore_database(_): """Restore database from file.""" db_name = _get_db_name() subprocess.run(['mysqladmin', '--force', 'drop', db_name], check=False) subprocess.run(['mysqladmin', 'create', db_name], check=True) with open(DB_BACKUP_FILE, 'r') as db_restore_file: subprocess.run(['mysql', db_name], stdin=db_restore_file, check=True) def main(): """Parse arguments and perform all duties.""" arguments = parse_arguments() subcommand = arguments.subcommand.replace('-', '_') subcommand_method = globals()['subcommand_' + subcommand] subcommand_method(arguments) if __name__ == '__main__': main()