# -*- coding: utf-8 -*-
#
# UnivNautes
# Copyright (C) 2014  Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option) any
# later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from django.core.management.base import BaseCommand, CommandError
from django.core.management import call_command

import os
import sys
import subprocess
from sp import pfconfigxml
import urllib2

from authentic2.saml.models import LibertyProvider
from django.conf import settings
from sp import pfconfigxml

METADATAS_DIR = os.path.join('/', 'var', 'db', 'univnautes-sp-federations')

def metadata_filename(codename, suffix=None):
    if not suffix:
        return os.path.join(METADATAS_DIR, codename, 'metadata.xml')
    else:
        return os.path.join(METADATAS_DIR, codename, 'metadata.xml.%s' % suffix)

def store_metadata(codename, metadata):
    tempfilename = '/var/tmp/%s-metadata.xml-%s' % (codename, os.getpid())
    f = open(tempfilename, 'wb')
    f.write(metadata)
    f.close()
    if not os.path.exists(METADATAS_DIR):
        os.mkdir(METADATAS_DIR, 0770)
    dir = os.path.join(METADATAS_DIR, codename)
    if not os.path.exists(dir):
        os.mkdir(dir, 0770)
    os.rename(tempfilename, metadata_filename(codename, 'downloaded'))

    # remove obsoletes files if they exist (just to be clean...)
    for suffix in ('bad_signature', 'disabled'):
        try:
            os.remove(metadata_filename(codename, suffix))
        except OSError:
            pass

def verify_metadata(codename, signcert):
    metadata = metadata_filename(codename, 'downloaded')
    if not signcert:
        print 'warn: do not verify %s metadata (no certificate provided)' % codename
        ret = True
    else:
        signcert_pem = metadata_filename(codename, 'signcert.pem')
        dir = os.path.join(METADATAS_DIR, codename)
        f = open(signcert_pem, 'wb')
        f.write(signcert)
        f.close()
        ret = 0 == subprocess.call(['xmlsec1', '--verify',
            '--id-attr:ID', 'EntitiesDescriptor',
            '--pubkey-cert-pem', signcert_pem,
            '--enabled-key-data', 'key-name',
            metadata])
    if ret:
        os.rename(metadata, metadata_filename(codename))
    else:
        print 'warn: bad signature for %s metadata' % codename
        os.rename(metadata, metadata_filename(codename, 'bad_signature'))
    return ret

def disable(codename):
    print 'disable %s metadata' % str(codename)
    LibertyProvider.objects.filter(federation_source=codename).delete()
    dir = os.path.join(METADATAS_DIR, codename)
    try:
        os.rename(metadata_filename(codename), metadata_filename(codename, 'disabled'))
    except OSError:
        pass


class Command(BaseCommand):
    help = 'update all metadatas in %s' % METADATAS_DIR

    def handle(self, *args, **options):
        actives = set()

        federations = pfconfigxml.get_federations()
        for federation in federations:
            url = federation.get('url')
            metadata = federation.get('metadata')
            codename = federation.get('codename')
            descr = federation.get('descr')
            signcert = federation.get('signcert')

            if not metadata:
                # get metadata from url
                try:
                    print 'download federation %s metadata from url: %s' % (str(codename), str(url))
                    metadata = urllib2.urlopen(url).read()
                except urllib2.HTTPError as e:
                    metadata = None
                    print 'Error loading metadata (%s)' % str(e)
                except urllib2.URLError as e:
                    metadata = None
                    print 'Error loading metadata (%s)' % str(e)
            else:
                print 'using local metadata for %s' % str(codename)

            if metadata:
                store_metadata(codename, metadata)
                if verify_metadata(codename, signcert):
                    try:
                        call_command('sync-metadata', metadata_filename(codename), source=codename, idp=True)
                    except Exception, e:
                        print 'sync-metadata aborted: %s' % str(e)
                        print 'can not active metadata for %s' % str(codename)
                    else:
                        actives.add(codename)

        present_in_filesystem = set(os.listdir(METADATAS_DIR))
        for codename in present_in_filesystem - actives:
            disable(codename)

        present_in_database = set([str(i['federation_source']) \
            for i in LibertyProvider.objects.distinct().values('federation_source').order_by('federation_source')])
        for codename in present_in_database - actives:
            disable(codename)

