NOM

perlfaq9 - Réseau ($Revision: 1.26 $, $Date: 1999/05/23 16:08:30 $)

DESCRIPTION

Cette section traite des questions relatives aux aspects réseau, à l'Internet et un peu au web.

Mon script CGI fonctionne en ligne de commande, mais pas avec le navigateur (500 Server Error)

Si vous pouvez démontrer que vous avez lu les FAQ suivantes et que votre problème n'est pas quelque chose de simple auquel on peut répondre facilement, vous recevrez probablement une réponse courtoise et utile à votre question si vous la postez dans comp.infosystems.www.authoring.cgi (si cela à un rapport avec les protocoles HTTP, HTML ou CGI). Les questions qui semblent être du Perl mais qui sont plutôt des questions relatives au CGI et qui sont postées dans comp.lang.perl.misc peuvent ne pas être très bien reçues.

Les FAQs utiles et les documents relatifs sont :

CGI FAQ
    http://www.webthing.com/tutorials/cgifaq.html

Web FAQ
    http://www.boutell.com/faq/

WWW Securite FAQ
    http://www.w3.org/Security/Faq/

HTTP Spec
    http://www.w3.org/pub/WWW/Protocols/HTTP/

HTML Spec
    http://www.w3.org/TR/REC-html40/
    http://www.w3.org/pub/WWW/MarkUp/

CGI Spec
    http://www.w3.org/CGI/

CGI Securite FAQ
    http://www.go2net.com/people/paulp/cgi-security/safe-cgi.txt

Comment faire pour obtenir un meilleur message d'erreur d'un programme CGI ?

Utilisez le module CGI::Carp. Il remplace warn et die, plus les fonctions normales carp, croak, et confess du module Carp qui sont des versions plus parlantes et sûres. Les erreurs continuent à être envoyées vers le fichier normal des erreurs du serveur.

use CGI::Carp;
warn "This is a complaint";
die "But this one is serious";

L'utilisation suivante de CGI::Carp redirige également les erreurs vers un fichier de votre choix, mais aussi les avertissements durant la phase de compilation en étant place dans un bloc BEGIN.

BEGIN {
    use CGI::Carp qw(carpout);
    open(LOG, ">>/var/local/cgi-logs/mycgi-log")
        or die "Unable to append to mycgi-log: $!\n";
    carpout(*LOG);
}

Vous pouvez vous arrangez pour que les erreurs fatales soient retournées au navigateur client, ceci vous permettant d'obtenir un meilleur debogage, mais pouvant paraître confus pour l'utilisateur final.

use CGI::Carp qw(fatalsToBrowser);
die "Bad error here";

Si l'erreur se produit avant même que vous ayez produit les en-têtes HTTP en sortie, le module essayera de les générer pour éviter une erreur 500 du serveur. Les avertissements normaux continueront à être envoyés vers le fichier de log des erreurs du serveur (ou là où vous les avez envoyées via carpout) préfixés par le nom du script et la date.

Comment enlever les marqueurs HTML d'une chaîne ?

La meilleure façon (mais pas obligatoirement la plus rapide) est d'utiliser HTML::Parser disponible sur le CPAN. Un autre moyen généralement correct est l'utilisation de HTML::FormatText qui non seulement retire le HTML mais aussi essaye de réaliser un petit formatage simple du texte brut résultant.

Plusieurs personnes essayent une approche simple par les expressions rationnelles, comme s/<.*?>//g, mais ceci ne fonctionne pas correctement dans de nombreux cas car les marqueurs HTML peuvent continuer après des sauts de lignes, ils peuvent contenir des < ou des > entre guillemets, ou des commentaires HTML peuvent être présents. De plus, ces personnes oublient de convertir les entités comme &lt; par exemple.

Voici une "solution-basique", qui fonctionne avec la plupart des fichiers :

#!/usr/bin/perl -p0777
s/<(?:[^>'"]*|(['"]).*?\1)*>//gs

Si vous souhaitez une solution plus complète, regardez le programme striphtml se décomposant en 3 étapes à l'adresse http://www.perl.com/CPAN/authors/Tom_Christiansen/scripts/striphtml.gz

Voici quelques cas piégeux auxquels vous devriez penser avant de choisir une solution :

    <IMG SRC = "foo.gif" ALT = "A > B">

    <IMG SRC = "foo.gif"
	 ALT = "A > B">

    <!-- <Un commentaire> -->

    <script>if (a<b && a>c)</script>

    <# Just data #>

    <![INCLUDE CDATA [ >>>>>>>>>>>> ]]>

Si les commentaires HTML incluent d'autres balises, ces solutions échoueraient aussi sur un texte tel que celui-ci :

<!-- Cette section est en commentaire.
    <B>You can't see me!</B>
-->

Comment extraire des URL ?

Une approche rapide mais imparfaite peut être

#!/usr/bin/perl -n00
# qxurl - tchrist@perl.com
print "$2\n" while m{
    < \s*
      A \s+ HREF \s* = \s* (["']) (.*?) \1
    \s* >
}gsix;

Cette version n'ajuste pas les URL relatives, ne comprend pas les instructions de base différentes, traite les commentaires HTML, traite les attributs HREF et NAME dans la même balise, comprend les qualificateurs supplémentaires comme TARGET, ou accepte les URL elles-mêmes en arguments. Elle fonctionne aussi à peu près 100 fois plus vite qu'une solution plus "complète" utilisant la suite de modules LWP, tel que le programme http://www.perl.com/CPAN/authors/Tom_Christiansen/scripts/xurl.gz

Comment télécharger un fichier de la machine d'un utilisateur ? Comment ouvrir un fichier d'une autre machine ?

Dans le contexte d'un formulaire HTML, vous pouvez utiliser ce qui est connu comme l'encodage multipart/form-data. Le module CGI.pm (disponible au CPAN) supporte cette solution dans la méthode start_multipart_form() qui n'est pas la même que la méthode startform().

Comment faire un menu pop-up en HTML ?

Utiliser les tags SELECT et OPTION. Le module CGI.pm (disponible au CPAN) support cette fonctionnalité, ainsi que de nombreuses autres, incluant quelques-unes qui se synthétisent intelligemment d'elles-mêmes.

Comment récupérer un fichier HTML ?

Utiliser le module LWP::Simple disponible au CPAN, qui fait partie de l'excellent package libwww-perl (LWP). D'un autre côté, si vous avez le navigateur en mode texte lynx installé sur votre système, il n'est pas mauvais de faire :

$html_code = `lynx -source $url`;
$text_data = `lynx -dump $url`;

Les modules libwww-perl (LWP) du CPAN fournissent une façon plus puissante de le faire. Ils n'ont pas besoin de lynx, mais tout comme lynx, ils peuvent fonctionner à travers les serveurs mandataires (proxy, NDT) :

# version la plus simple
use LWP::Simple;
$content = get($URL);

# ou affiche du HTML depuis un URL
use LWP::Simple;
getprint "http://www.linpro.no/lwp/";

# ou affiche de l'ASCII depuis le HTML d'un URL
# nécessite aussi le paquetage HTML-Tree du CPAN
use LWP::Simple;
use HTML::Parser;
use HTML::FormatText;
my ($html, $ascii);
$html = get("http://www.perl.com/");
defined $html
    or die "Can't fetch HTML from http://www.perl.com/";
$ascii = HTML::FormatText->new->format(parse_html($html));
print $ascii;

Comment automatiser la soumission d'un formulaire HTML ?

Si vous soumettez des valeurs en utilisant la méthode GET, créez un URL et codez le formulaire en utilisant la méthode query_form :

use LWP::Simple;
use URI::URL;

my $url = url('http://www.perl.com/cgi-bin/cpan_mod');
$url->query_form(module => 'DB_File', readme => 1);
$content = get($url);

Si vous utilisez la méthode POST, créez votre propre agent utilisateur et codez le contenu de façon appropriée.

use HTTP::Request::Common qw(POST);
use LWP::UserAgent;

$ua = LWP::UserAgent->new();
my $req = POST 'http://www.perl.com/cgi-bin/cpan_mod',
               [ module => 'DB_File', readme => 1 ];
$content = $ua->request($req)->as_string;

Comment décoder ou créer ces %-encodings sur le web ?

Voici un exemple de décodage :

$string = "http://altavista.digital.com/cgi-bin/query?pg=q&what=news&fmt=.&q=%2Bcgi-bin+%2Bperl.exe";
$string =~ s/%([a-fA-F0-9]{2})/chr(hex($1))/ge;

Le codage est un peu plus difficile, car vous ne pouvez pas juste changer aveuglément tous les caractères non alpha-numérique (\W) en échappements hexadécimaux. Il est important que les caractères ayant une signification spéciale tels que / et ? ne soient pas transformés. La façon la plus simple d'obtenir le résultat escompté n'est probablement pas de réinventer la roue, mais seulement d'utiliser le module URI::Escape disponible sur le CPAN.

Comment rediriger le navigateur vers une autre page ?

Au lieu de renvoyer un Content-Type comme en-tête de votre réponse, renvoyez plutôt un en-tête Location:. Officiellement, ceci doit être un en-tête URI:, aussi le module CGI.pm (disponible au CPAN) renvoie-t-il les 2.

Location: http://www.domain.com/newpage
URI: http://www.domain.com/newpage

Notez que les URL relatifs dans ces en-têtes peuvent provoquer des comportements étrange à cause des "optimisations" que les serveurs font.

$url = "http://www.perl.com/CPAN/";
print "Location: $url\n\n";
exit;

Pour viser un cadre particulier dans un frameset, incluez le champe "Window-target:" dans l'en-tête.

print <<EOF;
Location: http://www.domain.com/newpage
Window-target: <FrameName>

EOF

Pour être conforme aux spécifications, chacune de ces fins de lignes virtuelles devrait être de vraies séquences "\015\012" physiques le temps qu'elles arrivent au navigateur client. À part les scripts NPH, toutefois, cette fin de ligne locale devrait être traduite par votre serveur sous une forme standard, vous ne devriez donc pas avoir de problème à ce niveau, même si vous êtes coincés sous MacOS. Il est probable que personne d'autre ne le remarque.

Comment mettre un mot de passe sur mes pages Web ?

Cela dépend. Vous devriez certainement lire la documentation de votre serveur web, ou sinon vérifier quelques unes des FAQ référencées ci-dessus.

Comment éditer mes fichiers .htpasswd et .htgroup avec Perl ?

Les modules HTTPD::UserAdmin et HTTPD::GroupAdmin proposent une interface orientée objet cohérente pour ces fichiers, quelle que soit la façon dont ils sont stockés. Les bases de données peuvent être du texte, une dbm, une Berkley DB ou n'importe quelle autre base de données avec un pilote compatible DBI. HTTPD::UserAdmin supporte les fichiers utilisés par les mécanismes d'authentification 'Basic' et 'Digest'. Voici un exemple :

use HTTPD::UserAdmin ();
HTTPD::UserAdmin
      ->new(DB => "/foo/.htpasswd")
      ->add($username => $password);

Comment être sûr que les utilisateurs ne peuvent pas entrer de valeurs dans un formulaire qui fait faire de vilaines choses à mon script CGI ?

Lisez la CGI security FAQ, à l'adresse http://www-genome.wi.mit.edu/WWW/faqs/www-security-faq.html, et la FAQ Perl/CGI à l'adresse http://www.perl.com/CPAN/doc/FAQs/cgi/perl-cgi-faq.html.

En bref : utilisez la souillure (voir perlsec), qui s'assure que les données en provenance de l'extérieur de votre script (e.g. des paramètres CGI) ne sont jamais utilisées dans des appels eval ou system. En plus de la souillure, n'utilisez jamais la forme à un seul argument de system() ou d'exec(). À la place, fournissez la commande et les arguments en tant que liste, qui empêche la globalisation par le shell.

Comment parcourir un en-tête de mail ?

Pour une solution rapide et peu propre, essayez cette solution dérivée de la page 222 de la seconde édition de "Programming Perl" :

$/ = '';
$header = <MSG>;
$header =~ s/\n\s+/ /g;      # fusionne les lignes fractionnées
%head = ( UNIX_FROM_LINE, split /^([-\w]+):\s*/m, $header );

Cette solution ne fonctionne pas correctement si, par exemple, vous essayez de mettre à jour toutes les lignes reçues. Une approche plus complète consiste à utiliser le module Mail::Header du CPAN (faisant parti du paquetage MailTools).

Comment décoder un formulaire CGI ?

Vous utilisez un module standard, probablement CGI.pm. En aucune circonstance vous ne devez essayer de le faire à la main !

Vous verrez beaucoup de programmes CGI qui lisent aveuglément sur STDIN un nombre d'octets égal au CONTENT_LENGTH pour les POSTs, ou saisissent QUERY_STRING pour décoder les GETs. Ces programmes sont très mal écrits. Ils ne marchent que parfois. Ils oublient typiquement de vérifier la valeur de retour de l'appel système read(), ce qui est un péché cardinal. Ils ne traitent pas les requêtes HEAD. Ils ne traitent pas les formulaires en plusieurs parties utilisés pour les envois de fichiers. Ils ne traitent pas les combinaisons GET/POST où les champs de requête se trouvent en plusieurs endroits. Ils ne traitent pas les mots-clés dans la chaîne de la requête.

En deux mots, ce sont de mauvais bidouillages. Résistez-leur à tout prix. S'il vous plaît ne soyez pas tenté de réinventer la roue. À la place, utilisez CGI.pm ou CGI_Lite.pm (disponible sur le CPAN), ou si vous piégés dans le monde sans modules de perl1 .. perl4, vous devriez regarder dans cgi-lib.pl (disponible à http://www.bio.cam.ac.uk/web/form.html).

Assurez-vous de savoir s'il faut utiliser un GET ou un POST dans votre formulaire. Les GETs ne devraient être utilisés que pour quelque chose qui ne modifie pas le serveur. Autrement, vous pouvez obtenir des bases de données sens dessus-dessous et des courriers électroniques répétés. Le joli mot pour ça est ``idempotence''. Ceci veut simplement dire qu'il ne devrait y avoir aucune différence entre faire une requête GET pour un URL particulier et en faire plusieurs. C'est parce que la définition du protocole HTTP dit qu'une requête GET peut être mise en cache par le navigateur, ou le serveur, ou un mandataire intermédiaire. Les requêtes POST ne peuvent pas l'être, parce que chaque requête est indépendante et importante. Typiquement, les requêtes POST changent ou dépendent de l'état du serveur (consultation ou mise à jour d'une base de données, envoi de courrier, ou achat d'un ordinateur).

Comment vérifier la validité d'une adresse électronique ?

Vous ne pouvez pas, du moins pas en temps réel. Dommage, hein ?

Sans envoyer un mail à l'adresse et constater s'il y a un humain de l'autre côté pour vous répondre, vous ne pouvez pas déterminer si une adresse est valide. Même si vous appliquez l'en-tête standard d'email, vous pouvez rencontrer des problèmes, car il existe des adresses valides qui ne sont pas compatibles avec la RFC-822 (le standard des en-têtes des mails), et inversement il existe des adresses qui ne sont pas délivrables et qui sont compatibles.

Beaucoup sont tentés d'éliminer de fréquentes adresses email invalides avec une simple expression régulière, comme /^[\w.-]+\@([\w.-]\.)+\w+$/. C'est une très mauvaise idée. Cependant ceci rejette également beaucoup d'adresses valides, et ne dit rien sur la potentialité de cette adresse à être délivrable, ce n'est donc pas suggéré. À la place regardez : http://www.perl.com/CPAN/authors/Tom_Christiansen/scripts/ckaddr.gz , qui effectivement vérifie une compatibilité complète avec les spécifications RFC (excepté les commentaires imbriqués), vérifie qu'il ne s'agit pas d'une adresse que vous ne désirez pas (cad, Bill Clinton ou votre responsable de compte mail), puis s'assure que le nom d'hôte donné peut être trouvé dans les enregistrements MX du DNS. Ce n'est pas très rapide, mais ça marche pour ce que ça essaye de faire.

Notre meilleur conseil pour vérifier l'adresse de quelqu'un est de lui faire entrer deux fois son adresse, tout comme vous le faites pour changer un mot de passe. Ceci élimine habituellement les fautes de frappe. Si les deux versions sont égales, envoyez un courrier à cette adresse avec un message personnel ayant cette allure :

Dear someuser@host.com,

Please confirm the mail address you gave us Wed May  6 09:38:41
MDT 1998 by replying to this message.  Include the string
"Rumpelstiltskin" in that reply, but spelled in reverse; that is,
start with "Nik...".  Once this is done, your confirmed address will
be entered into our records.

Si vous recevez le message et s'ils ont suivi vos indications, vous pouvez être raisonnablement assuré que l'adresse est réelle.

Une stratégie proche moins ouverte à la tromperie est de leur donner un PIN (numéro d'identification personnel). Enregistrez l'adresse et le PIN (le mieux est qu'il soit aléatoire) pour un traitement ultérieur. Dans le message que vous envoyez, demandez-leur d'inclure le PIN dans leur réponse. Mais si le message rebondit, ou est inclus via un script "vacances", il sera là de toute façon. Il est donc plus efficace de leur demander de renvoyer un PIN légèrement modifié, par exemple inversé, ou une unité ajoutée à chaque chiffre, etc.

Comment décoder une chaîne MIME/BASE64 ?

Le package MIME-tools (disponible sur le CPAN) permet cela et beaucoup plus. Décoder du BASE64 devient aussi simple que cela :

use MIME::base64;
$decoded = decode_base64($encoded);

Une approche plus directe consiste à utiliser la fonction unpack() avec le formatage "u" après quelques translations mineures :

tr#A-Za-z0-9+/##cd;                   # supprime les caracteres non base-64
tr#A-Za-z0-9+/# -_#;                  # convertit dans le format uuencode
$len = pack("c", 32 + 0.75*length);   # calcule la longueur en octets
print unpack("u", $len . $_);         # uudecode et affiche

Comment renvoyer l'adresse électronique de l'utilisateur ?

Sur les systèmes supportant getpwuid, la variable $< et le module Sys::Hostname (qui fait partie de la distribution standard de Perl), vous pouvez probablement essayer d'utiliser quelque chose comme ceci :

use Sys::Hostname;
$address = sprintf('%s@%s', getpwuid($<), hostname);

La politique de la compagnie sur les adresses email peut signifier que ceci génère des adresses que le système de mail de la compagnie n'acceptera pas, ainsi vous devriez demander les adresses email des utilisateurs quand ceci compte. Qui plus est, tous les systèmes sur lesquels fonctionne Perl n'acceptent pas ces informations comme sur Unix.

Le module Mail::Util du CPAN (faisant partie du package MailTools) procure une fonction mailaddress() qui essaye de créer l'adresse email d'un utilisateur. Il effectue une démarche plus intelligente que le code précédent, utilisant des informations fournies quand le module a été installé, mais cela peut rester incorrect. Encore une fois, la meilleure manière est souvent de simplement poser la question à l'utilisateur.

Comment envoyer un mail ?

Utilisez directement le programme sendmail :

open(SENDMAIL, "|/usr/lib/sendmail -oi -t -odq")
                    or die "Can't fork for sendmail: $!\n";
print SENDMAIL <<"EOF";
From: User Originating Mail <me\@host>
To: Final Destination <you\@otherhost>
Subject: A relevant subject line

Body of the message goes here after the blank line
in as many lines as you like.
EOF
close(SENDMAIL)     or warn "sendmail didn't close nicely";

L'option -oi empêche sendmail d'interpréter une ligne constituée d'un seul point comme une "fin de message". L'option -t lui dit d'utiliser les en-têtes pour décider à qui envoyer le message, et -odq lui dit de placer le message dans la file d'attente. Cette dernière option signifie que votre message ne sera pas immédiatement envoyé, donc ne la mettez pas si vous voulez un envoi immédiat.

Alternativement, des approches moins pratiques comprennent l'appel direct à mail (parfois appelé mailx) ou la simple ouverture du port 25 pour avoir une conversation intime rien qu'entre vous et le démon SMTP distant, probablement sendmail.

Ou vous pourriez utiliser le module Mail::Mailer du CPAN :

use Mail::Mailer;

$mailer = Mail::Mailer->new();
$mailer->open({ From    => $from_address,
                To      => $to_address,
                Subject => $subject,
              })
    or die "Can't open: $!\n";
print $mailer $body;
$mailer->close();

Le module Mail::Internet utilise Net::SMTP qui est moins Unix-centrique que Mail::Mailer, mais moins fiable. Évitez les commandes SMTP crues. Il y a de nombreuses raisons pour utiliser un agent de transport de mail comme sendmail. Celles-ci comprennent la mise en file d'attente, les enregistrement MX, et la sécurité.

Comment lire du courrier ?

Vous pourriez utiliser le module Mail::Folder du CPAN (faisant partie du paquetage MailFolder) ou le module Mail::Internet (faisant lui aussi partie du paquetage MailTools), toutefois, un module est souvent un marteau pour écraser une mouche. Voici un filtre de mail.

#!/usr/bin/perl
# bysub1 - simple sort by subject
my(@msgs, @sub);
my $msgno = -1;
$/ = '';                    # lit un paragraphe
while (<>) {
    if (/^From/m) {
        /^Subject:\s*(?:Re:\s*)*(.*)/mi;
        $sub[++$msgno] = lc($1) || '';
    }
    $msgs[$msgno] .= $_;
}
for my $i (sort { $sub[$a] cmp $sub[$b] || $a <=> $b } (0 .. $#msgs)) {
    print $msgs[$i];
}

Ou de façon plus succincte,

#!/usr/bin/perl -n00
# bysub2 - awkish sort-by-subject
BEGIN { $msgno = -1 }
$sub[++$msgno] = (/^Subject:\s*(?:Re:\s*)*(.*)/mi)[0] if /^From/m;
$msg[$msgno] .= $_;
END { print @msg[ sort { $sub[$a] cmp $sub[$b] || $a <=> $b } (0 .. $#msg) ] }

Comment trouver mon nom de machine / nom de domaine / mon adresse IP ?

La façon normale de trouver votre nom de machine est d'appeler le programme `hostname`. Quoique parfois efficace, ceci pose des problèmes, comme le fait de ne pas savoir si vous avez le nom canonique ou pas. Il s'agit d'un de ces compromis entre facilité et portabilité.

Le module Sys::Hostname (qui fait partie de la distribution standard Perl), vous donnera le nom de machine, avec lequel vous pourrez trouvez l'adresse IP (en supposant que vous ayez un DNS en état de fonctionnement) avec un appel à gethostbyname().

use Socket;
use Sys::Hostname;
my $host = hostname();
my $addr = inet_ntoa(scalar(gethostbyname($name)) || 'localhost');

Probablement la manière la plus simple de connaître votre domaine DNS est de le récupérer dans /etc/resolv.conf, du moins sous Unix. Bien évidemment cela suppose plusieurs choses à propos de la configuration de votre /etc/resolv.conf, notamment qu'il existe.

(Nous continuons à espérer une bonne méthode pour connaître le domaine DNS pour les systèmes autre que Unix).

Comment récupérer un article de news ou les groupes actifs ?

Utilisez les modules Net::NNTP ou News::NNTPClient, tous les deux disponibles sur le CPAN. Ceux-ci peuvent rendre des tâches comme récupérer la liste des groupes de news aussi simple que :

perl -MNews::NNTPClient
  -e 'print News::NNTPClient->new->list("newsgroups")'

Comment récupérer/envoyer un fichier par FTP ?

LWP::Simple (disponible sur le CPAN) peut récupérer mais pas envoyer. Net::FTP (aussi disponible sur le CPAN) est plus complexe, mais peut envoyer aussi bien que récupérer.

Comment utiliser des RPC en Perl ?

Un module DCE::RPC est en cours de développement (mais n'est pas encore disponible), et sera livré comme partie du paquetage DCE-Perl (disponible sur le CPAN). La suite rpcgen, disponible CPAN/authors/id/JAKE/, est un générateur de stub RPC et inclut un module RPC::ONC.

AUTEUR ET COPYRIGHT

Copyright (c) 1997-1999 Tom Christiansen et Nathan Torkington. Tous droits réservés.

Lorsque ce travail est inclus comme un élément de la distribution standard de Perl ou comme une partie de sa documentation complète sous forme imprimée ou autrement, il ne peut être distribué que dans les limites fixées par la Perl's Artistic License. Toute distribution de ce fichier ou de ses dérivés hors de cet ensemble nécessite un accord particulier avec le titulaire des droits (voir perlfaq).

Indépendemment de sa distribution, tous les exemples de code de ce fichier sont ici placés dans le domaine public. Vous êtes autorisés et encouragés à utiliser ce code dans vos programmes que ce soit pour votre plaisir ou pour un profit. Un simple commentaire dans le code en précisant l'origine serait de bonne courtoisie mais n'est pas obligatoire.

TRADUCTION

Version

Cette traduction française correspond à la version anglaise distribuée avec perl 5.6.0. Pour en savoir plus concernant ces traductions, consultez http://perl.enstimac.fr/.

Traducteur

Aymeric Barantal <Aymeric.Barantal@grolier.fr>

Relecture

Régis Julié <Regis.Julie@cetelem.fr>, Roland Trique <roland.trique@uhb.fr> (mise à jour), Gérard Delafond.