list2form : gestion des erreurs et des petits fichiers (merci cgi.py…)
[progfou.git] / coda / coda-filter.py
... / ...
CommitLineData
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3#
4# Objectifs :
5# 05. si le destinataire est noreply@auf.org, on le remplace par l'émetteur
6# (ANNULÉ car ça ne devrait plus arriver)
7# (REPRIS pour le cas où le destinataire est impression-coda@auf.org)
8# 10. envoyer une copie à l'émetteur, sauf s'il est déjà le destinataire
9# (ANNULÉ car envoi déjà vers les bons destinataires)
10# 20. ajouter une en-tête X-Coda pour faciliter les filtrages
11# 30. préfixer le sujet par '[CODA] '
12# 40. remplacer l'organisation par AuF (forme longue, au lieu de CODA)
13# 50. renommer l'attachement en utilisant le sujet
14# Content-Type: application/pdf; name=attachment.pdf
15# Content-Disposition: inline; filename=attachment.pdf
16#
17import sys
18import email
19import email.charset
20from email.header import decode_header
21import smtplib
22import re
23
24# préfixe pour le sujet
25SUBJECT_PREFIX = "[CODA] "
26# contenu pour l'en-tête X-Coda qui sera ajoutée
27X_CODA_VALUE = "Filtre AuF pour Coda v2"
28# contenu pour l'en-tête Organisation
29ORGANIZATION = "Agence universitaire de la Francophonie"
30# masque de repérage du type et numéro de document
31DOC_PATTERN = r'^(.*:)?\s*(\S+)\s*/\s*(\d+)\s*$'
32# format du nouveau nom de document (à partir du type et numéro)
33DOC_FORMAT = 'CODA-%s-%08d.pdf'
34# réécriture des liens pour coda.auf
35BODY_PATTERN_1 = r'\n(http://coda.auf/)(.*)\n'
36BODY_FORMAT_1 = r'\n\nAccès depuis une implantation AUF :\n \1\2\n\nAccès depuis Internet :\n https://coda.auf.org/\2\n\n'
37# réécriture des liens pour form.coda.auf
38BODY_PATTERN_2 = r'\n(http://form.coda.auf/)(.*)\n'
39BODY_FORMAT_2 = r'\n\nAccès depuis une implantation AUF :\n \1\2\n\nAccès depuis Internet :\n https://form-coda.auf.org/\2\n\n'
40
41# codes de sortie venant de <sysexits.h>
42EX_TEMPFAIL = 75
43EX_UNAVAILABLE = 69
44
45# on récupère l'expéditeur et le(s) destinataire(s) en paramètres
46#email_from = 'jca.test@auf.org'
47email_from = sys.argv[1]
48email_to_list = sys.argv[2:]
49
50# on tente de récupérer le message arrivant sur l'entrée standard (pipe)
51try:
52 msg = email.message_from_file(sys.stdin)
53# en cas d'échec on demande à Postfix de considérer que c'est temporaire
54except Exception, err:
55 print "Error: %s" % err
56 sys.exit(EX_TEMPFAIL)
57
58#--------------------------------------------------------------------------
59# TRAITEMENTS
60#--------------------------------------------------------------------------
61
62# par défaut les textes seront codés en Quoted-Printable
63qp_charset = email.charset.Charset('utf-8')
64qp_charset.header_encoding = email.charset.QP
65qp_charset.body_encoding = email.charset.QP
66
67# 05. si le destinataire est noreply@auf.org, on le remplace par l'émetteur
68# (ANNULÉ car ça ne devrait plus arriver)
69# (REPRIS pour le cas où le destinataire est impression-coda@auf.org)
70if 'impression-coda@auf.org' in email_to_list:
71 old_to = 'impression-coda@auf.org'
72 email_to_list.remove(old_to)
73 email_to_list.append(email_from)
74 msg.replace_header('To', email_from)
75 msg.add_header('X-Coda-Was-To', old_to)
76
77# 10. on ajoute l'émetteur aux destinataires, sauf s'il est déjà le destinataire
78# (ANNULÉ car envoi déjà vers les bons destinataires)
79#if email_from not in email_to_list and not email_from.endswith('@ca.auf.org'):
80# email_to_list.append(email_from)
81# msg.add_header('Cc', email_from)
82
83# 20. on ajoute une en-tête pour aider aux filtrages
84msg.add_header('X-Coda', X_CODA_VALUE)
85
86# 30. on préfixe le sujet actuel par '[CODA] '
87subject = msg['Subject']
88try:
89 parts = decode_header(subject)
90 subject = u' '.join([unicode(s, e or 'ascii') for s,e in parts])
91except:
92 pass
93msg.replace_header('Subject', SUBJECT_PREFIX + subject)
94
95# 40. on remplace l'organisation par l'AuF
96del msg['ORGANISATON'] # non, ce n'est pas une faute de frappe…
97msg.add_header('Organization', ORGANIZATION)
98
99# 50. on renomme l'attachement
100
101# construction du nouveau nom de fichier
102# on tente de repérer le type et numéro de document
103try:
104 m = re.match(DOC_PATTERN, subject)
105 prefix, com_type, com_num = m.groups()
106 new_filename = DOC_FORMAT % (com_type, int(com_num))
107# en cas d'échec on ignore le problème
108except Exception, err:
109 print "Error: %s" % err
110 new_filename = 'document-coda.pdf'
111
112# parcours des différentes parties du courriel
113for part in msg.walk():
114 # traitement du corps de message pour y remplacer :
115 # http://coda.auf/coda/…
116 # par :
117 # Accès depuis une implantation AUF : http://coda.auf/coda/…
118 # Accès depuis Internet : https://coda.auf.org/coda/…
119 if part.get_content_type() == 'text/plain':
120 text = part.get_payload(decode=True)
121 new_text = re.sub(BODY_PATTERN_1, BODY_FORMAT_1, text)
122 new_text = re.sub(BODY_PATTERN_2, BODY_FORMAT_2, new_text)
123 if new_text != text:
124 del part['Content-Transfer-Encoding']
125 part.set_payload(new_text, qp_charset)
126 continue
127 # traitement uniquement si c'est un attachement de PDF
128 if part.get_content_type() != 'application/pdf':
129 continue
130 # adaptation du nom de fichier dans le Content-Type
131 try:
132 raw_param = part.get_param('name')
133 param = email.utils.collapse_rfc2231_value(raw_param)
134 if param == 'attachment.pdf':
135 part.set_param('name', new_filename)
136 except:
137 pass
138 # adaptation du nom de fichier dans le Content-Disposition
139 try:
140 raw_param = part.get_param('filename', header='Content-Disposition')
141 param = email.utils.collapse_rfc2231_value(raw_param)
142 if param == 'attachment.pdf':
143 part.set_param('filename', new_filename, header='Content-Disposition')
144 except:
145 pass
146
147#--------------------------------------------------------------------------
148
149# on se connecte à Postfix via le port de retour de filtrage
150client = smtplib.SMTP('localhost', 10026)
151#client.set_debuglevel(1)
152
153# on tente d'envoyer le courriel modifié
154try:
155 client.sendmail(email_from, email_to_list, msg.as_string())
156# en cas d'échec on demande à Postfix de considérer que c'est temporaire
157except Exception, err:
158 print "Error: %s" % err
159 sys.exit(EX_TEMPFAIL)
160
161# on termine proprement
162client.quit()
163sys.exit(0)