L'exemple précédent était relativement simple, et illustrait l'appel direct d'un script CGI. Maintenant nous allons aborder un problème un peu plus complexe et intéressant. L'exercice consiste à pouvoir saisir des données dans un formulaire et, grâce à un script CGI, effectuer un traitement quelconque sur ces données (ce qui est le mode de fonctionnement des moteurs de recherche).
Ceci nécessite deux étapes :
Pour pouvoir saisir des données dans une page HTML, nous allons
utiliser la balise FORM. Cette balise permet de réaliser un
questionnaire. Les réponses saisies par l'utilisateur sont codées par
le navigateur et transmises au serveur HTTP. Utilisons l'exemple suivant
(fichier form.html
) :
<HTML><HEAD><TITLE>Formulaire simple</TITLE></HEAD>
<BODY>
<H2>Répondez aux questions suivantes</H2>
<FORM ACTION="http://www.april.org/cgi-bin/treat.pl" METHOD=GET>
Prénom : <INPUT TYPE="text" NAME=prenom SIZE=20><BR>
Nom : <INPUT TYPE="text" NAME=nom SIZE=20><BR>
Age : <SELECT NAME=age>
<OPTION>- de 18 ans
<OPTION>19 à 40 ans
<OPTION>41 à 60 ans
<OPTION>+ de 60 ans
</SELECT><BR>
<INPUT TYPE=submit VALUE="Envoyer"> <INPUT TYPE=reset VALUE="Remettre
à zéro">
</FORM>
</BODY>
</HTML>
L'utilisateur pourra ainsi entrer son prénom, nom (champ de type texte) et sélectionner l'une des quatres valeurs possibles pour l'âge (champ de type SELECT).
Le champ d'un formulaire est identifié par l'attribut NAME. Les données sont transmises au serveur sous la forme de paires : name=value, codées au format URL et séparées par le symbole &. Le format de codage URL est le suivant :
il est absolument nécessaire de connaître ce codage pour écrire des scripts CGI. En effet, le programme doit savoir décoder ces chaînes. Il est également possible, en connaissant ce codage, d'appeler directement un script sans passer par la page HTML appelant le script.
Le script CGI est identifié par l'attribut ACTION de la balise
FORM, cet attribut contenant l'URL du programme externe. Dans notre exemple, le script est le fichier treat.pl
qui se
trouve sur le serveur www.april.org
(dans le répertoire
/usr/local/etc/httpd/cgi-bin/
comme nous l'avons vu précédemment). L'attribut METHOD de la balise
FORM spécifie le mode de transfert des données vers le serveur. On
peut en distinguer deux : GET et POST.
Dans la méthode GET le navigateur concatène à l'URL précisée par l'attribut ACTION, le symbole ? et la chaîne contenant les données saisies par l'utilisateur. Et lorsque l'utilisateur clique sur le bouton "Envoyer" on accède à cet URL.
Dans la méthode POST la chaine contenant les données saisies par l'utilisateur est insérée dans le corps de la requête HTTP.
Si dans notre formulaire, on saisit pour le prénom "Marcel", pour le nom "Dugenou" et pour l'âge on sélectionne "41 à 60 ans", la chaîne transmise sera alors la suivante :
prenom=Marcel&nom=Gnou&age=41+%E0+60+ans
Le %E0
correspond au caractère à
.
Alors, dans le cas de la méthode GET on accédera à l'URL suivante:
http://www.april.org/cgi-bin/treat.pl?prenom=Marcel&nom=Gnou&age=41+%E0+60+ans
Ce qui montre, par ailleurs, que l'on peut donc directement accéder à un script CGI, en lui passant ses paramètres de cette façon. Ceci peut être très dangereux si on n'écrit pas ses scripts avec beaucoup de précautions.
Dans le cas de la méthode POST on accédera à l'URL suivante:
http://www.april.org/cgi-bin/treat.pl
et la chaîne prenom=Marcel&nom=Gnou&age=41+%E0+60+ans
est
insérée dans le corps de la requête, et donc le programme ira
récupérer cette chaîne sur son entrée standard.
Nous avons écris le formulaire, on a vu comment transmettre les informations au script (GET ou POST), maintenant on va écrire le programme.
Précisons tout de suite le rôle du programme externe :
La première chose qui nous intéresse est d'extraire l'information envoyée par le navigateur à l'aide du formulaire. La méthode dépend en fait de celle choisie dans le formulaire.
Dans le cas de la méthode GET, l'information est contenue dans la
variable d'environnement QUERY_STRING
, qui a pour longueur la
valeur de la variable CONTENT_LENGTH
. Ainsi dans notre exemple
précédent, la variable contiendra la chaîne
prenom=Marcel&nom=Gnou&age=41+%E0+60+ans
.
Dans le cas de la méthode POST, le programme récupère les informations sur son entrée standard.
Pour mettre en pratique ces principes, nous allons juste écrire un programme qui récupère les informations, les décode et qui écrit sur sa sortie standard une page HTML contenant les données décodées (que le navigateur affichera alors). La partie traitement des données n'est pas abordée ici car cela dépend de ce que doit faire votre programme, et il n'y a rien à dire de spécifique par rapport au sujet.
Nous allons utiliser deux langages différents : Perl et le shell.
En général, la phase de récupération des données est toujours la même, ce qui permet d'écrire des fonctions d'extraction réutilisables.
Voyons le code source de treat.pl
:
#!/usr/bin/perl
# les donnees sont envoyees par methode GET
# donc on recupere les donnees dans la variable
# d'environnement QUERY_STRING
$buffer=$ENV{"QUERY_STRING"};
# on split la chaine de donnees en des paires name=value
local(@champs) = split(/&/, $buffer);
local($donnees) = "";
# affichage du debut du code HTML
printf STDOUT "Content-type: text/html\n\n";
printf STDOUT "<HTML><HEAD>";
printf STDOUT "<TITLE>Reponse au questionnaire</TITLE>";
printf STDOUT "</HEAD>";
printf STDOUT "<BODY BGCOLOR=\"#ffffff\">";
printf STDOUT "<H1>Résultat du traitement de votre questionnaire</H1>";
printf STDOUT "<H2>Chaine de données reçue par le programme</H2>";
printf STDOUT "QUERY_STRING <STRONG>%s</STRONG>",$buffer;
printf STDOUT "<H2>Liste des informations décodées</H2>";
printf STDOUT "<UL>";
printf STDOUT "<BL>";
# recuperation et mise en forme des donnees
# on parcourt la liste des paires name=value
foreach $i (0 .. $#champs) {
# On convertit les plus en espaces
$champs[$i] =~ s/\+/ /g;
# On separe chaque champ en une cle et sa valeur
($key, $val) = split(/=/,$champs[$i],2);
# On convertit les %XX de leur valeur hexadecimale en alphanumerique
$key =~ s/%(..)/pack("c",hex($1))/ge;
$val =~ s/%(..)/pack("c",hex($1))/ge;
# on affiche le resultat
printf STDOUT "<LI><STRONG>%s:</STRONG>%s\n",$key,$val;
}
printf STDOUT "</BL>";
printf STDOUT "</UL>";
printf STDOUT "</BODY>";
printf STDOUT "</HTML>";
Etant donné que la méthode choisie est GET, on récupère les données
dans la variable QUERY_STRING
, par la ligne:
$buffer=$ENV{"QUERY_STRING"};
. Si nous avions, au niveau du
formulaire, choisi la méthode POST, nous aurions récupéré les données
par la ligne: read(STDIN,$buffer,$ENV{"CONTENT_LENGTH"});
. La
variable CONTENT_LENGTH
contenant la longueur de QUERY_STRING
.
Pour bien comprendre l'exemple, il faut évidemment connaître Perl. Les commentaires mis dans le programme l'explicitant un peu.
Nous pouvons écrire le programme avec d'autres langages, voici le code
source de treat.sh
écrit en shell.
#!/bin/sh
#
if [ "$REQUEST_METHOD" = "POST" ]; then
read QUERY_STRING
fi
# on split la chaine de donnees en des paires name=value
OPTS=`echo $QUERY_STRING | sed 's/&/ /g'`
echo "Content-type: text/html"
echo ""
echo "<HTML><HEAD>"
echo "<TITLE>Reponse au questionnaire</TITLE>"
echo "</HEAD>"
echo "<BODY BGCOLOR=\"#ffffff\">"
echo "<H1>Résultat du traitement de votre questionnaire</H1>"
echo "<H2>Chaine de données reçue par le programme</H2>"
echo "QUERY_STRING <STRONG>"$QUERY_STRING
echo "</STRONG>"
echo "<H2>Liste des informations décodées</H2>"
# recuperation et mise en forme des donnees
# on parcourt la liste des paires name=value
echo "<UL>"
echo "<BL>"
for opt in $OPTS
do
NAME=`echo $opt | sed 's/=/ /g' | awk '{print $1}'`
VALUE=`echo $opt | sed 's/=/ /g' | awk '{print $2}' | sed 's,%,\\\x,g' | sed 's/+/ /g'`
printf "<LI><STRONG>$NAME:</STRONG>$VALUE"
done
echo "</BL>"
echo "</UL>"
echo "</BODY></HTML>"
Il suffit de remplacer treat.pl
par treat.sh
dans la balise
FORM du formulaire.
L'algorithme de décodage utilisé est le suivant (la chaîne à traîter
étant dans notre exemple
prenom=Marcel&nom=Gnou&age=41+%E0+60+ans
:
&
). En shell, cela se fait par OPTS=`echo $QUERY_STRING | sed 's/&/ /g'` (on remplace & par un espace)
et en perl on utilise un tableau : local(@donnees) = split(/&/, $buffer);
name
et value
value
, convertion des +
en espaces, et
tous les %xx
en leur valeur alphanumérique.Les deux méthodes GET et POST différencient le moyen de communication entre le serveur et le programme. La méthode GET est limitée par la taille maximale acceptée par le serveur au niveau d'un URL, la méthode POST n'étant pas restreinte par cette limite.
Testez le formulaire et son script sur http://www.april.org/groupes/doc/cgi-bin/form.html.
Les scripts CGI peuvent accéder aux variables d'environnement. Selon les systèmes, on peut accéder à un nombre plus ou moins important de variables d'environnement.
Certaines variables sont relatives au serveur HTTP (par exemple
HTTP_HOST
contient l'adresse IP de la machine hébergeant le
serveur HTTP). D'autres sont relatives à la connexion client-serveur
(par exemple REMOTE_ADDR
contient l'adresse IP de la machine
cliente effectuant la requête. Enfin, certaines sont relatives à la
requête (par exemple QUERY_STRING
contient la chaîne contenant
les informations de la requête).
Voici deux programmes permettant l'accès à l'ensemble des variables d'environnement, le premier en C, le deuxième en Perl :
Code de env.c
:
#include <stdio.h>
main(int argc, char **argv, char **env)
{
int i = 0;
printf("Content-Type: text/html\n\n");
printf("<HTML><HEAD><TITLE>Variables</TITLE></HEAD><BODY>\n");
while(*env){
printf("%s <BR>\n",*env++);
}
printf("</BODY></HTML>\n");
}
Code de env.pl
:
#!/usr/bin/perl
print "Content-Type: text/html\n\n";
print "<HTML><HEAD><TITLE>Variables</TITLE></HEAD><BODY>\n";
while(($name,$value) = each %ENV){
print "$name = $value <BR>\n";
}
print "</BODY></HTML>\n";
Il suffit alors de compiler env.c
et de copier l'exécutable
résultant dans le répertoire des cgi-bin du serveur. Pour le script
Perl, il suffit de le copier dans le répertoire, en n'oubliant pas de
le rendre lisible et exécutable par tous.
Cliquez sur les liens suivants pour voir le résultat : http://www.april.org/cgi-bin/env et http://www.april.org/cgi-bin/env.pl