#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-

u"""
Interface utilisateur du système de gestion des machines
et adhérents du crans

Copyright (C) Frédéric Pauget
Licence : GPLv2

Les fonctions set_* permettent de définir certains
paramètres liés à un adhérent ou une machine, elles posent les questions
puis apellent la méthode adpatée de la classe adhérent ou machine.
Elles prennent toute une instances de cette classe en paramètre.
Elles retournent 1 si l'action à échoué et None si OK.
Les fonction select permettent de choisir un objet dans la base
Retournent None si pas d'objet trouvé.
"""

### Rapport de bug automatique
# Destinataires, si vide n'envoi rien
To = ['root@crans.org']

import string
import os
import sys
import dialog as dialog_module
from whos import aff
import signal
import getopt
from time import strftime, strptime, localtime, mktime, time
import re
import subprocess
import affich_tools
import config
import config.cotisation as cotisation
from affich_tools import cprint, to_encoding, to_unicode
from lock import make_lock, remove_lock
from ldap_crans import crans_ldap, blacklist_items, droits_possibles, droits_critiques, smtpserv, script_utilisateur
from ldap_crans import Adherent, AssociationCrans, Club, Facture
from ldap_crans import Machine, MachineFixe, MachineWifi, MachineCrans, BorneWifi
from ldap_crans import tz, generalizedTimeFormat, fromGeneralizedTimeFormat
import user_tests

isadm = user_tests.isadm()
isdeconnecteur = user_tests.isdeconnecteur()

db = crans_ldap()
droits = db.search("uid=%s" % script_utilisateur)['adherent'][0].droits()
isimprimeur = u"Imprimeur" in droits
iscontroleur = u'Tresorier' in droits
isbureau = u'Bureau' in droits
encoding = sys.stdin.encoding or 'UTF-8'

if u'Nounou' in droits:
    # Si on est nounou
    if os.path.exists(os.path.expanduser('~/.dialogrc')):
        # Si on a un fichier de configuration du programme dialog dans son
        # HOME, alors on récupère son chemin.
        DIALOGRC = '~/.dialogrc'
    else:
        # Sinon on utilise celui du système.
        DIALOGRC = '/etc/dialog.rc'

    dlg = dialog_module.Dialog(DIALOGRC=DIALOGRC)
else:
    # Si on est pas nounou, on est libre de faire ce que l'on veut avec la
    # variable d'environnement DIALOGRC.
    DIALOGRC = ''

    dlg = dialog_module.Dialog()


def dialog(arg):
    """
    Raccourci permettant d'appeler :py:func:`affich_tools.dialog`.

    .. seealso:: :py:func:`affich_tools.dialog`
    """
    return affich_tools.dialog(u'Gestion des adhérents et machines du Crans', arg, DIALOGRC)

in_facture = None
#########################################################################
## Fonctions de remplissage ou modification des paramètres d'un adhérent

def set_bases(adher):
    u"""
    Définition des paramètres de bases d'un adhérent :
        * Nom
        * Prenom
        * Téléphone
        * Chambre
    """

    # Construction de la boite de dialogue
    arg = u'--title "Inscription adhérent" '
    arg += u'--form "" 0 0 0 '
    arg += u'"Nom :"       1 1 "%s" 1 13 20 20 ' % adher.nom()
    arg += u'"Prénom :"    2 1 "%s" 2 13 20 20 ' % adher.prenom()
    arg += u'"Numéro de téléphone :" 3 1 "%s" 3 23 15 00 ' % adher.tel()
    arg += u'"Chambre :"   4 1 "%s" 4 11 05 00 ' % adher.chbre()
    arg += u'"(bat+numéro)" 4 17 "" 0 0 0 0 '
    arg += u'"EXT pour chambre extérieure au campus" 5 1 "" 0 0 0 0 '

    # Affichage
    annul, result = dialog(arg)
    if annul:
        return 1

    # Traitement
    err = ''
    try: adher.nom(result[0])
    except ValueError, c: err += c.args[0] + '\n'

    try: adher.prenom(result[1])
    except ValueError, c: err += c.args[0] + '\n'

    try: adher.tel(result[2])
    except ValueError, c: err += c.args[0] + '\n'

    # Un adhérent du même nom existe-t-il déjà ?
    req = 'nom=' + result[0] + '&prenom=' + result[1]
    if len(db.search(req)['adherent']) > 0:
        arg = u'--title "Inscription adhérent" '
        arg += u'--yesno "Un adhérent du même nom existe déjà.'
        arg += u'\nEst-ce bien une première inscription ?" 0 0'
        no, res = dialog(arg)
        if no:
            return 1

    err += _set_chbre(adher, result[3])

    # Des erreurs ?
    if err:
        arg = u'--title "Inscription adhérent" '
        arg += u'--msgbox "%s\n\n" 0 0' % to_unicode(err)
        dialog(arg)
        # On redemande
        return set_bases(adher)

    if adher.chbre() == 'EXT':
        # Il faut demander l'adresse extérieure
        if set_addr_ext(adher):
            # Annulation
            return set_bases(adher)

def _set_chbre(adher, chbre):
    u""" Attribution de la chambre chbre à l'adhérent fourni
    Retourne une chaine (unicode) avec l'erreur éventuelle
    """
    if chbre == '????':
        # Réservé à un usage interne
        return u'Chambre invalide\n'

    try: c = adher.chbre(chbre)
    except EnvironmentError, c: return c.args[0] + '\n'
    except ValueError, c:
        if len(c.args) == 2:
            # La chambre est occupée
            squatteur = c.args[1]
            aff(squatteur)
            arg = u'--title "Inscription adhérent" '
            arg += u'--yesno "Un adhérent (%s) occupe déjà cette chambre.\n' % squatteur.Nom()
            arg += u'\nChanger la chambre de cet adhérent ?" 0 0'
            no, res = dialog(arg)
            if not no:
                squatteur.chbre('????')
                squatteur.save()
                # On recommence, le géneur doit être parti.
                return _set_chbre(adher, chbre)
        return c.args[0] + '\n'

    return u''

def set_addr_ext(adher):
    u""" Définition de l'adresse extérieure d'un adhérent
    La chambre de cet adhérent doit être EXT, sinon erreur """
    arg = u'--title "Adresse extérieure de %s" ' % adher.Nom()
    arg += u'--form "" 0 0 0 '
    arg += u'"" 1 1 "%s" 1 1 46 50 ' % adher.adresse()[0]
    arg += u'"" 2 1 "%s" 2 1 46 50 ' % adher.adresse()[1]
    arg += u'"Code postal :" 3 1 "%s" 3 15 6 0 ' % adher.adresse()[2]
    arg += u'"Ville :" 3 21 "%s" 3 30 17 30 ' % adher.adresse()[3]
    # Affichage
    annul, result = dialog(arg)
    if annul: return 1

    try: adher.adresse(result)
    except ValueError, c:
        arg = u'--title "Adresse extérieure de %s" ' % adher.Nom()
        arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
        dialog(arg)
        # On redemande
        return set_addr_ext(adher)

def set_etudes(adher):
    u"""
    Définition des études en 3 questions :
        * établissement
        * année administrative
        * section/labo
    """

    def __etudes_etab():
        arg = u'--title "Etudes de %s (1/3)" ' % adher.Nom()
        if adher.etudes(0):
            arg += u'--default-item "%s" ' % adher.etudes(0)
        arg += u'--menu "Choisissez l\'établissement : " 0 0 0 '
        if adher.etudes(0):
            arg += u'"Autre" "" '
        arg += u'"ENS" "" '
        arg += u'"IUT Cachan" "" '
        arg += u'"Maximilien Sorre" "" '
        arg += u'"Gustave Eiffel" "" '
        arg += u'"EFREI" "" '
        arg += u'"ESTP" "" '
        arg += u'"P1" "Université Panthéon Sorbonne" '
        arg += u'"P2" "Université Panthéon Assas" '
        arg += u'"P3" "Université de la Sorbonne Nouvelle" '
        arg += u'"P4" "Université Paris Sorbonne" '
        arg += u'"P5" "Université René Descartes" '
        arg += u'"P6" "Université Pierre et Marie Curie" '
        arg += u'"P7" "Université Paris Diderot" '
        arg += u'"P8" "Université Vincennes Saint Denis" '
        arg += u'"P9" "Université Paris Dauphine" '
        arg += u'"P10" "Université de Nanterre" '
        arg += u'"P11" "Université de Paris Sud (Orsay)" '
        arg += u'"P12" "Université Val de Marne" '
        arg += u'"P13" "Université Paris Nord" '
        arg += u'"IUFM" "" '
        arg += u'"Personnel ENS" "dans les appartements de l\'ENS" '
        if not adher.etudes(0):
            arg += u'"Autre" ""'

        annul, result = dialog(arg)
        if annul: return 1

        if result[0] == 'Autre':
            arg = u'--title "Etudes de %s (1/3)" ' % adher.Nom()
            arg += u'--inputbox "Précisez l\'établissement d\'études" 0 0 "%s"' % adher.etudes(0)
            annul, result = dialog(arg)
            if annul: return 1
            if result == ['']:
                # Pas bon
                arg = u'--title "Etudes (2/3)" '
                arg += u'--msgbox "Réponse invalide\n\n\n" 0 0'
                dialog(arg)
                return __etudes_etab()

        etudes[0] = result[0]

    def __etudes_annee():
        arg = u'--title "Etudes de %s (2/3)" ' % adher.Nom()
        if etudes[0] == 'ENS':
            arg += u'--default-item "%s" ' % adher.etudes(1)
            arg += u'--menu "Choisissez l\'année administrative" 0 0 0 '
            arg += u'"1" "License" '
            arg += u'"2" "Master 1" '
            arg += u'"3" "Agrégation" '
            arg += u'"4" "Master 2" '
            arg += u'"5" "1ère année thèse" '
            arg += u'"6" "2ème année thèse" '
            arg += u'"7" "3ème année thèse" '
            arg += u'"Autre" ""'
            annul, result = dialog(arg)
            if annul: return 1
        elif etudes[0] in ['P1', 'P2', 'P3', 'P4', 'P5', 'P6', 'P7', 'P8', 'P9', 'P10', 'P11', 'P12', 'P13']:
            arg += u'--default-item "%s" ' % adher.etudes(1)
            arg += u'--menu "Choisissez l\'année administrative" 0 0 0 '
            arg += u'"Deug 1" "Deug 1ère année" '
            arg += u'"Deug 2" "Deug 2ème année" '
            arg += u'"Licence" "" '
            arg += u'"Master 1" "Master 1ère année" '
            arg += u'"Master 2" "Master 2ème année" '
            arg += u'"Thèse 1" "1ème année thèse" '
            arg += u'"Thèse 2" "2ème année thèse" '
            arg += u'"Thèse 3" "3ème année thèse" '
            arg += u'"Autre" ""'
            annul, result = dialog(arg)
            if annul: return 1
        elif etudes[0] == "Personnel ENS":
            result = ['0']
        else:
            result = ['Autre']

        if result[0] == 'Autre':
            arg = u'--title "Etudes de %s (2/3)" ' % adher.Nom()
            arg += u'--inputbox "Année adminstrative :\nET UNIQUEMENT l\'ANNEE : la section sera demandée après." 0 0 "%s"' % adher.etudes(1)
            annul, result = dialog(arg)
            if annul: return 1
            if result == ['']:
                # Pas bon
                arg = u'--title "Etudes (2/3)" '
                arg += u'--msgbox "Réponse invalide\n\n\n" 0 0'
                dialog(arg)
                return __etudes_annee()

        etudes[1] = result[0]

    def __etudes_section():
        arg = u'--title "Etudes de %s (3/3)" ' % adher.Nom()

        # Pour l'ENS
        if etudes[0] == 'ENS':
            arg += u'--default-item "%s" ' % adher.etudes(2)
            if etudes[1] in '1234':
                arg += u'--menu "Choisissez la section : " 0 0 0 '
            else:
                arg += u'--menu "Choisissez le laboratoire :" 0 0 0 '
            if not etudes[1] in '1234':
                arg += u'"CMLA" "Centre de Mathématiques et de Leurs Applications" '
                arg += u'"GAPP" "Groupe d\'Analyse des Politiques Publiques" '
                arg += u'"IDHE" "Institutions et Dynamiques Historiques de l\'Economie" '
                arg += u'"LBPA" "Laboratoire de Biotechnologies et Pharmacologie génétique Appliquées" '
                arg += u'"LMT" "Laboratoire de Mécanique et Technologie" '
                arg += u'"LPQM" "Laboratoire de Photonique Quantique et Moléculaire" '
                arg += u'"LSV" "Laboratoire de Spécification et Vérification" '
                arg += u'"LURPA" "Laboratoire Universitaire de Recherche en Production Automatisée" '
                arg += u'"PPSM" "Laboratoire de Photophysique et Photochimie Supramoléculaires et Macromoléculaires" '
                arg += u'"SATIE" "Systèmes et Applications des Technologies de l\'Information et de l\'Energie" '
                arg += u'"STEF" "Sciences Techniques Education Formation" '
            if etudes[1] in '1234':
                arg += u'"A0" "Informatique" '
                arg += u'"A1" "Mathématiques" '
                arg += u'"A2" "Physique fondamentale" '
                arg += u'"A\'\'2" "Chimie" '
                arg += u'"A3" "Biochimie" '
                if etudes[1] == '1':
                    arg += u'"SAPHIRE" "Sciences Appliquées en PHysique et Ingénierie pour la Recherche et l\'Enseignement" '
                elif etudes[1] == '2':
                    arg += u'"EEA" "Électronique, électrotechnique et automatique" '
                    arg += u'"B1" "Mécanique" '
                    arg += u'"B2" "Génie civil" '
                    arg += u'"B3" "Génie mécanique" '
                else:
                    arg += u'"B1" "Mécanique" '
                    arg += u'"B2" "Génie civil" '
                    arg += u'"B3" "Génie mécanique" '
                    arg += u'"A\'2" "Physique appliquée" '
                    arg += u'"B4" "Génie électrique" '
                arg += u'"C" "Art et création industrielle" '
                arg += u'"D2" "Economie gestion" '
                arg += u'"D3" "Sciences sociales" '
                arg += u'"E" "Anglais" '

            arg += u'"Autre" ""'
            annul, result = dialog(arg)
            if annul: return 1
            if result == ['']:
                # Pas bon
                arg = u'--title "Etudes (2/3)" '
                arg += u'--msgbox "Réponse invalide\n\n\n" 0 0'
                dialog(arg)
                return __etudes_annee()
        elif etudes[0] == 'Personnel ENS':
            result = ['n/a']
        else:
            result = ['Autre']

        if result[0] == 'Autre':
            arg = u'--title "Etudes de %s (3/3)" ' % adher.Nom()
            arg += u'--inputbox "Section : " 0 0 "%s"' % adher.etudes(2)
            annul, result = dialog(arg)
            if annul: return 1
            if result == ['']:
                # Pas bon
                arg = u'--title "Etudes (3/3)" '
                arg += u'--msgbox "Réponse invalide\n\n\n" 0 0'
                dialog(arg)
                return __etudes_section()

        etudes[2] = result[0]

    etudes = ['', '', '']

    step = 1
    while 1:
        if step == 1:
            if __etudes_etab(): return 1
            else: step += 1
        if step == 2:
            if __etudes_annee(): step -= 1
            else: step += 1
        if step == 3:
            if __etudes_section(): step -= 1
            else: break
    try:
        adher.etudes(etudes)
    except ValueError, c:
        arg = u'--title "Etudes de %s" ' % adher.Nom()
        arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
        dialog(arg)
        return set_etudes(adher)

def set_contact(adher):
    u"""
    Choix d'une adresse mail crans ou extérieure.
    Retourne ensuite le résultat de :
        * set_mail si adresse ext
        * set_compte si compte crans
    """
    if u'Nounou' in adher.droits() and not isadm:
        arg = u'--title "Adresse mail de %s" '  % adher.Nom()
        arg += u'--msgbox "Vous n\'avez pas les droits necessaires pour effectuer cette opération.\n\n\n" 0 0'
        dialog(arg)
        return

    if not isinstance(adher, Club) and not adher.adherentPayant():
        set_mail(adher)
        return

    while 1:
        arg = u'--title "Adresse mail de %s" '  % adher.Nom()
        arg += u'--menu "Adresse mail de l\'adhérent :" 0 0 0 '
        arg += u'"Adresse mail extérieure" "" '
        if adher.compte():
            arg += u'"Laisser le compte crans" "(login : %s)"' % adher.compte()
        else:
            arg += u'"Créer un compte crans" "(adresse @crans.org)"'

        annul, result = dialog(arg)
        if annul: return 1

        if result[0].split()[0] == 'Laisser':
            break
        elif result[0].split()[1] == u'un':
            if not set_compte(adher):
                set_mail_ext(adher)
                break
        else:
            if not set_mail(adher):
                break

def set_mail(adher):
    """Demande l'adresse mail extérieure d'un adhérent
    """
    default = adher.mail()
    if default.endswith('@crans.org'):
        # C'était une adresse crans
        default = ''

    arg = u'--title "Adresse mail pour %s" '  % adher.Nom()
    arg += u'--inputbox "Adresse : " 0 0 "%s"' % default
    annul, result = dialog(arg)
    if annul: return 1

    try: adher.mail(result[0])
    except ValueError, c:
        arg = u'--title "Adresse mail de %s" ' % adher.Nom()
        arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
        dialog(arg)
        return set_mail(adher)

def set_mail_ext(adher):
    """Récupère une éventuelle adresse de redirection en cas de choix de compte
    Crans. Permet en règle générale d'ajouter un mail extérieur au moins pour
    les récupérations de mot de passe ou autre.

    """
    if not adher.compte():
        return 0

    cur = adher.email_exterieur()

    if u'Nounou' in adher.droits() and not isadm:
        arg = u'--title "Adresse mail extérieure de %s" '  % adher.Nom()
        arg += u'--msgbox "Vous n\'avez pas les droits necessaires pour effectuer cette opération.\n\n\n" 0 0'
        dialog(arg)
        return

    no = False

    if 'compte' in adher.modifs:
        arg = u'--title "Redirection des mails de %s" ' % (adher.Nom(),)
        arg += u'--colors --yesno '
        arg += u'"L\'adhérent souhaite-t-il mettre en place une redirection de ses emails vers une autre boîte ?\n'
        arg += u'Cette redirection lui permettra aussi de récupérer son mot de passe Crans en cas de perte de celui-ci." '
        arg += u'0 0'
        no, _ = dialog(arg)

    if no:
        return 0

    arg = u'--title "Adresse mail extérieure pour %s" '  % adher.Nom()
    arg += u'--inputbox "Adresse : " 0 0 "%s"' % (cur,)
    annul, result = dialog(arg)
    if annul:
        return 1

    try:
        adher.email_exterieur(result[0])
    except ValueError as c:
        arg = u'--title "Adresse mail extérieure de %s" ' % adher.Nom()
        arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
        dialog(arg)
        return set_mail_ext(adher)

def set_etat_civil(adher):
    """
    Modifie l'état-civil (nom, prénom) d'un adhérent.
    """
    if u'Nounou' in adher.droits() and not isadm:
        arg = u'--title "Adresse mail de %s" '  % adher.Nom()
        arg += u'--msgbox "Vous n\'avez pas les droits necessaires pour effectuer cette opération.\n\n\n" 0 0'
        dialog(arg)
        return

    # Construction de la boite de dialogue
    arg = u'--title "État-civil de %s" ' % adher.Nom()
    arg += u'--form "" 0 0 0 '
    arg += u'"Nom :"       1 1 "%s" 1 13 20 20 ' % adher.nom()
    arg += u'"Prénom :"    2 1 "%s" 2 13 20 20 ' % adher.prenom()

    # Affichage
    annul, result = dialog(arg)
    if annul: return

    # Traitement
    err = ''
    try: adher.nom(result[0])
    except ValueError, c: err += c.args[0] + '\n'

    try: adher.prenom(result[1])
    except ValueError, c: err += c.args[0] + '\n'

    # Des erreurs ?
    if err:
        arg = u'--title "État-civil de %s" ' % adher.Nom()
        arg += u'--msgbox "%s\n\n" 0 0' % to_unicode(err)
        dialog(arg)
        # On redemande
        return set_etat_civil(adher)

    # On change éventuellement le compte
    if adher.compte():
        # On demande au câbleur s'il faut garder le compte ou pas
        arg = u'--title "Modification du compte de %s" ' % adher.Nom()
        arg += u'--colors --defaultno --yesno "Changer le login de l\'adhérent ?\n'
        arg += u'Son login actuel est %s.\n\n' % adher.compte()
        arg += u'Choisir Oui si l\'adhérent n\'a jamais utilisé son compte.\n\n'
        arg += u'\Zr\Z1AVERTISSEMENT :\n'
        arg += u'Le changement de login entraîne la suppression irréversible du compte, '
        arg += u'et donc de tous les fichiers, mails, etc. associés !\Z0\ZR\n\n\n" '
        arg += u'0 0'
        no = not dialog(arg)[0]

        if no:
            # L'utilisateur n'utilise pas son compte, on le supprime...
            adher.supprimer_compte()
            # ..et on le recrée (ou on met une adresse mail extérieure)
            if set_contact(adher):
                # Le changement d'adresse a été annulé, on recommence tout
                adher.restore()
                return set_etat_civil(adher)

def set_compte(adher):
    u"""
    Créé un compte sur vert pour un adhérent.
    """

    # Message d'avertissement
    arg = u'--title "Création compte crans pour %s" ' % adher.Nom()
    arg += u'--colors --yesno "\Zr\Z1AVERTISSEMENT :\Zn \n'
    arg += u'L\'adhérent devra impérativement consulter l\'adresse mail associée\n\n\n\\ZnContinuer ?" '
    arg += u'0 0'
    no, res = dialog(arg)
    if no: return 1

    # Il faut déterminer le login
    login = adher.nom()

    # Première tentative
    err = 0
    try:
        login = adher.compte(login)
    except ValueError, c:
        try:
            c.args[1]
            # Le compte existe => 2ème tentative (1ere lettre prénom + nom)
            login = adher.prenom()[0] +login
            login = login.lower()
            err = 2
        except:
            err = 1
    except EnvironmentError, c: err = 1 # Locké

    if err:
        while 1:
            # Mauvais login => on demande
            arg = u'--title "Création d\'un compte crans pour %s" ' % adher.Nom()
            arg += u'--inputbox "Choix du login\n'
            arg += u'Le login doit faire au maximum %s caractères\n' % config.maxlen_login
            arg += u'Il ne doit pas être un pseudo ou prénom mais doit être relié au nom de famille\n'
            arg += u'Seuls les caractères alphabétiques et le - sont autorisés" '
            arg += u'0 0 "%s"' % login
            annul, result = dialog(arg)
            if annul: return 1
            login = result[0]

            e = 0
            try: login = adher.compte(login)
            except EnvironmentError, c: e = c.args[0]
            except ValueError, c: e = c.args[0]
            if e:
                arg = u'--title "Création compte crans pour %s" ' % adher.Nom()
                arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(e)
                dialog(arg)
                continue
            break

    txt = u"Le compte ne sera créé que lors de l'enregistrement des données\n\n"
    txt += u"L'adresse mail de l'adhérent est : %s@crans.org\n" % login

    a = adher.canonical_alias()
    if a:
        txt += u"L'adhérent possède également l'alias : %s" % a
    else:
        txt += u"\n\Zr\Z1L'adresse mail %s.%s@crans.org étant déja prise l'adhérent ne peut pas l'utiliser.\Zn" % (adher.prenom(), adher.nom())

    txt += u'\n'

    arg = u'--title "Création compte crans pour %s" ' % adher.Nom()
    arg += u'--colors --msgbox "%s\n\n\n" 0 0' % txt
    dialog(arg)

def set_droits(adher):
    u""" Définition des droits de l'adhérent """
    arg = u'--title "Droits de %s" ' % adher.Nom()
    arg += u'--separate-output '
    arg += u'--checklist "" 0 0 0 '

    droits = adher.droits()
    for key in droits_possibles:
        actif = key in droits
        if actif: actif = u'on'
        if isadm or key not in droits_critiques:
            arg += u'"%s" " " "%s" ' % (key, actif)

    annul, result = dialog(arg)
    if annul: return 1

    ### Traitement
    # On regarde les MLs auxquelles l'adhérent était inscrit grâce à ses
    # droits et auxquelles il ne sera pas désabonné
    # (MLs à débonnement manuel)
    droits_mls = config.droits_mailing_listes

    # On nettoie la sortie de dialog
    new = []
    for droit in result:
        droit = droit.strip()
        if droit == '': continue
        if droit not in droits_possibles:
            raise ValueError(u'Droit %s incorrect' % droit)
        new.append(droit)

    # Dans le cas où l'utilisateur qui modifie n'est pas Nounou, on ajoute les
    # droits critiques que l'adhérent possède déjà
    if not isadm:
        for key in [d for d in droits_critiques if d in adher.droits()]:
            new.append(key)

    # Droits supprimés
    diff = [droit for droit in adher.droits()
            if droit not in new]

    # Droits supprimés qui abonnaient à une ML
    old_mls = []
    for droit in diff:
        old_mls.extend([
            m for m in droits_mls.keys()
            if m[0] == '+' and droit in droits_mls[m]
            and m not in old_mls
        ])

    # MLs pour lesquelles un autre droit abonne encore
    for droit in new:
        for ml in [m for m in old_mls if droit in droits_mls.get(m, [])]:
            old_mls.remove(ml)

    # Choix des MLs auxquelles désabonner
    if old_mls:
        arg = u'--title "Mailing-listes dont désabonner %s" ' % adher.Nom()
        arg += u'--separate-output '
        arg += u'--checklist "%s a été abonné automatiquement' % adher.Nom()
        arg += u' aux MLs suivantes lorsqu\'il a obtenu ses droits." 0 0 0 '
        for ml in old_mls:
            arg += u'"%s" " " "" ' % ml[1:]
        annulmls, resultmls = dialog(arg)
        if annulmls: resultmls = []
        del_mls = []
        for resml in resultmls:
            if resml == '': continue
            del_mls.append(resml.strip())

        if del_mls:
            db.services_to_restart('desabonner_ml', [
                "%s$%s" % (adher.mail(), mailing_list)
                for mailing_list in resultmls
            ])

    # On modifie !
    e = None
    try:
        if not isadm:
            adher.droits(result, light=True)
        else:
            adher.droits(result)
    except EnvironmentError, c: e = c.args[0]
    except ValueError, c: e = c.args[0]
    if e:
        arg = u'--title "Modification des droits pour %s" ' % adher.Nom()
        arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(e)
        dialog(arg)
        return 1

def set_actif(adher):
    u"""
    Définition de l'état d'activité du compte.
    """
    # Initialisation des différentes checkbox
    inactif = on_off(adher.active() == False)

    # Construction de la boîte de dialogue
    texte = []
    checklist = []

    if inactif:
        checklist.append(u'"1" "Compte mail/serveur désactivé" "%s"' % (inactif))

    # Il y a qqch de modifiable, on construit la checklist
    arg = u'--title "Statut du compte %s" ' % adher.Nom()
    arg += u'--separate-output '
    arg += u'--checklist "" 0 0 0 '
    arg += u' '.join(checklist)

    annul, result = dialog(arg)
    if annul: return 1

    # Traitement
    if '1' in result:
        adher.active(False)
    else:
        adher.active(True)

def del_adher(adher):
    u"""
    Destruction adhérent
    """
    if u'Nounou' in adher.droits() and not isadm:
        arg = u'--title "Destruction adherent" '
        arg += u'--msgbox "Vous n\'avez pas les droits necessaires pour effectuer cette opération.\n\n\n" 0 0'
        dialog(arg)
        return

    quoi = u'Toutes les machines associées à cet adhérent seront détruites'
    if adher._data.get('uid', '') != '':
        # L'adhérent a un compte
        has_machines = (adher.machines() != [])
        if not has_machines:
            quoi = u'Le compte de cet adhérent sera détruit'
        else:
            # Et aussi des machines
            # -> soit on détruit tout
            # -> soit on garde le compte mais on vire les machines
            arg = u'--title "Destruction adhérent %s " ' % adher.Nom()
            arg += u'--menu "Cette adhérent possède également un compte." 0 0 0 '
            if adher:
                arg += u'"1" "Détruire seulement les machines de l\'adhérent" '
                arg += u'"2" "Détruire les machines et le compte de l\'adhérent" '
                annul, res = dialog(arg)
                if annul: return 1
                if res[0] == '2':
                    # On détruit tout
                    quoi += u' ainsi que son compte'
                else:
                    # Destruction uniquement des machines
                    arg = u'--title "Destruction machines adhérent %s " --colors ' % adher.Nom()
                    arg += u'--inputbox "\Zr\Z1ATTENTION\Zn : la destruction est définitive\nToutes les machines associées à cet adhérent seront détruites, seul le compte sera conservé.\nCommentaire à insérer ?" 0 0'
                    annul, res = dialog(arg)
                    if annul: return 1
                    for m in adher.machines():
                        m.delete(res[0])
                    adher.chbre('EXT')
                    arg = u'--title "Destruction machines" '
                    arg += u'--msgbox "Machines détruites\n\n\n" 0 0'
                    dialog(arg)
                    return

    while 1:
        arg = u'--title "Destruction adhérent %s " --colors ' % adher.Nom()
        arg += u'--inputbox "\Zr\Z1ATTENTION\Zn : la destruction est définitive\n\n%s.\n\nCommentaire à insérer ?" 0 0' % quoi
        annul, res = dialog(arg)
        if annul: return 1
        if  res[0]: break
        arg = u'--title "Destruction adherent" '
        arg += u'--msgbox "Le commentaire est obligatoire\n\n\n" 0 0'
        dialog(arg)

    try:
        adher.delete(res[0])
    except EnvironmentError, c:
        arg = u'--title "Destruction du compte" '
        arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
        dialog(arg)
        return

    arg = u'--title "Destruction adhérent" '
    arg += u'--msgbox "Adhérent détruit\n\n\n" 0 0'
    dialog(arg)

###############################################################
## Fonctions de remplissage ou modification des paramètres club

def set_responsable(club):
    u""" Modifie le responsable d'un club """
    arg = u'--title "Responsable du club" '
    arg += u'--msgbox "Séléctionnez l\'adhérent responsable du club\n\n\n" 0 0'
    dialog(arg)
    resp = select(club, u'du responsable du club a', 'ro')
    if not resp: return 1
    else:
        club.responsable(resp)

def set_imprimeurs(club):
    u""" Modifie les imprimeurs d'un club """
    while 1:
        arg = u'--title "Imprimeurs de %s" ' % club.Nom()
        if len(club.imprimeurs()) > 0:
            arg += u'--menu "Action ?" 0 0 0 '
            arg += u'"Enlever" "Enlever un imprimeur" '
        else:
            arg += u'--menu "Il n\'y a aucun imprimeur pour ce club." 0 0 0 '
        arg += u'"Ajouter" "Ajouter un imprimeur" '
        annul, res = dialog(arg)
        if annul: break
        if res[0] == 'Enlever':
            liste = map(lambda s: db.search("aid=%s" % s)['adherent'][0], club.imprimeurs())
            if len(liste) == 1:
                # Une seule réponse
                choix = liste[0]
            else:
                # Il faut choisir
                while 1:
                    os.system('clear')
                    choix = None
                    cprint(u"Ce club a plusieurs imprimeurs :")
                    aff(liste)
                    i = affich_tools.prompt(u'Votre choix ? (0 pour annuler) aid =')
                    if i == '0': break
                    for v in liste:
                        if v.id() == i:
                            choix = v
                            break
                    if not choix:
                        # Redemande le choix
                        cprint(u'Choix invalide')
                        continue
                    if choix: break

            if not choix:
                continue

            arg = u'--title "Enlever un imprimeur" '
            arg += u'--yesno "Enlever l\'imprimeur %s ? \n\n" 0 0' % choix.Nom()
            no, res = dialog(arg)
            if not no:
                if club.imprimeurs(retirer=str(choix.id())):
                    arg = u'--title "Enlever un imprimeur" '
                    arg += u'--msgbox "Imprimeur enlevé\n\n\n" 0 0'
                    dialog(arg)
                else:
                    arg = u'--title "Enlever un imprimeur" '
                    arg += u'--msgbox "Cet adhérent n\'est pas imprimeur du club !\n\n\n" 0 0'
                    dialog(arg)

        elif res[0] == u'Ajouter':
            newimp = select(club, u'd\'un nouvel imprimeur a', 'ro')
            if newimp:
                if club.imprimeurs(ajouter=str(newimp.id())):
                    arg = u'--title "Ajout imprimeur" '
                    arg += u'--msgbox "Imprimeur ajouté\n\n\n" 0 0'
                    dialog(arg)
                else:
                    arg = u'--title "Ajout imprimeur" '
                    arg += u'--msgbox "Cet adhérent est déjà imprimeur !\n\n\n" 0 0'
                    dialog(arg)
        continue

def set_club_nom(club):
    u"""Définit le nom du club"""
    # Nom du club
    arg = u'--title "Nom" '
    arg += u'--inputbox "Nom du club ?" 0 0 "%s"' % club.Nom()
    annul, res = dialog(arg)
    if annul: return 1
    else:
        try:
            club.Nom(res[0])
        except ValueError, c:
            arg = u'--title "Nom club" '
            arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
            dialog(arg)

def set_local(club):
    u""" Définit le local d'un club """
    try:
        club.chbre('&é"') # Fait une erreur
    except ValueError, c:
        locaux = c.args[1]

    arg = u'--title "Local du club" '
    arg += u'--default-item "%s" ' % club.chbre()
    arg += u'--menu "Choisissez le local du club :" 0 0 0 '
    key = locaux.keys()
    # on tri par ordre alphébétique avec 'EXT' à la fin
    key.pop(key.index('EXT'))
    key.sort()
    key.append('EXT')
    for k in key:
        arg += u'"%s" "%s" ' % (k, locaux[k])
    annul, result = dialog(arg)
    if annul: return 1
    else:
        club.chbre(result[0])


def set_club_compte(club):
    u"""
    Créé un compte sur vert pour un club.
    """

    while 1:
        arg = u'--title "Création d\'un compte crans pour %s" ' % club.Nom()
        arg += u'--inputbox "Choix du login\n'
        arg += u'Le nom pour le login doit faire au maximum %s caractères\n' % config.maxlen_login
        arg += u'Le login sur ssh sera club-\'nom du club\'\n'
        arg += u'Le site web du club sera accessible via l\'adresse http://clubs.ens-cachan.fr/\'nom du club\'\n'
        arg += u'Seuls les caractères alphabétiques et le - sont autorisés" '
        arg += u'0 0 ""'
        annul, result = dialog(arg)
        if annul: return 1
        login = result[0]

        e = 0
        try: login = club.compte(login)
        except EnvironmentError, c: e = c.args[0]
        except ValueError, c: e = c.args[0]
        if e:
            arg = u'--title "Création compte crans pour %s" ' % club.Nom()
            arg += u'--msgbox "%s\n\n\n" 0 0' % e
            dialog(arg)
            continue
        break

    txt = u"Le compte ne sera créé que lors de l'enregistrement des données\n\n"
    txt += u"L'adresse mail du club est : %s@crans.org\n" % login

    txt += u'\n'

    arg = u'--title "Création compte crans pour %s" ' % club.Nom()
    arg += u'--colors --msgbox "%s\n\n\n" 0 0' % txt
    dialog(arg)

######################################################################################
## Fonctions de remplissage ou modification des paramètres adhérent ou machine ou club
## (suivant la classe fournie)

def set_rque(clas):
    u"""Définit le commentaire (méthode info de clas)"""
    return __prompt_input_menu(clas.info, u'Remarque', u"Ajouter ou modifier une remarque\nPour ajouter une remarque modifier la dernière de la liste.")

def __prompt_input_menu(method, titre, prompt):
    arg = u'--title "%s" ' % titre
    arg += u'--extra-label "Ajout/modif" '
    arg += u'--inputmenu "%s\nPour supprimer, laisser le champ vide." 0 100 7 ' % prompt
    invite = '##nouveau##'

    i = 1
    for item in method():
        if item == '': continue
        arg += u'"%d" "%s" '% (i, item)
        i += 1
    arg += '"%d" "%s" '% (i, invite)

    annul, result = dialog(arg)
    if annul:
        return 1

    # si modif alors result = [ 'RENAMED X nouvelle remarque' ]
    result = result[0].split(' ', 2)
    if result[0] != 'RENAMED':
        # Pas de modif
        return

    # maintenant result = ['RENAMED', 'X', 'nouvelle remarque']
    num = int(result[1])
    val = result[2].strip()

    if val == invite:
        #  Rien à faire
        return

    try:
        if num == i:
            # Nouvelle valeur
            if val: method(val)
        else:
            method([num-1, val])

    except (EnvironmentError, ValueError) as c:
        arg = u'--title "%s" ' % titre
        arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
        dialog(arg)
        return __prompt_input_menu(method, titre, prompt)
    else:
        return __prompt_input_menu(method, titre, prompt)

def set_solde(clas):
    u""" Débit ou crédit d'un compte """
    while 1:
        arg = u'--title "Crédit / débit du compte de %s" ' % clas.Nom()
        arg += u'--inputbox "Solde actuel : %s\n Opération à effectuer (+ pour crédits et - pour débit) ?" 0 0 "" ' % clas.solde()
        annul, res = dialog(arg)
        if annul: return 1

        _montant = float(res[0])

        menu = []

        menu.append(u'"Liquide" "Espèces" ')
        menu.append(u'"Cheque" "Chèque" ')
        menu.append(u'"Carte" "Carte bancaire" ')
        menu.append(u'"Note" "Note Kfet (attention, moins traçable)" ')
        menu.append(u'"Arbitraire" "Mode de paiement qui crée ou détruit magiquement de l\'argent." ')

        if _montant > 0:
            _kword = u"Crédit"
            _kword2 = u"paiement"
        else:
            _kword = u"Débit"
            _kword2 = u"retrait"

        # Il y a qqch de modifiable, on construit la checklist
        arg = u'--title "%s du solde de %s" ' % (_kword, clas.Nom())
        arg += u'--menu "Mode de %s : " 0 0 0 ' % (_kword2,)
        arg += u''.join(menu)

        annul, result = dialog(arg)
        if annul: return 1

        _mode = result[0]

        # Ajout du commentaire
        arg = u'--title "%s du solde de %s" ' % (_kword, clas.Nom())
        arg += u'--inputbox "Commentaire à insérer (nom de note, …) ?\nAttention, \
             merci de bien justifier le motif en cas de mode de paiement arbitraire." 0 0'
        annul, comment = dialog(arg)

        if not annul:
            if comment[0]:
                comment = comment[0]
            else:
                comment = None

            f = Facture(clas)
            f.ajoute({'nombre': 1, 'code':'SOLDE', 'designation': "Modification du solde par un imprimeur. Moyen de paiement: %s, remarque: %s" % (_mode, comment), 'pu': _montant})
            f.modePaiement(_mode.lower())

            try:
                # Met aussi à jour le solde.
                f.recuPaiement(strftime("%Y-%m-%d %H:%M:%S"))
                f.save()
                db.services_to_restart('mail_solde', [
                    '%s a fait %s euros pour %s [mode: %s, remarque: %s]' %
                    (script_utilisateur, _montant, clas._data['uid'][0], _mode, comment)
                ])
            except ValueError, c:
                arg = u'--title "Opération impossible" '
                arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
                dialog(arg)

            arg = u'--title "%s du solde de %s" ' % (_kword, clas.Nom())
            arg += u'--msgbox "Modification effectuée, merci de noter le numéro de facture %s." 0 0' % (f.numero(),)
            dialog(arg)
        break

def set_vente(proprio):
    u"""
    Vend un objet à l'adherent : génère la facture associée.
    """
    from config.factures import ITEMS as items

    def choose_items():
        # Construction de la boîte de dialogue
        checklist = []
        texte = []

        for key, value in items.iteritems():
            if value.get('imprimeur', False) and not isimprimeur:
                continue
            if value['pu'] != '*':
                checklist.append(u'"%s" "%s (%s€)" "%s"' % (key, value['designation'], value['pu'], on_off(False)))
            else:
                checklist.append(u'"%s" "%s" "%s"' % (key, value['designation'], on_off(False)))

        if not checklist:
            # Il n'y a rien de modifiable
            dialog(u'--title "Rien n\'est en vente pour le moment" --msgbox "Rien n\'est en vente pour le moment\n" 0 0 ')
            return


        # Il y a qqch de modifiable, on construit la checklist
        arg = u'--title "Vente de consomables à %s" ' % proprio.Nom()
        arg += u'--separate-output '
        arg += u'--checklist "%s\n" 0 0 0 ' % '\n'.join(texte)
        arg += u' '.join(checklist)

        annul, result = dialog(arg)
        if annul: return 1

        f = Facture(proprio)
        # Traitement
        for key in items.keys():
            if key in result:
                while 1:
                    if items[key]['pu'] != '*':
                        arg = u'--title "Nombre de %s ?" ' % items[key]['designation']
                        arg += u'--inputbox "" 0 0 "1" '
                        annul, res = dialog(arg)
                        if annul: return 1
                        try:
                            nombre = int(res[0])
                            break
                        except ValueError, c:
                            arg = u'--title "Opération impossible" '
                            arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
                            dialog(arg)
                    else:
                        arg = u'--title "Montant pour %s ?" ' % items[key]['designation']
                        arg += u'--inputbox "" 0 0 "1" '
                        annul, res = dialog(arg)
                        if annul: return 1
                        try:
                            nombre = float(res[0])
                            break
                        except ValueError, c:
                            arg = u'--title "Opération impossible" '
                            arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
                            dialog(arg)
                if items[key]['pu'] != '*':
                    f.ajoute({'nombre': nombre, 'code': key, 'designation': items[key]['designation'], 'pu': items[key]['pu']})
                else:
                    f.ajoute({'nombre': 1, 'code': key, 'designation': items[key]['designation'], 'pu': nombre})

        texte = []
        for art in f.articles():
            texte.append(u"%dx %s à %s€" % (art['nombre'], art['designation'], art['pu']))
        texte.append(u"Total à payer: %s€" % f.total())
        arg = u'--title "Résumé de la facture à payer" '
        arg += u'--msgbox "%s\n" 0 0' % '\n'.join(texte)
        dialog(arg)

        return f


    def choose_paiement(f):
        menu = []
        texte = []

        menu.append(u'"Spc" "Espèces" ')
        menu.append(u'"Chq" "Chèque" ')
        menu.append(u'"Cb" "Carte bancaire" ')
        if isimprimeur and proprio.solde() - f.total() > 0:
            menu.append(u'"Sol" "Solde Crans (actuel : %s€)" ' % (proprio.solde()))

        # Il y a qqch de modifiable, on construit la checklist
        arg = u'--title "Vente de consomables à %s" ' % proprio.Nom()
        arg += u'--menu "Mode de paiement : " 0 0 0 '
        arg += u''.join(menu)

        annul, result = dialog(arg)
        if annul: return 1

        # Ajout du commentaire
        arg = u'--title "Crédit / débit du compte de %s" ' % proprio.Nom()
        arg += u'--inputbox "Commentaire à insérer ?" 0 0'
        if result[0] == "Spc":
            f.modePaiement('liquide')
            paiement = u"Espèce"
            annul, comment = dialog(arg)
        elif result[0] == "Cb":
            f.modePaiement('carte')
            paiement = u"Carte Bancaire"
            comment = None
        elif result[0] == "Chq":
            f.modePaiement('cheque')
            paiement = u"Chèque"
            annul, comment = dialog(arg)
        elif result[0] == "Sol" and isimprimeur:
            f.modePaiement('solde')
            paiement = u"Solde Crans"
            comment = None

        if comment and comment[0]:
            comment = comment[0]
        else:
            comment = None

        return (f, paiement, comment)

    def confirm_and_pay((f, paiement, comment)):
        arg = u'--title "Validation du paiement" '
        arg += u'--yesno "Le paiement de %s€ a-t-il bien été reçu (mode : %s) ?\n" 0 0 ' % (f.total(), paiement)
        no, result = dialog(arg)
        if no:
            arg = u'--title "Annulation de la vente" '
            arg += u'--msgbox "Le paiement n\'ayant pas été reçu\nla vente est annulée\n" 0 0'
            dialog(arg)
            return 1
        else:
            try:
                f.recuPaiement(strftime("%Y-%m-%d %H:%M:%S"))
                f.save()
	#	arg = u'--title "Impression facture" '
	#	arg += u'--yesno "Voulez vous imprimer cette facture ?\n" 0 0'
	#	no, res_1 = dialog(arg)
	#	if no:
	#	    return 1
	#	else:
	#	    subprocess.call(['/usr/scripts/cransticket/dump_creds.py','fid=%s' % f.numero()])
                arg = u'--title "Vente terminée" '
                arg += u'--msgbox "Vous pouvez remettre à l\'adherent les articles suivant :\n%s\n\nMerci de noter la facture: fid=%s" 0 0' % ('\n'.join([
                    "%s %s" % (art['nombre'], art['designation'])
                    for art in f.articles()
                    ]), f.numero())
                dialog(arg)
            except ValueError as error:
                f.delete()
                arg = u'--title "Annulation de la vente" '
                arg += u'--msgbox "%s\n" 0 0' % error
                dialog(arg)
                return 1

    f = choose_items()
    if isinstance(f, int):
        return f
    ret = choose_paiement(f)
    if isinstance(ret, int):
        return ret
    return confirm_and_pay(ret)


def confirm(clas):
    u""" Demande confirmation avant enregistrement"""
    global in_facture
    # On va faire en texte, les couleurs ne passent pas en curses
    os.system('clear')
    aff(clas)
    if in_facture is not None:
        cprint("Une facture d'un montant total de %s € sera confirmée." % (in_facture.total()), "rouge")
    while 1:
        r = affich_tools.prompt(u"Valider et enregister ? [O/N]")
        if r == 'O' or r == 'o':
            break
        elif r == 'N' or r == 'n':
            return 1
    try:
        res = clas.save()
        cprint(res)
        affich_tools.prompt(u"Appuyez sur ENTREE pour continuer")
        if isinstance(clas, MachineWifi):
	    arg = u'--title "Imprimer code wifi ?" '
	    arg += u'--yesno "Voulez vous imprimer ce code wifi ?\n" 0 0'
	    no, res_0 = dialog(arg)
	    if no:
	        pass
	    else:
	        subprocess.call(['/usr/scripts/cransticket/dump_creds.py', 'mid=%s' % clas.id()])
        if in_facture is not None:
            in_facture.recuPaiement(strftime("%Y-%m-%d %H:%M:%S"))
            in_facture.save()
	#    arg = u'--title "Impression facture" '
	#    arg += u'--yesno "Voulez vous imprimer cette facture ?\n" 0 0'
	#    no, res_2 = dialog(arg)
	#    if no:
	#        pass
	#    else:
	#        subprocess.call(['/usr/scripts/cransticket/dump_creds.py','fid=%s' % in_facture.numero()])
    except Exception as c:
        arg = u'--title "Enregistrement" '
        arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(unicode(c.args[0]))
        dialog(arg)
        return 1
    in_facture = None

def set_blackliste(clas):
    u""" Édite ou ajoute un item de la blackliste """
    bl = clas.blacklist()
    if not bl:
        # Pas d'entrée à éditer
        index = -1
    else:
        arg = u'--title "Édition blackliste de %s" ' % clas.Nom()
        arg += u'--menu "Choisir l\'entrée à éditer :" 0 0 0 '
        arg += u'"0" "Ajouter une nouvelle entrée" '
        i = 1
        for b in bl:
            champs = b.split('$')
            arg += '"%i" "%s [%s]" ' % (i, champs[2], champs[3])
            i += 1
        annul, res = dialog(arg)
        if annul: return 1
        index = int(res[0]) - 1

    # Édition
    if index != -1:
        t = clas.blacklist()[index].split('$')
        if t[0] != 'now':
            t[0] = strftime('%d/%m/%Y %H:%M', localtime(int(t[0])))
        if t[1] != 'now' and t[1] != '-':
            t[1] = strftime('%d/%m/%Y %H:%M', localtime(int(t[1])))
    else:
        t = ['now', '-', '', '']

    step = 1
    while 1:
        if step == 1:
            # Sanction
            arg = u'--title "Blacklistage %s" ' % clas.Nom()
            arg += u'--default-item "%s" ' % t[2]
            arg += u'--menu "Choisir la sanction :" 0 0 0 '
            for n, c in blacklist_items.items():
                arg += u'"%s" "%s" ' % (n, c)
            annul, res = dialog(arg)
            if annul: return 1
            t[2] = res[0]
            step += 1

        if step == 2:
            # Dates + commentaire
            arg = u'--title "Blacklistage %s" ' % clas.Nom()
            arg += u'--form "Entrez les dates de début et de fin (format jj/mm/aaaa hh:mm) ainsi que le commentaire associé\n" 0 0 0 '
            arg += u'"Début : " 1 1 "%s" 1 8 16 0 ' % t[0]
            arg += u'"now pour immédiatement " 1 25 "" 0 0 0 0 '
            arg += u'"Fin   : " 2 1 "%s" 2 8 16 0 ' % t[1]
            arg += u'"- pour fin indéterminée" 2 25 "" 0 0 0 0 '
            arg += u'"Les jours de début et de fin sont inclus." 3 1 "" 0 0 0 0 '
            arg += u'"Sanction : %s" 4 1 "" 0 0 0 0 ' % t[2]
            arg += u'"Commentaire (pas de dollar) : " 5 1 "%s" 6 1 52 0 ' % t[3]
            annul, r = dialog(arg)
            if annul: return 1

            # Ajout des heures
            t[0] = r[0].strip()
            if len(t[0]) == 10: t[0] += ' 00:00'
            t[1] = r[1].strip()
            if len(t[1]) == 10: t[1] += ' 23:59'

            # Vérification des heures
            try:
                if t[0] != 'now':
                    t[0] = int(mktime(strptime(t[0], '%d/%m/%Y %H:%M')))
            except:
                arg = u'--title "Erreur" '
                arg += u'--msgbox "Heure de début incorrecte (%s)\n\n\n" 0 0' % t[0]
                dialog(arg)
                step -= 1
                continue

            try:
                if t[1] != 'now' and t[1] != '-':
                    t[1] = int(mktime(strptime(t[1], '%d/%m/%Y %H:%M')))
            except:
                arg = u'--title "Erreur" '
                arg += u'--msgbox "Heure de fin incorrecte (%s)\n\n\n" 0 0' % t[1]
                dialog(arg)
                step -= 1
                continue

            # Commentaire
            c = r[2].strip()
            login = script_utilisateur
            if c:
                if c.split(' :')[0] != login:
                    t[3] = login + ' : ' + c
                else:
                    t[3] = c
            else:
                t[3] = login

            try:
                if index == -1:
                    clas.blacklist(t)
                else:
                    clas.blacklist((index, t))
                step += 1
            except ValueError, c:
                arg = u'--title "Erreur" '
                arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
                dialog(arg)
                step -= 1

        if step == 3:
            if confirm(clas): step -= 1
            else: break

##########################################################################
## Fonction de remplissage ou modification des paramètres club ou adhérent

def on_off(condition):
    u"""
    Renvoie 'on' ou 'off' selon la condition (pour dialog).
    """
    if condition:
        return 'on'
    else:
        return 'off'

def set_type_de_connexion(adherent):
    u"""
    Définition du type de connexion: gratuite limité ou normale.
    """
    if isinstance(adherent, Club): return

    annul, result = dlg.menu(u"Type de connexion",
                             choices=[("1", u"Connexion normale"),
                                      ("2", u"Connexion gratuite")])
    if annul: return 1
    adherent.adherentPayant(result == "1")

def set_admin(proprio):
    u"""
    Définition de l'état administratif : carte d'étudiant et paiement.
    """
    # Le proprietaire a-t-il une section carte d'étudiant (pas les clubs) ?
    potential_ma = proprio.idn != 'cid'

    # Initialisation des différentes checkbox
    if potential_ma:
        charte_MA = on_off(proprio.charteMA())

    # Construction de la boîte de dialogue
    texte = []
    checklist = []

    if (isbureau or isadm) and potential_ma:
        checklist.append(u'"1" "Charte des MA signee" "%s"' % charte_MA)

    if not checklist:
        # Il n'y a rien de modifiable
        dialog(u'--title "État administratif de %s non modifiable" --msgbox "%s\n" 0 0 ' %
               (proprio.Nom(), '\n'.join(texte)))
        return

    # Il y a qqch de modifiable, on construit la checklist
    arg = u'--title "Etat administratif de %s" ' % proprio.Nom()
    arg += u'--separate-output '
    arg += u'--checklist "%s\n" 0 0 0 ' % '\n'.join(texte)
    arg += u' '.join(checklist)

    annul, result = dialog(arg)
    if annul: return 1

    if potential_ma and (isadm or isbureau):
        if '1' in result:
            proprio.charteMA(True)
        else:
            proprio.charteMA(False)

def set_adhesion(proprio):
    """Maj de la période d'accès de l'adhérent"""
    global in_facture

    end = proprio.adhesion()
    annul = 0
    args = u'--title "Adhésion de %s" ' % proprio.Nom()
    if end and end - cotisation.delai_readh > time():
        t_end = strftime('%d/%m/%Y %H:%M:%S', localtime(end))
        args += u'--msgbox "Actuellement adhérent jusqu\'au %s.\nMerci de revenir lorsqu\'il restera moins de %s jours avant la fin." 0 0 ' % (t_end, cotisation.delai_readh_jour,)
        dialog(args)
        return 1
    elif end and end - cotisation.delai_readh <= time():
        t_end = strftime('%d/%m/%Y %H:%M:%S', localtime(end))
        args += u'--yesno "Adhésion jusqu\'au %s. Réadhérer ?" 0 0 ' %(t_end,)
        annul, res = dialog(args)
    else:
        args += u'--yesno "Adhésion pour un an, continuer ?" 0 0 '
        annul, res = dialog(args)

    if annul:
        return 1

    if in_facture is not None:
        f = in_facture
    else:
        f = None
    facture = proprio.adhesion(True, f)

    if float(facture.total()) == 0.0:
        facture.modePaiement('liquide')

    while True and not facture.modePaiement():
        arg = u'--title "Mode de paiement pour l\'adhésion de %s" ' % (proprio.Nom(),)
        arg += u'--menu "Comment %s souhaite-t-il payer ?" 0 0 0 ' % (proprio.Nom(), )
        arg += u'"Liquide" "En espèces : penser à mettre l\'argent dans une enveloppe." '
        arg += u'"Cheque" "Par chèque : ne pas oublier de vérifier signature, date, ordre et montant." '
        arg += u'"Carte" "Par CB : tromboner le ticket." '
        if proprio.solde() - facture.total() > 0:
            arg += u'"Solde" "Par solde : il a assez d\'argent pour ça." '
        annul, res = dialog(arg)
        if annul:
            facture._set('finAdhesion', [])
            facture._set('debutAdhesion', [])
            facture.supprime(pop=True)
            return 1
        _mode = res[0]
        if _mode in ["Liquide", "Cheque", "Carte"]:
            arg = u'--title "Avertissement" '
            arg += u'--msgbox "Une facture sera créée, après validation par le trésorier, l\'adhérent\npourra y accéder via l\'intranet ou la demander." 0 0'
            dialog(arg)
            facture.modePaiement(_mode.lower())
            break
        else:
            facture.modePaiement(_mode.lower())
            break
    in_facture = facture
    if not in_facture._data.get('finConnexion', []) and not in_facture._data.get('finAdhesion', []):
        in_facture = None
        proprio.restore()

def set_connexion(proprio):
    """Maj de la période d'accès de l'adhérent"""
    global in_facture

    # Si l'adhérent ne l'est plus, on commence par le faire adhérer, sauf s'il a une facture adhésion.
    adhEnd = proprio.adhesion()

    if in_facture is not None:
        adhEnd = max(adhEnd, fromGeneralizedTimeFormat(in_facture._data.get('finAdhesion', ["19700101000000Z"])[0]))

    if adhEnd - cotisation.delai_readh < time():
        stat = set_adhesion(proprio)

        if stat == 1 and adhEnd < time():
            return 1

        if in_facture is not None:
            adhEnd = max(adhEnd, fromGeneralizedTimeFormat(in_facture._data.get('finAdhesion', ["19700101000000Z"])[0]))

    end = proprio.connexion()
    args = u'--title "Connexion de %s" ' % proprio.Nom()
    if end > adhEnd:
        args += u'--msgbox "La fin de l\'adhésion arrivera avant le début de cette connexion.\nIl faudra d\'abord réadhérer." 0 0 '
        dialog(args)
        return 1

    while True:
        while True:
            args = u'--title "Connexion de %s" ' % proprio.Nom()
            if proprio.connexion() > time():
                args += u'--menu "Connexion jusqu\'au %s, choisir une durée de prolongation. : " 0 0 0 ' % (strftime("%d/%m/%Y %H:%M:%S", localtime(proprio.connexion())),)
            else:
                args += u'--menu "Connexion actuellement inactive, choisir une durée. : " 0 0 0 '
            args += u'"An" "Prolonger d\'un an." '
            args += u'"Select" "Prolonger de plusieurs mois." '
            args += u'"Mois" "Prolonger d\'un mois." '
            args += u'"NC" "Pas de connexion." '
            annul, res = dialog(args)

            if annul:
                if in_facture is not None:
                    in_facture.supprime(pop=True)
                return 1
            res = res[0]
            if res == "An":
                nb_mois = 12
                break
            elif res == "Select":
                back = False
                while True:
                    arg = u'--title "Nombre de mois de connexion ? (entre 2 et 12)" '
                    arg += u'--inputbox "" 0 0 "1" '
                    annul, res = dialog(arg)
                    if annul:
                        back = True
                        break
                    else:
                        try:
                            res = int(res[0])
                        except:
                            arg = u'--title "Opération impossible" '
                            arg += u'--msgbox "On a dit un entier…" 0 0'
                            dialog(arg)
                            continue
                        if res < 2 or res > 12:
                            arg = u'--title "Opération impossible" '
                            arg += u'--msgbox "On a dit un entier entre 2 et 12." 0 0'
                            dialog(arg)
                            continue
                        else:
                            nb_mois = res
                            break
                if back:
                    continue
                break
            elif res == "Mois":
                nb_mois = 1
                break
            else:
                nb_mois = 0
                return 0

        if in_facture is not None:
            f = in_facture
        else:
            f = None
        facture = proprio.connexion(nb_mois, f)

        newEnd = fromGeneralizedTimeFormat(facture._data.get('finConnexion', ["19700101000000Z"])[0])
        if newEnd > adhEnd:
            arg = u'--title "Avertissement" '
            arg += u'--yesno "La nouvelle fin de connexion (%s) arriverait après la fin de l\'adhésion actuelle (%s).\nIl sera nécessaire que l\'adhérent réadhère, (possible %s jours avant la fin de l\'adhésion actuelle).\n\nLe paiement ne vaut *PAS* réadhésion. Merci de lui préciser explicitement !" 0 0 ' % (strftime("%d/%m/%Y %H:%M:%S", localtime(newEnd)), strftime("%d/%m/%Y %H:%M:%S", localtime(adhEnd)), cotisation.delai_readh_jour)
            annul, res = dialog(arg)
            if annul:
                facture._set('finConnexion', [])
                facture._set('debutConnexion', [])
                facture.supprime(pop=True)
                break

        if not facture.modePaiement():
            arg = u'--title "Mode de paiement pour la connexion de %s" ' % (proprio.Nom(),)
            arg += u'--menu "Comment %s souhaite-t-il payer ?" 0 0 0 ' % (proprio.Nom(),)
            arg += u'"Liquide" "En espèces : penser à mettre l\'argent dans une enveloppe." '
            arg += u'"Cheque" "Par chèque : ne pas oublier de vérifier signature, date, ordre et montant." '
            arg += u'"Carte" "Par CB : tromboner le ticket." '

            if proprio.solde() - facture.total() > 0:
                arg += u'"Solde" "Par solde : il a assez d\'argent pour ça." '
            annul, res = dialog(arg)
            if annul:
                facture._set('finConnexion', [])
                facture._set('debutConnexion', [])
                facture.supprime(pop=True)
                continue
            _mode = res[0]
            if _mode in ["Liquide", "Cheque", "Carte"]:
                arg = u'--title "Avertissement" '
                arg += u'--msgbox "Une facture sera créée, après validation par le trésorier, l\'adhérent\npourra y accéder via l\'intranet ou la demander." 0 0'
                dialog(arg)
                facture.modePaiement(_mode.lower())
                break
            else:
                facture.modePaiement(_mode.lower())
                break
        else:
            break
    in_facture = facture

    if not in_facture._data.get('finConnexion', []) and not in_facture._data.get('finAdhesion', []):
        in_facture = None
        proprio.restore()

###############################################################
## Fonctions de remplissage ou modification des paramètres club

def new_club(club):
    step = 1
    while 1:

        if step == 1:
            # Responsable
            if set_responsable(club): return 1
            else: step += 1

        if step == 2:
            # Nom du club
            if set_club_nom(club): step -= 1
            else: step += 1

        if step == 3:
            # Local
            if set_local(club): step -= 1
            else: step += 1

        if step == 4:
            # Administratif
            if set_adhesion(club): step -= 1
            else: step += 1

        if step == 5:
            # Remarque
            if set_rque(club): step -= 1
            else: step += 1

        if step == 6:
            # Confirmation
            if confirm(club): step -= 1
            else: break

def modif_club(club):
    u"""
    Modification du club fourni (instance de club)
    Retourne 1 si annulation.
    """
    global in_facture

    arg = u'--title "Modification du club %s" ' % club.Nom()
    arg += u'--menu "Que souhaitez vous modifier ?" 0 0 0 '
    arg += u'"Adhesion" "Pour les réadhésions" '
    arg += u'"NomClub" "Modifier le nom du club" '
    arg += u'"Responsable" "Changer le responsable du club %s" ' % club.responsable().Nom()
    arg += u'"Imprimeurs" "Changer la liste des imprimeurs" '
    arg += u'"Administratif" "Données administratives" '
    arg += u'"Local" "Modifier le local du club" '
    arg += u'"Compte" "Créer un compte crans." '
    if club.compte():
        arg += u'"Alias" "Créer ou supprimer un alias mail" '
    if isdeconnecteur:
        arg += u'"Blackliste" "Modifier la blackliste du club" '
    if isimprimeur and club.compte():
        arg += u'"Solde" "Effectuer un débit/crédit pour ce club" '
    arg += u'"Vente" "Vendre un cable ou adaptateur ethernet ou autre" '

    arg += u'"Remarque" "Ajouter ou modifer un commentaire" '
    annul, res = dialog(arg)

    if annul: return 1

    elif res[0] == 'NomClub':
        set_club_nom(club)
    elif res[0] == 'Responsable':
        set_responsable(club)
    elif res[0] == 'Imprimeurs':
        set_imprimeurs(club)
    elif res[0] == 'Adhesion':
        set_adhesion(club)
    elif res[0] == 'Compte':
        set_club_compte(club)
    elif res[0] == 'Remarque':
        set_rque(club)
    elif res[0] == 'Blackliste':
        set_blackliste(club)
    elif res[0] == 'Local':
        set_local(club)
    elif res[0] == 'Alias':
        __prompt_input_menu(club.alias, 'Alias mail', "Entrez ou modifier un alias mail.\nPour ajouter un alias modifier le dernier de la liste.")
    elif res[0] == 'Solde':
        set_solde(club)
    elif res[0] == 'Vente':
        set_vente(club)

    if club.modifs or in_facture is not None:
        return confirm(club)

def select_club(clas):
    u""" Choix d'un club """
    arg = u'--title "Recherche d\'un club" '

    clubs = clas.search('cid=*')['club']
    if not clubs:
        # Pas de club dans la base
        arg += '--msgbox "Il n\'y a pas de clubs inscrits dans la base\n\n\n" 0 0 '
        dialog(arg)
        return
    arg += u'--menu "Choisissez un club :" 0 0 0 '
    for club in clubs:
        arg += '"%s" "%s" ' % (club.id(), club.Nom())
    annul, res = dialog(arg)
    if annul: return
    return clas.search('cid=%s' % res[0], 'w')['club'][0]

def del_club(club):
    u""" Destruction club """
    quoi = u'Toutes les machines associées à cet adhérent seront détruites'
    while 1:
        arg = u'--title "Destruction club " --colors '
        arg += u'--inputbox "\Zr\Z1ATTENTION\Zn : la destruction est définitive\n\nToutes les machines de ce club seront également détruites.\n\nCommentaire à insérer ?" 0 0'
        annul, res = dialog(arg)
        if annul: return 1
        if res[0]: break
        arg = u'--title "Destruction club" '
        arg += u'--msgbox "Le commentaire est obligatoire\n\n\n" 0 0'
        dialog(arg)

    club.delete(res[0])
    arg = u'--title "Destruction club" '
    arg += u'--msgbox "Club détruit\n\n\n" 0 0'
    dialog(arg)

##################################################################
## Fonctions de modif des factures

def set_facture_mode(facture):
    """Change le mode de paiement

    """

    arg = u'--title "Mode de paiement pour la facture fid=%s (%s)" ' % (facture.id(), facture.proprietaire().Nom(),)
    arg += u'--menu "Quel moyen de paiement enregistrer ?" 0 0 0 '
    arg += u'"Liquide" "En espèces : penser à mettre l\'argent dans une enveloppe." '
    arg += u'"Cheque" "Par chèque : ne pas oublier de vérifier signature, date, ordre et montant." '
    arg += u'"Carte" "Par CB : tromboner le ticket." '
    if facture.proprietaire().solde() - facture.total() > 0:
        arg += u'"Solde" "Par solde : il a assez d\'argent pour ça." '
    annul, res = dialog(arg)
    if annul:
        return 1
    facture.modePaiement(res[0].lower())

def set_facture_recu(facture):
    """Change le reçu d'une facture

    """
    arg = u'--title "Reçu pour la facture fid=%s (%s)" ' % (facture.id(), facture.proprietaire().Nom())
    arg += u'--separate-output '
    arg += u'--checklist "État du paiement\n" 0 0 0 '
    arg += u'"Pmt" "Paiement fourni." "%s"' % (on_off(facture.recuPaiement() is not None),)

    annul, res = dialog(arg)
    if annul:
        return 1
    if u"Pmt" in res:
        facture.recuPaiement(strftime("%Y-%m-%d %H:%M:%S"))
    else:
        facture.recuPaiement(False)

def set_facture_controle(facture):
    """Change le contrôle de la facture

    """
    arg = u'--title "Contrôle pour la facture fid=%s (%s)" ' % (facture.id(), facture.proprietaire().Nom())
    arg += u'--separate-output '
    arg += u'--checklist "État du contrôle\n" 0 0 0 '
    arg += u'"Ctl" "Contrôle OK." "%s" ' % (on_off(facture.controle() == "TRUE"),)
    arg += u'"NCtl" "Contrôle pas OK." "%s" ' % (on_off(facture.controle() == "FALSE"),)

    annul, res = dialog(arg)
    if annul:
        return 1
    if "Ctl" in res:
        facture.controle(True)
    elif "NCtl" in res:
        facture.controle(False)
    else:
        facture.controle("")

##################################################################
## Fonctions de remplissage ou modification des paramètres machine

def set_wifi(machine):
    """
    Définition des paramètres spécifiques d'une borne wifi :
        * Canaux
        * Puissance
        * Position GPS
        * Variables nvram
        * hotspot
    """
    if not isadm:
        #ça ne devrait jamais arriver mais on n'est jamais trop prudent
        arg = u'--title "Erreur" '
        arg += u'--msgbox "Vous n\'avez pas les droits de modification de cette machine.\n\n" 0 0'
        dialog(arg)
        # On redemande
        return 1

    nom = machine.nom()

    # Construction de la boite de dialogue
    arg = u'--title "Paramètres spécifiques wifi de "%s"" ' % (nom)
    arg += u'--form "" 0 0 0 '
    # Borne wifi
    arg += u'"Canaux :" 1 1 "%s" 1 14 10 0 ' % machine.canal(None, False)
    arg += u'"Puissance :" 2 1 "%s" 2 14 4 0 ' % machine.puissance()
    try:
        arg += u'"Position N :" 3 1 "%s" 3 14 12 0 ' % machine.position()[0]
        arg += u'"Position E :" 3 29 "%s" 3 42 12 0 ' % machine.position()[1]
    except:
        arg += u'"Position N :" 3 1 "%s" 3 14 12 0 ' % ""
        arg += u'"Position E :" 3 29 "%s" 3 42 12 0 ' % ""

    arg += u'"Hotspot :" 4 1 "%s" 4 10 5 0 ' % (machine.hotspot() and 'oui' or 'non')
    arg += u'"Nvram (cf wiki) :" 5 1 "%s" 6 1 100 0 ' % ', '.join(machine.nvram()).replace('"', '\\"')

    # Affichage
    annul, result = dialog(arg)
    if annul: return 1

    # Traitement
    err = ''
    try: machine.canal(result[0])
    except ValueError, c: err += c.args[0] + '\n'

    try: machine.puissance(result[1])
    except ValueError, c: err += c.args[0] + '\n'

    try:
        #On n'enregistre pas une position vide
        if result[2].strip() and result[3].strip():
            if result[2].strip().lower() == "none" or result[3].strip().lower() == "none":
                machine.position(None)
            else:
                machine.position((result[2].strip(), result[3].strip()))
    except ValueError, c: err += c.args[0] + '\n'

    if result[4].lower().strip() == "oui":
        machine.hotspot(True)
    elif result[4].lower().strip() == "non":
        machine.hotspot(False)
    else:
        err += u"Les valeurs possibles pour le parametre hotspot\n sont \"oui\" ou \"non\"\n"

    try:
        #On vérifie toutes les variables avant de sauvegarder
        for nvram in result[5].split(','):
            if len(nvram.strip()): #On évite les variables vides
                if not '=' in nvram:
                    raise ValueError("Une variable nvram doit etre de la forme : 'variable=valeur'\n")
        for nvram in result[5].split(','):
            if len(nvram.strip()): #On évite les variables vides
                variable = nvram.split('=')[0].strip()
                valeur = nvram.split('=')[1].strip()
                if valeur.lower() == "none":
                    valeur = None
                machine.nvram(variable, valeur)
    except ValueError, c: err += c.args[0] + '\n'
    # Des erreurs ?
    if err:
        arg = u'--title "Paramètres machine wifi" '
        arg += u'--msgbox "%s\n\n" 0 0' % to_unicode(err)
        dialog(arg)
        # On redemande
        return set_wifi(machine)

    if machine.modifs and confirm(machine):
        return set_wifi(machine)

def set_machine(machine):
    u"""
    Définition des paramètres d'une machine :
        * Nom de machine
        * Adresse MAC
        * IP
        * ports ouverts si adm
    """
    if machine.proprietaire().__class__ == AssociationCrans:
        if not isadm:
            arg = u'--title "Erreur" '
            arg += u'--msgbox "Vous n\'avez pas les droits de modification de cette machine.\n\n" 0 0'
            dialog(arg)
            # On redemande
            return 1
        nom = machine.nom()
        l = 50
    else:
        nom = machine.nom().split('.')[0]
        l = 17

    # Construction de la boite de dialogue
    arg = u'--title "Paramètres machine" '
    arg += u'--form "" 0 0 0 '
    arg += u'"Nom de machine :" 1 1 "%s" 1 18 %i 0 ' % (nom, l)
    arg += u'"Adresse mac :" 2 1 "%s" 2 15 17 0 ' % machine.mac()
    arg += u'"IP :" 3 1 "%s" 3 6 15 0 ' % machine.ip()
    if isadm:
        arg += u'"PortsTCP ext->machine :" 4 1 "%s" 4 25 50 0 ' % ' '.join(machine.portTCPin())
        arg += u'"PortsTCP machine->ext :" 5 1 "%s" 5 25 50 0 ' % ' '.join(machine.portTCPout())
        arg += u'"PortsUDP ext->machine :" 6 1 "%s" 6 25 50 0 ' % ' '.join(machine.portUDPin())
        arg += u'"PortsUDP machine->ext :" 7 1 "%s" 7 25 50 0 ' % ' '.join(machine.portUDPout())
    if isadm and machine.proprietaire().__class__ == AssociationCrans:
        arg += u'"Prise :" 8 1 "%s" 8 9 5 0 ' % machine.prise()

    else:
        p = u'Pour ajouter une remarque modifier la dernière de la liste.'

    # Affichage
    annul, result = dialog(arg)
    if annul: return 1

    # Traitement
    err = ''
    try: machine.nom(result[0])
    except ValueError, c:
        err += c.args[0] + '\n'
    except EnvironmentError, c:
        err += c.args[0] + '\n'
    try: machine.ip(result[2])
    except ValueError, c:
        if len(c.args) > 1 and c.args[1] == 1 and isadm:
            arg = u'--title "Plage d\'ip" '
            arg += u'--yesno "La machine va être ajoutée en v6 only, continuer ? \n" 0 0'
            no, res = dialog(arg)
            if no:
                return set_machine(machine)
            else:
                try: machine.ip(result[2], lock=True, force=True)
                except EnvironmentError, c:
                    err += c.__str__() + '\n'
                except RuntimeError, c:
                    err += c.args[0] + '\n' # Plus d'IP libres, peut-être à traiter differement ?
                except ValueError, c:
                    err += c.args[0] + '\n'
        else:
            err += c.args[0] + '\n'
    except EnvironmentError, c:
        err += c.__str__() + '\n'
    except RuntimeError, c:
        err += c.args[0] + '\n' # Plus d'IP libres, peut-être à traiter differement ?
    try: machine.mac(result[1])
    except ValueError, c:
        if len(c.args) > 1 and c.args[1] == 1 and isadm:
            # Mac en double
            arg = u'--title "Adresse MAC" '
            arg += u'--yesno "L\'adresse MAC existe déjà, continuer ? \n" 0 0'
            no, res = dialog(arg)
            if no:
                return set_machine(machine)
            else:
                try: machine.mac(result[1], 1)
                except ValueError, c:
                    err += c.args[0] + '\n'
                except EnvironmentError, c:
                    err += c.args[0] + '\n'
        elif len(c.args) > 1 and c.args[1] == 3 and isadm:
            # Mac douteuse
            arg = u'--title "Adresse MAC" '
            arg += u'--yesno "L\'adresse MAC ne correspond à aucun constructeur, continuer ? \n" 0 0'
            no, res = dialog(arg)
            if no:
                return set_machine(machine)
            else:
                try: machine.mac(result[1], 1)
                except ValueError, c:
                    err += c.args[0] + '\n'
                except EnvironmentError, c:
                    err += c.args[0] + '\n'
        else:
            try:
                err += c.args[0] + '\n'
            except UnicodeDecodeError:
                raise
                raise Exception("UnicodeDecodeError on %s" % repr(c.args))

    except EnvironmentError, c:
        err += c.args[0] + '\n'

    if isadm:
        try:
            machine.portTCPin(result[3].split())
            machine.portTCPout(result[4].split())
            machine.portUDPin(result[5].split())
            machine.portUDPout(result[6].split())
        except ValueError, c: err += c.args[0] + '\n'

    if isadm and machine.proprietaire().__class__ == AssociationCrans:
        try: machine.prise(result[-1])
        except ValueError, c: err += c.args[0] + '\n'

    # Des erreurs ?
    if err:
        arg = u'--title "Paramètres machine" '
        arg += u'--msgbox "%s\n\n" 0 0' % to_unicode(err)
        dialog(arg)
        # On redemande
        return set_machine(machine)

    if machine.modifs and confirm(machine):
        return set_machine(machine)

def set_machine_exemption(machine):
    u"""Définit les réseau exemptés de comptage d'upload pour la machine"""
    if __prompt_input_menu(machine.exempt, u'Exemption', u"Ajouter ou modifier un réseau/une ip vers lequel on ne compte pas l'upload (format x.y.z.t[/m])\nPour ajouter un réseau modifier la fin de la liste.") or confirm(machine):
        machine.restore()
        return 1

def set_machine_alias(machine):
    u"""Définit la liste des alias d'une machine"""
    if __prompt_input_menu(machine.alias, 'Alias machine', "Entrez ou modifier un alias machine.\nPour ajouter un alias modifier le dernier de la liste.") or confirm(machine):
        machine.restore()
        return 1

def set_machine_sshFingerprint(machine):
    u"""Définit la liste des fingerprints d'une machine"""
    if __prompt_input_menu(machine.sshFingerprint, 'SSHFingerprint machine', "Entrez ou modifier la fingerprint ssh de la machine.\nPour ajouter une fingerprint modifier le dernier de la liste.") or confirm(machine):
        machine.restore()
        return 1

def del_machine(machine):
    u"""
    Destruction machine
    """
    while 1:
        arg = u'--title "Destruction machine %s" --colors ' % machine.nom()
        arg += u'--inputbox "\Zr\Z1ATTENTION\Zn : la destruction est définitive\nCommentaire à insérer ?" 0 0'
        annul, res = dialog(arg)
        if annul: return 1
        if res[0]: break
        arg = u'--title "Destruction machine" '
        arg += u'--msgbox "Le commentaire est obligatoire\n\n\n" 0 0'
        dialog(arg)

    try:
        machine.delete(res[0])
    except EnvironmentError, c:
        arg = u'--title "Destruction machine" '
        arg += u'--msgbox "ERREUR n°%s\n\n\n" 0 0' % to_unicode(c.args[0])
        dialog(arg)
        return 1

    arg = u'--title "Destruction machine" '
    arg += u'--msgbox "Machine détruite\n\n\n" 0 0'
    dialog(arg)

def del_facture(facture):
    u"""
    Destruction facture
    """
    while 1:
        arg = u'--title "Destruction facture fid=%s" --colors ' % facture.id()
        arg += u'--inputbox "\Zr\Z1ATTENTION\Zn : la destruction est définitive, et aucun remboursement automatique n\'aura lieu.\nCommentaire à insérer ?" 0 0'
        annul, res = dialog(arg)
        if annul: return 1
        if res[0]: break
        arg = u'--title "Destruction facture" '
        arg += u'--msgbox "Le commentaire est obligatoire\n\n\n" 0 0'
        dialog(arg)

    try:
        facture.delete(res[0])
    except EnvironmentError, c:
        arg = u'--title "Destruction facture" '
        arg += u'--msgbox "ERREUR n°%s\n\n\n" 0 0' % to_unicode(c.args[0])
        dialog(arg)
        return 1

    arg = u'--title "Destruction facture" '
    arg += u'--msgbox "Facture détruite\n\n\n" 0 0'
    dialog(arg)

####################################
## Fonctions principales d'interface

def new_adher(adher):
    u"""
    Définition des propriétés d'un adhérent
    4 etapes :
        * set_bases
        * set_etudes
        * set_adhesion
        * set_connexion
        * set_contact
            (qui appelle set_mail_ext si on met un compte crans)
        * set_rque
    Retourne 1 si annulation.
    """

    while True:
        if set_bases(adher): return 1

        steps = [
            set_etudes,
            set_adhesion,
            set_connexion,
            set_contact,
            set_rque,
        ]

        step = 0
        while step < len(steps):
            if steps[step](adher): step -= 1
            else: step += 1

        if not confirm(adher): break

def modif_adher(adher):
    u"""
    Modification de l'adhérent fourni (instance de adhérent)
    Retourne 1 si annulation.
    """
    global in_facture

    # Préliminaire : si la chambre est inconnue on force la question
    if adher.chbre() == '????':
        res = ['Chambre']
        arg = u'--title "Modification de %s" ' % adher.Nom()
        arg += u'--msgbox "ERREUR : la chambre de cet adhérent est inconnue !\n\n\n" 0 0'
        dialog(arg)
    elif adher.mail() == '':
        res = ['Mail']
        arg = u'--title "Modification de %s" ' % adher.Nom()
        arg += u'--msgbox "ERREUR : l\'adresse mail de cet adhérent est inconnue !\n\n\n" 0 0'
        dialog(arg)
    else:
        payant = not isinstance(adher, Club) and adher.adherentPayant()

        arg = u'--title "Modification de %s" ' % adher.Nom()
        arg += u'--menu "Que souhaitez vous modifier ?" 0 0 0 '
        arg += u'"Connexion" "Mise à jour de l\'accès Internet (effectue la réadhésion si besoin)" '
        arg += u'"Adhesion" "Pour toute réadhésion *sans* connexion." '
        if isadm or isbureau:
            arg += u'"Administratif" "Pour renseigner la fourniture de la charte des MA." '
        arg += u'"Etat-civil" "Nom, prénom" '
        if adher.chbre() == 'EXT':
            arg += u'"Adresse" "Déménagement" '
        else:
            arg += u'"Chambre" "Déménagement" '
        arg += u'"Etudes" "Changement d\'année ou de filière" '
        arg += u'"Telephone" "Changement de numéro de téléphone" '
        if payant:
            arg += u'"Mail" "Créer un compte ou changer l\'adresse mail de contact" '
            if 'cransAccount' in adher._data['objectClass']:
                arg += u'"MailExt" "Ajouter une adresse mail de contact extérieur." '
                arg += u'"Alias" "Créer ou supprimer un alias mail" '
            arg += u'"GPGFingerprint" "Ajouter ou supprimer une empreinte GPG" '
            arg += u'"Remarque" "Ajouter ou modifer un commentaire" '
            if isadm or isbureau:
                if 'cransAccount' in adher._data['objectClass']:
                    arg += u'"Droits" "Modifier les droits alloués à cet adhérent" '
                if 'shadowAccount' in adher._data['objectClass']:
                    arg += u'"Etat" "Passer le compte à actif ou inactif" '
                if 'posixAccount' in adher._data['objectClass']:
                    arg += u'"Shell" "Changer le shell de cet utilisateur" '
            if isdeconnecteur:
                arg += u'"Blackliste" "Modifier la blackliste de cet adhérent" '
            if isimprimeur and adher.compte():
                arg += u'"Solde" "Effectuer un débit/crédit pour cet adhérent" '
            arg += u'"Vente" "Vendre un cable ou adaptateur ethernet ou autre" '

        annul, res = dialog(arg)

        if annul: return 1

    if res[0] == 'Etudes':
        set_etudes(adher)
    elif res[0] == 'Etat-civil':
        set_etat_civil(adher)
    elif res[0] == 'Administratif':
        set_admin(adher)
    elif res[0] == 'Mail':
        set_contact(adher)
    elif res[0] == 'MailExt':
        set_mail_ext(adher)
    elif res[0] == 'Remarque':
        set_rque(adher)
    elif res[0] == 'Droits':
        set_droits(adher)
    elif res[0] == 'Blackliste':
        set_blackliste(adher)
    elif res[0] == 'Adhesion':
        set_adhesion(adher)
    elif res[0] == 'Connexion':
        set_connexion(adher)
    elif res[0] == 'Adresse' or res[0] == 'Chambre':
        arg = u'--title "Déménagement de %s" ' % adher.Nom()
        arg += u'--menu "Question :" 0 0 0 '
        arg += u'"1" "Déménagement sur le campus ?" '
        arg += u'"2" "Déménagement à l\'extérieur en conservant les machines ?" '
        arg += u'"3" "Départ du campus en conservant son compte ?" '
        arg += u'"4" "Départ du campus en supprimant son compte ?" '
        annul, result = dialog(arg)
        if annul: return 1
        if result[0] == '2':
            if set_addr_ext(adher):
                # Annulation
                return modif_adher(adher)
            adher.chbre('EXT')
        elif result[0] == '3':
            arg = u'--title "Départ de %s" ' % adher.Nom()
            arg += u'--yesno "Le départ d\'un adhérent provoque la destruction de ses machines.\n'
            arg += u'\nDoit-on continuer ?" 0 0'
            no, res = dialog(arg)
            if no: return modif_adher(adher)
            for m in adher.machines():
                m.delete("Depart du campus")
            adher.chbre('EXT')
        elif result[0] == '4':
            if u'Nounou' in adher.droits() and not isadm:
                arg = u'--title "Destruction adherent" '
                arg += u'--msgbox "Vous n\'avez pas les droits necessaires pour effectuer cette opération.\n\n\n" 0 0'
                dialog(arg)
                return modif_adher(adher)

            arg = u'--title "Départ de %s" ' % adher.Nom()
            arg += u'--yesno "Le départ du campus de %s va provoquer la destruction de son compte.\n' % adher.Nom()
            arg += u'\nDoit-on continuer ?" 0 0'
            no, res = dialog(arg)

            if no:
                return modif_adher(adher)

            try:
                adher.delete("Depart du campus")
            except EnvironmentError, c:
                arg = u'--title "Destruction du compte" '
                arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
                dialog(arg)
            return

        else:
            while 1:
                arg = u'--title "Déménagement de %s" ' % adher.Nom()
                arg += u'--inputbox "Chambre ?" 0 0 '
                annul, res = dialog(arg)
                if annul: return modif_adher(adher)

                e = _set_chbre(adher, res[0])
                if res[0] == 'EXT':
                    # Il faut demander l'adresse extérieure
                    if set_addr_ext(adher):
                        # Annulation
                        continue
                if e:
                    arg = u'--title "Déménagement de %s" ' % adher.Nom()
                    arg += u'--msgbox "%s\n\n\n" 0 0' % e
                    dialog(arg)
                else:
                    break


    elif res[0] == 'Telephone':
        while 1:
            arg = u'--title "Changement numéro de téléphone de de %s" ' % adher.Nom()
            arg += u'--inputbox "Nouveau numéro ?" 0 0 "%s" ' % adher.tel()
            annul, res = dialog(arg)
            if annul: return modif_adher(adher)

            try:
                adher.tel(res[0].replace(' ', ''))
                break
            except ValueError, c:
                arg = u'--title "Changement numéro de téléphone de de %s" ' % adher.Nom()
                arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
                dialog(arg)

    elif res[0] == 'Alias':
        __prompt_input_menu(adher.alias, 'Alias mail', "Entrez ou modifier un alias mail.\nPour ajouter un alias modifier le dernier de la liste.")

    elif res[0] == 'GPGFingerprint':
        __prompt_input_menu(adher.gpgFingerprint, 'GPG Fingerprint', u"Entrez ou modifier une empreinte GPG que l'adhérent possède (tout abus sera sanctionné).\nPour ajouter une empreinte modifier le dernier de la liste.")

    elif res[0] == "Etat":
        set_actif(adher)

    elif res[0] == 'Shell':
        while 1:
            arg = u'--title "Nouveau shell pour %s" ' % adher.Nom()
            arg += u'--inputbox "Shell : " 0 0 "%s" ' % adher.chsh()
            annul, res = dialog(arg)
            if annul: return modif_adher(adher)

            try:
                adher.chsh(res[0])
                break
            except ValueError, c:
                arg = u'--title "Changement du shell de %s" ' % adher.Nom()
                arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
                dialog(arg)

    elif res[0] == 'Solde':
        set_solde(adher)
    elif res[0] == 'Vente':
        set_vente(adher)

    if adher.modifs or in_facture is not None:
        return confirm(adher)

def modif_machine(machine):
    u"""
    Modification de la machine fournie (instance de machine)
    Retourne 1 si annulation.
    """

    if not isadm:
        step = 1
        while 1:

            if step == 1:
                if set_machine(machine): return 1
                else: step = 2

            if step == 2:
                if set_rque(machine): step = 1
                else: return

    else:
        arg = u'--title "Modification de  %s" ' % machine.nom()
        arg += u'--menu "Que souhaitez vous modifier ?" 0 0 0 '
        arg += u'"Informations" "Modifier le nom de machine, l\'IP, adresse MAC" '
        arg += u'"Blackliste" "Modifier la blacklist de la machine" '
        arg += u'"Alias" "Créer ou supprimer un alias de la machine" '
        arg += u'"SSHFingerprint" "Ajouter ou enlever une empreinte ssh à la machine" '
        arg += u'"Exemptions" "Modifier la liste d\'exemption d\'upload de la machine" '
        arg += u'"Remarques" "Ajouter ou supprimer une remarque de la machine" '
        if isinstance(machine, BorneWifi) and isadm:
            # Borne wifi
            arg += u'"Wifi" "Modifier les paramètres spécifiques aux bornes wifi" '



        annul, res = dialog(arg)

        if annul: return 1

        if res[0] == 'Informations':
            set_machine(machine)
            return
        elif res[0] == 'Blackliste':
            set_blackliste(machine)
        elif res[0] == 'Alias':
            set_machine_alias(machine)
        elif res[0] == 'SSHFingerprint':
            set_machine_sshFingerprint(machine)
        elif res[0] == 'Exemptions':
            set_machine_exemption(machine)
        elif res[0] == 'Remarques':
            set_rque(machine)
        elif res[0] == 'Wifi':
            set_wifi(machine)
    if machine.modifs:
        return confirm(machine)

def modif_facture(facture):
    """Modifie la facture sélectionnée

    """

    if facture.controle() and not iscontroleur and not isadm:
        return 1

    arg = u'--title "Modification de la facture %s (%s)" ' % (facture.id(), facture.proprietaire().Nom(),)
    arg += u'--menu "Que souhaitez vous modifier ?" 0 0 0 '
    if not (facture.controle() == "TRUE" or facture.controle() == "FALSE") and not facture.recuPaiement():
        arg += u'"Mode" "Mode de paiement" '
    if not (facture.controle() == "TRUE" or facture.controle() == "FALSE") and facture.modePaiement():
        arg += u'"Recu" "Valider le reçu du paiement ou non" '
    if facture.recuPaiement() and (iscontroleur or isadm):
        arg += u'"Controle" "Valider ou non le contrôle de la facture." '

    annul, res = dialog(arg)

    if annul: return 1

    if res[0] == 'Mode':
        set_facture_mode(facture)
    elif res[0] == 'Recu':
        set_facture_recu(facture)
    elif res[0] == 'Controle':
        set_facture_controle(facture)

    if facture.modifs:
        return confirm(facture)

########################################################################
## Fonction de sélection (adhérent ou machine)

def select(clas, quoi, mde=''):
    u"""
    Interface de choix d'un adhérent, d'une machine ou d'un club
    Retourne une instance de la classe choisie.
    quoi est la chaine utilisée pour la demande (%sid=?)
    exemples : quoi = 'adhérent a' => Votre choix ? adhérent aid=:
    il faut que le dernier caractère de quoi soit a pour adhérent ou m pour machine
    Retourne None si annulation.

    si m = ro ouvre l'objet en mode read-only
    """

    s = ['', '', '', '', '', '', '', '', '', '', '', '', '', '']
    def unicodize(a):
        try:
            return unicode(a, 'utf-8')
        except:
            return a
    while 1:
        s = map(unicodize, s)
        arg = u'--title "Recherche %s" ' % ' '.join(quoi.split()[:-1])
        arg += u'--help-button '
        arg += u'--form "Entrez vos paramètres de recherche" 0 0 0 '
        arg += u'"Filtres adhérent" 1 8 "" 0 0 0 0 '
        arg += u'"Nom :"       2 1 "%s" 2 13 20 30 ' % s[0]
        arg += u'"Prenom :"    3 1 "%s" 3 13 20 30 ' % s[1]
        arg += u'"Téléphone :" 4 1 "%s" 4 13 20 00 ' % s[2]
        arg += u'"Chambre :"   5 1 "%s" 5 13 20 00 ' % s[3]
        arg += u'"aid :" 6  1 "%s" 6 13 20 5 ' % s[4]
        arg += u'"Mail :" 7 1 "%s" 7 13 20 00 ' % s[5]
        arg += u'"Filtres clubs" 1 40 "" 0 0 0 0 '
        arg += u'"Nom :"       2 37 "%s" 2 46 20 30 ' % s[6]
        arg += u'"Local :"   3 37 "%s" 3 46 20 00 ' % s[7]
        arg += u'"cid :"       4 37 "%s" 4 46 20 5 ' % s[8]
        arg += u'"Filtres facture" 6 40 "" 0 0 0 0 '
        arg += u'"fid :" 7 37 "%s" 7 46 20 5 ' % s[9]
        arg += u'"Filtres machine" 1 75 "" 0 0 0 0 '
        arg += u'"Nom :" 2 68 "%s" 2 77 20 50 ' % s[10]
        arg += u'"Mac :" 3 68 "%s" 3 77 20 17 ' % s[11]
        arg += u'"IP :"  4 68 "%s" 4 77 20 40 ' % s[12]
        arg += u'"mid :" 5 68 "%s" 5 77 20 5 ' % s[13]
        arg += u'"Remarque : les champs vides sont ignorés." 8 1 "" 0 0 0 0'

        annul, result = dialog(arg)
        if annul: return

        if result[0][:4] == 'HELP':
            arg = u'--title Aide '
            arg += u'--msgbox "Il n\'est pas necessaire de remplir tous les champs.\n'
            arg += u'Il est possible d\'utiliser les * dans les champs de recherche.\n\n\n" 0 0'
            dialog(arg)
            continue

        s = []
        for i in result:
            i = i.strip().decode(encoding)
            if not i:
                i = u'*'
            s.append(i)

        # Recherche par adresse mail/login
        if s[5] != '*' and '@' not in s[5]:
            s[5] += '@crans.org'

        ### Contruction de la chaîne de recherche
        filtre_adher = u'nom=%s&prenom=%s&tel=%s&chbre=%s&aid=%s&mail=%s&' % tuple(s[:6])
        filtre_clubs = u'nom=%s&chbre=%s&cid=%s&' % tuple(s[6:9])
        filtre_facture = u'fid=%s&' % s[9]
        filtre_machine = u'host=%s&macAddress=%s&ipHostNumber=%s&mid=%s&' % tuple(s[10:])

        filtre = u''
        if filtre_adher.count('=*&') != 6:
            # Au moins une condition adhérent
            filtre += filtre_adher[:-1]
        if filtre_machine.count('=*&') != 4:
            # Au moins une condition machine
            if filtre: filtre += '&'
            filtre += filtre_machine[:-1]
        if filtre_facture.count('=*&') != 1:
            if filtre: filtre += '&'
            filtre += filtre_facture[:-1]
        if filtre_clubs.count('=*&') != 3:
            if filtre: filtre += '&'
            filtre += filtre_clubs[:-1]
        if filtre == '':
            # Aucune condion => erreur
            arg = u'--title "Recherche" '
            arg += u'--msgbox "Il faut au moins une condition.\n\n\n" 0 0'
            dialog(arg)
            continue

        # Champs qui n'existent peut-être pas sur une machine
        for champ in ['ipHostNumber']:
            filtre = filtre.replace('%s=*&' % champ, '')

        ### Recherche
        try:
            if mde == 'ro':
                res = clas.search(filtre, 'ro')
            else:
                res = clas.search(filtre, 'w')
        except ValueError:
            arg = u'--title "Recherche" '
            arg += u'--msgbox "Caractère interdit.\n\n\n" 0 0'
            dialog(arg)
            continue

        # Affichage
        if quoi[-1] == 'a':
            valid = res['adherent']
            if not valid and (res['machine'] or res['facture']):
                # On va récupérer les adhérents correspondants aux machines trouvés
                deja = []
                for m in res['machine']:
                    a = m.proprietaire()
                    if a.id() in deja: continue
                    deja.append(a.id())
                    valid.append(a)
                for f in res['facture']:
                    a = f.proprietaire()
                    if a.id() in deja: continue
                    deja.append(a.id())
                    valid.append(a)
        elif quoi[-1] == 'm':
            valid = res['machine']
            if not valid and (res['adherent'] or res['club']):
                # On va récupérer les machines des adhérents trouvés
                for a in res['adherent'] + res['club']:
                    for m in a.machines():
                        valid.append(m)
        elif quoi[-1] == 'f':
            valid = res['facture']
            if not valid and (res['adherent'] or res['club']):
                # On va récupérer les machines des adhérents trouvés
                for a in res['adherent'] + res['club']:
                    for f in a.factures():
                        valid.append(f)
        else:
            raise TypeError('Argument fonction invalide')

        if not valid or (len(valid) == 1 and quoi[-1] == 'a' and valid[0].__class__ == AssociationCrans):
            arg = u'--title "Recherche" '
            arg += u'--msgbox "Aucun résultat.\n\n\n" 0 0'
            dialog(arg)
            # Retour au formulaire
            continue

        if len(valid) == 1:
            # Une seule réponse
            choix = valid[0]
        else:
            # Il faut choisir
            while 1:
                os.system('clear')
                choix = None
                cprint(u"Plusieurs réponses correspondant à votre requête ont été trouvées :")
                aff(valid)
                i = affich_tools.prompt(u'Votre choix ? (0 pour annuler) %sid =' % quoi)
                if i == '0': break
                for v in valid:
                    if v.id() == i:
                        choix = v
                        break
                if not choix:
                    # Redemande le choix
                    cprint('Choix invalide')
                    continue
                if choix: break

            if not choix:
                # Retour à l'interface de recherche
                continue

        os.system('clear')
        cprint(u"Sélection : ")
        aff(choix)

        while 1:
            r = affich_tools.prompt(u'Confirmer sélection ? [O/N]')
            if r == 'O' or r == 'o':
                break
            elif r == 'N' or r == 'n':
                # Annulation du choix
                choix = None
                break
            cprint(u'Répondre O ou N')
            # Retour à la confirmation

        if choix:
            if mde != 'ro' and not choix._modifiable:
                arg = u'--title "Recherche" '
                arg += u'--msgbox "Objet sélectionné locké, attendre.\n\n\n" 0 0'
                dialog(arg)
                return
            return choix
        # Sinon retour interface de sélection

def menu_principal():
    u"""
    Affiche le menu de choix initial
    """

    # que signifient toutes ces initiales ?
    #      (a)jouter, (m)odifier, (d)eleter
    #      (M)achine, (A)dherent, (C)lub, K=crans, (B)orne
    #      (c)ourant = déja selectionné

    proprio = None
    becane = None
    choix = None
    while 1:
        arg = u'--title "Menu principal" '
        arg += u'--help-button --item-help --cancel-label "Quitter" '
        arg += u'--default-item "%s" ' % choix
        arg += u'--menu "Que souhaitez vous faire ?" 0 55 13 '
        if proprio.__class__ == Adherent:
            arg += u'"mAc"'
        elif proprio.__class__ == Club:
            arg += u'"mCc"'
        if proprio:
            if proprio.__class__ != AssociationCrans:
                arg += u' "Modifier l\'inscription de %s" "" ' % proprio.Nom()
            arg += u'"aMc" "Ajouter une machine à %s" "" ' % proprio.Nom()
        if becane:
            arg += u'"mMc" "Modifier la machine %s" "Modification du nom, de l\'IP, de la MAC, des alias, des exemptions..." ' % becane.nom().split('.')[0]
        if proprio or becane:
            arg += u'"" "---------------------------------------" "" '

        arg += u'"aA" "Inscrire un nouvel adhérent" "" '
        arg += u'"mA" "Modifier l\'inscription d\'un adhérent" "Changer la chambre, la remarque, la section ou précâbler." '
        arg += u'"aMA" "Ajouter une machine à un adhérent" "" '
        arg += u'"dA" "Détruire un adhérent" "Suppression de l\'adhérent ainsi que de ses machines" '
        arg += u'"" "---------------------------------------" "" '
        arg += u'"mM" "Modifier une machine existante" "Changer le nom ou la MAC d\'une machine." '
        arg += u'"dM" "Détruire une machine" "" '
        arg += u'"" "---------------------------------------" "" '
        arg += u'"mF" "Modifier une facture existante" "Modifier les données relatives à la facture…" '
        arg += u'"dF" "Détruire une facture" "" '
        arg += u'"" "---------------------------------------" "" '
        arg += u'"aC" "Inscrire un nouveau club" "" '
        arg += u'"mC" "Modifier un club" "" '
        arg += u'"aMC" "Ajouter une machine à un club" "" '
        arg += u'"dC" "Détruire un club" "Suppression du club ainsi que de ses machines" '
        if isadm:
            arg += u'"" "---------------------------------------" "" '
        if isadm:
            arg += u'"aKM" "Ajouter une machine à l\'association" "" '
            arg += u'"aKB" "Ajouter une borne wifi" "" '
            arg += u'"aKB6" "Ajouter une borne wifi v6" "" '
        annul, result = dialog(arg)
        if annul: break

        if result[0][:4] == 'HELP':
            arg = u'--title Aide '
            arg += u'--msgbox "Interface utilisable au clavier ou a la souris pour les terminaux le supportant.\n'
            arg += u'Pour revenir à une question précédente utiliser le bouton annuler ou Ctrl +C.\n'
            arg += u'Pour quitter sans enregister les dernières modifications utilisez ESC.\n\n'
            arg += u'Pour toute remarque ou problème : fred@crans.org\n\n\n" 0 0'
            dialog(arg)
            continue

        choix = result[0]
        if not choix: continue

        if choix == 'aA':
            # Inscription nouvel adhérent
            proprio = Adherent()
            if new_adher(proprio):
                del proprio
                proprio = None
            else: choix = 'aMc' # ajout d'une machine

        elif choix == 'mA':
            # Modif adhérent
            proprio = select(db, u'adhérent à modifier a')
            if not proprio: continue
            choix = 'mAc'

        elif choix == 'aMA':
            # Ajout machine, adhérent à choisir
            proprio = select(db, u'adhérent auquel ajouter une machine a')
            if not proprio: continue
            choix = 'aMc'

        elif choix == 'aMC':
            # Ajout machine, club à choisir
            proprio = select_club(db)
            if not proprio: continue
            choix = 'aMc'

        elif choix == 'mM':
            # Modif machine, machine à choisir
            becane = select(db, u'machine à modifier m')
            if not becane: continue
            choix = 'mMc'

        elif choix == "mF":
            # Modif d'une facture. Choisir facture.
            facture = select(db, u'Facture à modifier f')
            if not facture: continue
            choix = "mFc"

        elif choix == 'aC':
            # Ajout d'un club
            proprio = Club()
            if new_club(proprio):
                del proprio ; proprio = None
            else: choix = 'aMc' # ajout d'une machine

        elif choix == 'mC':
            # Modif club
            proprio = select_club(db)
            if not proprio: continue
            choix = 'mCc'

        elif choix == 'dA':
            # Destruction adhérent
            proprio = select(db, u'adhérent à détruire a')
            if not proprio:
                continue
            if del_adher(proprio):
                continue
            del proprio
            proprio = None
            del becane
            becane = None

        elif choix == 'dM':
            # Destruction machine
            becane = select(db, u'machine à détruire m')
            if not becane: continue
            if del_machine(becane): continue
            del becane
            becane = None

        elif choix == 'dC':
            # Destruction club
            proprio = select_club(db)
            if not proprio: continue
            if del_club(proprio): continue
            del proprio
            proprio = None
            del becane
            becane = None

        elif choix == 'dF':
            # Destruction machine
            facture = select(db, u'facture à détruire f')
            if not facture: continue
            proprio = facture.proprietaire()
            if del_facture(facture): continue
            del facture
            facture = None

            proprio.update_adhesion()
            proprio.update_connexion()
            proprio.save()
            del proprio
            proprio = None

        elif choix == 'aKM':
            # Ajout machine au crans
            becane = MachineCrans(AssociationCrans(db.conn))
            if set_machine(becane):
                becane.restore()

        elif choix == 'aKB':
            # Ajout borne wifi
            becane = BorneWifi(AssociationCrans(db.conn))
            if set_machine(becane):
                becane.restore()

        elif choix == 'aKB6':
            # Ajout borne wifi v6
            becane = BorneWifi(AssociationCrans(db.conn))
            becane.bornev6 = ['True']
            if set_machine(becane):
                becane.restore()

        ##############################################

        if choix == 'aMc':
            # Ajout d'une machine à l'adhérent/au club courant

            # On ne peut avoir une machine fixe que si on a pas
            # déjà une machine fixe, sauf si on est membre actif

            machines_fixes = proprio.machines_fixes()
            if not isinstance(proprio, Club) and not proprio.adherentPayant():
                # Les gens qui ne paient pas n'ont le droit qu'à une
                # seule machine fixe
                if proprio.machines_fixes():
                    dlg.msgbox(u"Le type de compte de cet adhérent ne lui permet pas d'avoir de machine supplémentaire")
                    continue
                else:
                    choix = "Fixe"
            elif isinstance(proprio, Club) \
                    or not machines_fixes \
                    or proprio.droits() \
                    or proprio.etudes(0) == 'Personnel ENS':
                arg = u'--title "Nouvelle machine" '
                arg += u'--menu "Type de machine ?" 0 0 0 '
                arg += u'"Fixe" "Machine fixe" '
                arg += u'"Wifi" "Machine wireless" '
                annul, result = dialog(arg)
                if annul: continue
                choix = result[0]
            else:
                # Plus de machine fixe possible...
                arg = u'--title "Nouvelle machine" '
                arg += u'--menu "   Non membre actif\n\n'
                arg += u'Seuls les membres actifs peuvent posséder plusieurs\n'
                arg += u'machines fixes. L\'adhérent actuel n\'est pas membre\n'
                arg += u'actif, il n\'est donc possible que de lui ajouter une\n'
                arg += u'machine wifi..." 0 0 0 '
                arg += u'"OK" "OK, on lui rajoute une machine wifi" '
                arg += u'"Annul" "Bon, on abandonne..." '
                annul, result = dialog(arg)
                if annul or result[0] == "Annul": continue
                if result[0] == "OK":
                    choix = 'Wifi'
                else:
                    choix = 'Fixe'
            try:
                if choix == 'Fixe':
                    becane = MachineFixe(proprio)
                elif choix == 'Wifi':
                    becane = MachineWifi(proprio)
                # Suggestion: ne pas relever la mac
                if choix == 'Wifi' or (choix == 'Fixe' and proprio.chbre() not in ['', 'EXT']):
                    becane.mac("<automatique>")
            except ValueError, c:
                arg = u'--title "Nouvelle machine" '
                arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
                dialog(arg)
                continue
            if set_machine(becane):
                # Annulation
                del becane
                becane = None


        if choix == 'mAc':
            # Modif propriétaire courant
            del becane
            becane = None
            # Test club
            if isinstance(proprio, Club):
                if modif_club(proprio):
                    # Annulation des modifs
                    proprio.restore()
                # alors pas vu par l'instance actuelle de machine
            else:
                if modif_adher(proprio):
                    # Annulation des modifs
                    proprio.restore()

        elif choix == 'mMc':
            # Modif machine courante
            if not proprio:
                proprio = becane.proprietaire()
            if proprio.chbre() == '????':
                arg = u'--title "Ajout d\'une machine" '
                arg += u'--msgbox "ERREUR : la chambre de %s est inconnue !\n\n\n" 0 0' % proprio.Nom()
                dialog(arg)
            elif modif_machine(becane):
                # Annulation des modifs
                becane.restore()

        elif choix == 'mFc':
            # Modif machine courante
            if modif_facture(facture):
                # Annulation des modifs
                facture.restore()
            else:
                proprio = facture.proprietaire()
                proprio.update_adhesion()
                proprio.update_connexion()
                proprio.save()
                del proprio
                proprio = None

        elif choix == 'mCc':
            # Modif club courant
            if modif_club(proprio):
                # Annulation des modifs
                proprio.restore()

    os.system('clear')

def killed(a, z):
    sys.exit(254) # Va tomber dans les exceptions

if __name__ == '__main__':
    global debug
    debug = 0

    signal.signal(signal.SIGTERM, killed)     # Interception du signal TERM
    signal.signal(signal.SIGINT, signal.SIG_DFL) # Comportement normal de Ctrl-C

    # Traitement des options
    try:
        if len(sys.argv) > 1:
            options, arg = getopt.getopt(sys.argv[1:], '', ['debug'])
        else:
            options, arg = ([], '')
    except getopt.error, msg:
        cprint(msg)
        sys.exit(255)

    for opt, val in options:
        if opt == '--debug':
            debug = 1

    if isadm:
        debug = 1

    # Phase principale
    try:
        menu_principal()
        #os.system('clear')
        exit = 0
    except KeyboardInterrupt:
        os.system('clear')
        cprint(u"Interruption par l'utilisateur.")
        exit = 255
    except SystemExit, c:
        if c.__str__() == '254':
            os.system('reset')
            cprint(u"Votre session d'édition a été tuée.")
        exit = c
    except:
        if not debug: os.system('clear')
        cprint(u"""Une erreur fatale s'est produite durant l'exécution.""")

        # Report de bug
        import traceback
        from cStringIO import StringIO
        from smtplib import SMTP

        s = StringIO()
        sys.stderr = s
        traceback.print_exc()
        sys.stderr = sys.__stderr__
        traceback = s.getvalue()
        try:
            if not debug and To:
                # Paramètres pour le mail
                From = script_utilisateur + '@crans.org'

                entete_mail = """From: %s
To: %s
Subject: Bugreport %s

""" % (From, ','.join(To), sys.argv[0].split('/')[-1])

                # Envoi mail
                conn = SMTP(smtpserv)
                conn.sendmail(From, To, entete_mail + traceback)
                conn.quit()
                sys.stderr.write(to_encoding("Un rapport de bug à été automatiquement envoyé.\n", encoding))
            else:
                cprint(u"Merci de faire parvenir un rapport de bug à nounou")
        except:
            sys.stderr.write(to_encoding("Impossible d'envoyer le rapport de bug.\n", encoding))

        if debug:
            cprint('-'*40)
            cprint(u'Détails techniques :')
            sys.stderr.write(traceback)
            cprint('-'*40)
        exit = 1

    # Restart des services
    signal.signal(signal.SIGINT, signal.SIG_IGN) # Pas de Ctrl-C
    signal.signal(signal.SIGTERM, signal.SIG_IGN) # Pas de kill non plus

    try:
        serv = db.services_to_restart()
    except:
        # Erreur trop tot probablement
        serv = ''
    if serv:
        mn = int(strftime('%M'))
        # Restart toutes les 10 min : 03, 13, 23, 33, 43, 53
        t = (13 - mn % 10) % 10 + 1 # Certaines machines le font -Aà 4-b
        if t == 0: t = 10
        cprint(u"Les modifications apportées à la base seront prises en compte dans %i min environ." % t)
        if debug:
            cprint(u"Les services suivants seront redémarrés: ")
            for s in serv:
                cprint(u'\t%s' % s)

    if debug: cprint('-*- Fin -*-')

    # Rétablissement du Ctrl-C
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    sys.exit(exit)

