# encryption functions for mail

# python-gnupginterface
from GnuPGInterface import GnuPG

# django-newformsadmin
from django.conf import settings

def get_fingerprint(servername, gpg_output):
    """
    returns the fingerprint of the key for servername; 
    """
    return_next = False
    for line in gpg_output.splitlines():
        if return_next:
            fpr = line.partition('fpr:::::::::')[2]
            fpr = fpr.rpartition(':')[0]
            return fpr
        elif line.endswith("<%s>::scaESCA:" % servername):
            return_next = True
    return None

class SignatureValidationError(Exception):
    pass

class MokelGnuPG(GnuPG):
    """
    Our interface with GnuPG. Is a bit picky, requires calling binaries,
    but seems to work.
    """
    def __init__(self):
        GnuPG.__init__(self)
        self.options.armor = 1
        self.options.meta_interactive = 0
        # Keep in mind that GnuPG *needs* a home directory to function
        # correctly!
        self.options.homedir = settings.GPG_DIR
        self.options.extra_args.append('--no-default-keyring')
        self.options.extra_args.append('--with-fingerprint')

    def wrap_run(self, actions, string):
        proc = self.run(actions,
                create_fhs=['stdin', 'stdout', 'status', 'logger'])
        proc.handles['stdin'].write(string)
        proc.handles['stdin'].close()
        output = proc.handles['stdout'].read()
        proc.handles['stdout'].close()
        status = proc.handles['status'].read()
        proc.handles['status'].close()
        log = proc.handles['logger'].read()
        proc.handles['logger'].close()
        proc.wait()
        return (output, log, status)

    def encrypt_string(self, string, recipient):
        self.options.recipients = [recipient]
        output, log, status = self.wrap_run(['--encrypt', '--sign'], string)
        print log
        print status
        return output

    def decrypt_and_verify(self, string, key_id):
        output, log, status = self.wrap_run(['--decrypt'], string)
        print log
        print status
        for line in status.splitlines():
            if line.startswith('[GNUPG:] VALIDSIG %s' % key_id) and \
                    line.endswith(key_id):
                return output
        raise SignatureValidationError('The message did not verify against '
                'given key_id!')

    def verify_request_for_map(self, server, message):
        "encryption and verification is tricky business"
        from mail.models import Server

        if not isinstance(server, Server):
            return None
        key_id = server.key_id
        if key is not None:
            output = self.decrypt_and_verify(message, key_id)
        # TODO: implement veryfication and encrypting/signing of map 

    def import_keys(self, keydata):
        return self.wrap_run(['--import'], keydata)

    def update_keyring(self):
        """
        Retrieves all public keys from the database and updates the GPG
        Keyring with them. Also saves the fingerprint/keyid in the DB for 
        easier access.
        Call manage.py updatekeyring after adding a new
        server to the database, or after updating it's public key. 
        Servers that don't have a public key set won't be able to retrieve
        their own maps from the main server.
        """
        from mail.models import Server
        for server in Server.objects.all():
            key = server.clean_public_key
            if key is not None:
                # import key into keyring, print errors
                proc = self.run(['--import'],
                        create_fhs=['stdin', 'stdout', 'logger'])
                proc.handles['stdin'].write(key)
                proc.handles['stdin'].close()
                output = proc.handles['stdout'].read()
                proc.handles['stdout'].close()
                logger = proc.handles['logger'].read()
                proc.handles['logger'].close()
                print logger
                proc.wait()
                # extract keyid from the keyring; write into database
                # print errors
                proc = self.run(['--list-keys', '--with-fingerprint',
                    '--with-colon'], create_fhs=['stdout', 'logger'])
                output = proc.handles['stdout'].read() 
                proc.handles['stdout'].close()
                logger = proc.handles['logger'].read()
                proc.handles['logger'].close()
                server.key_id = get_fingerprint(server.name, output)
                server.save()
                print logger
                proc.wait()


    def signslavekeys(self):
        """
        Supposedly signs previously unsigned public keys from the server
        """
        pass

    def keygen(self):
        """
        Generates a new PRIVATE/public keypair for use on THE MAIN SERVER only
        Make sure that NOBODY except the user running the main server has
        access to settings.GPG_DIR or you'll be screwed.

        You're screwed anyway if you're not a mokelbude admin.
        """
        proc = self.run(['--gen-key', '--batch'], create_fhs=['stdin', 'stdout',
            'logger'])
        proc.handles['stdin'].write('''Key-Type: DSA
        %%echo Generating a standard key
        Name-Real: nobody
        Name-Comment: Don't ever use this key. Internal purposes only.
        Name-Email: %s 
        Key-Length: 1024
        Subkey-Type: ELG-E
        Subkey-Length: 1024
        Expire-Date: 10y
        %%commit
        %%echo done
        ''' % settings.SERVERNAME)
        proc.handles['stdin'].close()
        print proc.handles['logger'].read()
        proc.handles['logger'].close()
        proc.wait()

