#!/usr/bin/env python

# (c) 2019 Cr@ns <roots@crans.org>
# Authors : Alexandre IOOSS <erdnaxe@crans.org>
# Based on cranspasswords by : Daniel Stan <daniel.stan@crans.org>
#                             Vincent Le Gallic <legallic@crans.org>
#
# This file is part of Cr@ns ansible deploiement

"""
Ansible Vault CransPassword script.
========================================

Returns Ansible vault from CransPassword.

Configuration is read from `vault_cranspassword.ini`.
"""

import json
import os
import subprocess
import sys

from ansible.module_utils.six.moves import configparser
from ansible.plugins.vars import BaseVarsPlugin


class VarsModule(BaseVarsPlugin):
    @staticmethod
    def gpg_decrypt(crypt_text):
        full_command = ['gpg', '-d']
        proc = subprocess.Popen(full_command,
                                stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE,
                                stderr=sys.stderr,
                                close_fds=True)
        proc.stdin.write(crypt_text.encode())
        proc.stdin.close()
        clear_text = proc.stdout.read().decode()
        return clear_text

    def getfile_command(self, filename):
        """Exécute la commande distante, et retourne la sortie de cette
        commande"""
        # Get full command from settings file
        command = self.config.get('cranspassword', 'server_cmd').split(" ")
        command.append("getfiles")
        proc = subprocess.Popen(
            command,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=sys.stderr,
            close_fds=True
        )
        proc.stdin.write(json.dumps([filename]).encode())
        proc.stdin.flush()

        raw_out, raw_err = proc.communicate()
        ret = proc.returncode

        if ret != 0:
            print("Mauvais code retour côté serveur", file=sys.stderr)
            sys.exit(ret)

        try:
            answer = json.loads(raw_out.strip())
        except ValueError:
            print("Impossible de parser le résultat", file=sys.stderr)
            sys.exit(42)

        return answer[0]

    def get_encrypted(self, filename):
        """
        Get encrypted content of a cranspassword file
        """
        gotit, value = self.getfile_command(filename)
        if not gotit:
            print(value, file=sys.stderr)  # value contient le message d'erreur
        else:
            crypt_text = value['contents']
            return crypt_text

    def __init__(self):
        super().__init__()

        # Load config
        self.config = configparser.ConfigParser()
        self.config.read(os.path.dirname(os.path.realpath(__file__))
                         + '/vault_cranspassword.ini')

    def get_vars(self, loader, path, entities, cache=True):
        """
        Get all vars for entities, called by Ansible
        """
        super().get_vars(loader, path, entities)

        # We do not want to request N time the same file from cranspassword
        # But VarsModule object get instanced each time
        # So the hack is to use loader._FILE_CACHE that *should* be private
        # Sorry for this, don't judge me on this please <3

        if 'cranspassword' not in loader._FILE_CACHE:
            # Get text then decrypt and return
            crypt_text = self.get_encrypted('ansible_vault')
            clear_text = self.gpg_decrypt(crypt_text)
            data = loader.load(clear_text)
            loader._FILE_CACHE['cranspassword'] = data
        else:
            data = loader._FILE_CACHE['cranspassword']

        return data