Linux e WWW

Come usare i CGI-BIN

di Nando Santagata
lac0658@iperbole.bologna.it

Nel primo articolo di questa serie abbiamo visto come installare e configurare un server Web.
Ora occupiamoci di qualcosa di più interessante delle semplici pagine html.

Probabilmente tutti saprete che è possibile scrivere delle particolari pagine, chiamate forms, che rendono possibile l'interazione tra l'utente e il server Web.
Nelle forms l'utente può inserire dei dati che successivamente vengono elaborati da programmi che girano sul server e che possono generare come risposta una pagina html che viene rispedita all'utente.

Ma cosa può fare un programma così concepito?
Un uso molto comune delle forms è quello di creare front end per la consultazione di archivi, quindi i programmi sul server andranno ad interrogare i databases e genereranno pagine html contenenti i dati estratti.

...e come devono essere scritti i programmi per poter interagire con il server Web?
Esiste uno standard, chiamato CGI (Common Gateway Interface), che stabilisce come leggere i dati che l'utente ha immesso nella form e come passare i risultati delle elaborazioni al server, perché siano rispediti al client.

In quali linguaggi si possono scrivere questi programmi?
Basta un qualsiasi linguaggio che sia in grado di leggere lo standard input e le variabili di environment del sistema e di scrivere sullo standard output. Devo dire che non mi viene in mente nessun linguaggio che non risponda a questi requisiti minimi.

Il linguaggio che in questo momento gode il maggior favore per la scrittura dei cgi-bin (i programmi per l'interfacciamento al Web vengono così chiamati dal nome della directory in cui di solito risiedono) è il Perl.
I motivi per questa scelta sono vari: si impara molto facilmente a programmare in Perl se si conosce il C e si è pratici del linguaggio delle shell Unix, è un linguaggio interpretato con un ottimo set di strumenti per lo sviluppo (ha un ottimo debugger, un profiler ed altri tools), è molto più portabile del C, è un linguaggio di livello più alto del C, pur essendo abbastanza veloce e sono disponibili moduli aggiuntivi che ne permettono l'interfacciamento a tutti i più diffusi databases in commercio (Oracle, Ingres, Sybase, Informix, ecc.).

Dopo questa sviolinata sul Perl, che è comunque nella dotazione standard di qualsiasi installazione di Linux, passiamo alle forms e all'effettivo sviluppo di qualche programmino di esempio.

Come prima cosa dobbiamo scrivere una form:


<!-- Prima prova di form -->

<html>
<head>
<title>Ricerca indirizzi e numeri di telefono</title>
</head>
<body>

<h3><center>Front end per la ricerca di indirizzi e numeri di
telefono nel mio piccolo database di amici</center></h3>
<p>
Inserisci il nome, o parte di questo nel campo e premi
il tasto "Ricerca".
<p>

<form method=post action="http:/cgi-bin/ricerca">
Nome: <input size=25 maxlength=25 name="nome">
<p>

<input type="submit" value="Ricerca">

</body>
</html>

Se inseriamo questo file nella directory delle pagine html (il default per il server NCSA è /var/httpd/htdocs) come prova.html, possiamo usare il nostro client per verificarla caricandola come:

http://localhost/prova.html

La pagina che visualizzerete avrà un campo per l'immissione del nome della persona di cui volete avere l'indirizzo e numero di telefono e un tasto che serve per passare i dati immessi al cerver.

Analizziamo la struttura della form. Ciò che la identifica come tale è la riga:

<form method=post action="http:/cgi-bin/ricerca">

Questa riga inoltre dice al server che il programma che deve essere lanciato quando l'utente preme il tasto Ricerca si chiama ricerca e risiede nella directory /cgi-bin.
La directory è specificata a partire dalla radice delle directory accessibili dal server Web, quindi, sempre nel caso di una configurazione standard, a partire da /var/httpd.
Il metodo con cui il server passerà i dati al programma cgi-bin è il post, cioè i dati saranno passati come standard input nella forma campo=valore.

La riga:

Nome: <input size=25 maxlength=25 name="nome">

definisce un campo per l'immissione di un testo, lungo abbastanza per permettere di inserire 25 caratteri, presentato in modo tale che tutti i 25 caratteri siano visibili sullo schermo.
È possibile specificare campi scrollabili, cioè in cui è possibile immettere una stringa più lunga di quanto non sia possibile mostrare sullo schermo: provate a modificare i parametri size e maxlength.

Il parametro name dichiara il nome con cui questo campo sarà accessibile dal programma cgi-bin.

La riga:

<input type="submit" value="Ricerca">

definisce un pulsante di tipo submit, in grado di spedire il contenuto della form al server. La stringa che comparirà sul pulsante è data dal valore assegnato al parametro value.

A questo punto dobbiamo progettare un database di nomi, indirizzi e numeri di telefono da interrogare con il nostro cgi-bin.
Questo database sarà una cosina molto semplice. Tanto per iniziare propongo un file contenente i campi che ci interessano separati da ;.

Proviamo con qualcosa del genere:

Andrea Bianchi;V. dei ciclamini, 42;123456
Franco Neri;V. dei salici, 69;234567
Mario Rossi;V. delle rose, 13;345678
Filippo Verdi;V.le dei pini,17;456789

Salviamo questo file come /var/httpd/cgi-bin/dati, magari come appartenente a root e con permissions 644 (lettura e scrittura per il possessore e sola lettura per gli altri).

Il programma cgi-bin in Perl è questo:

#!/usr/bin/perl

read(STDIN,$_,$ENV{'CONTENT_LENGTH'});
($nome, $valore) = split(/=/);

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

$res = open(DB, "dati");
if($res == 0){
    print "Non posso aprire il database.<br>Contattare il Webmaster.\n";
    print "</body></html>\n";
    exit;
}

$flag = 0;
while(<DB>){
    ($nome, $via, $tel) = split(/;/);
    if($nome =~ /$valore/i){
        $flag = 1;
        last;
    }
}

if($flag == 1){
    print "Il signor/la signora <b>$nome</b><br>\n";
    print "abita in <b>$via</b><br>\n";
    print "Telefono: <b>$tel</b><p>\n";
}else{
    print "Mi dispiace, ma non ho trovato alcun <b>$valore</b> nel database!\n";
}
print "</body></html>\n";

Salviamo questo file come /var/httpd/cgi-bin/ricerca, con permesso di lettura ed esecuzione per tutti.

Analizziamo il programma. La riga:

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

legge lo standard input, per un numero di caratteri specificato nella variabile di environment CONTENT_LENGTH. Questa variabile viene settata dal server http nell'environment del programma chiamato (il nostro script "ricerca").

Il risultato della lettura viene immagazzinato nella variabile di default $_.
La riga successiva:

($nome, $valore) = split(/=/);

esamina la stringa contenuta nella variabile di default ed utilizza il carattere '=' come elemento separatore al fine di dividerla.
I due pezzi risultanti vengono immagazzinati delle variabili $nome e $valore che conterranno rispettivamente il nome del campo della form ed il suo contenuto.

A questo punto spariamo al server http la nostra parola magica:

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

che segnala la presenza di una pagina html di risposta. Questa pagina la genereremo run-time da programma.

Possiamo addirittura passare le prime istruzioni html al server:

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

in modo da segnalargli l'inizio della pagina.

Lo scopo del programma è quello di interrogare il nostro database, che altro non è che un normalissimo file.
Come in un qualsiasi altro linguaggio di programmazione apriamo il file e gestiamo eventuali errori:

$res = open(DB, "dati");
if($res == 0){
    print "Non posso aprire il database.<br>Contattare il Webmaster.\n";
    print "</body></html>\n";
    exit;
}

Il file aperto si chiama dati e il file descriptor associato, DB, verrà utilizzato per le operazioni di I/O.

Ora leggiamo il database e usciamo dal loop quando troviamo il nome cercato o quando arriviamo alla fine del file:

$flag = 0;
while(<DB>){
    ($nome, $via, $tel) = split(/;/);
    if($nome =~ /$valore/i){
        $flag = 1;
        last;
    }
}

Il costrutto <DB> legge una riga dal file descriptor indicato nella variabile di default $_. All'interno dell'istruzione while dà risultato falso quando si arriva a fine file, causando l'uscita dal loop.

L'istruzione:

$nome =~ /$valore/i

testa il contenuto della variabile $nome rispetto alla stringa contenuta nella variabile $valore.
Il test è fatto considerando il contenuto di $valore come una regular expression, del tipo di quelle che è possibile usare con il grep.
La i alla fine indica che il test deve essere fatto in modo case insensitive.

A questo punto non ci resta che presentare il risultato della nostra ricerca:

if($flag == 1){
    print "Il signor/la signora <b>$nome</b><br>\n";
    print "abita in <b>$via</b><br>\n";
    print "Telefono: <b>$tel</b><p>\n";
}else{
    print "Mi dispiace, ma non ho trovato alcun <b>$valore</b> nel database!\n";
}
print "</body></html>\n";

Con poche righe di Perl abbiamo realizzato uno strumento abbastanza sofisticato per la ricerca all'interno di un database.
Vale la pena notare che le ricerche possono essere condotte sia per stringhe (ad esempio inserendo nel campo la stringa rossi), sia per espressioni regolari (ad esempio inserendo nel campo la stringa r.ss., che può trovare sia i dati del signor Rossi, che del signor Russo).
Il programma per il momento restituisce i dati del primo soggetto trovato nel database.

A rileggerci alla prossima puntata.

Per dubbi, congratulazioni, correzioni, insulti & altro scrivete a Nando Santagata.