Commit | Line | Data |
---|---|---|
dd259590 P |
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 | # | |
17 | import sys | |
18 | import email | |
9f039cf1 | 19 | import email.charset |
dd259590 P |
20 | from email.header import decode_header |
21 | import smtplib | |
22 | import re | |
23 | ||
24 | # préfixe pour le sujet | |
25 | SUBJECT_PREFIX = "[CODA] " | |
26 | # contenu pour l'en-tête X-Coda qui sera ajoutée | |
27 | X_CODA_VALUE = "Filtre AuF pour Coda v2" | |
28 | # contenu pour l'en-tête Organisation | |
29 | ORGANIZATION = "Agence universitaire de la Francophonie" | |
30 | # masque de repérage du type et numéro de document | |
31 | DOC_PATTERN = r'^(.*:)?\s*(\S+)\s*/\s*(\d+)\s*$' | |
32 | # format du nouveau nom de document (à partir du type et numéro) | |
33 | DOC_FORMAT = 'CODA-%s-%08d.pdf' | |
9f039cf1 P |
34 | # réécriture des liens pour coda.auf |
35 | BODY_PATTERN_1 = r'\n(http://coda.auf/)(.*)\n' | |
36 | BODY_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 | |
38 | BODY_PATTERN_2 = r'\n(http://form.coda.auf/)(.*)\n' | |
39 | BODY_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' | |
dd259590 P |
40 | |
41 | # codes de sortie venant de <sysexits.h> | |
42 | EX_TEMPFAIL = 75 | |
43 | EX_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' | |
47 | email_from = sys.argv[1] | |
48 | email_to_list = sys.argv[2:] | |
49 | ||
50 | # on tente de récupérer le message arrivant sur l'entrée standard (pipe) | |
51 | try: | |
52 | msg = email.message_from_file(sys.stdin) | |
53 | # en cas d'échec on demande à Postfix de considérer que c'est temporaire | |
54 | except Exception, err: | |
55 | print "Error: %s" % err | |
56 | sys.exit(EX_TEMPFAIL) | |
57 | ||
58 | #-------------------------------------------------------------------------- | |
59 | # TRAITEMENTS | |
60 | #-------------------------------------------------------------------------- | |
61 | ||
9f039cf1 P |
62 | # par défaut les textes seront codés en Quoted-Printable |
63 | qp_charset = email.charset.Charset('utf-8') | |
64 | qp_charset.header_encoding = email.charset.QP | |
65 | qp_charset.body_encoding = email.charset.QP | |
66 | ||
dd259590 P |
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) | |
51090a4d P |
70 | if 'impression-coda@auf.org' in email_to_list: |
71 | old_to = 'impression-coda@auf.org' | |
dd259590 P |
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 | |
84 | msg.add_header('X-Coda', X_CODA_VALUE) | |
85 | ||
86 | # 30. on préfixe le sujet actuel par '[CODA] ' | |
87 | subject = msg['Subject'] | |
88 | try: | |
89 | parts = decode_header(subject) | |
90 | subject = u' '.join([unicode(s, e or 'ascii') for s,e in parts]) | |
91 | except: | |
92 | pass | |
93 | msg.replace_header('Subject', SUBJECT_PREFIX + subject) | |
94 | ||
95 | # 40. on remplace l'organisation par l'AuF | |
96 | del msg['ORGANISATON'] # non, ce n'est pas une faute de frappe… | |
97 | msg.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 | |
103 | try: | |
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 | |
108 | except Exception, err: | |
109 | print "Error: %s" % err | |
110 | new_filename = 'document-coda.pdf' | |
111 | ||
112 | # parcours des différentes parties du courriel | |
113 | for part in msg.walk(): | |
9f039cf1 P |
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 | |
dd259590 P |
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 | |
150 | client = smtplib.SMTP('localhost', 10026) | |
151 | #client.set_debuglevel(1) | |
152 | ||
153 | # on tente d'envoyer le courriel modifié | |
154 | try: | |
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 | |
157 | except Exception, err: | |
158 | print "Error: %s" % err | |
159 | sys.exit(EX_TEMPFAIL) | |
160 | ||
161 | # on termine proprement | |
162 | client.quit() | |
163 | sys.exit(0) |