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

import sys, signal, os
import getopt

# Imports pour LDAP
sys.path.append('/usr/scripts/gestion')
from config import droits_groupes, droits_mailing_listes
from gen_confs import gen_config, anim, cprint, OK, ERREUR
from ldap_crans import crans_ldap, preattr, ldap, CransLdap, strip_accents

try:
    sys.path.append('/usr/lib/mailman')
    from Mailman import MailList
    from Mailman import Utils
    from Mailman.UserDesc import UserDesc
except:
    # Machine sans mailman, les ML ne seront pas reconfigurées
    pass

class droits(gen_config) :
    def restart(s) :
        # Rien à faire
        pass

    def __str__(s):
        return "droits"

class droits_ldap(CransLdap, droits):
    ####### Les groupes
    base_group_dn = 'ou=Group,dc=crans,dc=org'

    def build_group(self) :
        """ Reconstruit les groupes dans la base LDAP """
        self.anim.iter = len(droits_groupes.keys() )
        for group, fonctions in droits_groupes.items() :
            self.anim.cycle()
            # Qui doit être dans ce groupe ?
            res = []
            for f in fonctions :
                res += self.search('droits=%s' % f)['adherent']

            # Récupération de la constitution du groupe actuel
            dn = 'cn=%s,%s' % (group, self.base_group_dn)
            data = self.conn.search_s(dn ,0,'objectClass=posixGroup')[0][1]
            init_data = data.copy()

            # Supression de tout les membres
            data['memberUid'] = []

            # Ajout des bonnes personnes
            for adher in res :
                if not adher.droitsGeles():
                    uid = preattr(adher.compte())[1]
                    if uid and uid not in data['memberUid'] :
                        data['memberUid'].append(uid)

            if group == 'adm':
                # Ajout de logcheck et d'arpwatch
                data['memberUid'].append('logcheck')
                data['memberUid'].append('arpwatch')
            elif group == 'webradio':
                # Ajout de michel et webradio
                data['memberUid'].append('michel')
                data['memberUid'].append('webradio')

            # Sauvegarde
            modlist = ldap.modlist.modifyModlist(init_data,data)
            self.conn.modify_s(dn,modlist)

    def print_liste(self,poste) :
        """ Donne la liste des membres actifs """
        for adh in self.search('droits=%s&chbre!=EXT' % poste)['adherent'] :
            print "%s %s" % (adh.nom(), adh.prenom())

    def config_ML(self, mode='autosync', args=None) :
        """ Reconfigure les MLs.
        mode désigne le mode de fonctionnement :
         * autosync est le mode par défaut, il resynchronise les MLs avec les
            droits
         * forcedel fait comme autosync, en demandant un args du type
            [('ml1', 'all'), ('ml2', 'adresse_mail1')]
            et supprime les adresses mail concernées de la mailing-liste,
            'all' pour toutes les MLs ne correspondant pas à des gens avec
            des droits (seulement si elles ne seront pas réinscrites
            automatiquement)
         * getunsync retourne les gens qui devraient être inscrits/désinscrits
            de chaque ML
        args désigne l'argument au mode choisi."""

        try:
            self.anim.iter = len(droits_mailing_listes.keys())
        except:
            pass

        if mode == 'getunsync':
            unsync = {}

        for ML, fonctions in droits_mailing_listes.items() :
            try:
                self.anim.cycle()
            except:
                pass

            if ML[0] == '+' :
                ML = ML[1:]
                only_add = True
            else :
                only_add = False

            # Instance correspondant à la ML
            mlist = MailList.MailList(ML)
            self.mlist_to_unlock = mlist

            # Qui doit être dans cette ML ?
            res = []
            for f in fonctions :
                res += self.search('droits=%s' % f)['adherent']

            # Liste des personnes déja inscrites
            deja_inscrits = {} # { email en miniscules : email avec case n'importe comment }
            for addr in mlist.getMemberCPAddresses(mlist.getMembers()):
                deja_inscrits[addr.lower()] = addr

            # Mails à ajouter
            to_add = []
            mail_traite = []
            for adher in res :
                mail = adher.mail().lower()
                if mail in mail_traite : continue
                mail_traite.append(mail)
                if adher._data.get('uid', '') != '' and not mail.endswith('@crans.org') : mail += '@crans.org'
                if mail not in deja_inscrits.keys() :
                    # Visiblement pas inscrit
                    to_add.append([ mail, adher.Nom() ])
                else :
                    # L'adhérent est déja inscrit
                    deja_inscrits.pop(mail)

            if mode == 'autosync' or mode == 'forcedel':
                # Ajout
                for mail, nom in to_add :
                    pw = Utils.MakeRandomPassword()
                    userdesc = UserDesc(mail, strip_accents(nom), pw)
                    mlist.ApprovedAddMember(userdesc)
                if not only_add or mode == 'forcedel':
                    # Supression des personnes inscritees en trop
                    if not only_add or (mode == 'forcedel' and args != None
                                        and (ML, 'all') in args):
                        for mail in deja_inscrits.values() :
                            mlist.ApprovedDeleteMember(mail)
                    else:
                        # Suppression des mails demandés
                        for mail in deja_inscrits.values() :
                            if (ML, mail) in args:
                                mlist.ApprovedDeleteMember(mail)

                mlist.Save()
            elif mode == 'getunsync':
                if to_add or deja_inscrits:
                    unsync[ML] = (only_add, map(lambda (x,y): y, to_add),
                            deja_inscrits.values())

            mlist.Unlock()
            self.mlist_to_unlock = None

        if mode == 'getunsync':
            return unsync

    def gen_conf(self):
        self.anim = anim('\tconfiguration groupes')
        try:
            self.build_group()
            self.anim.reinit()
            print OK
        except:
            self.anim.reinit()
            print ERREUR
            if self.debug :
                import traceback
                traceback.print_exc()

        self.anim = anim('\tconfiguration ML Crans')
        try:
            self.config_ML()
            self.anim.reinit()
            print OK
        except:
            self.anim.reinit()
            print ERREUR
            if self.debug :
                import traceback
                traceback.print_exc()
            try:
                # Au cas où...
                self.mlist_to_unlock.Unlock()
            except :
                pass

class desabonner_ml(droits_ldap):
    def gen_conf(self):
        self.anim = anim('\tdesabonnement MLs Crans')
        try:
            to_del = []
            for arg in self.args:
                (mail, ml) = arg.split('$')
                to_del.append((ml, "%s@crans.org" % mail))
            self.config_ML(mode='forcedel', args=to_del)
            self.anim.reinit()
            print OK
        except:
            self.anim.reinit()
            print ERREUR
            if self.debug :
                import traceback
                traceback.print_exc()

class droits_openbsd(droits) :
    def build_master_passwd_group(self) :
        """Reconstruit les entrées à ajouter dans master.passwd

        Reconstruit également /etc/group pour le group wheel
        """
        master = ""
        group = "wheel:*:0:root"
        self.anim.iter = 2
        for fonction in ("Nounou", "Apprenti"):
            self.anim.cycle()
            # Qui doit être dans ce groupe ?
            res = crans_ldap().search('droits=%s' % fonction)['adherent']
            for a in res:
                if fonction == "Nounou":
                    # On rajoute à /etc/group
                    group = "%s,%s" % (group, a._data['uid'][0])
                #On conserve le shell
                #Il faut ôter les emplacements spécifique à linux (type
                #/usr/bin/zsh)
                if "zsh" in a._data['loginShell'][0]:
                    shell = "/bin/zsh"
                elif "bash" in a._data['loginShell'][0]:
                    shell = "/bin/bash"
                else:
                    shell = "/bin/zsh"
                # On ajoute dans master
                # Le mot de passe est bidon
                master = "%s%s:$1$rQcJgpD8$ZZjjszWKnSp9rR6iZ9GPm2:%s:1000:ldap:0:0:%s:%s:%s\n" % (master,
                                                                a._data['uid'][0],
                                                                a._data['uidNumber'][0],
                                                                a._data['gecos'][0],
                                                                a._data['homeDirectory'][0],
                                                                shell)

        group = "%s\n" % group
        # On va réécrire /etc/master.passwd
        # cf man master.passwd
        fichier = file("/etc/master.passwd")
        for line in fichier:
            if line.split(":")[4].strip() != "ldap":
                master = "%s%s" % (line,master)
        fichier.close()
        # On va écrire par-dessus
        fichier = file("/etc/master.passwd", "w")
        fichier.write(master)
        fichier.close()
        os.system("pwd_mkdb -p /etc/master.passwd")

        # On réécrit /etc/group
        fichier = file("/etc/group")
        for line in fichier:
            if line.split(":")[0].strip() != "wheel":
                group = "%s%s" % (group,line)
        fichier.close()
        # On va réécrire par-dessus
        fichier = file("/etc/group", "w")
        fichier.write(group)
        fichier.close()

    def gen_conf(self) :
        self.anim = anim('\tconfiguration master.passwd')
        try:
            self.build_master_passwd_group()
            self.anim.reinit()
            print OK
        except:
            self.anim.reinit()
            print ERREUR
            if self.debug :
                import traceback
                traceback.print_exc()

def usage():
    print "%s [options] [arguments]" % sys.argv[0]
    print "Options :"
    print " * -h : afficher cette aide"
    print " * -d ml1:mail1,mail2 ml2:all : enlever des adresses mail"
    print " * -p droit1 droit2 : afficher les membres ayant tels droits"
    print " * -u : affiche les MLs avec des gens à inscrire / enlever"

if __name__ == '__main__' :
    try:
        opts, args = getopt.getopt(sys.argv[1:], "dhpu", [])
    except:
        usage()
        sys.exit()

    if len(opts) == 0 or '-h' in sys.argv[1:]:
        usage()
        sys.exit()

    cl = droits_ldap()
    for o, a in opts:
        if o == '-d':
            # Suppression d'adresses mail
            to_del = []
            for arg in args:
                if ':' in arg:
                    [ml, mails] = arg.split(':', 1)
                    to_del.extend(map(lambda m: (ml, m), mails.split(',')))
            print "Suppression forcée : %s ..." % str(to_del)
            cl.config_ML(mode='forcedel', args=to_del)

        elif o == '-p':
            # Affichage des membres ayant certains droits
            for arg in args:
                titre = "%s : " % arg
                print titre
                print "-" * len (titre)
                cl.print_liste(arg)

        elif o == '-u':
            # Affichage des MLs non synchronisées
            print "MLs non synchronisées :"
            unsync = cl.config_ML(mode='getunsync')
            for ml in unsync.keys():
                (only_add, to_add, to_del) = unsync[ml]
                print u"%s: À ajouter: %s / À enlever: %s%s" % (ml,
                    ', '.join(to_add), ','.join(to_del),
                    u" (ne seront pas enlevées automatiquement)"
                    if to_add == 1 else "")
