#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
#
# Auteur : Pierre-Elliott Bécue <becue@crans.org>
# Licence : GPLv3
# Date : 01/09/2014

import sys
import argparse
import re
import time
from ldap import SIZELIMIT_EXCEEDED

from gestion.config import encoding
from gestion import affichage
from gestion import annuaires_pg
import lc_ldap.shortcuts
import lc_ldap.objets
import lc_ldap.filter2 as lfilter
import lc_ldap.crans_utils

ENCODING = encoding.in_encoding

MACRO_FILTRE_PRISE = re.compile(r'\(prise=(?P<prise>[a-zA-Z][0-9]{3})\)')
MACRO_FILTRE_ADHESION = re.compile(r'\(adhesion=ok\)')
MACRO_FILTRE_CONNEXION = re.compile(r'\(connexion=ok\)')

def explore_db(args):
    """Utilise le contenu de args pour décider comment explorer la base de données."""
    data = search_ldap(args)
    data = macro_expand(data, args)
    data = limits(data, args)
    dataLen = sum([len(elem) for elem in data.itervalues()])
    if dataLen:
        for (key, elem) in data.iteritems():
            if len(elem) == 0:
                continue

            _header = affichage.style(u"Résultats de type %s trouvés dans la base." % (key,), ['cyan'])
            print _header.encode(ENCODING)
            if len(elem) == 1:
                elem[0].display(
                    historique=args.historique,
                    blacklist=args.blacklist,
                    disp_adresse=args.adresse,
                    disp_telephone=args.telephone,
                    sshfp=args.sshfp,
                    ipsec=args.ipsec,
                    )
            else:
                print lc_ldap.printing.sprint_list(elem).encode(ENCODING)
                print "%s résultats" % len(elem)

def search_ldap(args):
    """Cherche et trie"""
    data = {}
    if args.ldap:
        search_filter = args.filtre.decode(ENCODING)
    else:
        search_filter = lfilter.human_to_ldap(args.filtre.decode(ENCODING))

    if args.macro_filtre:
        search_filter = filter_macro(search_filter)
    elif args.wild:
        search_filter = wild_search_filter(search_filter)

    try:
        resultats = LDAP.search(search_filter, sizelimit=args.limite)
    except SIZELIMIT_EXCEEDED:
        raise EnvironmentError("La limite de résultats LDAP (%s) a été dépassée. Vous pouvez l'augmenter avec l'option -l" % (args.limite,))

    for elem in resultats:
        if elem not in data.get(elem.__class__.__name__, []):
            data.setdefault(elem.__class__.__name__, []).append(elem)

    return data

def macro_expand(data, args):
    """Gère les macros spécifiques à whos_lc.
    Permet de récupérer les propriétaires d'objets cherchés,
    ou les factures ou les machines. De chercher par prise,
    etc etc"""

    new_data = dict(data)

    if args.proprietaire:
        for elem_list in data.itervalues():
            for elem in elem_list:
                if hasattr(elem, 'proprio'):
                    _proprio = elem.proprio()

                    # On a pas besoin du Crans dans le dico
                    if isinstance(_proprio, lc_ldap.objets.AssociationCrans):
                        continue

                    if _proprio not in data.get(_proprio.__class__.__name__, []):
                        new_data.setdefault(_proprio.__class__.__name__, []).append(_proprio)
        args.adherent = True

    return new_data

def filter_macro(filtre):
    """Étend les macros du filtre passé en argument"""

    thetime = lc_ldap.crans_utils.to_generalized_time_format(time.time())

    prise_match = MACRO_FILTRE_PRISE.match(filtre)
    adhesion_match = MACRO_FILTRE_ADHESION.match(filtre)
    connexion_match = MACRO_FILTRE_CONNEXION.match(filtre)

    if prise_match:
        prise = prise_match.groupdict()['prise']
        bat = prise[0]
        chbre = bat + annuaires_pg.reverse(bat, prise[1:])[0]
        filtre = filtre.replace(u"(prise=%s)" % (prise,), u"(chbre=%s)" % (chbre,))

    if adhesion_match:
        args.limite = 0
        filtre = filtre.replace(u"(adhesion=ok)", u"(&(finAdhesion>=%s)(|(aid=*)(cid=*)))" % (thetime,))

    if connexion_match:
        args.limite = 0
        filtre = filtre.replace(u"(connexion=ok)", u"(&(finConnexion>=%s)(|(aid=*)(cid=*)))" % (thetime,))

    return filtre

def wild_search_filter(filtre):
    """Recherche sur un gros volume d'attributs dans la base de données"""

    kw = filtre[1:-1]

    attr_list = [
        u'nom', u'prenom', u'tel', u'mail', u'chbre', u'mailAlias', u'canonicalAlias', u'mailExt', u'uid',
        u'macAddress', u'host', u'hostAlias', u'ipHostNumber', u'ip6HostNumber',
    ]

    filtre = u"(|"
    for elem in attr_list:
        filtre += u"(%s=%s*)" % (elem, kw)
    filtre += u")"

    return filtre

def limits(data, args):
    """Applique les limitations dans la recherche.
    Les cas sont a priori conflictuels.

    """
    data_restricted = {}
    data_restricted.update(data)
    contentFilter = []

    if args.adherent:
        contentFilter = ["adherent"]
    elif args.club:
        contentFilter = ["club"]
    elif args.machine:
        contentFilter = ["machineFixe", "machineWifi", "machineCrans", "switchCrans", "borneWifi"]
    elif args.crans:
        contentFilter = ["machineCrans", "switchCrans", "borneWifi"]
    elif args.switch:
        contentFilter = ["switchCrans"]
    elif args.borne:
        contentFilter = ["borneWifi"]
    # Special cases.
    elif args.adm:
        contentFilter = []
        out = []
        for machine in data.get('machineCrans', []):
            if lc_ldap.crans_utils.find_rid_plage(machine['rid'][0].value)[0].startswith('adm'):
                out.append(machine)
        data_restricted = {'machineCrans' : out,}
    elif args.special:
        contentFilter = []
        out = []
        for machine in data.get('machineCrans', []):
            if lc_ldap.crans_utils.find_rid_plage(machine['rid'][0].value)[0].startswith('special'):
                out.append(machine)
        data_restricted = {'machineCrans' : out,}
    elif args.serveur:
        contentFilter = []
        out = []
        for machine in data.get('machineCrans', []):
            if lc_ldap.crans_utils.find_rid_plage(machine['rid'][0].value)[0].startswith('serveur'):
                out.append(machine)
        data_restricted = {'machineCrans' : out,}

    if contentFilter:
        data_restricted = {a: data.get(a, []) for a in contentFilter}

    return data_restricted

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Recherche dans la base des adhérents", add_help=False)
    parser.add_argument('-A', '--adresse', help="Affiche l'adresse de l'adhérent.", action="store_true")
    parser.add_argument('-d', '--blacklist', type=int, help="Choix du nombre d'entrées blacklist à afficher pour les entrées détaillées.", action="store", default=10)
    parser.add_argument('-h', '--help', help="Affiche ce message et quitte.", action="store_true")
    parser.add_argument('-i', '--ipsec', help="Affichage de la clef wifi de la machine.", action="store_true")
    parser.add_argument('-l', '--limite', type=int, help="Modifier la taille limite de recherche dans la base LDAP", action="store", default=1000)
    parser.add_argument('-L', '--historique', type=int, help="Choix du nombre d'entrées d'historique à afficher pour les entrées détaillées.", action="store", default=10)
    parser.add_argument('-s', '--sshfp', help="Affiche les fingerprint SSH si elles existent.", action="store_true")
    parser.add_argument('-t', '--ldap', help="Utiliser les filtres tels que définis dans ldap", action="store_true")
    parser.add_argument('-T', '--telephone', help="Afficher le numéro de téléphone de l'adhérent.", action="store_true")
    parser.add_argument('--test', help="Se connecter à la base de test", action="store_true")
    parser.add_argument('-v', '--verbose', help="Rend le script (très) verbeux.", action="store_true")
    parser.add_argument('filtre', type=str, nargs="?", help="Le filtre whos à utiliser")

    macro_group = parser.add_mutually_exclusive_group(required=False)
    macro_group.add_argument('-f', '--macro-filtre', help="Flag activant la gestion des macros pour le filtre LDAP.", action="store_true")
    macro_group.add_argument('-w', '--wild', help="Cherche de façon agressive dans la base de données à partir du filtre", action="store_true")

    type_group = parser.add_mutually_exclusive_group(required=False)
    type_group.add_argument('-a', '--adherent', help="Limite l'affichage aux adhérents.", action="store_true")
    type_group.add_argument('--adm', help="Limite l'affichage aux machines adm.", action="store_true")
    type_group.add_argument('-b', '--borne', help="Limite l'affichage aux bornes.", action="store_true")
    type_group.add_argument('-c', '--club', help="Limite l'affichage aux clubs.", action="store_true")
    type_group.add_argument('--crans', help="Limite l'affichage aux machines crans.", action="store_true")
    type_group.add_argument('-m', '--machine', help="Limite l'affichage aux machines.", action="store_true")
    type_group.add_argument('-P', '--proprietaire', help="Récupère le propriétaire de l'objet cherché.", action="store_true")
    type_group.add_argument('--serveur', help="Limite l'affichage aux serveurs.", action="store_true")
    type_group.add_argument('--special', help="Limite l'affichage aux machines spéciales.", action="store_true")
    type_group.add_argument('--switch', help="Limite l'affichage aux switches (pas encore implémenté).", action="store_true")

    args = parser.parse_args()

    if args.help:
        parser.print_help()
        sys.exit(0)
    if args.test:
        LDAP = lc_ldap.shortcuts.lc_ldap_test()
    else:
        LDAP = lc_ldap.shortcuts.lc_ldap_readonly()

    explore_db(args)
