# -*- coding: utf-8 -*-

# Copyright 2016 Mir Calculate. http://www.calculate-linux.org
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import sys
import os
from os import path
from calculate.core.server.func import MethodsInterface
from calculate.server.server import Server
from calculate.lib.utils.files import listDirectory
from calculate.lib.cl_ldap import LDAPConnectError, ldap
import shutil

_ = lambda x: x
from calculate.lib.cl_lang import (setLocalTranslate, getLazyLocalTranslate)

setLocalTranslate('cl_ldap3', sys.modules[__name__])
__ = getLazyLocalTranslate(_)


class LdapError(Exception):
    pass


class Ldap(Server):
    """
    Основной объект для выполнения действий связанных
    с настройкой LDAP сервиса
    """

    class Method(object):
        Setup = "ldap_setup"
        All = (Setup,)

    class Service(object):
        LDAP = "slapd"

    def init(self):
        pass

    def preconfigureTemplates(self):
        """
        Выполнить шаблоны для предварительной конфигурации
        """
        self.clVars.Set('cl_ldap_preconfigure_set', 'on', force=True)
        try:
            return self.applyTemplates("/", False, False, "/", False, True)
        finally:
            self.clVars.Set('cl_ldap_preconfigure_set', 'off', force=True)

    def set_ldap_connection(self, binddn, bindpw):
        self.clVars.Set('ldap.cl_ldap_bind_dn', binddn, force=True)
        self.clVars.Set('ldap.cl_ldap_bind_pw', bindpw, force=True)
        return True

    def clear_directory(self, rmpath):
        """
        Удалить каталог
        """
        for fname in listDirectory(rmpath, fullPath=True):
            if path.isdir(fname):
                shutil.rmtree(fname)
            else:
                os.unlink(fname)

    def remove_ldap_db(self, database_path):
        """
        Удалить базу LDAP
        """
        try:
            self.clear_directory(database_path)
        except OSError:
            raise LdapError(_("Failed to erase LDAP database"))
        return True

    def remove_ldap_branch(self, branch):
        """
        Удалить указанную ветку LDAP
        :param branch: удаляемая ветка
        :return:
        """
        ldap_connect = self.clVars.Get('ldap.cl_ldap_connect')
        try:
            try:
                dn_list = ldap_connect.conLdap.search_s(branch,
                                                        ldap.SCOPE_SUBTREE,
                                                        '(objectclass=*)',
                                                        [''])
            except ldap.NO_SUCH_OBJECT  as e:
                self.printWARNING(_("Unix LDAP branch not found"))
                return True
            except ldap.LDAPError as e:
                raise LdapError("searchDN: " + e[0]['desc'])
            for dn, f in sorted(dn_list, key=lambda x: len(x[0]), reverse=True):
                try:
                    ldap_connect.conLdap.delete_s(dn)
                except ldap.LDAPError, e:
                    raise LdapError("deleteDN: " + e[0]['desc'])
        except LDAPConnectError as e:
            raise LdapError(str(e))
        return True

    def save_variables(self):
        for varname in ('ldap.ld_base_dn',):
            header, o, writename = varname.partition(".")
            self.clVars.Write(writename, self.clVars.Get(varname),
                              header=header)
        return True

    def generate_password(self, bind_dn, password, service):
        try:
            ldap_connect = self.clVars.Get('ldap.cl_ldap_connect')

            attributes = []
            attributes.append((ldap.MOD_REPLACE, 'userPassword',
                               password))
            ldap_connect.ldap_modify_attrs(bind_dn, attributes)
            return True
        except LDAPConnectError as e:
            raise LdapError(
                _("Failed to generate password for {service} service: "
                  "{error}").format(service=service, error=str(e)))
