[couverture de Linux Magazine 79]

Perles de Mongueurs (19)

Article publié dans Linux Magazine 79, janvier 2006.

[+ del.icio.us] [+ Developers Zone] [+ Bookmarks.fr] [Digg this] [+ My Yahoo!]

La perle de ce mois-ci a été rédigée par Sébastien Aperghis-Tramoni, de Marseille.pm.

Récupérer ses mails

Avec un titre pareil, vous allez vous dire que ça part mal : pour récupérer ses mails, on utilise son client mail (quel qu'il soit), et ça marche très bien. Exact, je préfère ça aussi. Mais récemment, suite à un déménagement, je me suis retrouvé coupé de tout accès au net, et donc dans l'impossibilité de récupérer mes mails. Or je reçois environ 200 mails par jour et autant de spam. Et le quota sur Free n'est que de 25 Mo.

Donc au bout de d'un mois, mon compte s'est dangereusement rapproché de la limite supérieure, et il me fallait récupérer mes mails avant que les suivants ne soient refusés. La réponse toute faite de la plupart des personnes est d'utiliser Fetchmail. Sauf que Fetchmail tient absolument à renvoyer les mails sur un serveur qui se chargera de les délivrer (un MDA, Mail Delivery Agent). C'est une solution, mais je voulais simplement récupérer mes mails, les stocker tous dans un simple fichier au classique format mbox. A priori, Fetchmail ne permet pas de faire ça. Voici donc un petit script Perl pour récupérer les mails par POP3.

    #!/usr/bin/perl
    use strict;
    use Email::Simple;
    use Net::POP3;

    sub usage { die "usage: getmail file\n" }

    my $server = 'pop.free.fr';
    my $login  = 'maddingue';
    my $passwd = '5eckr3t';

    my $mbox = shift or usage();

    $| = 1;

    print "connecting to $server.. ";
    my $pop = new Net::POP3 $server
      or die "error: can't connect to $server: $!\n";
    print "ok\n";

    $pop->login($login, $passwd);
    $pop->ok or die "error: wrong username or password\n";

    my ($undeleted, $size) = $pop->popstat;
    my $last = $pop->last;

    print "mail box size: $size\n",
          "$undeleted unread mail(s).\n",
          "last read mail was number $last\n\n";

    open(MBOX, '>', $mbox) or die "error: can't write '$mbox': $!\n";
    my $fetched = 0;
    for my $num (1..$undeleted) {
        my $msg = $pop->get($num);
        next unless ref $msg;
        mbox_envelope($msg);
        print MBOX @$msg, $/;
        $fetched += $pop->list($num);
        printf "\rfetched %2.0f%%", $fetched*100/$size;
        $pop->delete($num);
    }
    close(MBOX);
    print $/;
    $pop->quit;

    sub mbox_envelope {
        my $text = $_[0];
        my $msg = new Email::Simple join '', @$text;
        my $date = $msg->header('Date');
        my $from = $msg->header('Return-Path');
        $from = $msg->header('From') unless $from;
        $from =~ s/[<>]//g;
        $from =~ /(\S+\@\S+)/ and $from = $1;
        unshift @$text, "From $from $date\n"
    }

Vous reconnaîtrez dans le début du script celui présenté il y a un an et demi pour vérifier son compte POP3. Il est augmenté d'une boucle qui récupère les messages l'un après l'autre et les stocke dans le fichier dont le nom a été donné en argument du script. Détaillons son déroulement.

Après s'être connecté ($pop = new Net::POP3 $server), authentifié ($pop->login($login, $passwd)) et avoir récupéré le nombre de mails à lire ($pop->popstat), une boucle se charge de traiter chaque message. À noter qu'elle commence à 1 et non 0. On télécharge chaque message avec $pop->get($num), qui le renvoie sous la forme d'une référence à tableau de lignes. On le passe à la fonction mbox_envelope() dont le rôle est d'ajouter une ligne au format From EXPEDITEUR DATE.

Cette ligne, dite d'enveloppe, contient l'adresse de l'expéditeur telle qu'elle a été donnée au serveur mail d'envoi avec la commande SMTP MAIL FROM:, suivie de la date d'envoi. On la reconstitue en prenant la valeur du champ Return-Path:, s'il est présent, qui contient justement cette adresse, et sinon en prenant celle du champ From:. Cela peut sembler inutile mais cette ligne d'enveloppe, qui précède les entêtes RFC-822, est nécessaire pour que le fichier soit au format mbox et que les clients mails puissent ensuite le lire.

Cette ligne est ensuite insérée en début du tableau qui contient le message. Puis celui-ci est stocké dans le fichier, et le message est marqué pour destruction sur le serveur POP3. À noter que les messages ne sont effectivement détruits que lorsqu'on exécute $pop->quit(), donc jusqu'à ce moment-là, le script peut à tout moment être interrompu sans que cela n'affecte vos mails sur le serveur.

On peut noter que ce script utilise, en plus du module Net::POP3, le module Email::Simple du projet PEP[1] (Perl Email Project). Ce projet initié par Simon Cozens consiste à fournir des modules plus propres et plus simples que ceux qui existaient avant dans Mail::* (y compris les siens). Il faut reconnaître qu'ici, son nom en ::Simple n'est pas abusif puisque l'interface est très naturelle : on passe le message en argument de new(), et on peut récupérer chaque entête avec la méthode header(). La prochaine fois que vous avez besoin d'un module Perl pour manipuler les mails, je vous recommande donc très chaudement de regarder d'abord les modules du projet PEP, qui sont véritablement simples à utiliser, même s'ils souffrent parfois d'un certain manque de documentation.

Enfin, pour ceux qui se demanderaient si j'ai vraiment utilisé ce script, je réponds oui, et même plus d'une fois. Au total, j'ai ainsi pu récupérer les quelques 6000 mails (hors spam) qui se sont accumulés en deux mois sur mon compte.

Liens

[1] http://pep.kwiki.org/

À vous !

Envoyez vos perles à perles@mongueurs.net, elles seront peut-être publiées dans un prochain numéro de Linux Magazine.

[IE7, par Dean Edwards] [Validation du HTML] [Validation du CSS]