scripts/gestion/config_mail.py
2013-03-10 19:22:16 +01:00

373 lines
11 KiB
Python
Executable file

#! /usr/bin/env python
# -*- 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