Introduction à l'écriture de CGI

Une CGI (Common Gateway Interface) permet d'interfacer une page HTML avec une application externe de traitement des données saisies dans cette page.

Le schéma ci-dessous donne un bref aperçu du fonctionnement d'une CGI :

 

Le client (navigateur) en 1 saisit les données dans la page HTML. Ces données sont envoyées au serveur 2 qui lance la CGI en 3. Le résultat est ensuite retourné au client.

La CGI est un programme exécutable qui peut donc utiliser n'importe quel langage de programmation :

Pour des raisons de portabilité, le langage PERL est souvent utilisé pour l'écriture des CGI.
Cependant, pour des raisons de simplicité de syntaxe, nous utiliserons ici le shell UNIX standard (Bourne Shell).

Format d'appel d'une CGI  

Une page HTML désirant utiliser une CGI aura le format suivant:

<HEAD>
<TITLE>Test CGI</TITLE>
</HEAD>

<BODY>
<HR>
<FORM ACTION="action.cgi">
NOM : <INPUT NAME="NOM" SIZE="40"><BR>
PRENOM : <INPUT NAME="PRENOM" SIZE="40"><BR>
<P>
<INPUT NAME="SUBMIT" TYPE=SUBMIT VALUE="Envoyer">
<INPUT NAME="RESET" TYPE=RESET VALUE=Effacer">

</FORM>
</BODY>
</PRE>

Le nom du programme CGI est passé au paramètre ACTION de la définition de la forme (FORM).
Les types INPUT permettent de saisir les données, les boutons de type SUBMIT et RESET permettent respectivement la validation des données (et donc le lancement de la CGI action.cgi) et la remise à zéro des zones de saisie. La liste des types utilisés par les formes est disponible sur http://www.htmlhelp.com/reference/wilbur/quickref.html

Le résultat de l'affichage de cette page puis de la saisie d'un nom et d'un prénom est le suivant:

En appuyant sur Envoyer, on obtient la page HTML:

Cette page a donc été calculée dynamiquement à partir des données fournies lors de la saisie.      

Avant de détailler l'écriture d'une CGI, il est nécessaire de définir quelques bases concernant l'utilisation du shell UNIX.

Notion de Shell UNIX

2.1 Définition
Le shell est un langage de commande interprétée, similaire aux célèbres fichiers .BAT du système MS-DOS.
Un shell (ou interpréteur) est affecté à tout utilisateur UNIX. Le shell standard est le Bourne Shell correspondant au programme /bin/sh.
Par défaut, le shell utilise le clavier comme entrée standard et l'écran comme sortie standard mais il est très simple de les rediriger en utilisant les caractères '<' et '>' ou bien le pipe symbolisé par le caractère '|'.

2.2 Utilisation
Le shell permet de lancer des commandes simples ou bien des suites de commandes que l'on peut sauver dans un fichier appelé shell-script. Par exemple:
#!/bin/sh
# Cet exemple simple dit bonjour
echo Bonjour $USER !

donne comme résultat

pierre@pcpf % ./test1.sh
Bonjour pierre !

Le caractère # indique un commentaire. La première ligne est théoriquement obligatoire et indique au système quel interpréteur utiliser pour traiter le fichier (ici le shell /bin/sh). Pour un programme en PERL on aurait:

#!/usr/local/bin/perl

print "Ceci est un programme PERL";
exit (0);

Attention car un shell-script doit être EXECUTABLE sinon on obtient le message:

pierre@pcpf % ./test1.sh
bash: ./test1.sh: Permission denied

Il faut alors faire:

pierre@pcpf % sh test1.sh
Bonjour pierre !

ou bien le rendre exécutable par:
chmod +x test1.sh

2.3 Les variables d'environnement
Cette notion est fondamentale dans le système UNIX. Tout processus UNIX s'exécute dans un environement hérité de son père. Cet environnement comprend un certain nombre de variables contenant des chaînes de caractères. Un utilisateur peut connaître la liste des variables de son environnement par la commande env:

pierre@pcpf % env
HOSTNAME=pcpf.lectra.fr
LOGNAME=pierre
EMACS=t
MINICOM=-c on
TERM=dumb
HOSTTYPE=i386
PATH=:/bin:/usr/bin:/usr/X11R6/bin:/usr/ucb:/usr/local/bin:/sbin:
/usr/X11/bin:/usr/andrew/bin:/usr/openwin/bin:/usr/games:.
:/usr/local/bin:/sbin:/usr/X11/bin:/usr/andrew/bin:/usr/openwin/bin:/usr/games:.
HOME=/home/system/pierre
SHELL=/bin/bash
PS1=pierre@pcpf %
PS2=>
USER=pierre
MANPATH=/usr/local/man:/usr/man/preformat:/usr/man:/usr/X11/man:/usr/openwin/man
LESS=-MM
COLUMNS=86
DISPLAY=:0.0
OSTYPE=Linux
...

On peut également connaître la valeur d'une variable donnée en faisant simplement:

pierre@pcpf % echo $HOME
/home/system/pierre

ou bien créer une nouvelle variable en faisant:

pierre@pcpf % X=titi
pierre@pcpf % export X
pierre@pcpf % echo $X
titi

Le commande export permet à la variable d'être visible depuis les environnements fils de l'environnement courant. Pour effacer le contenu d'une variable, on fera:

pierre@pcpf % unset X
pierre@pcpf % echo $X

pierre@pcpf %

A l'intérieur d'un shell-script, les variables spéciales $1, $2, etc. représentent les paramètres passés au shell-script. Le shell-script:

#! /bin/sh
echo "mes paramètres sont:"
echo
echo "Le premier: $1"
echo "Le deuxième: $2"
echo "Tous: $*"
echo "Le nombre de paramètres: $#"

donne à l'exécution:

pierre@pcpf % par.sh toto tutu titi
mes paramètres sont:

Le premier: toto
Le deuxième: tutu
Tous: toto tutu titi
Le nombre de paramètres: 3

La variable spéciale $* contient tous les paramètres passés. La variable $# contient le nombre de ces paramètres. La commande set permet de re-affecter les valeurs des paramètres au cours de l'exécution:

#! /bin/sh
echo "mes paramètres sont:"
echo
echo "Le premier: $1"
echo "Le deuxième: $2"
echo "Tous: $*"
echo "Le nombre de paramètres: $#"

# Nouveaux paramètres
NOUVEAU="un deux"
set $NOUVEAU

echo "Nouveaux paramètres:"
echo
echo "Le premier: $1"
echo "Le deuxième: $2"
echo "Tous: $*"
echo "Le nombre de paramètres: $#"

qui donne:

pierre@pcpf % par2.sh titi toto
mes paramètres sont:

Le premier: titi
Le deuxième: toto
Tous: titi toto
Le nombre de paramètres: 2
Nouveaux paramètres:

Le premier: un
Le deuxième: deux
Tous: un deux
Le nombre de paramètres: 2
 

Il est également possible d'effectuer des boucles et des tests sur les valeurs des variables.

Voici un exemple de boucle:
pierre@pcpf % for i in un deux trois
> do
> echo "La valeur de i est $i"
> done
La valeur de i est un
La valeur de i est deux
La valeur de i est trois

Voici un exemple de test. Soit le shell-script:

#! /bin/sh
if [ "$SEX" = "M" ]; then
    echo "Sans contrefaçon, je suis un garçon !"
else
    echo "Femme des années 80, lalalère !"
fi

qui donne à l'exécution:

pierre@pcpf % export SEX=M
pierre@pcpf % ./sex.sh
Sans contrefaçon, je suis un garçon !
pierre@pcpf % unset SEX
pierre@pcpf % ./sex.sh
Femme des années 80, lalalère !

Un possibilité très intéressante est l'affectation d'une variable par le résultat de l'exécution d'une autre commande:

pierre@pcpf % X=`date`
pierre@pcpf % echo $X
Mon May 12 09:50:18 MET DST 1997

Ceci est très utilisé dans la rédaction des CGI.
Il est également possible d'évaluer le contenu d'une variable dans le shell courant par la commande eval. Bien entendu ce contenu doit respecter la syntaxe du shell:

pierre@pcpf % X=`echo "Y=2"`
pierre@pcpf % echo $X
Y=2
pierre@pcpf % echo $Y

pierre@pcpf % eval $X
pierre@pcpf % echo $Y
2

Cette commande est utilisée systématiquement pour le traitement d'une CGI en shell.

On peut aussi affecter une variable en lisant sa valeur sur l'entrée standard:
pierre@pcpf % read X
toto
pierre@pcpf % echo $X
toto

Les commandes vues précédemment sont intégrées au shell (elle sont dites builtin):

pierre@pcpf % type echo
echo is a shell builtin
pierre@pcpf % type eval
eval is a shell builtin
pierre@pcpf % type set
set is a shell builtin
pierre@pcpf % type unset
unset is a shell builtin

Quelques commandes utiles

Les commandes UNIX suivantes sont très utilisées pour la rédaction des CGI en shell.

CAT
Cette commande permet d'afficher le contenu d'un fichier sur la sortie standard:

pierre@pcpf % cat /etc/issue

Welcome to Linux 2.0.29.

Un syntaxe particulère permet aussi d'afficher les lignes précédant un marqueur donné (très utile pour afficher du HTML dynamiquement):

pierre@pcpf % cat <<FIN
> tuti
> titu
> titi
> FIN
tuti
titu
titi

GREP
Permet de rechercher une chaîne de caractères dans un ensemble de fichiers:

pierre@pcpf % grep BODY *.html
adresses.html:<BODY>
adresses.html:</BODY>agences.html:<BODY>
agences.html:</BODY>
cartes.html:<BODY>
cartes.html:</BODY>
...

Grâce au shell, on peut également affecter une variable avec le résultat de la commande:

SITELINE=`grep [${i} ] $SCRIPTFILE`
 

SED
SED (Stream EDitor) est la version ligne du fameux éditeur ED (connu déjà sur Mini6 !). Il est d'un maniement assez peu convivial mais il est très puissant pour le traitement des chaînes de caractères:

pierre@pcpf % echo tititoto
tititoto
pierre@pcpf % echo tititoto | sed -e 's/toto/tutu/g'
tititutu

Cette commande est à la base du décodage des paramètres d'un CGI en shell.

Structure d'un script CGI

4.1 Structure de base
Voici un exemple simple de CGI minimal:

#!/bin/sh
echo MIME-Version: 1.0
echo Content-type: text/html
echo
echo "<BODY>Script minimal</BODY>"

Les 3 commandes echo au début du fichier sont indispensables pour indiquer au serveur la version et le type de données MIME (Multimedia Internet Mail Extensions) retourné par la CGI. Dans notre cas, la CGI génère dynamiquement une page HTML (d'ou le type text/html). Cette CGI n'utilise pas de variable et affiche simplement un message dans la page HTML.

4.2 Transfert des données entre
HTML et CGI

Dans le cas de pages plus évoluées, le système de CGI utilise 2 méthodes pour transférer les données saisies dans la page HTML:

o La méthode GET. Dans cette méthode, les données sont transmises à la CGI grâce à la variable d'environnement QUERY_STRING sous la forme:

NOM=Pierre&PRENOM=Ficheux&SUBMIT=Envoyer

Il faut donc extraire les données en utilisant par exemple l'utilitaire sed dans une ligne du type:

QUERY=`echo $QUERY_STRING | sed -e "s/=/='/g" -e "s/&/';/g" -e "s/+/ /g" -e "s/%0d%0a/<BR>/g" -e "s/$/'/" `

pour obtenir la variable QUERY que l'on peut évaluer dans le shell courant:

NOM='Pierre';PRENOM='Ficheux';SUBMIT='Envoyer'

ce qui crée 3 nouvelles variables NOM, PRENOM et SUBMIT utilisables dans la suite du shell-script.

o La méthode GET est utilisée par défaut. Elle n'est cependant pas recommandée dans le cas du passage d'une grande quantité de données à cause des troncatures possibles de la chaîne QUERY_STRING.

o La méthode POST. Les données sont alors transmises par l'entrée standard de la CGI. On validera alors une pseudo variable QUERY_STRING par

read QUERY_STRING

Dans ce cas la, la taille des données n'est pas limitée. On peut connaître cette taille par le contenu de la variable CONTENT_LENGTH. L'utilisation de la méthode POST nécessite une commande HTML du type:

<FORM ACTION="action.cgi" METHOD="POST">

Le type de méthode utilisé peut-être récupéré par la CGI dans le variable REQUEST_METHOD.

Voici donc une nouvelle version du script capable de traiter une méthode GET ou POST:

#!/bin/sh

echo MIME-Version: 1.0
echo Content-type: text/html
echo

if [ "$REQUEST_METHOD" = "POST" ]; then
 read QUERY_STRING
fi
QUERY=`echo $QUERY_STRING | sed -e "s/=/='/g" -e "s/&/';/g" -e "s/+/ /g" -e "s/%0d%0a/<BR>/g" -e "s/$/'/" `
eval $QUERY

cat <<EOF
<BODY>
<H1>Je m'appelle $NOM $PRENOM</H1>
</BODY>
EOF

4.3 Test de CGI sans serveur

Le système de CGI nécessite à priori un serveur HTTP pour fonctionner. Il est cependant possible de tester une CGI en mode GET en dehors de l'utilisation d'un serveur et même d'un client HTTP. Pour cela il suffit:

- d'affecter la variable QUERY_STRING avec une chaîne respectant la syntaxe de transfert des données CGI.

pierre@gwladys % QUERY_STRING="NOM=Pierre&PRENOM=Ficheux&SUBMIT=Envoyer"
pierre@gwladys % export QUERY_STRING

- d'exécuter la CGI dans le shell courant:
pierre@gwladys % ./action.cgi
MIME-Version: 1.0
Content-type: text/html

<BODY>
<H1>Je m'appelle Pierre Ficheux</H1>
</BODY>

On peut également utiliser l'option <EM>-x</EM> pour utiliser le mode trace du
shell:

pierre@gwladys % sh -x action.cgi
+ echo MIME-Version: 1.0
MIME-Version: 1.0
+ echo Content-type: text/html
Content-type: text/html
+ echo

+ [  = POST ]
+ echo NOM=Pierre&PRENOM=Ficheux&SUBMIT=Envoyer
+ sed -e s/=/='/g -e s/&/';/g -e s/+/ /g -e s/%0d%0a/<BR>/g -e s/$/'/
QUERY=NOM='Pierre';PRENOM='Ficheux';SUBMIT='Envoyer'
+ eval NOM='Pierre';PRENOM='Ficheux';SUBMIT='Envoyer'
NOM=Pierre
PRENOM=Ficheux
SUBMIT=Envoyer
+ cat
<BODY>
<H1>Je m'appelle Pierre Ficheux</H1>
</BODY>
<BODY>
<H1>Je m'appelle Pierre Ficheux</H1>
</BODY>
 

Bibliographie

http://www.univ-rennes1.fr/CRI/doc-html/cgi-ismap/GUT.html (World-Wide Web et les formulaires électroniques)

http://www.htmlhelp.com/reference/wilbur (Manuel de référence HTML-3.2)

 

 

 


© Copyright 2000 Diamond Editions/Linux magazine France - Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; A copy of the license is included in the section entitled "GNU Free Documentation License".