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

# Copyright 2008-2010 Calculate Ltd. 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
import hashlib
import crypt
import string
import time
from random import choice
from base64 import encodestring as b64encode
import smbpasswd
from cl_print import color_print
# для создания сертификата
import pwd
from server.utils import execProg
import cl_lang
# Перевод модуля
tr = cl_lang.lang()
tr.setLocalDomain('cl_lib')
tr.setLanguage(sys.modules[__name__])

class encrypt(color_print):
    """Класс хранения общих методов используемых для настройки сервисов

    Методы шифрования, создания сертификатов и.т. д
    """

    def __GenCryptSalt__(self, len=2):
        """Генерация соли для хеширования пароля (CRYPT)"""
        chars = string.letters + string.digits + "./"
        salt = ""
        for i in range(len):
            salt = salt + choice(chars)
        return salt

    def getHashPasswd(self, password, SecHashAlg):
        """Генерация хеша пароля,

        Поддерживаемые алгоритмы шифрования пароля:
        plain, md5, smd5, crypt, sha, ssha, lm, nt, shadow_ssha512,
        shadow_ssha256, shadow_md5
        """
        if not password:
            self.printERROR(_("ERROR") + " getHashPasswd: " +\
                            _("password empty"))
            return False

        hashPwd = ""
        if SecHashAlg == "plain":
            hashPwd = password

        elif SecHashAlg == "md5":
            h = hashlib.md5(password)
            hashPwd = "{MD5}" + b64encode(h.digest())

        elif SecHashAlg == "smd5":
            salt = os.urandom(4)
            h = hashlib.md5(password)
            h.update(salt)
            hashPwd = "{SMD5}" + b64encode(h.digest() + salt)

        elif SecHashAlg == "shadow_ssha512":
            salt = self.__GenCryptSalt__(8)
            hashPwd = crypt.crypt(password, "$6$%s$"%salt)

        elif SecHashAlg == "shadow_ssha256":
            salt = self.__GenCryptSalt__(8)
            hashPwd = crypt.crypt(password, "$5$%s$"%salt)

        elif SecHashAlg == "shadow_md5":
            salt = self.__GenCryptSalt__(8)
            hashPwd = crypt.crypt(password, "$1$%s$"%salt)

        elif SecHashAlg == "crypt":
            salt = self.__GenCryptSalt__()
            hashPwd = "{CRYPT}" + crypt.crypt(password, salt)

        elif SecHashAlg == "sha":
            h = hashlib.sha1(password)
            hashPwd = "{SHA}" + b64encode(h.digest())

        elif SecHashAlg == "ssha":
            salt = os.urandom(4)
            h = hashlib.sha1(password)
            h.update(salt)
            hashPwd = "{SSHA}" + b64encode(h.digest() + salt)

        elif SecHashAlg == "lm":
            hashPwd = smbpasswd.lmhash(password)

        elif SecHashAlg == "nt":
            hashPwd = smbpasswd.nthash(password)

        else:
            self.printERROR(_("ERROR") + " getHashPasswd: " +\
                            _("Can not support '%s' crypto algorithm")\
                            %SecHashAlg)
            return False
        return hashPwd


    def createCertificate(self, sslCountry="US",
                                sslState="California",
                                sslLocality="Santa Barbara",
                                sslOrganization="SSL Server",
                                sslUnit="For Testing Purposes Only",
                                sslCommonName="localhost",
                                sslEmail="root@localhost",
                                nsCertType="server",
                                sslDays=730,
                                sslBits=1024,
                                userName="root",
                                certFile="/tmp/server.pem",
                                certFileMode=0400,
                                keyFile="/tmp/server.key",
                                keyFileMode=0400):
        """Создает сертификат"""
        certAndKeyFiles = [certFile, keyFile]
        foundCertFiles = filter(lambda x: os.path.exists(x), certAndKeyFiles)
        if len(foundCertFiles)==2:
            return True
        # Удаляем файл сертификата
        map(lambda x: os.remove(x), foundCertFiles)
        # получаем id и gid пользователя
        try:
            pwdObj = pwd.getpwnam(userName)
        except:
            self.printERROR(_("Not found user %s")%userName)
            return False
        uid = pwdObj.pw_uid
        gid = pwdObj.pw_gid
        textCnf="""[ req ]
prompt             = no
default_bits       = %s
distinguished_name = req_dn

[ req_dn ]
C                  = %s
ST                 = %s
L                  = %s
O                  = %s
OU                 = %s
CN                 = %s
emailAddress       = %s

[ cert_type ]
nsCertType = %s
"""%(sslBits, sslCountry, sslState, sslLocality, sslOrganization, sslUnit,
     sslCommonName, sslEmail, nsCertType)
        # генерируем название файла конфигурации
        strData = time.strftime("%Y%m%d%H%M%S",time.localtime(time.time()))
        cnfFile = "/tmp/%s.cnf" %strData
        sslFile = "/usr/bin/openssl"
        if not os.path.exists(sslFile):
            self.printERROR(_("Can not found %s")%sslFile)
            return False
        # Cоздание директорий
        for fileName in certAndKeyFiles:
            dirName = os.path.split(fileName)[0]
            if not os.path.exists(dirName):
                self._createDir(0, 0, dirName, 0755)
        # Создание конфигурационного файла
        self._createFile(cnfFile, textCnf, 0, 0, 0600)
        # Создание сертификата
        textLine = execProg(\
            "%s req -new -x509 -nodes -config %s -days %s -out %s -keyout %s"\
            %(sslFile, cnfFile, sslDays, certFile, keyFile))
        # Удаление конфигурационного файла
        if os.path.exists(cnfFile):
            os.remove(cnfFile)
        # Меняем права
        if os.path.exists(certFile):
            os.chown(certFile, uid,gid)
            os.chmod(certFile, certFileMode)
        if os.path.exists(keyFile):
            os.chown(keyFile, uid,gid)
            os.chmod(keyFile, keyFileMode)
        if textLine is False:
            self.printERROR(_("Can not create certificate %s")%certFile)
            return False
        # Проверка сертификата
        textLine = execProg("%s x509 -subject -fingerprint -noout -in %s"\
                                 %(sslFile, certFile))
        if textLine is False:
            self.printERROR(_("Can not create certificate %s")%certFile)
            return False
        return True

    def _createDir(self, uid, gid, dirName, mode=0700):
        """Создание пользовательской директории"""
        if not os.path.exists(dirName):
            os.makedirs(dirName)
            if mode:
                os.chmod(dirName,mode)
            os.chown(dirName,uid,gid)
            return True
        else:
            self.printERROR(_("Path %s exists") %dirName)
            return False

    def _createFile(self, fileName, fileTxt, uid, gid, mode=0644):
        """Создает пользовательский файл с содержимым

        Если директория файла не существует то ошибка
        """
        dirName = os.path.split(fileName)[0]
        if not os.path.exists(dirName):
            self.printERROR(_("Path %s not exists") %dirName)
            return False
        fd = os.open(fileName, os.O_CREAT)
        os.close(fd)
        os.chmod(fileName, mode)
        os.chown(fileName,uid,gid)
        if fileTxt:
            FD = open(fileName, "r+")
            FD.write(fileTxt)
            FD.close()
        return True
