#!/bin/bash /usr/scripts/python.sh
# -*- coding: utf-8 -*-
###############################################################################
# config_mail : gestion du .forward et .procmailrc des adhérents
###############################################################################
# The authors of this code are
#   Etienne Chové <etienne.chove@crans.org>
#
# Copyright (C) 2006 Etienne Chové
# All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
###############################################################################

############################################################################
## Chaines de formatage pour le procmailrc

"""
Script permetant de gérer sa configuration mail

Usage    : ConfigMail.py [--forward=(|<mail>)] [--spam=(accepte|marque|deplace|supprime)]
Retourne :
    forward=(|<mail>)
    spam=(accepte|marque|deplace|supprime)
"""

procmail_warning = """################################################################
# Ce fichier de configuration a été automatiquement généré par #
# l'intranet.                                                  #
#                                                              #
# ATTENTION : ne le modifiez que si vous savez ce que vous     #
#             faites !!!                                       #
################################################################

"""

# Pour éviter des crash
procmail_mark_old = """# Passage des mails dans spamassassin
:0 Whbf
* < 256000
| spamc

# Serveur blacklisté
:0
* ^X-Reject: 554
* !^X-Spam-Status: Yes
{
  :0 Whf
  * ^Subject:\/.*
  | formail -i "Subject: *****SPAM***** $MATCH"
}

"""

procmail_mark = """# Passage des mails dans spamassassin
:0 Whbf
* < 256000
| spamc

# Serveur blacklisté
:0
* ^X-Reject: 554
* !^X-Spam-Status: Yes
{
  :0 Whf
  * ^Subject:\/.*
  | formail -i "Subject: *****SPAM***** $MATCH"
}

:0
* ^X-Spam-Status: Yes
{
  :0 Whf
  * ^Subject:\/.*
| formail -i "Subject: *****SPAM***** $MATCH"
}

"""

procmail_delete_spam = """# Suppression des spams
:0 Whf
* ^Subject: *****SPAM*****
/dev/null
"""

procmail_move = """#Déplacement des spams
:0 Whbf
* < 256000
| spamc

# Serveur blacklisté
:0
* ^X-Reject: 554
* !^X-Spam-Status: Yes
.%(spamdir)s

# Mail de spam
:0
* ^X-Spam-Status: Yes
.%(spamdir)s
"""

procmail_forward = """# Redirection des mails
:0
!%s
"""

forward_procmail = '"|exec /usr/bin/procmail"\n'

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


from commands import getstatusoutput
import os
import sys
import pwd
import getopt
home = pwd.getpwuid(os.getuid())[5]
import re

class MailConfigError(ReferenceError):
    pass

def Log(content):
    with open('/tmp/intranet.log', 'a') as log:
        log.writelines(content+"\n")

############################################################################
## Fonctions utiles

def _IsMail(mail):
    """
    Dit si la chaine fournie est une adresse mail valide
    """
    return bool(re.match('[a-z0-9_\.-]+@[a-z0-9_-]+(\.[a-z0-9_-]+)+',mail.lower()))

def _Clean(texte):
    """
    Nettoie une chaine de caractère/liste en supprimant les lignes vides/commentés,
    et retourne une liste
    """
    if type(texte) != list:
        texte = texte.split('\n')
    return [ x.strip() for x in texte if x.strip() and x[0]!='#' ]

def _GetConfig():
    """ Retourne la configuration de l'utilisateur courant,
    sous forme d'un dictionnaire
    """
    ## lecture du fichier .forward
    try:
        fic_forward = _Clean( open('%s/.forward'%home).readlines() )
        if len(fic_forward) != 1:
            raise MailConfigError, 'Fichier forward trop long'
        fic_forward = fic_forward[0]
    except IOError:
        return {'forward':'', 'spam':'accepte', 'spamdir':None}

    # forward simple
    if _IsMail(fic_forward):
        return {'forward':fic_forward, 'spam':'accepte', 'spamdir':None}

    # utilisation de procmail
    if fic_forward != _Clean(forward_procmail)[0]:
        raise MailConfigError, 'Fichier forward non compréhensible'

    ## lecture du .procmailrc
    fic_procmail = _Clean( open('%s/.procmailrc'%home).readlines() )

    # forward
    if _IsMail( fic_procmail[-1][1:].strip() ) and fic_procmail[-2] == ":0" :
        forward = fic_procmail[-1][1:].strip()
        fic_procmail = fic_procmail[:-2]
    else:
        forward = ''

    # forward simple dans le procmailrc
    if not fic_procmail:
        return {'forward':forward, 'spam':'accepte', 'spamdir':None}
    
    # marquage des spams
    tmp = _Clean( procmail_mark )
    tmp2 = _Clean( procmail_move )
    tmp3 = _Clean( procmail_mark_old )
    mark = False
    move = False
    if fic_procmail[:len(tmp)] != tmp and fic_procmail[:len(tmp)] != tmp3:
        if len(fic_procmail) != len(tmp2):
            raise MailConfigError, 'Fichier de procmail non compréhensible'
        else:
            spamdir = fic_procmail[-1][1:]
            fic_procmail = fic_procmail[len(tmp2):]
            move = True
    else:
        fic_procmail = fic_procmail[len(tmp):]
        spamdir = ""
        mark = True

    # suppression des spams ?
    if (not fic_procmail) and mark:
        return {'forward':forward, 'spam':'marque', 'spamdir':None}
    elif (not fic_procmail) and move: 
        return {'forward':forward, 'spam':'deplace', 'spamdir':spamdir}
    elif fic_procmail == _Clean(procmail_delete_spam):
        return {'forward':forward, 'spam':'supprime', 'spamdir':None}
    else:
        raise MailConfigError, 'Fichier de procmail non compréhensible'

def _SetConfig(forward = None, spam= None, spamdir=None):
    """ Modifie la configuration de l'utilisateur courant """

    # variable new_spam
    if spam in ['accepte', 'deplace', 'supprime','marque']:
        new_spam = spam
    elif spam == None:
        new_spam = _GetConfig()['spam']
    else:
        raise ValueError, 'Valeur interdite pour le paramètre spam'

    # variable forward
    if forward == None:
        new_forward = _GetConfig()['forward']
    elif _IsMail(forward) or forward=='':
        new_forward = forward
    else:
        raise ValueError, 'Adresse mail invalide'

    if spamdir == None:
        new_spamdir = _GetConfig()['spamdir']
    elif re.match('^[a-zA-Z0-9_-]+$', spamdir):
        new_spamdir = spamdir
    else:
        raise ValueError, 'Nom de dossier invalide'
        

    # génération des fichiers
    if new_spam=='accepte':
        # suppression du .procmailrc
        try:
            os.remove('%s/.procmailrc'%home)
        except:
            pass
        # remplissage du .forward
        if new_forward:
            open('%s/.forward'%home,'w').write('%s\n'%new_forward)
        else:
            os.remove('%s/.forward'%home)
    else:
        # écriture du .procmailc
        if new_spam == 'marque':
            txt = procmail_warning + procmail_mark
        if new_spam=='supprime':
            txt = procmail_warning + procmail_mark + procmail_delete_spam
        if new_spam == 'deplace':
            txt = procmail_warning + procmail_move % ({'spamdir':new_spamdir})
        if new_forward:
            txt += procmail_forward % new_forward
        open('%s/.procmailrc'%home,'w').write(txt)
        # écriture du .forward
        open('%s/.forward'%home,'w').write(forward_procmail)

def _Sudo(uid, forward=None, spam=None, spamdir=None):
    """ Execute le script pour un autre utilisateur """
    # construction de la ligne de commande
    if __file__[-4:]=='.pyc':
        f = __file__[:-1]
    else:
        f = __file__
    c = "/usr/bin/sudo -u %s %s" % (uid, os.path.abspath(f))
    if forward!=None:
        c += " --forward=%s" % forward
    if spam!=None:
        c += " --spam=%s" % spam
    if spamdir!=None:
        c += " --spamdir=%s" %spamdir

    # execution de la commande
    status, output = getstatusoutput(c)

    # code d'erreur
    if status:
        sys.stderr.write("Erreur sudo : %s\n"%c)
        sys.stderr.write(output)
        sys.stderr.flush()
        sys.exit(status)

    # valeurs de retour
    res = {}
    for line in output.split('\n'):
        line = line.split('=')
        res[line[0]] = len(line)==2 and line[1] or ''
    return res

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

def MailConfig(uid=None, forward=None, spam=None, spamdir=None):
    """ Modifie ou retourne la configuration mail de l'utilisateur

    user    = utilisateur à configurer, si None configure l'utilisateur courant
    forward = adresse vers laquelle rediriger les mails, chaine vide si pas de redirection
    spam    = action à effectuer sur les spams (accepte, supprime, marque)
    spamdir    = dossier où déplacer le spam.

    Pour les champs forward et spam, la valeur None ne touche pas au champ.

    Retourne un dictionnaire { 'forward':'', 'spam':'', 'spamdir:''}
    """

    ## demande pour un autre utilisateur
    if uid:
        return _Sudo(uid=uid, forward=forward, spam=spam, spamdir=spamdir)

    ## nettoyage des variables
    cfg = _GetConfig()
    if forward == cfg['forward']:
        forward = None
    if spam == cfg['spam']:
        spam = None
    if spamdir == cfg['spamdir']:
        spamdir = None
    if spamdir == "":
        spamdir = None

    ## modifications
    if forward != None or spam != None or spamdir != None:
        _SetConfig(forward=forward, spam=spam, spamdir=spamdir)

    ## on renvoie la configuration
    return _GetConfig()

if __name__=="__main__":
    ## parsage des arguments
    forward = None
    spam = None
    spamdir = None
    opts, args = getopt.getopt(sys.argv[1:], "", ['forward=', 'spam=', 'spamdir='])
    if ('--spam','deplace') in opts:
        optgood = False
        for o, v in opts:
            if o == '--spamdir':
                optgood = True
        if not optgood:
            raise ValueError, 'Il faut renseigner un dossier pour déplacer le spam'
    for o, v in opts:
        if o == "--forward":
            forward = v
        elif o == '--spam':
            spam = v
        elif o == '--spamdir':
            spamdir = v


    ## execution de MailConfig
    res = MailConfig(forward=forward, spam=spam, spamdir=spamdir)

    ## affichage des résultats
    for i in res.items():
        print "%s=%s" % i

