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

# Copyright 2012-2015 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 os
import sys
from sid_func import client_sid
from function import get_sid, get_ip_mac_type
from create_cert import (generateRSAKey, makePKey,
                         makeRequest, passphrase_callback)
import hashlib
from client_class import HTTPSClientCertTransport
from cert_verify import VerifyError
from calculate.core.datavars import DataVarsCore

VERSION = 0.11

_ = lambda x: x
from calculate.lib.cl_lang import setLocalTranslate

setLocalTranslate('cl_core3', sys.modules[__name__])


def client_post_cert(client):
    """ send a certificate server for check """
    sid = get_sid(client.SID_FILE)
    results = client.service.post_cert()

    if results[0][0] == -4:
        print _("Certificate not found in the server database!")
        sys.exit(1)
    client_sid(sid, client, cert_id=results[0][0])
    if results[0][0] == -3:
        print _("Certificate not sent!")
    else:
        print _(" Your certifitate ID = %d") % (results[0][0])
        try:
            if results[0][1] == -2:
                print _("Certificate expired")
            elif results[0][1] > 0:
                print _("The certificate expires after %d days") % (
                    results[0][1])
        except (IndexError, AttributeError):
            pass


# Creation of secret key of the client
def new_key_req(key, cert_path, server_host_name, auto=False):
    rsa = generateRSAKey()
    rsa.save_key(key + '_pub', cipher=None, callback=passphrase_callback)

    pkey = makePKey(rsa)
    pkey.save_key(key, cipher=None, callback=passphrase_callback)

    req = makeRequest(rsa, pkey, server_host_name, auto)
    crtreq = req.as_pem()

    req_file = os.path.join(cert_path, '%s.csr' % server_host_name)
    crtfile = open(req_file, 'w')
    crtfile.write(crtreq)
    crtfile.close()
    return req_file


def delete_old_cert(client):
    os.unlink(client.CERT_FILE)
    os.unlink(client.REQ_FILE)
    os.unlink(client.PKEY_FILE)
    os.unlink(client.PubKEY_FILE)


def client_post_request(cert_path, by_host):
    if os.path.exists(cert_path + 'req_id'):
        print _("You already sent a certificate signature request.")
        print _("Request ID = %s") % open(cert_path + 'req_id', 'r').read()
        ans = raw_input(_("Send a new request? y/[n]: "))
        if not ans.lower() in ['y', 'yes']:
            return 0

    port = raw_input(_("Enter the port number: "))
    try:
        port = int(port)
    except ValueError:
        print _('The port number must be int')
        return 1
    url = "https://%s:%d/?wsdl" % (by_host, port)
    print url + '\n' + _("connecting...")
    from sudsds.client import Client
    # try:
    client = Client(url, transport=HTTPSClientCertTransport(None, None,
                                                            cert_path))
    # except (KeyboardInterrupt, urllib2.URLError), e:
    # print '\n'+_("Closed. Connection error.")
    # print _("Error code: %s") %e
    # return 0

    server_host_name = client.service.get_server_host_name()

    key = cert_path + server_host_name + '.key'
    csr_file = cert_path + server_host_name + '.csr'
    if os.path.exists(key) and os.path.exists(csr_file):
        print _("the private key and request now exist")
        ask = raw_input(_("Create a new private key and request? y/[n]: "))
        if ask.lower() in ['y', 'yes']:
            new_key_req(key, cert_path, server_host_name)
    else:
        new_key_req(key, cert_path, server_host_name)

    ip, mac, client_type = get_ip_mac_type()
    data = open(csr_file).read()
    res = client.service.post_client_request(request=data, ip=ip,
                                             mac=mac, client_type=client_type)
    if int(res) < 0:
        print _("This server is not enabled to sign certificates!")
        return 1
    fc = open(cert_path + 'req_id', 'w')
    fc.write(res)
    fc.close()
    print _("Your request ID = %s") % res
    return 0


def client_get_cert(cert_path, args):
    if not os.path.exists(cert_path + 'req_id'):
        print _("request not sent or file %s deleted") % (cert_path + 'req_id')
        return 1
    fc = open(cert_path + 'req_id', 'r')
    req_id = fc.read()
    fc.close()

    print '\n' + _("the URL looks like"), "https://%s:[port]/?wsdl" \
                                          % args.from_host
    port = raw_input(_("Enter the port number: "))
    try:
        port = int(port)
    except ValueError:
        print _('The port number must be int')
        return 1
    url = "https://%s:%d/?wsdl" % (args.from_host, port)
    print url + '\n' + _("connecting...")

    from sudsds.client import Client

    try:
        client = Client(url, transport=HTTPSClientCertTransport(None, None,
                                                                cert_path))
    except KeyboardInterrupt:
        print '\n' + _("Closed. Connection error.")
        return 1

    server_host_name = client.service.get_server_host_name()

    if not os.path.exists(cert_path + server_host_name + '.csr'):
        print _('Request %s not found') % (
            cert_path + server_host_name + '.csr')
        return 1
    request = open(cert_path + server_host_name + '.csr').read()
    md5 = hashlib.md5()
    md5.update(request)
    md5sum = md5.hexdigest()

    result = client.service.get_client_cert(req_id, md5sum)
    cert = result[0][0]
    ca_root = result[0][1]
    if cert == '1':
        print _('The signature request was rejected!')
        return 1
    elif cert == '2':
        print _("The signature request has not been examined yet.")
        print _("Your request ID = %s") % req_id
        return 1
    elif cert == '3':
        print _("The signature request does not match earlier data.")
        return 1
    elif cert == '4':
        print _("The request was sent from another IP.")
        return 1
    fc = open(cert_path + server_host_name + '.crt', 'w')
    fc.write(cert)
    fc.close()
    os.unlink(cert_path + 'req_id')
    print 'OK. Certificate saved. Your certificate ID = %s' % req_id

    if ca_root:
        cl_vars = DataVarsCore()
        cl_vars.importCore()
        cl_vars.flIniFile()
        system_ca_db = cl_vars.Get('cl_glob_root_cert')
        if os.path.exists(system_ca_db):
            if ca_root in open(system_ca_db, 'r').read():
                return 0

        cl_client_cert_dir = cl_vars.Get('cl_client_cert_dir')
        home_path = cl_vars.Get('ur_home_path')

        cl_client_cert_dir = cl_client_cert_dir.replace("~", home_path)
        root_cert_md5 = cl_client_cert_dir + "/ca/cert_list"

        md5 = hashlib.md5()
        md5.update(ca_root)
        md5sum = md5.hexdigest()

        if not os.path.exists(root_cert_md5):
            fc = open(root_cert_md5, "w")
            fc.close()

        filename = None
        with open(root_cert_md5) as fd:
            t = fd.read()
            # for each line
            for line in t.splitlines():
                # Split string into a words list
                words = line.split(' ', 1)
                if words[0] == md5sum:
                    filename = words[1]
        import OpenSSL

        if not filename:
            certobj = OpenSSL.crypto.load_certificate(
                OpenSSL.SSL.FILETYPE_PEM, ca_root)
            issuer = certobj.get_issuer().get_components()
            for item in issuer:
                if item[0] == 'CN':
                    filename = item[1]

            fc = open(root_cert_md5, "a")
            fc.write('%s %s\n' % (md5sum, filename))
            fc.close()

            if not filename:
                print _('Field "CN" not found in the certificate!')
                return 1

            fd = open(cl_client_cert_dir + '/ca/' + filename, 'w')
            fd.write(ca_root)
            fd.close()

            user_root_cert = cl_vars.Get('cl_user_root_cert')
            user_root_cert = user_root_cert.replace("~", home_path)
            fa = open(user_root_cert, 'a')
            fa.write(ca_root)
            fa.close()
            print _("Certificate added")
        else:
            print _("file containing the CA certificate now exists")
    return 0


def client_post_auth(client):
    """ authorization client or post request """
    sid = get_sid(client.SID_FILE)
    try:
        if os.path.exists(client.CERT_FILE):
            pass  # client_post_cert(client)
        else:
            # client_post_request(client)
            print _('You do not have a certificate. Use option '
                    '--gen-cert-by HOST to generate a new certificate '
                    'or --get-cert-from HOST to get the certificate '
                    'from the server.')
            sys.exit()
        print client.service.versions(sid, VERSION)
    except VerifyError, e:
        print e.value
        sys.exit()


# show rights on requested certificated
def cert_right_inf(client, sid, cert_id):
    s = client.service.view_cert_right(cert_id)
    if s[0][0] == "-1":
        print _("Certificate not found on the server!")
        return -1
    if s[0][0] == "-2":
        print _("Failed to create the certificate ID!")
        return -2
    if s[0][0] == "Permission denied":
        print _("%s: Permission denied") % s[0][1]
        return -3

    print _("The certificate with ID = %d can execute:") % cert_id
    for meth in s[0]:
        print " - %s" % meth


# Viewing rights of any certificate on server by its id
def client_view_cert_right(client):
    cert_id = raw_input(_("Certificate ID: "))
    try:
        cert_id = int(cert_id)
    except ValueError:
        print _("Invalid certificate ID")
        return 1
    try:
        sid = get_sid(client.SID_FILE)
        if cert_id > 0:
            cert_right_inf(client, sid, cert_id)
        else:
            print _("Please enter a valid certificate ID!")
    except Exception, e:
        if e[0][0] == 403:
            print _('Permission denied')
        else:
            print e
        # print _("Failed to get data")
        return 1
    return 0
