Mettre en oeuvre des scripts Sieve dans Cyrus-Imap
Le 8 novembre 2006, à 1:37 par Ulhume...

sieve est malheureusement un grand oublié des serveurs IMAP. Peu de monde connaît ce petit langage qui pourtant 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. Voici donc un petit panorama pour régler cette injustice.

Pour mieux comprendre ce que Sieve peut vous apporter, un petit exemple parlant :
  1. require ["reject", "fileinto", "comparator-iascii-numeric", "relational", "imapflags"]
  2.  
  3. if header :contains ["X-Spam-Level"] ["**"]
  4. {
  5.     fileinto "INBOX.Spams"
  6.     stop
  7. }

D'un coup on comprends mieux à quoi cet outil peut servir. Sieve est donc un langage permettant divers opérations sur les courriels reçus par le serveur IMAP. Dans notre exemple, il analyse le header de chaque message et s'il s'agit d'un spam, le pause directement dans la sous-boîte spams. En gros, sieve permet de faire à peu près tout ce que permettent les classiques fonctions de filtrage des clients courriels, ais de manière automatique, au niveau du serveur lui-même. Ainsi, grâce à des scripts sieve bien construits, le mail est directement ventilé dans les sous boîtes, même avec un accès de type webMail.

Sieve dans cyrus

D'un point de vue physique, sieve est un sous-serveur de cyrus placé sur le port 2000, il est activé dans le fichier /etc/cyrus.conf qui doit contenir la ligne suivante :

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

Ensuite il faut simplement relancer cyrus (service cyrus-imapd restart) pour constate l'arrivé du serveur sieve (netstat -anlp | grep 2000).

Création des scripts

Comme sieve est un serveur, vous pouvez acceder aux scripts à distance. Le moyen le plus simple que j'ai trouvé c'est un module KIOSLAVE dédié de KDE. Pour ceux qui ne connaissent l'architecture KIO de KDE est proprement géniale. En effet, KDE fournit une interface de type plugin pour prendre en charge les protocoles de lecture et décriture de fichier ou de dossier. Par exemple, si vous prenez konqueror et que vous tappez dans l'url imap://mon_login@mon_serveur_imap vous avez accès, après autentification, à vos mails en lecture...De la même manière vous pouvez accedez aux scritps sieve d'un compte par l'url sieve://mon_login_sieve@.

Logiquement donc, konqueror devrait après autentification afficher une page vide. Il n'y a en effet pour l'instant aucun script pour ce compte imap. Pour en créer un, clique droit et nouveau document texte que vous appellerez default.

Maintenant, très simplement, vous double-cliqué sur ce fichier default, qui s'ouvre sans broncher avec un éditeur de texte comme kwrite et vous pouvez éditer votre script. A chaque sauvegarde, votre travail est envoyé sur le serveur sieve, compilé et immédiatement actif. S'il y a une erreur de syntaxe, kwrite vous donnera même la ligne de l'erreur. Magique non ?

Petite vérification d'usage pour vérifier que le script est bien actif :

  1. sieveshell -u toto -a toto serveur_sieve
  2. # affichage du prompt sieve, on veut la liste des scripts de toto
  3. list
  4. #    default
  5.  
  6. # on constate que le script n'est pas actif !
  7. activate default
  8. list
  9.  
  10. quit

Brêve explication de la syntaxe

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

  1. # importation des bibliothèques utiles
  2. require ["reject", "fileinto", "comparator-iascii-numeric", "relational", "imapflags"]

Ensuite, vous pouvez inserez vos règles sous la forme de tests :

  1. if BLOC_CONDITION
  2. {
  3.    ACTION_1
  4.    ...
  5.    ACTION_N
  6. }

Les actions les plus "utiles" sont :

discard
qui entraine la destruction du courriel ayant vérifié la condition.
stop
qui, si la conditon est vérifié, demande à sieve de ne plus regarder les autres règles pour le message.
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 (allof) ou des OU (anyof) :

  1. # condition simple
  2. if CONDITION_SIMPLE
  3. {
  4.   # liste des actions
  5. }
  6.  
  7. # conditions liées par OU
  8. if anyof (CONDITION_SIMPLE_1, CONDITION_SIMPLE_2, ..., CONDITION_SIMPLE_N)
  9. {
  10.   # liste des actions
  11. }
  12.  
  13. # conditions liées par ET
  14. if allof (CONDITION_SIMPLE_1, CONDITION_SIMPLE_2, ..., CONDITION_SIMPLE_N)
  15. {
  16.   # liste des actions
  17. }

une condition simple se compose de l'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 PARAMETRE_CONDITION_1 ... PARAMETRE_CONDITION_N  

Les objets de conditions classiques sont :

header
La condition porte sur un 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 chaine ("CHAINE"), une taille (1M pour 1mo), un tableau de chaines (["CHAINE_1","CHAINE_2",...,"CHAINE_N"]), etc...

enfin les grand types de condition sont :

contains P1 P2
Vérifie que l'objet est contenu à au moins une valeur de P1 dans la P2. P1 et P2 peuvent être des chaînes pu des tableaux.
matches P1 P2
Comme containes mis à part que P2 peut contenur des chaines avec des jocker (ex. "*@karma-lab.net")
over P1
Opérateur "superieur à" P1

Application, une gestion fine des SPAMS

L'exemple qui suit est une mise en application des principes énoncés plus haut. L'objectif est de filrer les spams flaggés par spamassassin selon 3 niveaux. Les spams de niveau 3 et plus seront détruits, les spams de niveau 2 et les erreurs de trasport de courries lié à l'usurpation d'adresse sont placés dans le dossier "spams", enfin les spams de niveau 1, sont placés dans le dossier "spams.ham".

  1. # spamassassin a taggé le mail de niveau 3, on le détruit donc directement
  2. if header :contains ["X-Spam-Level"] ["***"]
  3. {
  4.   # supression du mail
  5.   discard
  6.  
  7.   # c'est fini pour lui, on ne cherche pas à exécuter plus loin...  
  8.   stop
  9. }
  10.  
  11. # Par un composition de conditions de type OU, on cherche maintenant à filtre les autres
  12. # spams pour les placer dans la boite "spam"
  13. if anyof
  14. (
  15.    # si le spam assassin a taggé ce mail de niveau 3
  16.    header :contains ["X-Spam-Level"] ["**"] ,  
  17.  
  18.    # si le mail est une erreur de distribution de courrier
  19.    allof
  20.    (
  21.       # sujet typique d'une erreur de distribution, on utilise "matches" pour bénéficier de *
  22.       header :matches "Subject" ["failure notice", "Delivery Status Notification*","Returned Mail:*"],
  23.       # on évite de jeter les erreurs de distributions de notre propre FAI
  24.       not header :matches "Received" ["*.mon_fai.fr"]
  25.    )
  26. )
  27. {
  28.   # on déplace le courriel dans la boîte des SPAMS
  29.   fileinto "INBOX.Spams"
  30.  
  31.   # le traitement s'arrête ici
  32.   stop
  33. }    
  34.  
  35. # Le spam est de niveau 1, on peut avoir un doute, on le place donc dans le dossier spams.ham
  36. if header :contains ["X-Spam-Level"] ["*"]
  37. {
  38.   # on déplace le courriel dans la boîte des SPAMS
  39.   fileinto "INBOX.Spams.ham"
  40.  
  41.   # le traitement s'arrête ici
  42.   stop
  43. }

Ce bref apperçu de la syntaxe sieve permet surtout de saisir la puissance du filtrage proposé.

Recettes sieve

Maintenant quelques exemples simples de tests sieve? N'oubliez pas de mettre la ligne require comme indique plus.

On vérifie ici que la courriel est membre d'une mailling-liste (tag List-Id) et selons les cas, on ventile dans différents dossiers :

  1. if header :contains ["List-Id"] ["xorg.lists.freedesktop.org"]
  2. {
  3.   fileinto "INBOX.Mailling-Listes.xorg"
  4.   stop
  5. }

Si le courriel vient d'une adresse donnée, on le place dans un dossier spécial. Noter que "contains" implique *@mon_client.com :

  1. if header :contains ["From"] ["mon_client.com"]
  2. {
  3.   fileinto "INBOX.Mailling-Listes.Ma Societe.mon client"
  4.   stop
  5. }

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

  1. if header :contains ["To"] [""]
  2. {
  3.   fileinto "INBOX.Mailling-Listes.Publicites"
  4.   stop
  5. }

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

  1. if header :contains ["Received"] ["194.211.211.18"]
  2. {
  3.   fileinto "INBOX.Ma Societe"
  4.   stop
  5. }

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

  1. if size :over 1000K
  2. {
  3.   fileinto "INBOX.gros courriels"
  4.   stop
  5. }

Commentaires

Jean-François , le 10 September, 2007 - 17:48

Je suis en train de mettre en place un serveur smtp Postfix couplé à un serveur imap Cyrus. Autant dire que je suis heureux de lire cet article. Chapeau.

Une question : les filtres Sieve sont-ils forcément propres à UN utilisateur ? J'aimerais avoir un filtre qui classe certains messages arrivant dans une boîte, dans un dossier d'une autre boîte. Possible ?

Ulhume, le 10 September, 2007 - 22:45

Ravis que cela puisse servir (vu le peu de commentaires, dés fois j'ai des doutes Wink

Pour un script global, pour autant que je m'en souvienne, il suffit d'utiliser sieveshell avec l'utilisateur cyrus. Le script sera alors exécuté à l'échelle globale.

Dit moi si cela fonctionne !

Jean-François , le 11 September, 2007 - 10:27

Ben non justement ça marche pas pour un script global, c'est ce que j'avais essayé mais ça marche pas Frown

Autre chose qui serait de grande utilité : un tuto pour une installation complète Postfix / MySQL / cyrus-sasl / cyrus-imapd / Clamav / DSPAM / sieve le tout gérant des domaines virtuels. J'en trouve plein avec courier-imap mais cyrus-imap : désert. Ou alors sans le cas des domaines virtuels... avec un LDAP à la place de MySQL ça irait aussi... comme quoi je suis pas si difficile que ça !

Ulhume, le 11 September, 2007 - 11:09

C'est plus un tuto mais un livre qu'il faudrait là Smiling

Sur le site en cherchant tu devrais trouver "postfix/spamd", "postfix/cyrus/ssl", et "Cyrus/Sieve". Si tu ne trouves pas, dis-le moi, je te chercherais les pointeurs.

Pour ce qui est de postfix, j'avais le projet d'en écrire un mais c'est un monde à part entière ce soft. J'ai jamais eu le courage Smiling Et en plus cela demanderais une maîtrise du bignou que je suis bien loin d'avoir... Sinon, j'ai pas vraiment exploité les domaines virtuels, juste une attaque à la sauvage postfix/virtual.cf. Donc rien de bien serieux.

Pour MySql je ne sais pas, j'utilise pas.

Poster un nouveau commentaire

Le contenu de ce champ est gardé secret et ne sera pas montré publiquement.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • To highlight piece of code, just surround them with <code type="language"> Your code &tl;/code>>. Language can be java,c++,bash,etc... Everything Geshi support.
  • Les lignes et les paragraphes vont à la ligne automatiquement.
  • Textual smileys will be replaced with graphical ones.
  • Les adresses de pages web et de messagerie électronique sont transformées en liens automatiquement.

Plus d'informations sur les options de formatage

Connexion utilisateur
Les derniers bavardages...