<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Artisan Numérique</title>
  <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net/node/1083"/>
  <link rel="self" type="application/atom+xml" href="http://artisan.karma-lab.net/node/1083/atom/feed"/>
  <id>http://artisan.karma-lab.net/node/1083/atom/feed</id>
  <updated>2008-11-04T11:34:48+01:00</updated>
  <entry>
    <title>Mettre en oeuvre des scripts Sieve dans Cyrus-Imap</title>
    <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net/node/1083" />
    <id>http://artisan.karma-lab.net/node/1083</id>
    <published>2008-11-04T01:40:43+01:00</published>
    <updated>2008-11-04T11:34:48+01:00</updated>
    <author>
      <name>Ulhume</name>
    </author>
    <category term="Serveurs" />
    <category term="OK" />
    <category term="Planet Libre" />
    <category term="Tutoriel" />
    <summary type="html"><![CDATA[<p>
  Peu de monde connaît <kbd>Sieve</kbd> présent sur la majorité des serveurs <kbd>IMAP</kbd>, dont <a class='external' target='_blank' href='http://fr.wikipedia.org/wiki/Dovecot' >Dovecot</a> et <a class='external' target='_blank' href='http://fr.wikipedia.org/wiki/Cyrus_(logiciel)' >Cyrus</a>. 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.
</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>
  Peu de monde connaît <kbd>Sieve</kbd> présent sur la majorité des serveurs <kbd>IMAP</kbd>, dont <a class='external' target='_blank' href='http://fr.wikipedia.org/wiki/Dovecot' >Dovecot</a> et <a class='external' target='_blank' href='http://fr.wikipedia.org/wiki/Cyrus_(logiciel)' >Cyrus</a>. 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.
</p>
<!--break-->

	<a name='chapter_1'></a>
  <h2>Qu'est-ce que sieve ?</h2>
	
<p>
  Sieve est un langage de filtrage des courriels <a class='external' target='_blank' href='http://rfc-ref.org/RFC-TEXTS/3028/index.html' >normalisé</a> 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" :
  
  <div class='code-block code-block-fragment'>
  <div class='container'>
  <span class="co2"># Est-ce que spamassassin a mis le flag &quot;SPAM&quot; ?</span><br />
<span class="kw1">if</span> header <span class="sy0">:</span>contains <span class="st0">&quot;X-Spam-Flag&quot;</span> <span class="st0">&quot;YES&quot;</span> <span class="br0">&#123;</span><br />
&nbsp; &nbsp; <span class="co2"># on déplace le mail dans la benne à pourriels...</span><br />
&nbsp; &nbsp; fileinto <span class="st0">&quot;spams&quot;</span>;<br />
&nbsp; &nbsp; stop;<br />
<span class="br0">&#125;</span>
  </div>
  
  </div>
</p>
<p>
  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 <kbd>spams</kbd>. 
</p>
<p>
  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. 
</p>



	<a name='chapter_2'></a>
  <h2>Sieve dans cyrus</h2>
	
<p>
  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 <kbd>/etc/cyrus.conf</kbd> de sorte à obtenir la ligne suivante :
  
  <div class='code-block code-block-fragment'>
  <div class='container'>
  sieve &nbsp; &nbsp; &nbsp; &nbsp; cmd=&quot;timsieved&quot; listen=&quot;localhost:sieve&quot; prefork=1
  </div>
  
  </div>
</p>
<p>
  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 <kbd>localhost:</kbd> (et de mettre en place un cryptage !!). 
</p>
<p>
  Ensuite il faut simplement relancer cyrus pour constater que le serveur sieve est bien actif
  
  <div class='code-block code-block-traces'>
  <div class='container'>
  <div class='command'><span class='prompt'>root#</span>/etc/init.d/cyrus-imapd restart</div><div class='result'>Arrêt de cyrus-imapd :                                          [  OK  ]</div><div class='result'>Lancement de cyrus-imapd :                                      [  OK  ]</div><div class='command'><span class='prompt'>root#</span>netstat -anlp | grep 2000</div><div class='result'>tcp        0      0 127.0.0.1:2000              0.0.0.0:*                   LISTEN      25047/timsieved</div><div class='command'><span class='prompt'>root#</span><span class='cursor'>&nbsp;</span></div>
  </div>
  
  </div>
</p>

	<a name='chapter_3'></a>
  <h2>Premier script Sieve</h2>
	
<p>
  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 :
  
  <div class='code-block code-block-fragment'>
  <div class='container'>
  require <span class="br0">&#91;</span><span class="st0">&quot;fileinto&quot;</span>,<span class="st0">&quot;envelope&quot;</span>,<span class="st0">&quot;reject&quot;</span>,<span class="st0">&quot;vacation&quot;</span>,<span class="st0">&quot;imapflags&quot;</span>,<span class="st0">&quot;relational&quot;</span>,<span class="st0">&quot;comparator-i;ascii-numeric&quot;</span>,<span class="st0">&quot;regex&quot;</span>,<span class="st0">&quot;notify&quot;</span><span class="br0">&#93;</span>;<br />
<span class="kw1">if</span> header <span class="sy0">:</span>contains <span class="st0">&quot;X-Spam-Flag&quot;</span> <span class="st0">&quot;YES&quot;</span> <span class="br0">&#123;</span><br />
&nbsp; fileinto <span class="st0">&quot;spams&quot;</span>;<br />
&nbsp; stop;<br />
<span class="br0">&#125;</span>
  </div>
  <div class='caption'>mon_script.sieve</div>
  </div>
</p>
<p>
  Pour ajouter un script il faut utiliser la commande <kbd>sieveshell</kbd>. 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 :

  <div class='code-block code-block-traces'>
  <div class='container'>
  <div class='command'><span class='prompt'>root#</span>sieveshell -u gaston -a gaston localhost</div><div class='command'><span class='prompt'>></span>put mon_script.sieve default</div><div class='command'><span class='prompt'>></span>list</div><div class='result'>default</div><div class='command'><span class='prompt'>></span>activate default</div><div class='command'><span class='prompt'>></span>list</div><div class='result'>default*</div><div class='command'><span class='prompt'>></span>quit</div><div class='command'><span class='prompt'>root#</span></div><div class='command'><span class='prompt'>root#</span><span class='cursor'>&nbsp;</span></div>
  </div>
  
  </div>
</p>
<p>
  A l'inverse, la commande <kbd>get default mon_vieux_script.sieve</kbd> va vider le script serveur <kbd>default</kbd>
dans le fichier local <kbd>mon_vieux_script.sieve</kbd>.
</p>
<p>
  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 <kbd>sieveshell</kbd>. Pour l'utiliser il faut d'abord récupérer <a class='external' target='_blank' href='http://artisan.karma-lab.net/files/artisan/Shell.pm' >Shell.pm</a>  et <a class='external' target='_blank' href='http://artisan.karma-lab.net/files/artisan/sieveshell.pl.txt' >sieveshell.pl</a>. Ensuite vous devez remplacer les fichiers d'origines qui proviennent du paquet <kbd>cyrus-imapd-utils</kbd> pour <kbd>sieveshell</kbd>et <kbd>perl-cyrus</kbd> pour <kbd>Shell.pm</kbd>. Pour localiser ces fichiers, aidez-vous de la commande <kbd>rpm -ql cyrus-imapd-utils</kbd> et <kbd>rpm -ql perl-cyrus</kbd>. 
</p>
<p>
  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 :
  
  <div class='code-block code-block-fragment'>
  <div class='container'>
  <span class="co0">#! /bin/sh</span><br />
<span class="re2">server=</span>$<span class="nu0">1</span><br />
<span class="re2">user=</span>$<span class="nu0">2</span><br />
<span class="re2">password=</span>$<span class="nu0">3</span><br />
sieveshell -a <span class="re1">$user</span> -u <span class="re1">$user</span> -p <span class="re1">$password</span> --exec <span class="st0">&quot;get default /tmp/default&quot;</span> <span class="re1">$server</span><br />
vi <span class="sy0">/</span>tmp<span class="sy0">/</span>default<br />
sieveshell -a <span class="re1">$user</span> -u <span class="re1">$user</span> -p <span class="re1">$password</span> --exec <span class="st0">&quot;put /tmp/default default&quot;</span> <span class="re1">$server</span>
  </div>
  <div class='caption'>sievedit</div>
  </div>
</p>
<p>
  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 <kbd>vi</kbd> <img src="http://artisan.karma-lab.net/sites/all/modules/contrib/smileys/packs/crystal/wink2.gif" title="Wink" alt="Wink" class="smiley-content"/>
</p>


	<a name='chapter_4'></a>
  <h2>Brève introduction à la syntaxe Sieve</h2>
	
<p>
   Un script sieve commence par la déclaration des librairies pré-requises :
  
  <div class='code-block code-block-fragment'>
  <div class='container'>
  <span class="co2"># importation des bibliothèques utiles</span><br />
require <span class="br0">&#91;</span><span class="st0">&quot;reject&quot;</span>, <span class="st0">&quot;fileinto&quot;</span>, <span class="st0">&quot;comparator-i;ascii-numeric&quot;</span>, <span class="st0">&quot;relational&quot;</span>, <span class="st0">&quot;imapflags&quot;</span><span class="br0">&#93;</span>;
  </div>
  
  </div>
</p>
<p>
 Là c'est un peu "ceinture-bretelles", on inclut tout ou presque histoire d'être paré <img src="http://artisan.karma-lab.net/sites/all/modules/contrib/smileys/packs/crystal/wink2.gif" title="Wink" alt="Wink" class="smiley-content"/>. Ensuite, vous pouvez insérez vos règles sous la forme de tests :
  
  <div class='code-block code-block-fragment'>
  <div class='container'>
  <span class="kw1">if</span> BLOC_CONDITION &nbsp;<span class="br0">&#123;</span><br />
&nbsp; &nbsp;ACTION_1<span class="sy0">;</span><br />
&nbsp; &nbsp;...<br />
&nbsp; &nbsp;ACTION_N<span class="sy0">;</span><br />
<span class="br0">&#125;</span>
  </div>
  
  </div>
</p>
<p>
  Les actions les plus "utiles" sont :
  <dl>
    <dt>discard</dt>
    <dd>qui entraîne la destruction du courriel.</dd>
    <dt>stop</dt>
    <dd>qui arrête ici le traitement.</dd>
    <dt>fileinto "NOM_BOITE"</dt>
    <dd>qui déplace le courriel dans une autre boîte IMAP.</dd>
  </dl>
</p>
<p>
  <kbd>BLOC_CONDITION</kbd> quant à lui peut être une condition simple ou une composition de conditions simples liées entre elle par des ET (mot clef <kbd>allof</kbd>) ou des OU (mot clef <kbd>anyof</kbd>) :
  
  <div class='code-block code-block-fragment'>
  <div class='container'>
  <span class="co1"># condition simple</span><br />
<span class="kw1">if</span> CONDITION_SIMPLE <span class="br0">&#123;</span><br />
&nbsp; <span class="co1"># liste des actions</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="co1"># conditions liées par OU</span><br />
<span class="kw1">if</span> anyof <span class="br0">&#40;</span>CONDITION_SIMPLE_1<span class="sy0">,</span> CONDITION_SIMPLE_2<span class="sy0">,</span> ...<span class="sy0">,</span> CONDITION_SIMPLE_N<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; <span class="co1"># liste des actions</span><br />
<span class="br0">&#125;</span><br />
<br />
<span class="co1"># conditions liées par ET</span><br />
<span class="kw1">if</span> allof <span class="br0">&#40;</span>CONDITION_SIMPLE_1<span class="sy0">,</span> CONDITION_SIMPLE_2<span class="sy0">,</span> ...<span class="sy0">,</span> CONDITION_SIMPLE_N<span class="br0">&#41;</span> <span class="br0">&#123;</span><br />
&nbsp; <span class="co1"># liste des actions</span><br />
<span class="br0">&#125;</span>
  </div>
  
  </div>
</p>
<p>
  Une condition simple se compose d'un <b>objet</b> sur lequel porte la condition, d'un <b>type</b> de condition et de <b>paramètres</b>. La syntaxe globale d'une condition simple est donc (notez bien le <kbd>:</kbd> et le <kbd>not</kbd> optionnel) :
  
  <div class='code-block code-block-fragment'>
  <div class='container'>
  [not] OBJET_CONDITION :TYPE_CONDITION PARAMÈTRE_CONDITION_1 ... PARAMÈTRE_CONDITION_N &nbsp;
  </div>
  
  </div>  
</p>
<p>
  Les objets souvent utilisés sont :
  <dl>
    <dt>header</dt><dd>Il s'agit des champs de l'en-tête du message (Received, Subject, etc...)</dd>
    <dt>size</dt><dd>La taille du message</dd>
  </dl> 
</p>
<p>
  Les paramètres des conditions dépendent quant à eux beaucoup du type de condition. Cela peut être une chaîne (ex. <kbd>"CHAINE"</kbd>), une taille (ex. <kbd>1M</kbd> pour 1mo), un tableau de chaînes (ex. <kbd>["CHAINE_1","CHAINE_2",...,"CHAINE_N"]</kbd>), etc...
</p>
<p>
  Enfin les grand types de condition sont :
  <dl>
    <dt>objet <kbd>contains</kbd> P V</dt><dd>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.</dd>
    <dt>objet <kbd>matches</kbd> P V</dt><dd>Comme <kbd>contains</kbd> mis à part que V peut contenir des chaînes avec des "jockers" (ex. "*@karma-lab.net")</dd>
    <dt>objet <kbd>over</kbd> V</dt><dd>Qui vérifie que la valeur de l'objet est "supérieur à" V</dd>
  </dl> 
</p>


	<a name='chapter_5'></a>
  <h2>Quelques exemples</h2>
	
<p>
  Pour que tout cela passe mieux, voyons quelques exemple classiques :
  
  <div class='code-block code-block-fragment'>
  <div class='container'>
  <span class="kw1">if</span> header <span class="sy0">:</span>contains <span class="br0">&#91;</span><span class="st0">&quot;List-Id&quot;</span><span class="br0">&#93;</span> <span class="br0">&#91;</span><span class="st0">&quot;xorg.lists.freedesktop.org&quot;</span><span class="br0">&#93;</span> <span class="br0">&#123;</span><br />
&nbsp; fileinto <span class="st0">&quot;Mailling-Listes.xorg&quot;</span><span class="sy0">;</span><br />
&nbsp; stop<span class="sy0">;</span><br />
<span class="br0">&#125;</span>
  </div>
  
  </div>
</p>
<p>
  On vérifie ici que la courriel est membre d'une mailling-liste donnée (champ <kbd>List-Id</kbd> du header) et on le déplace dans un dossier spécifique si tel est le cas.<br>
  
  <div class='code-block code-block-fragment'>
  <div class='container'>
  <span class="kw1">if</span> header <span class="sy0">:</span>contains <span class="br0">&#91;</span><span class="st0">&quot;From&quot;</span><span class="br0">&#93;</span> <span class="br0">&#91;</span><span class="st0">&quot;mon_client.com&quot;</span><span class="br0">&#93;</span> <span class="br0">&#123;</span><br />
&nbsp; fileinto <span class="st0">&quot;mon client&quot;</span><span class="sy0">;</span><br />
&nbsp; stop<span class="sy0">;</span><br />
<span class="br0">&#125;</span>
  </div>
  
  </div>
</p>
<p>
  Si le courriel vient d'une adresse donnée, on le place dans un dossier spécial.<br>
  
  <div class='code-block code-block-fragment'>
  <div class='container'>
  <span class="kw1">if</span> header <span class="sy0">:</span>contains <span class="br0">&#91;</span><span class="st0">&quot;To&quot;</span><span class="br0">&#93;</span> <span class="br0">&#91;</span><span class="st0">&quot;<script>document.write(String.fromCharCode(60,97,32,104,114,101,102,61,39,109,97,105,108,116,111,58,99,100,105,115,99,111,117,110,116,64,103,97,115,116,111,110,46,110,101,116,39,62,99,100,105,115,99,111,117,110,116,64,103,97,115,116,111,110,46,110,101,116,60,47,97,62));</script>&quot;</span><span class="br0">&#93;</span> <span class="br0">&#123;</span><br />
&nbsp; fileinto <span class="st0">&quot;Publicites&quot;</span><span class="sy0">;</span> <br />
&nbsp; stop<span class="sy0">;</span><br />
<span class="br0">&#125;</span>
  </div>
  
  </div>
</p>
<p>
  Si le courriel est destiné à quelqu'un en particulier.<br>
  
  <div class='code-block code-block-fragment'>
  <div class='container'>
  <span class="kw1">if</span> header <span class="sy0">:</span>contains <span class="br0">&#91;</span><span class="st0">&quot;Received&quot;</span><span class="br0">&#93;</span> <span class="br0">&#91;</span><span class="st0">&quot;12.34.56.78&quot;</span><span class="br0">&#93;</span> &nbsp;<span class="br0">&#123;</span><br />
&nbsp; fileinto <span class="st0">&quot;Societe Betadure&quot;</span><span class="sy0">;</span><br />
&nbsp; stop<span class="sy0">;</span><br />
<span class="br0">&#125;</span>
  </div>
  
  </div>
</p>
<p>
  Si le courriel a été reçu via un serveur SMTP (relais) particulier.<br>
  
  <div class='code-block code-block-fragment'>
  <div class='container'>
  <span class="kw1">if</span> size <span class="sy0">:</span>over 1M <span class="br0">&#123;</span><br />
&nbsp; fileinto <span class="st0">&quot;gros courriels&quot;</span><span class="sy0">;</span><br />
&nbsp; stop<span class="sy0">;</span><br />
<span class="br0">&#125;</span>
  </div>
  
  </div>
</p>
<p>
  Si le message est très gros, on le place dans un dossier particulier...
</p>

	<a name='chapter_6'></a>
  <h2>Les dossiers</h2>
	
<p>
  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 <kbd>fileinto</kbd> du genre <kbd>Invalid mailbox name</kbd> ou <kbd>mailbox doesn't exists</kbd>. Les différentes pistes sont les suivantes :
<ul>
  <li>L'encodage des accents. Certains serveur demande à ce que <kbd>répondeur</kbd> soit écrit <kbd>deux.r&AOk-pondeur</kbd> (ce n'est pas le cas de cyrus).</li>
  <li>Le séparateur de dossiers. Selon les versions de cyrus, le séparateur doit être un <kbd>.</kbd> ou un <kbd>/</kbd>.</li>
  <li>Racine de la boîte. Dans certaines version de cyrus les dossiers doivent commencer par la racine <kbD>INBOX</kbd>.</li>
  <lI>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 <kbd>Shared Folders</kbd>.</li>
</ul>
</p>


	<a name='chapter_7'></a>
  <h2>Conclusion</h2>
	
<p>
  Voilà, fin du petit tour du petit langage sieve qui rend finalement de bien grands services. 
</p>
    ]]></content>
  </entry>
</feed>
