--- /dev/null
+Voici la technique utilisée pour intercepter les courriels sortant de CODA.
+
+== Configuration du serveur SMTP sortant dans CODA ==
+
+ * ouvrir une session sur CODA-web utilisant le compte INSTALL
+ * aller dans le menu Administration > Transports des messages > Maitre de transport de message
+ * dans la liste déroulant sélectionner SMTP puis effectuer la recherche (cliquer sur la flèche verte)
+ * dans le champ Hôte saisir "smtp-coda.auf"
+ * dans le nom d'utilisateur saisir "reflet@ca.auf.org" ("reflet@ca.auf.org" est un alias créé préalablement)
+ * enregistrer la configuration (cliquer sur la l'icône en forme de disquette)
+ * terminé, donc fermer la session INSTALL
+
+== Mise en place du serveur SMTP interceptant les courriels de CODA ==
+
+Mettre en place un serveur [[Debian]] standard.
+
+Installer le paquet `python`, pour le script de filtrage des courriels sortant de CODA.
+
+Créer le fichier `/usr/local/sbin/coda-filter.py` avec le contenu provenant du dépôt git : {{{
+wget -O /usr/local/sbin/coda-filter.py 'http://git.auf.org/?p=progfou.git;a=blob_plain;f=coda/coda-filter.py'
+}}}
+
+Rendre ce script exécutable : {{{
+chmod +x /usr/local/sbin/coda-filter.py
+}}}
+
+Installer Postfix en mode ''Internet avec smarthost'',
+pour qu'il puisse recevoir les courriels de Coda via SMTP,
+les traiter, puis les renvoyer au SMTP sortant local.
+
+Ajouter les lignes suivantes à `/etc/postfix/master.cf` : {{{#!postfix
+# accès au filtre pour Coda
+codafilter unix - n n - 10 pipe
+ flags=Rq user=nobody null_sender=
+ argv=/usr/local/sbin/coda-filter.py ${sender} ${recipient}
+
+# accès pour le retour du filtre pour Coda (non filtré)
+localhost:10026 inet n - n - 10 smtpd
+ -o content_filter=
+ -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks,no_milters,no_address_mappings
+ -o smtpd_helo_restrictions=
+ -o smtpd_client_restrictions=
+ -o smtpd_sender_restrictions=
+ -o smtpd_recipient_restrictions=permit_mynetworks,reject
+ -o mynetworks=127.0.0.0/8
+ -o smtpd_authorized_xforward_hosts=127.0.0.0/8
+}}}
+
+Éditer le fichier `/etc/postfix/main.cf` pour s'assurer qu'il contienne les valeurs suivantes : {{{#!postfix
+myhostname = smtp-coda.auf
+mydestination = localhost
+relayhost = smtp.ca.auf.org
+mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 10.36.0.240/28
+content_filter = codafilter:dummy
+}}}
+
+Relancer le service Postfix : {{{
+service postfix restart
+}}}
--- /dev/null
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+#
+# Objectifs :
+# 05. si le destinataire est noreply@auf.org, on le remplace par l'émetteur
+# (ANNULÉ car ça ne devrait plus arriver)
+# (REPRIS pour le cas où le destinataire est impression-coda@auf.org)
+# 10. envoyer une copie à l'émetteur, sauf s'il est déjà le destinataire
+# (ANNULÉ car envoi déjà vers les bons destinataires)
+# 20. ajouter une en-tête X-Coda pour faciliter les filtrages
+# 30. préfixer le sujet par '[CODA] '
+# 40. remplacer l'organisation par AuF (forme longue, au lieu de CODA)
+# 50. renommer l'attachement en utilisant le sujet
+# Content-Type: application/pdf; name=attachment.pdf
+# Content-Disposition: inline; filename=attachment.pdf
+#
+import sys
+import email
+from email.header import decode_header
+import smtplib
+import re
+
+# préfixe pour le sujet
+SUBJECT_PREFIX = "[CODA] "
+# contenu pour l'en-tête X-Coda qui sera ajoutée
+X_CODA_VALUE = "Filtre AuF pour Coda v2"
+# contenu pour l'en-tête Organisation
+ORGANIZATION = "Agence universitaire de la Francophonie"
+# masque de repérage du type et numéro de document
+DOC_PATTERN = r'^(.*:)?\s*(\S+)\s*/\s*(\d+)\s*$'
+# format du nouveau nom de document (à partir du type et numéro)
+DOC_FORMAT = 'CODA-%s-%08d.pdf'
+
+# codes de sortie venant de <sysexits.h>
+EX_TEMPFAIL = 75
+EX_UNAVAILABLE = 69
+
+# on récupère l'expéditeur et le(s) destinataire(s) en paramètres
+#email_from = 'jca.test@auf.org'
+email_from = sys.argv[1]
+email_to_list = sys.argv[2:]
+
+# on tente de récupérer le message arrivant sur l'entrée standard (pipe)
+try:
+ msg = email.message_from_file(sys.stdin)
+# en cas d'échec on demande à Postfix de considérer que c'est temporaire
+except Exception, err:
+ print "Error: %s" % err
+ sys.exit(EX_TEMPFAIL)
+
+#--------------------------------------------------------------------------
+# TRAITEMENTS
+#--------------------------------------------------------------------------
+
+# 05. si le destinataire est noreply@auf.org, on le remplace par l'émetteur
+# (ANNULÉ car ça ne devrait plus arriver)
+# (REPRIS pour le cas où le destinataire est impression-coda@auf.org)
+if msg['To'] in ['noreply@auf.org', 'impression-coda@auf.org']:
+ old_to = msg['To']
+ email_to_list.remove(old_to)
+ email_to_list.append(email_from)
+ msg.replace_header('To', email_from)
+ msg.add_header('X-Coda-Was-To', old_to)
+
+# 10. on ajoute l'émetteur aux destinataires, sauf s'il est déjà le destinataire
+# (ANNULÉ car envoi déjà vers les bons destinataires)
+#if email_from not in email_to_list and not email_from.endswith('@ca.auf.org'):
+# email_to_list.append(email_from)
+# msg.add_header('Cc', email_from)
+
+# 20. on ajoute une en-tête pour aider aux filtrages
+msg.add_header('X-Coda', X_CODA_VALUE)
+
+# 30. on préfixe le sujet actuel par '[CODA] '
+subject = msg['Subject']
+try:
+ parts = decode_header(subject)
+ subject = u' '.join([unicode(s, e or 'ascii') for s,e in parts])
+except:
+ pass
+msg.replace_header('Subject', SUBJECT_PREFIX + subject)
+
+# 40. on remplace l'organisation par l'AuF
+del msg['ORGANISATON'] # non, ce n'est pas une faute de frappe…
+msg.add_header('Organization', ORGANIZATION)
+
+# 50. on renomme l'attachement
+
+# construction du nouveau nom de fichier
+# on tente de repérer le type et numéro de document
+try:
+ m = re.match(DOC_PATTERN, subject)
+ prefix, com_type, com_num = m.groups()
+ new_filename = DOC_FORMAT % (com_type, int(com_num))
+# en cas d'échec on ignore le problème
+except Exception, err:
+ print "Error: %s" % err
+ new_filename = 'document-coda.pdf'
+
+# parcours des différentes parties du courriel
+for part in msg.walk():
+ if part.get_content_type() != 'application/pdf':
+ continue
+ # adaptation du nom de fichier dans le Content-Type
+ try:
+ raw_param = part.get_param('name')
+ param = email.utils.collapse_rfc2231_value(raw_param)
+ if param == 'attachment.pdf':
+ part.set_param('name', new_filename)
+ except:
+ pass
+ # adaptation du nom de fichier dans le Content-Disposition
+ try:
+ raw_param = part.get_param('filename', header='Content-Disposition')
+ param = email.utils.collapse_rfc2231_value(raw_param)
+ if param == 'attachment.pdf':
+ part.set_param('filename', new_filename, header='Content-Disposition')
+ except:
+ pass
+
+#--------------------------------------------------------------------------
+
+# on se connecte à Postfix via le port de retour de filtrage
+client = smtplib.SMTP('localhost', 10026)
+#client.set_debuglevel(1)
+
+# on tente d'envoyer le courriel modifié
+try:
+ client.sendmail(email_from, email_to_list, msg.as_string())
+# en cas d'échec on demande à Postfix de considérer que c'est temporaire
+except Exception, err:
+ print "Error: %s" % err
+ sys.exit(EX_TEMPFAIL)
+
+# on termine proprement
+client.quit()
+sys.exit(0)