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

# Copyright 2012-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.

from __future__ import print_function
import sys
from M2Crypto import RSA, X509, EVP, m2
from calculate.lib.cl_lang import setLocalTranslate
from binascii import hexlify
import hashlib
from calculate.lib.utils.text import _u8

from M2Crypto import m2
from M2Crypto.X509 import X509_Extension
from calculate.lib.utils.files import writeFile, readFile
from ctypes import *
 

_ = lambda x: x
setLocalTranslate('cl_core3', sys.modules[__name__])


def passphrase_callback(v):
    return ""


def generateRSAKey():
    return RSA.gen_key(2048, m2.RSA_F4)


def makePKey(key):
    pkey = EVP.PKey()
    pkey.assign_rsa(key)
    return pkey


def makeRequest(pubkey, pkey, serv_host, port):
    """ create query to the signing on server """
    req = X509.Request()
    # Seems to default to 0, but we can now set it as well, so just API test
    req.set_version(req.get_version())
    req.set_pubkey(pkey)
    name = X509.X509_Name()
    c = raw_input(_("Enter the certificate date manually? [y]/n: "))
    if c.lower() in ['n', 'no']:
        name.CN = 'root_cert'  # (Common Name);
        name.OU = 'www.calculate-linux.ru'  # (Organization Unit);
        name.O = 'calculate-linux'  # (Organization Name);
        name.L = ''  # (Locality Name);
        name.ST = 'Spb'  # (State Name);
        name.C = 'En'  # (Country);
    else:
        import socket

        print(_('Do not use spaces or tabs.'))
        host_name = socket.getfqdn()
        # if serv_host == host_name:
        # print '\n'+_("Want to create self-signed certificate?\n"
        # "Use key --gen-cert-self")
        # return None
        if serv_host in host_name:
            host_name = host_name.replace('.' + serv_host, '')
            list_host_name = host_name.split('.')
            print('list_host_name = ', list_host_name)
            result_host_name = \
                list_host_name[len(list_host_name) - 1] + "." + serv_host
        else:
            host_name = socket.getfqdn()
            list_host_name = host_name.split('.')
            result_host_name = list_host_name[0] + "." + serv_host

        def cleardata(x):
            if x:
                return x.replace(' ', '_').replace('\t', '_')
            return ""

        _CN = raw_input(_('Hostname [%s] : ') % _u8(result_host_name))
        name.CN = _CN or result_host_name or ""
        _OU = raw_input(_('Organization unit: '))
        name.OU = cleardata(_OU)
        _O = raw_input(_('Organization name: '))
        name.O = cleardata(_O)
        network = _('Full network address (host:port)')
        _L = raw_input(network + ' [%s:%d]: ' % (_u8(host_name), port))
        name.L = cleardata(_L) or (_u8(host_name) + ':' + str(port))
        _ST = raw_input(_('City: '))
        name.ST = cleardata(_ST)
        _C = raw_input(_('Country (two letters only!): '))
        name.C = _C or "C"

    req.set_subject_name(name)
    ext1 = X509.new_extension('nsComment', 'Auto Generated')
    extstack = X509.X509_Extension_Stack()
    extstack.push(ext1)
    req.add_extensions(extstack)
    req.sign(pkey, 'md5')
    return req

class CreateCertError(Exception):
    pass

def create_selfsigned_ca(dn_data, keyfile, certfile):
    from OpenSSL import crypto

    certpem = readFile(keyfile)
    if not certpem:
        raise CreateCertError(_("Key file {} not found").format(keyfile))

    try:
        pkey = crypto.load_privatekey(
            crypto.FILETYPE_PEM, certpem)
        ca = crypto.X509()
        ca.set_version(2)
        subject = ca.get_subject()
        subject.countryName = dn_data['C']
        subject.commonName = dn_data['CN']
        subject.stateOrProvinceName = dn_data['ST']
        subject.localityName = dn_data['L']
        subject.organizationName = dn_data['O']
        subject.organizationalUnitName = dn_data['OU']

        ca.gmtime_adj_notBefore(-60*60*24)
        ca.gmtime_adj_notAfter(60*60*24*365*20)
        ca.set_issuer(subject)
        ca.set_pubkey(pkey)
        ca.add_extensions([
            crypto.X509Extension(b'basicConstraints', True, b'CA:TRUE'),
            #crypto.X509Extension(b'keyUsage', False, b'keyCertSign, cRLSign'),
            crypto.X509Extension(b'subjectKeyIdentifier', False, b'hash', subject=ca)])
        ca.add_extensions([crypto.X509Extension(b'authorityKeyIdentifier', False, b'keyid:always',issuer=ca)])
        ca.sign(pkey, 'sha1')

        with writeFile(certfile) as f:
            f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, ca))
    except crypto.Error as e:
        raise CreateCertError(str(e))

def sign_client_certifacation_request(ca_keyfile, ca_certfile, requestfile, out_cert, group):
    from OpenSSL import crypto
    cakeyfilepem = readFile(ca_keyfile)
    cacertpem = readFile(ca_certfile)
    requestpem = readFile(requestfile)
    if not cakeyfilepem:
        raise CreateCertError(
            _("Key file {} not found").format(ca_keyfile))
    if not cacertpem:
        raise CreateCertError(
            _("CA certitficate file {} not found").format(ca_certfile))
    if not requestpem:
        raise CreateCertError(
            _("Request file {} not found").format(requestfile))

    try:
        pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, cakeyfilepem)
        ca = crypto.load_certificate(crypto.FILETYPE_PEM, cacertpem)
        req = crypto.load_certificate_request(crypto.FILETYPE_PEM, requestpem)

        cert = crypto.X509()
        cert.set_version(2)
        cert.gmtime_adj_notBefore(-60*60*24)
        cert.gmtime_adj_notAfter(60*60*24*365*20)
        cert.set_issuer(ca.get_subject())
        cert.set_subject(req.get_subject())
        cert.set_pubkey(req.get_pubkey())

        cert.add_extensions([
            crypto.X509Extension(b'basicConstraints', False, b'CA:FALSE'),
            crypto.X509Extension(b'nsCertType', False, b'client'),
            crypto.X509Extension(b'keyUsage', False, b'digitalSignature, keyEncipherment'),
            crypto.X509Extension(b'extendedKeyUsage', False, b'clientAuth'),
            crypto.X509Extension(b'nsComment', False, 'group:{}'.format(group)),
        ])
        cert.sign(pkey, 'sha1')

        with writeFile(out_cert) as f:
            f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
    except crypto.Error as e:
        raise CreateCertError(str(e))
