# SPDX-License-Identifier: AGPL-3.0-or-later """Configuration helper for WordPress.""" import os import pathlib import random import shutil import string import augeas from plinth import action_utils from plinth.actions import privileged _public_access_file = pathlib.Path('/etc/wordpress/is_public') _config_file_path = pathlib.Path('/etc/wordpress/config-default.php') _static_config_file_path = pathlib.Path('/etc/wordpress/freedombox-static.php') _db_file_path = pathlib.Path('/etc/wordpress/database.php') _db_backup_file = pathlib.Path( '/var/lib/plinth/backups-data/wordpress-database.sql') DB_HOST = 'localhost' DB_NAME = 'wordpress_fbx' DB_USER = 'wordpress_fbx' @privileged def setup(): """Create initial configuration and database for WordPress.""" if _db_file_path.exists() or _config_file_path.exists(): if _config_file_path.exists(): _upgrade_config_file() return db_password = _generate_secret_key(16) _create_config_file(DB_HOST, DB_NAME, DB_USER, db_password) with action_utils.service_ensure_running('mysql'): _create_database(DB_NAME) _set_privileges(DB_HOST, DB_NAME, DB_USER, db_password) def _create_config_file(db_host, db_name, db_user, db_password): """Create a PHP configuration file included by WordPress.""" secret_keys = [_generate_secret_key() for _ in range(8)] config_contents = f'''~`+=,.:/?|') rand = random.SystemRandom() return ''.join(rand.choice(chars) for _ in range(length)) def _upgrade_config_file(): """Upgrade existing config file to add changes.""" include_line = f"include_once('{_static_config_file_path}');" lines = _config_file_path.read_text(encoding='utf-8').splitlines() if include_line not in lines: lines.insert(2, include_line) # Insert on 3rd line _config_file_path.write_text('\n'.join(lines), encoding='utf-8') @privileged def set_public(enable: bool): """Allow/disallow public access.""" if enable: _public_access_file.touch() else: _public_access_file.unlink(missing_ok=True) action_utils.service_reload('apache2') def is_public() -> bool: """Return whether public access is enabled.""" return _public_access_file.exists() @privileged def dump_database(): """Dump database to file.""" _db_backup_file.parent.mkdir(parents=True, exist_ok=True) with action_utils.service_ensure_running('mysql'): with _db_backup_file.open('w', encoding='utf-8') as file_handle: action_utils.run([ 'mysqldump', '--add-drop-database', '--add-drop-table', '--add-drop-trigger', '--user', 'root', '--databases', DB_NAME ], stdout=file_handle, check=True) @privileged def restore_database(): """Restore database from file.""" with action_utils.service_ensure_running('mysql'): with _db_backup_file.open('r', encoding='utf-8') as file_handle: action_utils.run(['mysql', '--user', 'root'], stdin=file_handle, check=True) _set_privileges(DB_HOST, DB_NAME, DB_USER, _read_db_password()) def _read_db_password(): """Return the password stored in the DB configuration file.""" aug = _load_augeas() return aug.get('./$dbpass').strip('\'"') def _load_augeas(): """Initialize augeas.""" aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD + augeas.Augeas.NO_MODL_AUTOLOAD) aug.transform('Phpvars', str(_db_file_path)) aug.set('/augeas/context', '/files' + str(_db_file_path)) aug.load() return aug @privileged def uninstall(): """Remove config files and drop database.""" _drop_database(DB_HOST, DB_NAME, DB_USER) for file_ in [_public_access_file, _config_file_path, _db_file_path]: file_.unlink(missing_ok=True) def _drop_database(db_host, db_name, db_user): """Drop the mysql database that was created during install.""" with action_utils.service_ensure_running('mysql'): query = f"DROP DATABASE {db_name};" action_utils.run(['mysql', '--user', 'root'], input=query.encode(), check=False) query = f"DROP USER IF EXISTS {db_user}@{db_host};" action_utils.run(['mysql', '--user', 'root'], input=query.encode(), check=False)