Le mois dernier, nous avons présenté les modules Mail::Mailer et Net::SMTP permettant d'écrire des scripts Perl capables d'envoyer des messages sur un serveur SMTP local ou distant.
Nous avons aussi étudié le module Net::Pop3 permettant de récupérer des messages en attente sur un serveur POP. Ce même module, on l'a vu, permet également de manipuler les messages en attente afin de pouvoir éventuellement les filtrer.
Lorsque nous avons présenté Net::SMTP, nous avons vu qu'il provenait d'une bibliothèque, nommée libnet, intégrant aussi d'autres modules de gestion des services réseau. L'article de ce mois-ci se propose de présenter ces modules.
Les lecteurs ayant expérimenté Perl auront aisément remarqué que je ne suis pas ce qu'il convient d'appeler un « fin perliste » dans la mesure où bien des subtilités de ce fabuleux langage m'échappent encore. J'espère en tout cas donner des exemples suffisamment clairs pour qu'un programmeur Perl minimal puisse utiliser les modules décrits. Si, toutefois, certains d'entre vous trouvent des hérésies dans mon code, qu'ils n'hésitent pas à m'en faire part : je serai à l'écoute de tout ce qui peut me faire progresser.
Poster des articles sur Usenet
Utilisation de Net::NNTP
Le module Net::NNTP est le moyen que j'utilise pour gérer les news Usenet via un script Perl. Il dispose de méthodes permettant de poster et de récupérer des articles ainsi que des méthodes permettant de produire toute commande respectant le protocole NNTP. La section DESCRIPTION de sa page (perldoc Net::NNTP) le décrit de la façon suivante :
Net::NNTP est une classe implantant en Perl un client NNTP simple, conforme à la description qu'en fait le RFC977. Net::NNTP hérite ses méthodes de communication de Net::Cmd.
Les curieux pourront aussi récupérer et tester le module News::NNTPClient. Personnellement, je ne l'ai jamais utilisé mais, au gré des articles lus sur fr.comp.lang.perl, j'ai pu constater qu'il était lui aussi utilisé pour dialoguer avec un serveur NNTP. Tout commentaire sur son utilisation est le bienvenu.
Plutôt que de longs discours, étudions ce script Perl qui poste un article de test dans fr.test (qui est, je le rappelle, le groupe pour effectuer ses tests dans la hiérarchie fr.*) :
$ cat testpost.pl
#/usr/bin/perl -w
use strict;
use Net::NNTP;
my $serveur = 'localhost';
my $groupe = 'fr.test';
my $article = 'texte.txt';
open(ARTICLE, $article) or
die "$article est introuvable\n";
my $nntp = Net::NNTP->new($serveur);
$nntp->reader;
$nntp->group($groupe);
$nntp->post();
$nntp->datasend(<<EOF);
From: Eric Jacoboni <jaco\@titine.fr.eu.org>;
Subject: test d'envoi via Perl
Followup-To: poster
Newsgroups: $groupe
EOF
my $signature = '/home/jaco/.signature';
$nntp->datasend(<ARTICLE>);
close(ARTICLE);
$nntp->datasend("\n");
$nntp->datasend("-- \n");
open(SIGN, $signature);
$nntp->datasend(<SIGN>);
close(SIGN);
$nntp->dataend();
$nntp->quit;
exit 0;
Je poste ici sur mon serveur local. Si vous n'en avez pas, remplacez la valeur de $serveur par celle de votre serveur distant et assurez-vous d'être connecté lorsque vous lancerez le script. Modifiez évidemment la valeur du champ From !
J'utilise ici un fichier texte, texte.txt, qui est censé contenir le corps de l'article et le fichier ~/.signature qui contient ma signature. Si le premier n'est pas trouvé, le script s'interrompt.
Comme avec de nombreux modules, on crée un nouvel objet en utilisant la méthode new() du module (voir la page du manuel pour connaître les options possibles de cette méthode), puis on indique au serveur que l'on est un reader (un lecteur) et non un autre serveur. Enfin, on sélectionne le groupe fr.test comme étant le groupe courant, celui sur lequel s'appliqueront nos commandes NNTP, et on poste un article sur le serveur sélectionné, dans le groupe sélectionné grâce à la méthode post(). La page du manuel vous montrera qu'utilisée comme elle l'est ici (ie. sans paramètres), cette méthode implique d'avoir recours aux méthodes datasend() et dataend() du module Net::Cmd, dont Net::NNTP hérite, et c'est ce que nous faisons.
Nous aurions aussi pu construire d'abord notre article dans un tableau de chaînes représentant les lignes de l'article ou dans une référence à un tableau. Cette dernière pratique sera préférée si le contenu du message doit être généré dynamiquement par le script.
Puis, vient l'envoi proprement dit, via la méthode datasend() évoquée plus haut. Comme le mois dernier, nous utilisons la possibilité qu'offre Perl de placer du contenu en ligne. Tout ce qui est compris entre le datasend(<<EOF) et la ligne EOF sera pris tel quel, comme une seule chaîne. Notez bien la ligne blanche avant le EOF : elle est nécessaire car NNTP attend que les en-têtes soient séparés du corps de l'article par une ligne vide.
Vient ensuite l'envoi du fichier : on passe à la commande datasend(). Le résultat est renvoyé par l'opérateur diamant (<>). Ce résultat, évalué dans un contexte de liste, est l'ensemble des lignes du fichier décrit par le descripteur ARTICLE. On fait de même pour la signature après l'avoir séparée du corps de l'article par la chaîne '-- '(notez l'espace et le retour à la ligne) : c'est ainsi que l'on fait des signatures conformes aux usages de fr.* (pour être complet, précisons que le texte de la signature ne doit pas excéder 4 lignes de 72 caractères : les tours Eiffel en « Ascii-Art » sont donc à proscrire...)
On met fin et on valide l'envoi par l'appel à dataend() et on clôt la connexion au serveur par un appel à quit().
Autres méthodes utiles de Net::NNTP
Net::NNTP offre d'autres méthodes pour l'envoi des articles. La méthode postok(), notamment, devrait être utilisée avant toute tentative de postage car elle renvoie un booléen indiquant si le serveur accepte ce que l'on poste. Une utilisation possible serait d'ajouter la ligne suivante après la connexion au serveur :
$nntp-postok() or die "Non autorisé à poster\n";
La méthode authinfo(user,password) est nécessaire si le serveur requiert une authentification du client avant accès.
Lire des articles sur Usenet
Utilisation de Net::NNTP
Ce module dispose, évidemment, des méthodes permettant de lire des articles sur un serveur NNTP : article(), body(), head(), xhdr(), xpat(). Il dispose aussi de méthodes permettant de définir un « article courant » : nntpstat(), last(), next() et de consulter le fichier active du serveur : list(), newgroups(), newnews(), active(), xgtitle(), listgroup(). La page de manuel de Net::NNTP explicite succinctement le rôle de chacune de ces méthodes et les paramètres qu'elles attendent.
Encore une fois, un exemple permettra de tester les méthodes les plus courantes :
$ cat get-active.pl
#/usr/bin/perl -w
use strict;
use Net::NNTP;
use News::Article;
my $serveur = 'localhost';
my $nntp = Net::NNTP->new($serveur);
$nntp->reader();
my $ref_active = $nntp->active("fr.*"); # groupes francophones
print "Groupes francophones distribués par $serveur :\n";
foreach (keys %$ref_active)
{
print "$_\n";
}
my $groupe = 'fr.comp.lang.perl'; # il y est, forcément !!!
my ($nb_articles, $premier, $dernier, $nomgroupe) =
$nntp->group($groupe);
print "Il y a $nb_articles dans $groupe\n";
foreach ($premier..$dernier)
{
print "@{$nntp->article()}\n";
print "Pressez Entrée pour continuer :";
<STDIN>;
$nntp->next();
}
$nntp->quit();
exit 0;
Encore une fois, ce script n'est qu'un exemple simplissime (mais que j'espère lisible...) des possibilités offertes. On pourrait, notamment, l'adapter en vue de filtrer les articles sur leurs en-têtes (en utilisant la méthode head() à la place d'article() : en utilisant ce qu'elle renvoie, on pourrait aisément repérer l'article et ne pas le récupérer...).
Comme pour le module Net::POP3, certaines des valeurs renvoyées le sont sous la forme de références à des tableaux (cas de article()) ou à des hachages (cas de active()), d'où les notations @$ et %$ utilisées pour le déréférencement.
Certaines des méthodes utilisées dans ce script ne font pas partie du RFC977 et ne seront peut-être pas reconnues par tous les serveurs... Il a été testé avec succès sur un serveur INN 2.2. (voir la page de manuel de Net::NNTP pour la liste de ces commandes).
Il ne vous aura pas échappé qu'il est dangereux de coder en dur le nom du groupe dans le script car ce groupe peut très bien ne pas être distribué par le serveur (cela dit, si fr.comp.lang.perl n'y figure pas, changez de fournisseur !). La meilleure solution consiste, évidemment, à analyser ce qui est retourné par la méthode group() ou à utiliser le résultat des commandes active() ou list() pour vérifier que le groupe voulu s'y trouve. Voici ce que l'on pourrait rajouter à notre script pour tester la présence de fr.comp.lang.perl, juste après l'appel à la méthode group($groupe) :
die "$groupe n'est pas distribué\n" unless defined($nomgroupe);
Vous aurez aussi noté que le script ne tenait pas compte des messages déjà lus : il se borne à lire tous les messages du groupe concerné encore sur le serveur. Il faudrait pouvoir tenir compte des messages déjà lus pour ne s'intéresser qu'aux nouveaux arrivés.
La plupart des logiciels clients NNTP utilisent un fichier .newsrc, présent dans le répertoire personnel de chaque utilisateur Usenet. Ce fichier, comme la plupart des fichiers de configuration d'Unix, se présente sous la forme d'une suite de lignes, une par groupe auquel l'utilisateur a souscrit :
$ cat ~/.newsrc
[...]
fr.comp.os.linux.moderated: 1-467
gnu.emacs.gnus: 1-982
comp.emacs.xemacs: 1-923
fr.comp.applications.x11: 1-613
comp.os.linux.announce: 1-643
fr.comp.lang.perl: 1-845
fr.comp.text.tex: 1-419
fr.comp.mail: 1-1160
fr.usenet.logiciels: 1-612
fr.comp.os.linux.configuration: 1-8753
fr.comp.os.linux.debats: 1-2748
fr.comp.os.linux.annonces: 1-111
fr.comp.os.unix: 1-923
fr.comp.applications.emacs: 1-197
[...]
Chacune des lignes de ce fichier est composée de trois champs :
- le nom du groupe ;
- une marque de souscription : le caractère ':' indique que l'on est abonné au groupe, un caractère '!' indique que l'on s'est désabonné du groupe ;
- une liste d'articles exprimée sous la forme d'une liste d'entiers séparés par des virgules. On peut aussi utiliser des intervalles sous la forme début-fin
Si l'on examine bien l'appel à la méthode group(), on notera que celle-ci renvoie une liste contenant entre autres le premier numéro d'article ($premier) et le dernier numéro ($dernier) : nous ne les utilisons que pour compter le nombre d'articles présents sur le serveur, mais en les comparant à ceux apparaissant dans l'entrée correspondant à fr.comp.lang.perl dans ~/.newsrc, il est aisé de noter ceux qui n'ont pas encore été lus. Il suffit alors que la boucle d'affichage des articles ne traite que ceux-là...
Le problème se complique, direz-vous, car il s'agit maintenant d'ausculter le fichier ~/.newsrc pour obtenir la liste des articles consultés pour un groupe donné... Heureusement, le module News::Newsrc est votre ami !
Présentation du module News: :Newsrc
Comme son nom et sa page de manuel l'indiquent, News::Newsrc a été conçu pour gérer les fichiers .newsrc
La place nous manque ici pour décrire ce module. Nous ne le citons que pour vous inviter à l'utiliser si vous avez besoin de consulter ou de modifier des fichiers .newsrc. Sa page de manuel est suffisamment claire pour vous permettre d'utiliser ses méthodes. Vous y trouverez notamment tout ce qu'il faut pour gérer les listes d'articles marqués qui nous occupaient dans la section précédente.
Télécharger des fichiers à partir d'un site FTP
Le module Net::FTP permet d'intégrer dans vos scripts Perl le téléchargement de fichiers via le protocole FTP. Comme Net::SMTP, Net::POP3 et Net::NNTP, il fait partie de la bibliothèque libnet de CPAN.
La page man de Net::FTP est très complète, comme les fonctionnalités de ce module.
La façon la plus simple d'utiliser ce module est de retranscrire une session FTP telle qu'on pourrait l'effectuer avec la commande en ligne ftp, par exemple :
1 On se connecte au serveur (méthode new()) ;
2 On s'identifie auprès du serveur (méthode login()) ;
3 On se place dans le répertoire distant où se trouve le fichier que l'on souhaite récupérer (méthode cwd()) ;
4 Éventuellement, on change le mode de transfert en ASCII ou en binaire en passant les options ascii ou binary à la méthode type() ;
5 On lance la récupération du fichier par la méthode get() ou son envoi par la méthode put() ;
6 On ferme la connexion par la méthode quit().
Voici un exemple résumant tout cela :
$ cat getftp.pl
#/usr/bin/perl -w
use strict;
use Net::FTP;
my $serveur = 'ftp.lip6.fr';
my $source = 'README';
my $ftp = Net::FTP->new($serveur);
$ftp->login();
$ftp->cwd('/pub/perl/CPAN');
$ftp->get("$source");
$ftp->quit;
exit 0;
Nous effectuons ici un « ftp anonyme », d'où l'absence de paramètres de la méthode login(). Si vous utilisez un compte privé, vous devrez spécifier le nom de ce compte et le mot de passe, respectivement : $ftp->login("moi","moi@machin.truc").
Évidemment, nous avons ici supposé que le script plaçait le fichier récupéré dans son répertoire : pour qu'il en soit autrement, vous pouvez donner le nom du fichier cible comme deuxième argument de la méthode get().
La prochaine fois...
Nous arrêtons là pour le moment en ce qui concerne les modules de communication de Perl. Notre but, rappelons-le, est de faire un tour d'horizon des multiples possibilités de ce langage, pas d'essayer d'égaler des ouvrages par ailleurs fort bien faits et dont nous donnons les références ci-dessous.
Le mois prochain, nous étudierons les modules permettant à Perl de dialoguer avec les SGBD, et notamment PostgreSQL... À moins que je préfère me pencher sur l'interfaçage de Perl avec Tk afin de créer de belles applications X ?
Eric Jacoboni
Voici une liste non exhaustive des sources d'informations utilisées pour écrire cet article.
man perl, man perlfaq9 ;
Le forum Usenet fr.comp.lang.perl ;
Les sites http:/www.perl.com/perl/, http://www.perl.org/ ;
Introduction à Perl, (R. Schwartz & T. Christiansen), Éd. O'Reilly ;
Programmation en Perl, (L.Wall, R.Schwartz & T. Christiansen), Éd. O'Reilly ;
Advanced Perl Programming, (S. Srinivasan), Éd. O'Reilly ;
Perl Cookbook, (T. Christiansen & N. Torkington),
Éd. O'Reil ly ;