#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import glob
import socket
from os.path import isfile, join
from os import unlink, system
from getpass import getpass
import readline

from creole import cert
from pyeole.ihm import question_ouinon
from creole.client import CreoleClient
from pyeole.ansiprint import print_red

from arv.db.initialize import initialize_database, commit_database
from arv.db.edge import *
from arv.db.node import *
from arv.config import (sqlite_database, strongswan_database,
                strongswan_tmpconfigs_directory, arv_address_ip,
                ssl_dir, ca_conf_file, x509_default_key_bits,
                ssl_default_cert_time, CACert)
from arv.lib.util import (split_pkcs7, decrypt_privkey, password_OK,
                  ipsec_restart, purge_file)
from arv.lib.usezephir import Zephir
try:
    from zephir.zephir_conf import zephir_conf
    zephir_recorded = True
except:
    print_red("Le serveur n'est pas enregistré sur Zéphir.")
    question = 'Voulez-vous continuer et créer une base ARV minimale ?'
    if question_ouinon(question, level='warn') == 'non':
        exit(0)
    else:
        zephir_recorded = False

defaults = {}

# Help entering path in prompt
readline.set_completer_delims(' \t\n;')
readline.parse_and_bind("tab: complete")

def saisie(variable, message):
    """effectue la saisie d'une variable en utilisant
    une valeur par défaut si disponible
    """
    try:
        val_def = " (%s par défaut)" % defaults[variable]
    except Exception as e:
        val_def = ""
    val = input(message + val_def + " : ")
    if val == "" and val_def != "":
        return defaults[variable]
    else:
        return val

def gen_local_ca():
    try:
        cert.load_conf({'ssl_dir': ssl_dir, 'start_index': "01",
            'ca_conf_file': ca_conf_file, 'ca_file': join(ssl_dir, 'certs/CaCert.pem'),
            'ssl_default_key_bits': x509_default_key_bits,
            'ssl_default_cert_time': '5475'})
        cert.gen_ca(regen=True, extensions="ac-ext")
    except Exception as e:
        print("ERROR: --- unable to create the certificate --- : %s " % str(e))
        sys.exit(1)

def populate_database():
    conf_eole = CreoleClient().get_creole()
    if conf_eole['activer_haute_dispo'] == 'esclave':
        print("Abandon : Ce serveur est esclave pour la haute disponibilité.")
        sys.exit(1)
    if isfile(sqlite_database):
        question = "Une base ARV existe déjà. Écraser la base actuelle ?"
        if question_ouinon(question, level='warn') == 'non':
            sys.exit()
        unlink(sqlite_database)
        if isfile(strongswan_database):
            unlink(strongswan_database)
        for filename in glob.glob('%s*.db'% strongswan_tmpconfigs_directory):
            unlink(filename)

    gen_local_ca()
    initialize_database(create=True)
    #add local certification authority
    credential = open(cert.ca_file, 'r').read()
    credauth = add_credential_auth(credential=credential, local=True)
    #add TmplNode Sphynx and Etablissement
    tmplnode1 = add_tmpl_node(name="Sphynx", mimetype='sphynx')
    tmplnode2 = add_tmpl_node(name="Etablissement", mimetype='etablissement')
    tmplnode3 = add_tmpl_node(name="Roadwarrior", mimetype='roadwarrior')
    tmplnode3.add_tmpl_vertex(name="RW_SourceIP", mimetype='ip')

    if zephir_recorded:
        try:
            login = input("Entrez le login Zéphir : ")
            passwd = getpass("Entrez le mot de passe Zéphir : ")
            zephir = Zephir(user=login, password=passwd)
        except:
            print("La connexion à Zéphir n'a pas pu être établie.")
            sys.exit(1)
        sphynxmodule = zephir.get_module('sphynx')['moduleid']
        amonmodule = zephir.get_module('amon')['moduleid']

        subnet_eth1 = tmplnode1.add_tmpl_vertex(name="reseau_eth1", mimetype='network', zephir_module=sphynxmodule, zephir_var_ip1="adresse_network_eth1", zephir_var_ip2="adresse_netmask_eth1")
        subnet10 = tmplnode1.add_tmpl_vertex(name="reseau_10", mimetype='network', zephir_module="", zephir_var_ip1="10.0.0.0", zephir_var_ip2="255.0.0.0")
        subnet192 = tmplnode1.add_tmpl_vertex(name="reseau_192", mimetype='network', zephir_module="", zephir_var_ip1="192.168.0.0", zephir_var_ip2="255.255.0.0")
        subnet172 = tmplnode1.add_tmpl_vertex(name="reseau_172", mimetype='network', zephir_module="", zephir_var_ip1="172.16.0.0", zephir_var_ip2="255.240.0.0")
        subnet_ader = tmplnode1.add_tmpl_vertex(name="reseau_ader", mimetype='network', zephir_module="", zephir_var_ip1="161.48.0.0", zephir_var_ip2="255.255.224.0")

        subnet_admin = tmplnode2.add_tmpl_vertex(name="admin", mimetype='network', zephir_module=amonmodule, zephir_var_ip1="adresse_network_eth1", zephir_var_ip2="adresse_netmask_eth1")
        subnet_pedago = tmplnode2.add_tmpl_vertex(name="pedago", mimetype='network', zephir_module=amonmodule, zephir_var_ip1="adresse_network_eth2", zephir_var_ip2="adresse_netmask_eth2")
        subnet_dmz = tmplnode2.add_tmpl_vertex(name="dmz", mimetype='network', zephir_module=amonmodule, zephir_var_ip1="adresse_network_eth3", zephir_var_ip2="adresse_netmask_eth3")

        #add node Sphynx
        lzephir = zephir.get_sphynxs()
        not_onzephir = False
        current_id_zephir = zephir_conf.id_serveur
        current_sphynx = zephir.get_etab_server(id_server=current_id_zephir)
        current_rne = current_sphynx['rne']
        current_libelle = current_sphynx['libelle']

        if lzephir:
            i = 1
            menu = []
            for sphynx in lzephir:
                menu.append("{0}- {1}--{2} - Id Zéphir : {3} - Version : {4}".format(i, sphynx[1], sphynx[0], sphynx[2], sphynx[3]))
                if sphynx[1] == current_rne and str(sphynx[0]) == current_libelle or sphynx[2] == current_id_zephir:
                    defaults['choix_sphynx'] = i
                i += 1
            menu.append("{0}- ajouter un serveur Sphynx non enregistré".format(i))
            print("\n".join(menu))
            choix_sphynx = saisie('choix_sphynx', 'Choisissez le serveur Sphynx à ajouter')
            try:
                choix_sphynx = int(choix_sphynx)
            except:
                print("Choix entre 1 et {0}".format(i))
                pass
            while choix_sphynx < 1 or choix_sphynx > i:
                print("\n".join(menu))
                choix_sphynx = saisie('choix_sphynx', 'Choisissez le serveur Sphynx à ajouter')
                try:
                    choix_sphynx = int(choix_sphynx)
                except:
                    print("Choix entre 1 et {0}".format(i))
                    pass
            if choix_sphynx == i:
                not_onzephir = True
            else:
                #print lzephir[choix_sphynx-1][0]
                name = lzephir[choix_sphynx-1][0]
                rne = lzephir[choix_sphynx-1][1]
                id_zephir = lzephir[choix_sphynx-1][2]
                eole_version = lzephir[choix_sphynx-1][3]
                node1 = tmplnode1.add_node(name=name, uai=rne, id_zephir=id_zephir, eole_version=eole_version)
                print("Serveur {0}--{1} - Id Zéphir : {2} - Version : {3} ajouté".format(str(rne), str(name), str(id_zephir), str(eole_version)))

                haute_dispo = zephir.get_var(uai=rne, name=name, var="activer_haute_dispo", id_zephir=id_zephir)
                if haute_dispo == "non":
                    pub_ip = zephir.get_var(uai=rne, name=name, var="adresse_ip_eth0", id_zephir=id_zephir)
                else:
                    vips_name = zephir.get_var(uai=rne, name=name, var="vip_resource_name").split('| ', id_zephir=id_zephir)
                    vips_adresseip = zephir.get_var(uai=rne, name=name, var="vip_resource_adresseip").split('| ', id_zephir=id_zephir)
                    try:
                        index = vips_name.index('VIP_externe')
                        pub_ip = vips_adresseip[index]
                    except:
                        print_red('La ressource "VIP_externe" n\'existe pas !!!')
                        print_red('Vérifier la configuration haute dispo du Sphynx')
                        print_red('Ou ajoutez le Sphynx haute dispo maitre')
                        raise Exception()
                node1.add_extremity(pub_ip=pub_ip)
                network_eth1 = zephir.get_var(uai=rne, name=name, var="adresse_network_eth1", id_zephir=id_zephir)
                netmask_eth1 = zephir.get_var(uai=rne, name=name, var="adresse_netmask_eth1", id_zephir=id_zephir)
                subnet_eth1.add_vertex(node1, ip1=network_eth1, ip2=netmask_eth1)

        if not_onzephir:
            print("Ajout du serveur sphynx...")
            name = conf_eole['nom_machine']
            rne = conf_eole['numero_etab']
            node1 = tmplnode1.add_node(name=name, uai=rne)
            if arv_address_ip != None:
                node1.add_extremity(pub_ip=arv_address_ip)

        #Add vertex for each tmpl_vertex for sphynx node
        subnet10.add_vertex(node1, ip1="10.0.0.0", ip2="255.0.0.0")
        subnet192.add_vertex(node1, ip1 = "192.168.0.0", ip2 = "255.255.0.0")
        subnet172.add_vertex(node1, ip1 = "172.16.0.0", ip2 = "255.240.0.0")
        subnet_ader.add_vertex(node1, ip1 = "161.48.0.0", ip2 = "255.255.224.0")

        add_credential('sphynx', 'eole', node1, 'autosigned')
        #add Toulouse CA credential
        cred_auth = add_certificate_authorities(certif=CACert)

        question = """Voulez-vous ajouter un certificat pour le serveur sphynx
    (les fichiers clé et certificat doivent être accessibles) ?"""
        if question_ouinon(question, level='warn') == 'oui':
            #add sphynx and CA credential from pkcs7 file and sphynx private key
            print("Ajout du certificat du serveur sphynx...")
            cred_file = input("Chemin complet du fichier pkcs7 : ")
            while not isfile(cred_file):
                cred_file = input("Fichier inexistant, réessayez : ")
            privkey_file = input("Chemin complet de la clé privée : ")
            while not isfile(privkey_file):
                privkey_file = input("Fichier inexistant, réessayez : ")

            with open(cred_file, 'rb') as fh:
                try:
                  credential = fh.read()
                except UnicodeDecodeError as e:
                    print("Problème de lecture du fichier : %s" % e )
                    raise Exception()
            ca_cred, cred = split_pkcs7(credential)

            with open(privkey_file, 'r') as fh:
                privkey = fh.read()
            passwd = getpass("Saisir passphrase de la clé privée : ")
            while not password_OK(private_key=privkey_file, password=passwd):
                passwd = getpass("erreur passphrase, veuillez réessayer : ")
            decrypted_key = decrypt_privkey(privkey_string=privkey, passwd=passwd)

            node1.import_credential(private_key=decrypted_key, credential=credential, passwd=passwd)

        #Add crl for CA
        crls = conf_eole['url_crl']
        for uri in crls:
            if uri:
                add_crl_uri(cred_auth, uri)

        #create tmplconnect and tmpledges
        tmplconnect = add_tmpl_connect("amon-sphynx", tmplnode2, tmplnode1, cred_auth)
        tmplconnect.add_tmpl_edge("admin-reseau_eth1", subnet_admin, subnet_eth1)
        tmplconnect.add_tmpl_edge("admin-reseau10", subnet_admin, subnet10)
        tmplconnect.add_tmpl_edge("admin-reseau192", subnet_admin, subnet192)
        tmplconnect.add_tmpl_edge("admin-reseau172", subnet_admin, subnet172)
        tmplconnect.add_tmpl_edge("admin-reseau_ader", subnet_admin, subnet_ader)
        tmplconnect.add_tmpl_edge("pedago-reseau10", subnet_pedago, subnet10)
        tmplconnect.add_tmpl_edge("pedago-reseau192", subnet_pedago, subnet192)
        tmplconnect.add_tmpl_edge("pedago-reseau172", subnet_pedago, subnet172)
        tmplconnect.add_tmpl_edge("pedago-reseau_ader", subnet_pedago, subnet_ader)
        tmplconnect.add_tmpl_edge("dmz-reseau10", subnet_dmz, subnet10)
        tmplconnect.add_tmpl_edge("dmz-reseau192", subnet_dmz, subnet192)
        tmplconnect.add_tmpl_edge("dmz-reseau172", subnet_dmz, subnet172)
        tmplconnect.add_tmpl_edge("dmz-reseau_ader", subnet_dmz, subnet_ader)
    else:
        print("Ajout du serveur sphynx...")
        name = conf_eole['nom_machine']
        rne = conf_eole['numero_etab']
        node1 = tmplnode1.add_node(name=name, uai=rne)
        if arv_address_ip != None:
            node1.add_extremity(pub_ip=arv_address_ip)


    #Restart ARV
    system("service arv restart")
    if isfile(strongswan_database):
        purge_file(strongswan_database)
    ipsec_restart()


populate_database()
commit_database()


