#!/usr/bin/python3
# This file is part of Epoptes, https://epoptes.org
# Copyright 2010-2023 the Epoptes team, see AUTHORS.
# SPDX-License-Identifier: GPL-3.0-or-later
"""
Communicate with epoptes-clients on SSL 789
and with GUIs on /run/epoptes/epoptes.socket.
Communication flow:
  epoptesd.py imports bashplex.py <=SSL=> epoptes-client.
  epoptesd.py imports guiplex.py <=UNIX=> uiconnection imported by gui.py.
So, epoptesd, guiplex, bashplex and exchange run as root.
"""
import grp
import os

from OpenSSL import SSL
from zope.interface import implementer
from twisted.application import internet, service
from twisted.application.service import IServiceMaker
from twisted.internet import ssl
from twisted.python import usage
from twisted.plugin import IPlugin

from epoptes.common import config
from epoptes.daemon import bashplex, guiplex


class Options(usage.Options):
    """Define the epoptes service command line parameters."""
    optParameters = [
        ("client-port", "p", 789, "Client Port"),
        ('ping-interval', 'i', 10),
        ('ping-timeout', 't', 10)
    ]


class ServerContextFactory(ssl.ContextFactory):
    """Provide the SSL context."""
    def getContext(self):
        ctx = SSL.Context(SSL.SSLv23_METHOD)
        ctx.use_certificate_file("/etc/epoptes/server.crt")
        ctx.use_privatekey_file("/etc/epoptes/server.key")
        return ctx


def filter_bash(script):
    """Strip comments from client-functions, to save some bandwidth."""
    with open(script) as file:
        functions = file.readlines()
    result = ''
    for line in functions:
        if line.strip() != '' and line.strip()[0] == '#':
            continue
        result += line
    return result


@implementer(IServiceMaker, IPlugin)
class ServiceMaker(object):
    """Communicate with epoptes-clients on SSL 789
    and with GUIs on /run/epoptes/epoptes.socket.
    """
    tapname = "epoptes"
    description = "Epoptes Daemon"
    options = Options

    def makeService(self, options):
        """Override IServiceMaker.makeService."""
        factory = bashplex.DelimitedBashReceiverFactory()
        factory.ping_interval = int(options['ping-interval'])
        factory.ping_timeout = int(options['ping-timeout'])
        factory.startup_commands = filter_bash(
            '/usr/share/epoptes/client-functions')

        if config.system['ENCRYPTION']:
            client_service = internet.SSLServer(
                int(config.system['PORT']), factory, ServerContextFactory())
        else:
            client_service = internet.TCPServer(
                int(config.system['PORT']), factory)

        gid = grp.getgrnam(config.system['SOCKET_GROUP'])[2]

        if not os.path.isdir(config.system['DIR']):
            # `man 2 mkdir` cannot set the sticky bit, a chmod is needed
            os.makedirs(config.system['DIR'], 0o2770)
        os.chmod(config.system['DIR'], 0o2770)
        os.chown(config.system['DIR'], -1, gid)

        gui_service = internet.UNIXServer(
            "%s/epoptes.socket" % config.system['DIR'],
            guiplex.GUIFactory())

        top_service = service.MultiService()
        top_service.addService(client_service)
        top_service.addService(gui_service)

        return top_service


serviceMaker = ServiceMaker()
