#-*- 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 absolute_import
from calculate.consolegui import qt
import urllib.request as urllib2
import OpenSSL, hashlib

from .more import show_msg, LabelWordWrap, show_question, get_ip_mac, get_icon
from .create_cert import RequestCreate
from .client_class import HTTPSClientCertTransport, Client_suds
from calculate.lib.utils.files import readFile
import os

class CertClass (qt.QWidget):
    def __init__(self, parent, ClientObj, window):
        self.ClientObj = ClientObj
        super().__init__()
        
        # get_cert_path
        self.default_cert_path = ClientObj.path_to_cert
#        self.default_cert_path = self.default_cert_path.replace("~",homePath)
        
        self.sendlayout = qt.QGridLayout()
        self.sendlayout.setContentsMargins(8,8,8,8)
        self.mainlayout = qt.QVBoxLayout()
        
        # gen ceth by host
        self.sendlayout.addWidget(LabelWordWrap(_('Host'), self), 0,0)
        self.send_host = qt.QLineEdit('localhost', self)
        self.sendlayout.addWidget(self.send_host, 0, 1, 1, 2)
        
        self.sendlayout.addWidget(LabelWordWrap(_('Port'), self), 1,0)
        
        cl_core_port = self.ClientObj.VarsApi.Get('core.cl_core_port')
        self.send_port = qt.QLineEdit(cl_core_port, self)
        self.send_port.setValidator(qt.QIntValidator(self))
        self.sendlayout.addWidget(self.send_port, 1, 1, 1, 2)

        layout_button = qt.QHBoxLayout()

        Send_button = qt.QPushButton(_("Send a request"), self)
#        Send_button.setFixedWidth(140)
        Send_button.clicked.connect(self.send)
        layout_button.addWidget(Send_button)

        Get_button = qt.QPushButton(_("Get the certificate"), self)
#        Get_button.setFixedWidth(140)
        Get_button.clicked.connect(self.get)
        layout_button.addWidget(Get_button)
        
        Quit_button = qt.QPushButton(_("Quit"), self)
#        Quit_button.setFixedWidth(60)
        Quit_button.setShortcut(qt.QKeySequence(qt.Qt.Key_Return))
        Quit_button.clicked.connect(self.close)
        layout_button.addWidget(Quit_button)

        self.sendlayout.addLayout(layout_button, 2, 0, 1, 3)
        self.sendlayout.setColumnStretch(0,0)
        self.sendlayout.setColumnStretch(1,1)
        self.sendlayout.setColumnStretch(2,1)

        self.GroupBoxSend = qt.QGroupBox(_("Certificate signature request"))
#        self.GroupBoxSend.setContentsMargins(10,0,0,0)
        self.GroupBoxSend.setLayout(self.sendlayout)

        self.mainlayout.addWidget(self.GroupBoxSend)
        self.setLayout(self.mainlayout)
        self.setFocus()
        # for clear memory after closed this window
        self.setAttribute(qt.Qt.WA_DeleteOnClose)

        self.setFixedSize(self.sizeHint().width(), self.sizeHint().height())
        self.move(window.geometry().x() + window.geometry().width() / 2 \
                  - self.size().width() / 2, \
                  window.geometry().y() + window.geometry().height() / 2 \
                  - self.size().height() / 2 - 50)  

        self.setWindowTitle (_('Certificates'))
        self.setWindowIcon (get_icon("view-certificate"))

    def send(self):
    ### client_post_request analog
        # validation correctness of values
        by_host = self.send_host.text()

        if by_host == '':
            show_msg (_('Enter the hostname or the IP address'), \
                        _('Incorrect "Host" value!'))
            return 1
        port = self.send_port.text()

        if port == '' or not port.isdigit():
            show_msg (_('Enter the port number'), _('Incorrect "Port" value!'))
            return 1

        # send request
        cert_path = self.default_cert_path

        cert_path_req_id = cert_path + 'req_id'
        if os.path.exists(cert_path_req_id):
            text=_("You have already sent a signature request!")
            informative_text = _("Request ID: %s") \
                                %readFile(cert_path_req_id)+'\n' \
                                + _("Send a new request?")
            reply = show_question(self, text, informative_text,
                                  title = _('Calculate Console'))

            if reply == qt.QMessageBox.No:
                return 0

            elif reply == qt.QMessageBox.Yes:
                pass

        url = "https://%s:%d/?wsdl" %(by_host, int(port))
        _print ('URL = ', url)
        try:
            self.client = Client_suds(url, \
                    transport = HTTPSClientCertTransport(None, None, \
                    cert_path, parent = self.ClientObj))
        except (KeyboardInterrupt, urllib2.URLError) as e:
            try:
                # show_msg(e.reason.strerror.decode('utf-8'), _("Closing. Connection error."), self)
                show_msg(e.reason.strerror, _("Closing. Connection error."), self)
            except  (UnicodeDecodeError, UnicodeEncodeError):
                show_msg(e.reason.strerror, _("Closing. Connection error."), self)
            return 1
        except (UnicodeDecodeError, UnicodeEncodeError):
            show_msg (_('Enter the hostname or the IP address'),
                      _('Input error'), self)
            return 1
        self.client.wsdl.services[0].setlocation(url)
        try:
            server_host_name = self.client.service.get_server_host_name()
        except AttributeError:
            return 1
        except urllib2.URLError as e:
            _print ('method send in CertificateClass Exception')
            show_msg (e, _("Not connected!"), self)
            return 1
        key = cert_path + server_host_name + '.key'
        self.csr_file = cert_path + server_host_name +'.csr'
        if os.path.exists(key) and os.path.exists(self.csr_file):
            
            text = _('The private key and the request now exist!')
            informative_text = \
                        _("Create a new private key and signature request?")
            reply = show_question(self, text, informative_text,
                                  title = _('Calculate Console'))
            
            if reply == qt.QMessageBox.Yes:
                self.req_obj = RequestCreate(self, self.ClientObj, key, \
                                             cert_path, server_host_name)
                self.req_obj.show()
                    
            elif reply == qt.QMessageBox.No:
                pass

        else:
            self.req_obj = RequestCreate(self, self.ClientObj, key, \
                                         cert_path, server_host_name)
            self.req_obj.setWindowModality(qt.Qt.WindowModal)
            self.req_obj.show()

    def end_send(self):
        ip, mac = get_ip_mac()
        data = readFile(self.csr_file)
        try:
            res = self.client.service.post_client_request(request = data, \
                                    ip = ip, mac = mac, client_type = 'gui')
        except urllib2.URLError as e:
            _print ('method end_send in CertificateClass Exception')
            show_msg (e, _("Not connected!"), self)
            return 1
        del (self.client)
        if int(res) < 0:
            show_msg (_("The server has not signed the certificate!"),
                      parent=self)
            return 1
        with open(self.default_cert_path + 'req_id', 'w') as fc:
            fc.write(res)
        show_msg (_("Your request ID: %s") %res + '\n' + \
                  _("To submit the certificate request on the server use command") + \
                  '\n'+'cl-core --sign-client %s' %res,
                  title = _('Request'), parent=self)
        return 0

    def get(self):
        cert_path = self.default_cert_path
        if not os.path.exists(cert_path + 'req_id'):
            show_msg (_("Request not sent, or file %s deleted") \
                                                %(cert_path + 'req_id'),
                                                _("Certificates"), self)
            return 1
        with open(cert_path + 'req_id', 'r') as fc:
            req_id = fc.read()

        from_host = self.send_host.text()
        if from_host == '':
            show_msg (_('Enter the hostname or the IP address'), \
                        _('Incorrect "Host" value!'), self)
            return 1
        port = self.send_port.text()

        if port == '' or not port.isdigit():
            show_msg (_('Enter the port number'), _('Incorrect "Port" value!'),
                      self)
            return 1

        url = "https://%s:%s/?wsdl" %(from_host, port)

        try:
            client = Client_suds(url, \
                        transport = HTTPSClientCertTransport(None, None, \
                        cert_path, parent = self.ClientObj))
        except (KeyboardInterrupt, urllib2.URLError) as e:
            show_msg(_("Error code: %s") %e, _("Closing. Connection error."),\
                     self)
            return 1
        except (UnicodeDecodeError, UnicodeEncodeError):
            show_msg (_('Enter the hostname or the IP address'), \
                        _('Input error'), self)
            return 1
        client.wsdl.services[0].setlocation(url)
        try:
            server_host_name = client.service.get_server_host_name()
        except urllib2.URLError as e:
            _print ('method get in CertificateClass Exception')
            show_msg (e, _("Not connected!"), self)
            return 1

        if not os.path.exists(cert_path + server_host_name + '.csr'):
            show_msg(_('Request %s not found on the client`s side') \
                            %(cert_path + server_host_name + '.csr'))
            return 1
        request = readFile(cert_path + server_host_name + '.csr')
        md5 = hashlib.md5()
        md5.update(request.encode('UTF-8'))
        md5sum = md5.hexdigest()

        try:
            result = client.service.get_client_cert(req_id, md5sum)
        except urllib2.URLError as e:
            _print ('get_client_cert in CertificateClass Exception')
            show_msg (e, _("Not connected!"))
            return 1

        cert = result[0][0]
        try:
            ca_root = result[0][1]
        except Exception as e:
            ca_root = None
#            show_msg(e.message)
        if cert == '1':
            show_msg (_('Signature request rejected!'))
            return 1
        elif cert == '2':
            show_msg (_("The request has not been reviewed yet.") + '\n' + \
                      _("Your request ID: %s") %req_id + '\n'+\
                      _("To submit the certificate request on the server use command")+\
                      '\n'+'cl-core --sign-client %s'%req_id,
                      title = _('Request'), parent = self)
            return 1
        elif cert == '3':
            show_msg (_('Either the request or the signature does not match '
                        'the earlier ones.'))
            return 1
        fc = open(cert_path + server_host_name + '.crt', 'w')
        fc.write(cert)
        fc.close()
        os.unlink(cert_path + 'req_id')
        show_msg (_('Certificate saved. Your certificate ID = %s') %req_id)

        if ca_root:
            system_ca_db = self.ClientObj.VarsApi.Get('core.cl_glob_root_cert')
            if os.path.exists(system_ca_db):
                if ca_root in readFile(system_ca_db):
                    return 0

            cl_client_cert_dir = self.ClientObj.VarsApi.Get \
                                                    ('core.cl_client_cert_dir')
            homePath = self.ClientObj.VarsApi.Get('ur_home_path')

            cl_client_cert_dir = cl_client_cert_dir.replace("~",homePath)
            if not os.path.isdir(cl_client_cert_dir):
                os.mkdir(cl_client_cert_dir)
            if not os.path.isdir(cl_client_cert_dir+'/ca'):
                os.mkdir(cl_client_cert_dir+'/ca')
            root_cert_md5 = cl_client_cert_dir + "/ca/cert_list"

            md5 = hashlib.md5()
            md5.update(ca_root.encode('UTF-8'))
            md5sum = md5.hexdigest()
            _print ("md5sum = ", md5sum)

            if not os.path.isfile(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]
            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:
                    show_msg \
                        (_('Field "CN" not found in the root certificate!'))
                    return 1

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

                user_root_cert = \
                    self.ClientObj.VarsApi.Get('core.cl_user_root_cert')
                user_root_cert = user_root_cert.replace("~",homePath)
                fa = open(user_root_cert, 'a')
                fa.write(ca_root)
                fa.close()
                _print (_("Filename = "), filename)
                show_msg (_("Root certificate added") + '\n' + filename)
            else:
                _print (_("File with the CA certificate now exists"))
        return 0
