# -*- 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.

import urllib2 as u2
import os
import sys
import calculate.contrib
from suds.transport.http import HttpTransport
from httplib import HTTPConnection, HTTPSConnection
import socket
from calculate.core.datavars import DataVarsCore
from calculate.lib.cl_lang import setLocalTranslate

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


class clientHTTPSConnection(HTTPSConnection):
    def __init__(self, host, port=None, key_file=None, cert_file=None,
                 strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
                 source_address=None, cert_path=None):
        HTTPConnection.__init__(self, host, port, strict, timeout,
                                source_address)
        self.key_file = key_file
        self.cert_file = cert_file
        self.cert_path = cert_path

    # get filename store cert server
    def cert_list(self, host, ca_certs, server_cert):
        if not os.path.exists(self.ca_path):
            try:
                os.makedirs(self.ca_path)
            except OSError:
                pass
        if not os.path.exists(ca_certs):
            fc = open(ca_certs, "w")
            fc.close()
        filename = None
        try:
            with open(ca_certs) as fd:
                t = fd.read()
                # for each line
                for line in t.splitlines():
                    # Split string into a words list
                    words = line.split()
                    if len(words) > 1:
                        # if first word...
                        if words[0] == host:
                            filename = words[1]
            if not filename:
                return None
        except (IOError, IndexError):
            print _("Certificate not found on the client's side")
            return None
        try:
            fd = open(self.ca_path + filename, 'r')
            store_cert = fd.read()
            fd.close()
            if store_cert == server_cert:
                return filename
        except IOError as e:
            print _("Failed to open the file") + "%s%s %s" % (self.ca_path,
                                                              filename, str(e))
        return None

    # add certificate server in trusted
    def add_server_cert(self, cert):
        print _("Untrusted server certificate!")
        import OpenSSL

        certobj = OpenSSL.crypto.load_certificate(OpenSSL.SSL.FILETYPE_PEM,
                                                  cert)
        print '\n' + _("Fingerprint = %s") % certobj.digest('SHA1')
        print _("Serial number = "), certobj.get_serial_number()
        Issuer = certobj.get_issuer().get_components()
        print '\n' + _("Issuer")
        for i in Issuer:
            print "%s : %s" % (i[0], i[1])
        Subject = certobj.get_subject().get_components()
        print '\n' + _("Subject")
        for item in Subject:
            print "%s : %s" % (item[0], item[1])

        choice = raw_input(
            _("add this certificate to trusted and continue? y/[n]: "))
        if choice in ['y', 'yes', 'Y', 'YES']:
            ca_certs = self.ca_path + "cert.list"

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

            filename = self.host
            fc = open(self.ca_path + filename, "w")
            fc.write(cert)
            fc.close()
            with open(ca_certs) as fd:
                t = fd.read()
                # for each line
                for line in t.splitlines():
                    # Split string into a words list
                    words = line.split()
                    if len(words) > 1:
                        # if first word...
                        if words[0] == self.host:
                            return 0
            # Open file with compliance server certificates and server hostname
            fcl = open(ca_certs, "a")
            fcl.write(self.host + ' ' + filename + '\n')
            fcl.close()

        else:
            sys.exit()

    def connect_trusted_server(self, sock):
        import ssl

        if hasattr(ssl, "PROTOCOL_TLSv1_2"):
            ssl_version = ssl.PROTOCOL_TLSv1_2
        else:
            print _("SSL library is not support TLSv1_2")
            return 1
        self.ca_path = self.cert_path + "ca/"
        ca_certs = self.ca_path + "cert.list"
        server_cert = ssl.get_server_certificate(addr=(self.host, self.port))
        if (not hasattr(HTTPSClientsCertTransport, 'filename') or
                    HTTPSClientsCertTransport.filename is None):
            HTTPSClientsCertTransport.filename = self.cert_list(
                self.host, ca_certs, server_cert)
        if HTTPSClientsCertTransport.filename:
            try:
                self.sock = ssl.wrap_socket(sock,
                                            certfile=self.cert_file,
                                            keyfile=self.key_file,
                                            ssl_version=ssl_version,
                                            cert_reqs=ssl.CERT_NONE)

                dercert_after_connect = self.sock.getpeercert(True)
                cert_after_connect = ssl.DER_cert_to_PEM_cert(
                    dercert_after_connect)
                filename2 = self.cert_list(self.host, ca_certs,
                                           cert_after_connect)

                if not HTTPSClientsCertTransport.filename == filename2:
                    print '\n' + _("WARNING!!! %s trying to replace the "
                                   "certificate!") % self.host + '\n'
                    self.sock.close()
                    return 2
                return 0
            except Exception:
                print _("Error. The server certificate and the private "
                        "key are probably invalid!")
                HTTPSClientsCertTransport.filename = None
                return 1
        else:
            self.sock = ssl.wrap_socket(sock)
            self.add_server_cert(server_cert)

    def connect(self):
        """Connect to a host on a given (SSL) port."""
        timeout = 15
        sock = socket.create_connection((self.host, self.port),
                                        timeout, self.source_address)
        if self._tunnel_host:
            self.sock = sock
            self._tunnel()

        clVars = DataVarsCore()
        clVars.importCore()
        if not clVars.flIniFile():
            sys.exit(1)
        import ssl

        if hasattr(ssl, "PROTOCOL_TLSv1_2"):
            ssl_version = ssl.PROTOCOL_TLSv1_2
        else:
            print _("SSL library is not support TLSv1_2")
            sys.exit(1)

        self.sock = ssl.wrap_socket(sock,
                                    certfile=self.cert_file,
                                    keyfile=self.key_file,
                                    ssl_version=ssl_version,
                                    cert_reqs=ssl.CERT_NONE)


class HTTPSClientAuthHandler(u2.HTTPSHandler):
    def __init__(self, key, cert, cert_path):
        u2.HTTPSHandler.__init__(self)
        self.key = key
        self.cert = cert
        self.cert_path = cert_path

    def https_open(self, req):
        # Rather than pass in a reference to a connection class, we pass in
        # a reference to a function which, for all intents and purposes,  
        # will behave as a constructor 
        return self.do_open(self.getConnection, req)

    def getConnection(self, host, timeout=300):
        return clientHTTPSConnection(host, key_file=self.key,
                                     cert_file=self.cert,
                                     cert_path=self.cert_path)


class HTTPSClientsCertTransport(HttpTransport):
    def __init__(self, key, cert, path_to_cert, *args, **kwargs):
        HttpTransport.__init__(self, *args, **kwargs)
        self.key = key
        self.cert = cert
        self.cert_path = path_to_cert

    def u2open(self, u2request):
        """
        Open a connection.
        @param u2request: A urllib2 request.
        @type u2request: urllib2.Requet.
        @return: The opened file-like urllib2 object.
        @rtype: fp
        """
        tm = self.options.timeout
        url = u2.build_opener(HTTPSClientAuthHandler(self.key, self.cert,
                                                     self.cert_path))

        # from urllib2 import URLError
        # try:
        if hasattr(self, "u2ver"):
            if self.u2ver() < 2.6:
                socket.setdefaulttimeout(tm)
                return url.open(u2request)
            else:
                return url.open(u2request, timeout=tm)
        else:
            return url.open(u2request, timeout=tm)
