Cette page décrit la technique utilisée pour intercepter les courriels sortant de Coda.

Configuration du serveur SMTP sortant dans Coda

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 suivant :

   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 # 10. envoyer une copie à l'émetteur, sauf s'il est déjà le destinataire
   7 # 20. ajouter une en-tête X-Coda pour faciliter les filtrages 
   8 # 30. préfixer le sujet par '[CODA] '
   9 # 40. remplacer l'organisation par AuF (forme longue, au lieu de CODA)
  10 # 50. renommer l'attachement en utilisant le sujet
  11 #       Content-Type: application/pdf; name=attachment.pdf
  12 #       Content-Disposition: inline; filename=attachment.pdf
  13 #
  14 import sys
  15 import email
  16 import smtplib
  17 import re
  18 
  19 # préfixe pour le sujet
  20 SUBJECT_PREFIX = "[CODA] "
  21 # contenu pour l'en-tête X-Coda qui sera ajoutée
  22 X_CODA_VALUE = "Filtre AuF pour Coda v2"
  23 # contenu pour l'en-tête Organisation
  24 ORGANIZATION = "Agence universitaire de la Francophonie"
  25 # masque de repérage du type et numéro de document
  26 DOC_PATTERN = r'^(.*:)?\s*(\S+)\s*/\s*(\d+)\s*$'
  27 # format du nouveau nom de document (à partir du type et numéro)
  28 DOC_FORMAT = 'CODA-%s-%08d.pdf'
  29 
  30 # codes de sortie venant de <sysexits.h>
  31 EX_TEMPFAIL = 75
  32 EX_UNAVAILABLE = 69
  33 
  34 # on récupère l'expéditeur et le(s) destinataire(s) en paramètres
  35 #email_from = 'jca.test@auf.org'
  36 email_from = sys.argv[1]
  37 email_to_list = sys.argv[2:]
  38 
  39 # on tente de récupérer le message arrivant sur l'entrée standard (pipe)
  40 try:
  41     msg = email.message_from_file(sys.stdin)
  42 # en cas d'échec on demande à Postfix de considérer que c'est temporaire
  43 except Exception, err:
  44     print "Error: %s" % err
  45     sys.exit(EX_TEMPFAIL)
  46 
  47 #--------------------------------------------------------------------------
  48 # TRAITEMENTS
  49 #--------------------------------------------------------------------------
  50 
  51 # 05. si le destinataire est noreply@auf.org, on le remplace par l'émetteur
  52 if msg['To'] in ['noreply@auf.org']:
  53     old_to = msg['To']
  54     email_to_list.remove(old_to)
  55     email_to_list.append(email_from)
  56     msg.replace_header('To', email_from)
  57     msg.add_header('X-Coda-Was-To', old_to)
  58 
  59 # 10. on ajoute l'émetteur aux destinataires, sauf s'il est déjà le destinataire
  60 if email_from not in email_to_list and email_from != 'root@ca.auf.org':
  61     email_to_list.append(email_from)
  62     msg.add_header('Cc', email_from)
  63 
  64 # 20. on ajoute une en-tête pour aider aux filtrages
  65 msg.add_header('X-Coda', X_CODA_VALUE)
  66 
  67 # 30. on préfixe le sujet actuel par '[CODA] '
  68 subject = msg['Subject']
  69 msg.replace_header('Subject', SUBJECT_PREFIX + subject)
  70 
  71 # 40. on remplace l'organisation par l'AuF
  72 del msg['ORGANISATON'] # non, ce n'est pas une faute de frappe…
  73 msg.add_header('Organization', ORGANIZATION)
  74 
  75 # 50. on renomme l'attachement
  76 
  77 # construction du nouveau nom de fichier
  78 # on tente de repérer le type et numéro de document
  79 try:
  80     m = re.match(DOC_PATTERN, subject)
  81     prefix, com_type, com_num = m.groups()
  82     new_filename = DOC_FORMAT % (com_type, int(com_num))
  83 # en cas d'échec on ignore le problème
  84 except Exception, err:
  85     print "Error: %s" % err
  86     new_filename = 'document-coda.pdf'
  87 
  88 # parcours des différentes parties du courriel
  89 for part in msg.walk():
  90     if part.get_content_type() != 'application/pdf':
  91         continue
  92     # adaptation du nom de fichier dans le Content-Type
  93     try:
  94         raw_param = part.get_param('name')
  95         param = email.utils.collapse_rfc2231_value(raw_param)
  96         if param == 'attachment.pdf':
  97             part.set_param('name', new_filename)
  98     except:
  99         pass
 100     # adaptation du nom de fichier dans le Content-Disposition
 101     try:
 102         raw_param = part.get_param('filename', header='Content-Disposition')
 103         param = email.utils.collapse_rfc2231_value(raw_param)
 104         if param == 'attachment.pdf':
 105             part.set_param('filename', new_filename, header='Content-Disposition')
 106     except:
 107         pass
 108 
 109 #--------------------------------------------------------------------------
 110 
 111 # on se connecte à Postfix via le port de retour de filtrage
 112 client = smtplib.SMTP('localhost', 10026)
 113 #client.set_debuglevel(1)
 114 
 115 # on tente d'envoyer le courriel modifié
 116 try:
 117     client.sendmail(email_from, email_to_list, msg.as_string())
 118 # en cas d'échec on demande à Postfix de considérer que c'est temporaire
 119 except Exception, err:
 120     print "Error: %s" % err
 121     sys.exit(EX_TEMPFAIL)
 122 
 123 # on termine proprement
 124 client.quit()
 125 sys.exit(0)

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 :

# 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 :

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