# SPDX-License-Identifier: AGPL-3.0-or-later """ FreedomBox app for Quassel. """ import pathlib from django.urls import reverse_lazy from django.utils.translation import ugettext_lazy as _ from plinth import actions from plinth import app as app_module from plinth import cfg, frontpage, menu from plinth.daemon import Daemon from plinth.modules import names from plinth.modules.firewall.components import Firewall from plinth.modules.letsencrypt.components import LetsEncrypt from plinth.utils import format_lazy from plinth.modules.users.components import UsersAndGroups from .manifest import backup, clients # noqa, pylint: disable=unused-import version = 1 managed_services = ['quasselcore'] managed_packages = ['quassel-core'] managed_paths = [pathlib.Path('/var/lib/quassel/')] _description = [ format_lazy( _('Quassel is an IRC application that is split into two parts, a ' '"core" and a "client". This allows the core to remain connected ' 'to IRC servers, and to continue receiving messages, even when ' 'the client is disconnected. {box_name} can run the Quassel ' 'core service keeping you always online and one or more Quassel ' 'clients from a desktop or a mobile can be used to connect and ' 'disconnect from it.'), box_name=_(cfg.box_name)), _('You can connect to your Quassel core on the default Quassel port ' '4242. Clients to connect to Quassel from your ' 'desktop and ' 'mobile devices ' 'are available.'), ] port_forwarding_info = [('TCP', 4242)] app = None class QuasselApp(app_module.App): """FreedomBox app for Quassel.""" app_id = 'quassel' def __init__(self): """Create components for the app.""" super().__init__() info = app_module.Info(app_id=self.app_id, version=version, name=_('Quassel'), icon_filename='quassel', short_description=_('IRC Client'), description=_description, manual_page='Quassel', clients=clients) self.add(info) menu_item = menu.Menu('menu-quassel', info.name, info.short_description, info.icon_filename, 'quassel:index', parent_url_name='apps') self.add(menu_item) shortcut = frontpage.Shortcut( 'shortcut-quassel', info.name, short_description=info.short_description, icon=info.icon_filename, description=info.description, configure_url=reverse_lazy('quassel:index'), clients=info.clients, login_required=True) self.add(shortcut) firewall = Firewall('firewall-quassel', info.name, ports=['quassel-plinth'], is_external=True) self.add(firewall) letsencrypt = LetsEncrypt( 'letsencrypt-quassel', domains=get_domains, daemons=managed_services, should_copy_certificates=True, private_key_path='/var/lib/quassel/quasselCert.pem', certificate_path='/var/lib/quassel/quasselCert.pem', user_owner='quasselcore', group_owner='quassel', managing_app='quassel') self.add(letsencrypt) daemon = Daemon('daemon-quassel', managed_services[0], listen_ports=[(4242, 'tcp4'), (4242, 'tcp6')]) self.add(daemon) users_and_groups = UsersAndGroups('users-and-groups-quasselcore', reserved_usernames=['quasselcore']) self.add(users_and_groups) def init(): """Initialize the quassel module.""" global app app = QuasselApp() setup_helper = globals()['setup_helper'] if setup_helper.get_state() != 'needs-setup' and app.is_enabled(): app.set_enabled(True) def setup(helper, old_version=None): """Install and configure the module.""" helper.install(managed_packages) helper.call('post', app.enable) app.get_component('letsencrypt-quassel').setup_certificates() def get_available_domains(): """Return an iterator with all domains able to have a certificate.""" return (domain.name for domain in names.components.DomainName.list() if domain.domain_type.can_have_certificate) def set_domain(domain): """Set the TLS domain by writing a file to data directory.""" if domain: actions.superuser_run('quassel', ['set-domain', domain]) def get_domain(): """Read TLS domain from config file select first available if none.""" domain = None try: with open('/var/lib/quassel/domain-freedombox') as file_handle: domain = file_handle.read().strip() except FileNotFoundError: pass if not domain: domain = next(get_available_domains(), None) set_domain(domain) return domain def get_domains(): """Return a list with the configured domain for quassel.""" domain = get_domain() if domain: return [domain] return []