#
# Copyright (C) 2014  FreeIPA Contributors see COPYING for license
#

from __future__ import absolute_import

import logging
import os

import ldap

from ipaserver.install import service
from ipaserver.install import installutils
from ipapython.dn import DN
from ipapython import directivesetter
from ipapython import ipautil
from ipaplatform.constants import constants
from ipaplatform.paths import paths
from ipaplatform import services
from ipalib import errors, api

logger = logging.getLogger(__name__)


class ODSExporterInstance(service.Service):
    def __init__(self, fstore=None):
        super(ODSExporterInstance, self).__init__(
            "ipa-ods-exporter",
            service_desc="IPA OpenDNSSEC exporter daemon",
            fstore=fstore,
            keytab=paths.IPA_ODS_EXPORTER_KEYTAB,
            service_prefix=u'ipa-ods-exporter'
        )
        self.enable_if_exists = False

    suffix = ipautil.dn_attribute_property('_suffix')

    def create_instance(self, fqdn, realm_name):
        self.backup_state("enabled", self.is_enabled())
        self.backup_state("running", self.is_running())
        self.fqdn = fqdn
        self.realm = realm_name
        self.suffix = ipautil.realm_to_suffix(self.realm)

        try:
            self.stop()
        except Exception:
            pass

        self.step("setting up DNS Key Exporter", self.__setup_key_exporter)
        self.step("setting up kerberos principal", self.__setup_principal)
        self.step("disabling default signer daemon", self.__disable_signerd)
        self.step("starting DNS Key Exporter", self.__start)
        self.step("configuring DNS Key Exporter to start on boot", self.__enable)
        self.start_creation()

    def __enable(self):

        try:
            self.ldap_configure('DNSKeyExporter', self.fqdn, None,
                                self.suffix)
        except errors.DuplicateEntry:
            logger.error("DNSKeyExporter service already exists")

    def __setup_key_exporter(self):
        directivesetter.set_directive(paths.SYSCONFIG_IPA_ODS_EXPORTER,
                                   'SOFTHSM2_CONF',
                                   paths.DNSSEC_SOFTHSM2_CONF,
                                   quotes=False, separator='=')

    def __setup_principal(self):
        assert constants.ODS_GROUP.gid is not None

        for f in [paths.IPA_ODS_EXPORTER_CCACHE, self.keytab]:
            try:
                os.remove(f)
            except OSError:
                pass

        installutils.kadmin_addprinc(self.principal)

        # Store the keytab on disk
        installutils.create_keytab(paths.IPA_ODS_EXPORTER_KEYTAB,
                                   self.principal)
        p = self.move_service(self.principal)
        if p is None:
            # the service has already been moved, perhaps we're doing a DNS reinstall
            dns_exporter_principal_dn = DN(
                ('krbprincipalname', self.principal),
                ('cn', 'services'), ('cn', 'accounts'), self.suffix)
        else:
            dns_exporter_principal_dn = p

        # Make sure access is strictly reserved to the ods user
        os.chmod(self.keytab, 0o440)
        os.chown(self.keytab, 0, constants.ODS_GROUP.gid)

        dns_group = DN(('cn', 'DNS Servers'), ('cn', 'privileges'),
                       ('cn', 'pbac'), self.suffix)
        mod = [(ldap.MOD_ADD, 'member', dns_exporter_principal_dn)]

        try:
            api.Backend.ldap2.modify_s(dns_group, mod)
        except ldap.TYPE_OR_VALUE_EXISTS:
            pass
        except Exception as e:
            logger.critical("Could not modify principal's %s entry: %s",
                            dns_exporter_principal_dn, str(e))
            raise

        # limit-free connection

        mod = [(ldap.MOD_REPLACE, 'nsTimeLimit', '-1'),
               (ldap.MOD_REPLACE, 'nsSizeLimit', '-1'),
               (ldap.MOD_REPLACE, 'nsIdleTimeout', '-1'),
               (ldap.MOD_REPLACE, 'nsLookThroughLimit', '-1')]
        try:
            api.Backend.ldap2.modify_s(dns_exporter_principal_dn, mod)
        except Exception as e:
            logger.critical("Could not set principal's %s LDAP limits: %s",
                            dns_exporter_principal_dn, str(e))
            raise

    def __disable_signerd(self):
        signerd_service = services.knownservices.ods_signerd
        if self.get_state("singerd_running") is None:
            self.backup_state("singerd_running", signerd_service.is_running())
        if self.get_state("singerd_enabled") is None:
            self.backup_state("singerd_enabled", signerd_service.is_enabled())

        # disable default opendnssec signer daemon
        signerd_service.stop()
        signerd_service.mask()

    def __start(self):
        self.start()

    def remove_service(self):
        try:
            api.Command.service_del(self.principal)
        except errors.NotFound:
            pass

    def uninstall(self):
        if not self.is_configured():
            return

        self.print_msg("Unconfiguring %s" % self.service_name)

        # just eat states
        self.restore_state("running")
        self.restore_state("enabled")

        # stop and disable service (IPA service, we do not need it anymore)
        self.disable()
        self.stop()

        # restore state of dnssec default signer daemon
        signerd_enabled = self.restore_state("singerd_enabled")
        signerd_running = self.restore_state("singerd_running")
        signerd_service = services.knownservices.ods_signerd

        signerd_service.unmask()

        # service was stopped and disabled by setup
        if signerd_enabled:
            signerd_service.enable()

        if signerd_running:
            signerd_service.start()

        ipautil.remove_keytab(self.keytab)
        ipautil.remove_ccache(ccache_path=paths.IPA_ODS_EXPORTER_CCACHE)
