La pratique de PERL: Le chameau tisse sa toile

Par Hal Pomeranz traduction Philippe Bereski

Ceux d'entre vous qui ont passé les 12 derniers mois sur une île déserte n'ont probablement aucune idée de ce qu'est le Web (*). Mais la pluspart des autres s'est certainement déjà essayée à écrire quelque lignes d'HTML. Une des applications les plus attrayantes du Web est son utilisation comme interface d'accès à des données externes au Web comme les bases de données ou à des applications système. Un des mécanismes utilisès pour créer ces interfaces sont les CGI, acronyme de "Common Gateway Interface".

Quelques notions de base sur les CGI

En bref, un CGI est un programme qui écrit, sur sa sortie standard, une entête particulière suivie d'un nombre arbitraire d'autres lignes. Le serveur HTTP auquel vous vous connectez exécute le CGI et passe à votre browser (**) le résultat de son exécution. (En général la partie la plus compliquée de cette chaîne de traitement conciste à comprendre comment configurer votre serveur HTTP pour qu'il exécute le CGI). Ce programme peut-être écrit dans n'importe quel langage, mais pourquoi utiliseriez-vous autre chose que PERL ?

Un exemple trivial

     #!/bin/perl

     print "Content-type: text/html\n\n"; 
     print <<"EOmyPage"; 
     <TITLE>"Hello World!" Page</TITLE>
     <H1>HELLO WORLD!</H1>
     EOmyPage
La première ligne de ce script imprime l'entête qui détermine le type du document suivant l'entête. Dans ce cas nous indiquons qu'il s'agit d'un document HTML. Il est nécessaire qu'une ligne blanche suive l'entête (remarquez à ce sujet les deux \n). Le reste du programme n'est rien d'autre que le texte du document. Arrivé à ce point vous vous dites sans doute "C'est simple comme bonjour !" et vous avez raison. Il n'y a rien de trés mystérieux dans l'utilisation des CGI.

Fichiers externes et applications

Les possibilité de ce mecanisme ne doivent cependant pas être exagérées. Votre programme peut être d'une complexitée illimitée aussi longtemps qu'il génère des sortie syntaxiquement correctes. Vous pouvez par exemple lire un fichier et écrire dans un autre :
     #!/bin/perl

     print "Content-type: text/html\n\n";

     $visitors = 'cat countfile'; 
     $visitors++; 	
     if (open(OUT, "> countfile")) { 
          print OUT $visitors; 	
          close(OUT); 	
          print <<"EOmyPage"; 	
     <TITLE>Welcome</TITLE> 
     Hello visitor number $visitors. 
     EOmyPage 
     } 
     else { 
          print "Sorry, an error occurred\n"; 
     }
Notez bien que votre serveur HTTP tourne certainement sous l'identité d'un utilisateur particulier qui n'a pas acces aux fichiers de votre système. (Essayez toujours de confiner votre serveur dans l'espace d'un compte sans priviléges, comme "nobody" et surtout ne luis donnez JAMAIS les droits du super utilisateur). Vérifiez donc bien que les fichiers que le CGI manipule ont des droits d'accès nécessaires à votre serveur.

Ce n'est jamais une bonne idée d'interrompre brutalement un programme CGI au milieu de son exécution. N'oubliez pas que vous avez un correspondant à l'autre bout de la connexion et qu'il s'attend à recevoir une réponse. C'est pourquoi le programme ci-dessus envoie un message d'erreur quand open() échoue plutôt que d'appeler die() comme c'est généralement le cas avec les scripts PERL.

Gardez également présent à l'esprit que vous pouvez manipuler la sortie d'autres programmes depuis votre CGI. Dans l'exemple ci-dessus, nous avons utilisé la commande UNIX cat pour lire le contenu d'un fichier, mais les CGI vous permettent d'augmenter la richesse des informations disponibles sur le Web en publiant le résultat d'autres programmes. Par exemple, voici un petit CGI qui affiche la liste des processus tournant sur la machine où il s'exécute en utilisant le résultat de ps. (On pourait facilement imaginer que ceci est une des pieces d'une panoplie d'outils de diagnostiques à distance d'un grans réseau).

     #!/bin/perl 	
     print "Content-type: text/plain\n\n"; 
     if (open(PS, "ps -ef |")) { 	 
          while (<PS>) { print; } 	
     } 	
     else { 	 
          print "An error occurred\n"; 
     }
Remarquez que nous avons utilisé une type de contenu différent Content-type. Le type "plain text" est généralement affiché par les browsers dans une police de taille fixe (Courier) en préservant les espaces ce qui n'est pas le cas avec le type "html". Pour ceux qui connaissent HTML, l'affichage aura le style qu'il aurait eu s'il avait été enclos dans un bloc <PRE>.

Vous pouvez appeler n'importe quel autre programme. Vous pouvez l'interfacer avec n'importe lequel des services réseau comme WAIS, gopher et même NNTP ( et si nous avions un lecteur de news multi-threadé sur le Web ? (***) ). Vous pouriez interfacer une partie des bases d'information de votre société et proposer un annuaire téléphonique ou permetre aux gens de consulter leurs profits via le Web. Faites cependant attention aux aspects de sécurité avant de vous lancer dans de grandes avantures. Vous n'avez certainement pas envie de donner l'accès à la planète entière à l'intégralité de vos données. Même l'exemple du ps ci-dessus peut donner plus d'informations que vous ne l'auriez souhaité.

L'environment CGI

Avant d'exécuter un programme CGI, votre serveur HTTP va définir un certain nombre de variables d'environnement. Les spécifications des CGI (cf http://hoohoo.ncsa.uiuc.edu/cgi/interface.html) en donnent la liste exacte, mais voici un petit programme de test très util pour vous les lister vous même.
     #!/bin/perl

     print "Content-type: text/plain\n\n";
     foreach $var (sort keys %ENV) { 	 
          print "\$ENV{$var} = '$ENV{$var}'\n"; 
     }
Par exemple, les variables REMOTE_HOST et REMOTE_ADDR donnent le nom entièrement qualifié et l'adresse IP de la machine connectée à votre serveur HTTP. Â "Netmarket" on nous demande souvent "Mais comment faites vous cela" à propos de notre "home page" qui affiche un message "Merci de vous connecter depuis $ENV{'REMOTE_HOST'}".

Le browser client peut également donner des information à votre serveur HTTP. Le serveur les transmettra à son tour au CGI au travers de variables d'environnement dont le nom est préfixé par HTTP_. En particulier, le client donnera une chaîne d'indentification comme NCSA Mosaic for the X Window System/2.4 libwww/2.12 modified dans la variable HTTP_USER_AGENT. Malheureusement, il n'y a pas de standard pour passer des informations depuis le client. Aussi est-il impossible d'identifier à coup sur la nature du browser de votre correspondant encore que ce soit possible pour les plus usuels.

Mais pourquoi donc se préocuper de la nature de browser ? N'oubliez pas que les anciens browsers peuvent ne pas supporter les derniers rafinements des spécification HTML. Par exemple, vous ne voudriez pas envoyer une table à un Mosaic Version 2.4 par ce qu'il ne saurait pas les interpreter, de même que vous ne n'enverez pas un images à un browser textuel comme Lynx car son utilisateur ne pourrait pas la voir (****).

Le traitement des formes

HTML permet de créer des pages dans lesquelles l'utilisateur saisira des informations qu'il enverra au serveur. Voici un exemple de forme :
     <TITLE>Send Us Email!</TITLE>

     We'd love to hear from you. Enter your email address and
     comments in the spaces provided and we'll respond as quickly as we
     can!<P>

     <FORM METHOD="POST" 
          ACTION="bin/process_form">
     Your E-mail address<BR> 
     <INPUT NAME="email" SIZE=45 MAXLENGTH=45><BR> 	
     Your Message<BR> 
     <TEXTAREA NAME="comments" ROWS=12 COLS=45></TEXTAREA><P>
     <INPUT TYPE="submit" VALUE="Send your comments"> 	
     </FORM>
Le tag <FORM ... ACTION=" ... "> indique le nom du programme que le serveur doit exécuter aprés que les informations auront été envoyées. Cette forme crée un champ pour saisir l'adresse électronique et une zône de saisie libre pour entrer le message. Le bouton Send your comments permet à l'utilisateur d'envoyer l'information.

Lorsque l'utilisateur appuie sur le bouton Send your comments button, le browser regroupe toutes les infomrations et les envoies au serveur HTTP avec une demande d'exécution du programme spécifié dans <FORM ... ACTION=" ... ">. La façon dont le programme récupère ces informations dépend du tag <FORM ... METHOD=" ... ">. Dans l'exemple ci dessus, nous avons utilisé POST. Ceci signifie que les informations doivent être lues par le CGI sur son entrée standard. Vous devez vous attendre à recevoir un bloc d'informations dont la taille est donnée par la variable d'environnement CONTENT_LENGTH. La méthode la plus simple pour lire cette information est d'utiliser la fonctionread()  :

     #!/bin/perl

     read(STDIN,$stuff $ENV{`CONTENT_LENGTH'}); 	
     . . .
Maintenant il ne vous reste plus qu'à exploitabler les informations. Elles sont livrées en une suite d'égalités de la forme nom=valeur séparées par un &. Les noms sont des chaînes quelconque que vous spécifiez dans la forme en utilisant le tag <... NAME=" ... "...>. Dans l'exemple ci dessus, le nom de l'adresse electronique est email et le message comments. Il faut également savoir que les blancs sont traduits en + et que les caractères non alphabétiques sonf convertis en %<hed><hex> représente la valeur hexadécimale de leur code ASCII. Le traitement typique d'une forme commence par ;nbsp;:
     #!/bin/perl

     read(STDIN, $stuff, $ENV{'CONTENT_LENGTH'}); 	
     @pairs = split(/\&/, $stuff); 
     for (@pairs) { 	
          ($field, $val) = split(/=/); 
          $field =~ s/\+/ /g; 	
          $field =~ s/%(\w\w)/sprintf("%c", hex($1))/eg; 
          $val =~ s/\+/ /g; 	
          $val =~ s/%(\w\w)/sprintf("%c", hex($1))/eg; 
          $entries{$field} = $val; 
     } 
     ...
Nous commencons par lire l'ensemble des données sur l'entrée standard puis nous les découpons en une liste d'égalitées nom=valeur. Ensuite nous traitons chaque égalité en séparant le nom de la valeur, puis nous convertissons les + et &hex en leur caracter originaux. N'essayez pas de substituer en une fois la chaîne lue sur l'entrée standard car certains drapaux de codage peuvent être des & ou des =. Substituez toujours en premier les + en espace car certain des caracters codés par &hexpeuvent être des blancs.

Maintenant que vous avez distribué l'intégralité des données dans un tableau associatif, vous pouvez faire ce que bon vous semble avec elles. N'oubliez pas de retourner une notification à votre correspondant aprés le traitement de sa forme  :

     print "Content-type: text/html\n\n";

     if (open(MAIL, 
     "| /usr/lib/sendmail webmaster")) 
     { 	 
          print MAIL <<"EOdoc"; 
     From: The Comments Page <webmaster> 
     To: webmaster 
     Subject: Comments Mail

     Mail from: $entries{"email"}

     $entries{"comments"} 
     EOdoc 	 
          close(MAIL);

          print <<"EOpage"; 
     <TITLE>Thanks!</TITLE> 	
     Thanks for taking the time to send us comments!<P> 	
     We will be responding promptly.<P> 	
     EOpage 	
     } 
     else { 	 
          print <<"EOpage"; 	
     <TITLE>Bummer!</TITLE> 	
     We encountered an error trying to send your comments.<P> 	
     Please send mail to <I>webmaster\@netmarket.com</I><P> 
     EOpage 	
     }
Soyez TRÈS attentif à ce que vous faites des données que vous récupérer d'une forme. Souvenez-vous que votre correspondant peut saisir n'IMPORTE QUOI et peut causer des dégâts considérables si vous ne vous méfiez pas. N'utilisez même pas une chaîne de la forme dans une commande que vous exécuteriez dans votre CGI. Notez que je ne passe même pas l'adresse du correspondant dans le champ From parce qu'elle pourrait contenir une commande sendmail dans le cas ou le mail rebondirait.

Pour en savoir plus

La meilleur façon de vous familiariser avec les CGI et de commencer a écrire des programmes simples. Vous aurez certainement à installer un serveur HTTP pour que tâter de sa configuration. Le serveur httpd NCSA (accessible par ftp anonyme à ftp.ncsa.uiuc.edu) et gratuit et facile à construre et à installer encore qu'il ne soit pas le plus rapide. Vous lirez avec beaucoup d'interêt l'introduction au CGI (http://hoohoo.ncsa.uiuc.edu/cgi/overview.html) ainsi que les trucs pour écrire des CGI sécurisés (http://hoohoo.ncsa.uiuc.edu/cgi/security.html).

Des exemples simples de CGI sont dispobible partout sur le Web (NCSA propose une archive d'exemple pour vous aider à commencer)

Traduction de ;login: Vol. 20 No. 4, août 1994

* World Wild Web (www), la toile d'araignée mondiale (ndtr).
** Nous n'arrivons pas à nous accommoder de la pédanterie qui veut que tous les termes techniques soient traduits. Aussi continurons nous à utiliser le terme de browser au lieu de buttineur et autre brouteur de toile. (ndtr)
*** A l'époque où Hal écrivait ces lignes, les outils du web n'étaient pas encore aussi développés qu'aujourd'hui et M$ n'avait pas encore inventé l'INTERNET. (ndtr)
**** A cette époque le WEB n'était encore fréquenté que par des gens policés qui se souciaient d'économiser la bande passante et de pouvoir inter-opérer avec la concurrence. Mais bon, M$ n'avait pas encore inventé l'INTERNET. (ndtr)


Dernière édition: 4 avril 1997 phb
Original 11/27/96
Back to the original
Retour à l'index