Artisan Numérique

/système/courrier/cyrus/imap/sieve/ Mettre en oeuvre des scripts Sieve dans Cyrus-Imap

Peu de monde connaît Sieve présent sur la majorité des serveurs IMAP, dont Dovecot et Cyrus. Pourtant il peut rendre d'immenses services comme trier automatiquement le spam, les publicités, ou même renvoyer un message d'absence lorsque vous êtes en vacance, le tout sans avoir à paramétrer le moindre client.

Peu de monde connaît Sieve présent sur la majorité des serveurs IMAP, dont Dovecot et Cyrus). Pourtant il peut rendre d'immenses services comme trier automatiquement le spam, les publicités, ou même renvoyer un message d'absence lorsque vous êtes en vacance, le tout sans avoir à paramétrer le moindre client.

Qu'est-ce que sieve ?

Sieve est un langage de filtrage des courriels normalisé qui permet d'automatiser à peu prés tous les tris imaginables. Pour mieux comprendre ce qu'il peut vous apporter, prenons un petit exemple "parlant" :

# Est-ce que spamassassin a mis le flag "SPAM" ?
if header :contains "X-Spam-Flag" "YES" {
    # on déplace le mail dans la benne à pourriels...
    fileinto "spams";
    stop;
}

Ici nous demandons à analyser l'en-tête de chaque message à la recherche de l'étiquette posée par un outil comme spamassassin. S'il s'agit bien d'un spam, le courriel est directement déplacé dans le dossier spams.

Sieve se place donc comme un excellent remplacement des classiques fonctions de filtrage présentes sur les clients de messagerie. Le gros avantage de son utilisation est que les règles de tris sont écrites une fois pour toutes et chaque client en bénéficie sans paramétrage.

Sieve dans cyrus

Dans l'outil Cyrus, sieve est intégré sous la forme d'un serveur qui écoute le port 2000. Il doit être activé en modifiant le fichier /etc/cyrus.conf de sorte à obtenir la ligne suivante :

sieve         cmd="timsieved" listen="localhost:sieve" prefork=1

Ici l'écoute est forcée sur localhost seulement (127.0.0.1). Pour rendre sieve accessible du monde extérieur il suffit d'enlever localhost: (et de mettre en place un cryptage !!).

Ensuite il faut simplement relancer cyrus pour constater que le serveur sieve est bien actif

root/etc/init.d/cyrus-imapd restart
Arrêt de cyrus-imapd :                                          [  OK  ]
Lancement de cyrus-imapd :                                      [  OK  ]
rootnetstat -anlp | grep 2000
tcp        0      0 127.0.0.1:2000              0.0.0.0:*                   LISTEN      25047/timsieved

Premier script Sieve

Pour commencer, il va falloir créer notre premier script. Reprenons pour cela notre exemple en ajoutant la clause require (les librairies) qui lui manquait :

require ["fileinto","envelope","reject","vacation","imapflags","relational","comparator-i;ascii-numeric","regex","notify"];
if header :contains "X-Spam-Flag" "YES" {
  fileinto "spams";
  stop;
}
mon_script.sieve

Pour ajouter un script il faut utiliser la commande sieveshell. Cet outil se connecte sur le serveur, s'authentifie et permet d'uploader un script, de downloader un script existant et de rendre un des scripts actif. Une sorte de FTP ultra basique :

rootsieveshell -u gaston -a gaston localhost
>put mon_script.sieve default
>list
default
>activate default
>list
default*
>quit
root#

A l'inverse, la commande get default mon_vieux_script.sieve va vider le script serveur default dans le fichier local mon_vieux_script.sieve.

Maintenant pour une édition régulière ce n'est pas très pratique. Le mieux serait un script bash qui permettrait d'éditer directement le script par défaut et de le sauver à la sortie. Pour y arriver, j'utilise une version améliorée par la communauté Debian de sieveshell. Pour l'utiliser il faut d'abord récupérer Shell.pm et sieveshell.pl. Ensuite vous devez remplacer les fichiers d'origines qui proviennent du paquet cyrus-imapd-utils pour sieveshellet perl-cyrus pour Shell.pm. Pour localiser ces fichiers, aidez-vous de la commande rpm -ql cyrus-imapd-utils et rpm -ql perl-cyrus.

Une fois le remplacement effectué, vous disposez d'un shell capable entre autre de prendre un mot de passe en paramètre. Le script est alors trivial :

#! /bin/sh
server=$1
user=$2
password=$3
sieveshell -a $user -u $user -p $password --exec "get default /tmp/default" $server
vi /tmp/default
sieveshell -a $user -u $user -p $password --exec "put /tmp/default default" $server
sievedit

Il s'utilise simplement en passant le nom de serveur en premier paramètre (ex. localhost), le nom de l'utilisateur en second (ex. gaston) et enfin son mot de passe. Évidement vous n'êtes pas obligé d'utiliser vi ;-)

Brève introduction à la syntaxe Sieve

Un script sieve commence par la déclaration des librairies pré-requises :

# importation des bibliothèques utiles
require ["reject", "fileinto", "comparator-i;ascii-numeric", "relational", "imapflags"];

Là c'est un peu "ceinture-bretelles", on inclut tout ou presque histoire d'être paré ;-). Ensuite, vous pouvez insérez vos règles sous la forme de tests :

if BLOC_CONDITION  {
   ACTION_1;
   ...
   ACTION_N;
}

Les actions les plus "utiles" sont :

discard qui entraîne la destruction du courriel. stop qui arrête ici le traitement. fileinto "NOM_BOITE" qui déplace le courriel dans une autre boîte IMAP.

BLOC_CONDITION quant à lui peut être une condition simple ou une composition de conditions simples liées entre elle par des ET (mot clef allof) ou des OU (mot clef anyof) :

# condition simple
if CONDITION_SIMPLE {
  # liste des actions
}

# conditions liées par OU
if anyof (CONDITION_SIMPLE_1, CONDITION_SIMPLE_2, ..., CONDITION_SIMPLE_N) {
  # liste des actions
}

# conditions liées par ET
if allof (CONDITION_SIMPLE_1, CONDITION_SIMPLE_2, ..., CONDITION_SIMPLE_N) {
  # liste des actions
}

Une condition simple se compose d'un objet sur lequel porte la condition, d'un type de condition et de paramètres. La syntaxe globale d'une condition simple est donc (notez bien le : et le not optionnel) :

[not] OBJET_CONDITION :TYPE_CONDITION PARAMÈTRE_CONDITION_1 ... PARAMÈTRE_CONDITION_N

Les objets souvent utilisés sont :

header Il s'agit des champs de l'en-tête du message (Received, Subject, etc...) size La taille du message

Les paramètres des conditions dépendent quant à eux beaucoup du type de condition. Cela peut être une chaîne (ex. "CHAINE"), une taille (ex. 1M pour 1mo), un tableau de chaînes (ex. ["CHAINE_1","CHAINE_2",...,"CHAINE_N"]), etc...

Enfin les grand types de condition sont :

objet contains P V qui vérifie que l'objet à au moins une valeur V dans sa partie P. P et V pouvant être des chaînes ou des tableaux. objet matches P V Comme contains mis à part que V peut contenir des chaînes avec des "jockers" (ex. "*@karma-lab.net") objet over V Qui vérifie que la valeur de l'objet est "supérieur à" V

Quelques exemples

Pour que tout cela passe mieux, voyons quelques exemple classiques :

if header :contains ["List-Id"] ["xorg.lists.freedesktop.org"] {
  fileinto "Mailling-Listes.xorg";
  stop;
}

On vérifie ici que la courriel est membre d'une mailling-liste donnée (champ List-Id du header) et on le déplace dans un dossier spécifique si tel est le cas.

if header :contains ["From"] ["mon_client.com"] {
  fileinto "mon client";
  stop;
}

Si le courriel vient d'une adresse donnée, on le place dans un dossier spécial.

if header :contains ["To"] ["cdiscount@gaston.net"] {
  fileinto "Publicites"; 
  stop;
}

Si le courriel est destiné à quelqu'un en particulier.

if header :contains ["Received"] ["12.34.56.78"]  {
  fileinto "Societe Betadure";
  stop;
}

Si le courriel a été reçu via un serveur SMTP (relais) particulier.

if size :over 1M {
  fileinto "gros courriels";
  stop;
}

Si le message est très gros, on le place dans un dossier particulier...

Les dossiers

Si vos messages ne sont pas filtrer et donc qu'une des règles semble échouer, il faut aller voir du côté des logs de votre serveur IMAP. Il se peut que vous ayez simplement un problème dans le nommage du dossier pour la commande fileinto du genre Invalid mailbox name ou mailbox doesn't exists. Les différentes pistes sont les suivantes :

  • L'encodage des accents. Certains serveur demande à ce que répondeur soit écrit deux.r&AOk-pondeur; (ce n'est pas le cas de cyrus).
  • Le séparateur de dossiers. Selon les versions de cyrus, le séparateur doit être un . ou un /.
  • Racine de la boîte. Dans certaines version de cyrus les dossiers doivent commencer par la racine INBOX.
  • Racine des dossiers partagés. Selon le paramétrage de cyrus, la racine des dossiers partagée est la même que les dossiers standards ou doit être Shared Folders.

Conclusion

Voilà, fin du petit tour du petit langage sieve qui rend finalement de bien grands services.