La programmation CGI

Retour


PRINCIPE

Le code HTML ne permet pas à lui seul de créer des documents dynamiques ou interactifs capables par exemple d'indiquer la date du jour ou de donner le résultat d'une requête sur une base de données.

L'idée de la programmation CGI (Common Gateway Interface) est de construire le document HTML correspondant à un lien hypertexte au moment même où l'on clique sur ce lien. Le document est envoyé au client au fur et à mesure de sa construction sans jamais être stocké dans un fichier.

Ceci est réalisé à l'aide de liens exécutables. Le client indique le nom d'un fichier à l'aide d'une URL, non pour recevoir le contenu, mais pour demander son exécution au serveur. Ce dernier exécute le programme indiqué et renvoie au client la sortie standard de ce programme (c'est à dire ce que l'on aurait obtenu à l'écran en lançant le programme à la mains sur une ligne de commande).

Pour pouvoir être exécutés à distance, les scripts CGI doivent être installés sur le serveur dans un répertoire appelé /cgi-bin. Le serveur sait alors qu'il s'agit d'un script exécutable.

Tout langage capable d'avoir une sortie standard peut être utilisé pour écrire des scripts cgi. Sous Unix: C, Fortran, Perl, Tcl ou les shells.

Sous PC-Windows: Visual C++, Visual Basic.

Sous Mac: Apple Script.

Pour des raisons de sécurité, il faut veiller à limiter le nombre de personnes autorisées à écrire des scripts, limiter le nombre de répertoires pouvant accueillir des scripts, éviter de rendre les codes sources accessibles sur le réseau, éviter les commandes permettant de lancer des sous processus qui pourraient être détournées.

LA SORTIE STANDARD

La sortie standard se compose de deux parties séparées par une ligne vide, l'en-tête et le corps. Le corps contient les données du document, l'en-tête indique leur type.

Un des principaux rôles de l'en-tête est d'indiquer le type de données que le script va générer. Il peut s'agir par exemple de code HTML, mais aussi d'image ou de son. La syntaxe utilisée est la syntaxe MIME.

Type/sous-type Description
text/html De loin le plus utilisé, il indique au client que les données doivent être interprétées comme des commandes HTML
text/plain Il indique au client que les données sont du texte simple et ne doivent pas être interprétées.
image/gif image/jpeg image/x-xbitmap Ce sont le trois types d'images supportées en général par le clients WWW sans appel à un lecteur externe;
audio/basic C'est un type englobant tous les formats son .au et .snd. En général, le client fait appel à un programme externe pour entendre ces sons.
application/postscript Données au format postscript. En général, le client fait appel à un interpréteur.

* Exemple: un compteur d'accès écrit en C

#include <stdio.h>
#include <stdlib.h>
/* Definition bitmap des 10 chiffres */
/* Chaque chiffre est correspond a 8*16 points */
char *digits[] =
{"0xff","0xff","0xff","0xc3","0x99","0x99","0x99","0x99",
"0x99","0x99","0x99","0x99","0xc3","0xff","0xff","0xff",
"0xff","0xff","0xff","0xcf","0xc7","0xcf","0xcf","0xcf",
"0xcf","0xcf","0xcf","0xcf","0xcf","0xff","0xff","0xff",
"0xff","0xff","0xff","0xc3","0x99","0x9f","0x9f","0xcf",
"0xe7","0xf3","0xf9","0xf9","0x81","0xff","0xff","0xff",
"0xff","0xff","0xff","0xc3","0x99","0x9f","0x9f","0xc7",
"0x9f","0x9f","0x9f","0x99","0xc3","0xff","0xff","0xff",
"0xff","0xff","0xff","0xcf","0xcf","0xc7","0xc7","0xcb",
"0xcb","0xcd","0x81","0xcf","0x87","0xff","0xff","0xff",
"0xff","0xff","0xff","0x81","0xf9","0xf9","0xf9","0xc1",
"0x9f","0x9f","0x9f","0x99","0xc3","0xff","0xff","0xff",
"0xff","0xff","0xff","0xc7","0xf3","0xf9","0xf9","0xc1",
"0x99","0x99","0x99","0x99","0xc3","0xff","0xff","0xff",
"0xff","0xff","0xff","0x81","0x99","0x9f","0x9f","0xcf",
"0xcf","0xe7","0xe7","0xf3","0xf3","0xff","0xff","0xff",
"0xff","0xff","0xff","0xc3","0x99","0x99","0x99","0xc3",
"0x99","0x99","0x99","0x99","0xc3","0xff","0xff","0xff",
"0xff","0xff","0xff","0xc3","0x99","0x99","0x99","0x99",
"0x83","0x9f","0x9f","0xcf","0xe3","0xff","0xff","0xff"
};

main () {
FILE *fp = NULL;
FILE *out = NULL;
char numb[7];
char hold[8]= "00000000";
char cc[]= "0";
int holdlen;
int num, len, x, y, c, i;

/* On récupère l'ancien nombre d'accès dans le fichier count.txt */
fp = fopen("count.txt","r");
fgets(numb, 8, fp);
fclose(fp);
sscanf(numb,"%d",&num);
/* On incrémente de 1 le nombre d'accès */
num++;
/* On met a jour le fichier count.txt */
out = fopen("count.txt","w");
fprintf(out,"%d",num);
fclose(out);
/* On met Si le nombre d'accès est 1234 alors numb="1234" */
/* et hold="00001234" */
len = strlen(numb);
for (i=0; i<len; i++) {
hold[8-len+i] = numb[i];
}

/* Création de l'en-tête de la sortie standard du script */
printf ("Content-type: image/x-xbitmap%c%c",10,10);

/* Création du corps */
printf ("#define count_width 56\n");
printf ("#define count_height 16\n");
printf ("static char count_bits[] = {\n");

/* Le bitmap est écrit sur la sortie standard */
/* ligne par ligne */
for (x=0; x<16; x++) {
for (y=1; y<8; y++) {
cc[0]=hold[y];
sscanf(cc,"%d",&c);
printf(digits[((c*16)+x)]);
if (y<7) { printf(", "); }
}
if (x==15) { printf("};");}{printf(",\n");}
}
printf("\n");

}


Ce script sera compilé par la commande:

cc -o compteur compteur.c


Puis il sera placé dans le répertoire /cgi.bin. Il faudra alors créer un fichier (ouvert en écriture!) et y insérer une première valeur (1 pour l'initialiser)

FORMULAIRES ET CGI


* L'encodage des données


La programmation CGI appliquée aux formulaires se différencie de la programmation CGI classique dans la mesure où le client doit envoyer au serveur l'ensemble des données saisies par l'utilisateur. Ces données sont codées selon le format suivant:

nom_champ1=valeur1&nom_champ2=valeur2...


où chaque couple (nom du champ, valeur de saisie) est séparé par un '&' et où nom du champ et valeur de saisie sont séparés par un '='.

De plus, cette chaîne est URL-encodée: les espaces y sont remplacés par des '+' et les caractères spéciaux par leur valeur hexadécimale sous le format '%xx'.

Exemple:

NOM=de+la+Maisonneuuve&PRENOM=Ren%E9

* Le transport


Une fois la chaîne des données construite, elle est envoyée au serveur selon la méthode indiquée par l'attribut METHOD de la balise <FORM>.
C'est la méthode POST qui est la plus répandue. La chaîne de caractères contenant l'ensemble des couples (nom du champ, valeur) est envoyée au serveur par une séquence HTTP spéciale. Avant de lancer le script cgi, le serveur positionne la variable d'environnement CONTENT-LENGHT pour indiquer la longueur de la chaîne qu'il a reçue.

Exemple en Perl:

read(STDIN, $in, $ENV{'CONTENT_LENGTH'});


* La programmation


Le premier travail consiste à récupérer la chaîne de caractères contenant les données, à la décoder, puis à en extraire les couples (nom du champ, valeur saisie). Ce travail peut être fait en utilisant les bibliothèques existantes: la fonction ReadParse en Perl dans la librairie cgi-lib.pl et la fonction get_form_entries() en C dans la librairie libcgi.
Après traitement, les résultats sont envoyés sous forme d'une page HTML. À titre d'exemple, voici le programme en Perl qui donne accès à une page de menu écrite en HTML après avoir vérifié la validité du mot de passe entré par l'utilisateur.

#!/usr/local/bin/perl
#chargement de la librairie cgi-lib.pl
require "cgi-lib.pl";

MAIN:
{
# Lit les variables entrées par l'utilisateur grâce à la fonction ReadParse

&ReadParse(*input);

# Imprime l'en-tête grâce à la fonction PrintHeader

print &PrintHeader;
print "<html><head>\n";
print "<title>mot de passe</title>\n";
print "</head>\n<body>\n";
print "<FONT SIZE=+1>\n";

# Vérifie le mot de passe. S'il est exact affiche le menu, sinon affiche 'code erronné'

($code = $input{'code'});

if ($code eq "enfer") {

print "<H1>BIBLIOTH&Eacute;QUE PRIV&Eacute;E</H1>\n";
print "<HR>\n";
print "<UL>\n";
print "<LI><A HREF=\"http://www.univ-paris8.fr/~hyperion/bibliotheque/prince.html\">Le petit Prince</A> (Saint-Exup&eacute;ry)\n";
print "<LI>Mort &agrave; cr&eacute;dit (C&eacute;line)\n";
print "<LI><A HREF=\"http://www.univ-paris8.fr/~hyperion/bibliotheque/Rigodon.html\">Rigodo</A> (C&eacute;line)\n";
print "<LI><A HREF=\"http://www.univ-paris8.fr/~hyperion/bibliotheque/Lightman.html\">QuandEinstein r&ecirc;vait </A> (Alan Lightman)\n";
print "<LI><A HREF=\"http://www.univ-paris8.fr/~hyperion/bibliotheque/jardin.html\">Le jardin aux sentiers qui bifurquent </A> (Borges)\n";
print "</UL>\n";
print "<A HREF=\"http://www.univ-paris8.fr/~hyperion/biblio.htm\">Retour biblioth&egrave;que</A>\n";
print "<HR>\n";
}
else {
print "code erron&eacute;<P>\n";
print "<A HREF=\"http://www.univ-paris8.fr/~hyperion/prive.html\">Retour </A><P>\n";
print "<HR>\n";
}

print "</body></html>\n";

Retour