Linux, il WWW e tutto il resto (cgi-bin inclusi).
Parte III

di Nando Santagata
lac0658@comune.bologna.it

Nell'ultimo articolo di questa serie abbiamo scritto la nostra prima form e il nostro primo programma di interfaccia con il Web, usando il protocollo CGI.
Ora proviamo a scrivere qualcosa di più impegnativo e, allo stesso tempo, cerchiamo di riutilizzare quanto è stato già fatto nel campo dei programmi CGI.

Proviamo a scrivere un programma che legga le informazioni inserite in una form e le spedisca al Webmaster del nostro server.
Uno scopo secondario di questo articolo è quello di mostrare che al mondo esistono moduli già fatti e che possiamo riutilizzarli per rendere più veloce la scrittura del nostro programma, aumentarne la leggibilità, diminuirne il numero di linee e quindi il numero dei possibili bugs (secondo la sperimentata filosofia che meno si scrive e meno si sbaglia :-)

Cominciamo con il comporre una pagina HTML: la nostra form per la richiesta dei dati:

<html>
<head>
<title>Messaggio al  webmaster del Mio Server</title>
</head>
<body>
<h2>Messaggio al  webmaster del Mio Server</h2>
<hr>
Se si desidera richiedere nuovi servizi, suggerire modifiche o dare dei
pareri sull'impostazione del Server WEB, non esitare ad inserire un
messaggio nell'apposita casella.
<p>
Assicurati di aver riempito <b>TUTTI</b> i campi.

<FORM METHOD="POST" ACTION="/cgi-bin/posta_in_arrivo">
<PRE>Cognome e Nome:        <INPUT NAME="name" size=30><p>
<PRE>Indirizzo di E-mail:   <INPUT NAME="email" size=30><p>
<PRE>Oggetto del commento:  <INPUT NAME="object" size=30>
<INPUT NAME="recipient" VALUE="root" TYPE="hidden">
<P>Testo:<br>
<TEXTAREA NAME="content" ROWS=6 COLS=60></TEXTAREA> <P>
 <INPUT TYPE="submit" VALUE="spedisci"> (per spedire il tuo messaggio)<br>
 <INPUT TYPE="reset" VALUE="cancella"> (per ripulire la pagina). 
<hr>
</form>
</body>
</html>
Questa volta abbiamo usato anche un campo nascosto: recipient, che contiene il nome dell'utente che dovrà ricevere la nostra mail.
I campi nascosti sono utili per registrare all'interno della pagina dei parametri che dovranno essere letti dal programma CGI.
Questa particolarità è molto utile se abbiamo una serie di forms generate da programma. I campi nascosti possono servire per passare informazioni di stato che altrimenti non potremmo conservare tra una chiamata e l'altra, a causa della mancanza di stato propria del protocollo HTTP.

In questo caso avremmo potuto codificare il nome del destinatario nel nostro script, ma probabilmente un file HTML è più facile da manutenere di un programma per chi non è un programmatore, come probabilmente non lo sarà il nostro cliente-tipo.

Salviamo questa pagina come

/var/httpd/htdocs/email.html.

A questo punto dobbiamo scrivere il programma posta_in_arrivo, come specificato nella form, e salvarlo sotto

/var/httpd/cgi-bin/

Come mio solito scriverò questo programma in Perl, usando il modulo CGI::Form.

Questo modulo non fa parte della distribuzione base del Perl 5, ma va prelevato da uno dei siti del CPAN (Comprehensive Perl Archive Network).

Proviamo subito questo modulo, scrivendo un programmino che legga la form e ci mostri quali variabili il server passa al nostro programma e come le informazioni immesse nella pagina arrivano a noi:

#!/usr/bin/perl

use CGI::Form;
$query = new CGI::Form;
print $query->header;
print $query->FmtRequest();
Questo è proprio ciò che si definisce un ``programmino''!
In realtà il nostro script è in grado di leggere sia il contenuto della form, che le variabili di environment che riceve dal server http e di crearci al volo una pagina HTML di risposta.

Per provarlo salviamolo come

/var/httpd/cgi-bin/posta_in_arrivo

poi, come root, diamo il comando:

$ chmod a+x posta_in_arrivo

ATTENZIONE: i programmi CGI di solito vengono eseguiti come utente nobody, quindi non è sufficiente assegnare permessi di esecuzione al solo possessore del file!

A questo punto potete eseguire una prova: se state lavorando sulla stessa macchina su cui avete il server Web leggete il documento all'indirizzo:

http://localhost/email.html

riempite i campi e premete il pulsante spedisci. Il vostro server vi risponderà con qualcosa del tipo:



Request Parameters: (CGI::Request version 2.72)
content = 'Messaggio per il Webmaster.'
email = 'lac0658@iperbole.bologna.it'
name = 'Santagata Nando'
recipient = 'root'
subject = 'Hello!'



CGI Interface Variables: (CGI::Base version 2.73)
AUTH_TYPE = undefined
CONTENT_LENGTH = '128'
CONTENT_TYPE = 'application/x-www-form-urlencoded'
GATEWAY_INTERFACE = 'CGI/1.1'
HTTP_ACCEPT = 'image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*'
HTTP_REFERER = 'http://localhost/email.html'
HTTP_USER_AGENT = 'Mozilla/2.0b5 (X11; I; Linux 1.2.13 i486)'
PATH_INFO = undefined
PATH_TRANSLATED = undefined
QUERY_STRING = 'name=Santagata+Nando&amp;email=lac0658@iperbole.bologna.it&amp;subject=Errori+vari&amp;recipient=root&amp;content=Ci+sono+alcuni+piccoli+errori.'
REMOTE_ADDR = '127.0.0.1'
REMOTE_HOST = 'localhost'
REMOTE_IDENT = undefined
REMOTE_USER = undefined
REQUEST_METHOD = 'POST'
SCRIPT_NAME = '/cgi-bin/posta_in_arrivo'
SERVER_NAME = 'localhost'
SERVER_PORT = '80'
SERVER_PROTOCOL = 'HTTP/1.0'
SERVER_SOFTWARE = 'NCSA/1.3'
URI = http://localhost:80/cgi-bin/posta_in_arrivo


Come vedete ci sono alcune informazioni che potrebbero esserci utili, ad esempio il REMOTE_ADDR o il HTTP_USER_AGENT (utile quest'ultimo se vogliamo cercare di massimizzare le prestazioni del singolo client).

Procediamo: abbiamo visto che il nostro programma riesce a passarci tutte le informazioni che sono state immesse. Ora non ci resta che comporre una mail.
Riscriviamo così il nostro programma:

#!/usr/bin/perl

use CGI::Form;
$query = new CGI::Form;

$email     = $query->param('email');
$recipient = $query->param('recipient');
$name      = $query->param('name');
$object    = $query->param('object');
$content   = $query->param('content');

open (SENDMAIL, "|/usr/bin/sendmail -oi -t")
	|| die "Non posso usare sendmail: $!";

print SENDMAIL <<QQ_EOF_QQ;
To: $recipient
From: $name <$email>
Cc: $name <$email>
Subject: $object

Commento da parte di $name all'indirizzo $email:

$content

QQ_EOF_QQ

close SENDMAIL;
if ($?){
	warn "sendmail non e` stato eseguito correttamente: $?"
}

print $query->header;
print $query->start_html(-title => 'Grazie del commento',
                         -BGCOLOR => "#00A0A0");

print <<EOF;
<h3>Ti ringraziamo per la collaborazione</h3>
<p>
Il tuo messaggio è stato recapitato al Webmaster e una copia
ti è stata spedita per conoscenza.
EOF
 
print $query->end_html;
Salviamo questo programma sempre con il nome:

/var/httpd/cgi-bin/posta_in_arrivo

e diamogli permessi di esecuzione per tutti gli utenti.
Quando questo programma verrà eseguito spedirà due mail: una al Webmaster (in questo caso `root') e una per conoscenza all'utente che ha scritto il commento.

Analizziamo questo programma. La linea:

 $query = new CGI::Form;
crea un oggetto di tipo CGI::Form e contemporaneamente legge il contenuto della form, sia che il metodo associato fosse il GET, sia che fosse il POST.

Le righe seguenti:

$email     = $query->param('email');
$recipient = $query->param('recipient');
$name      = $query->param('name');
$object    = $query->param('object');
$content   = $query->param('content');
scrivono i valori dei campi all'interno di alcune variabili.
A questo punto non resta che invocare sendmail per spedire la posta:
open (SENDMAIL, "|/usr/bin/sendmail -oi -t")
	|| die "Non posso usare sendmail: $!";
in questo modo viene aperta una pipe sulla quale si potrà scrivere come se fosse un file qualsiasi.
Le righe successive eseguono una serie di print sul file handle SENDMAIL e alla fine la pipe viene chiusa, non senza aver controllato il codice di errore restituito dal sistema.

Non ci resta ora che avvertire l'utente che tutto è andato a buon fine. La riga:

print $query->header;
si limita a sputare fuori la formula magica:

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

che rende noto al nostro server che stiamo scrivendo una pagina HTML.
La riga:

print $query->start_html(-title => 'Grazie del commento',
                         -BGCOLOR => "#00A0A0");
compone la testata della pagina, con il titolo e l'indicazione del colore da assegnare allo sfondo, mentre la riga:
print $query->end_html;
emette un:

</body></html>

A rileggerci alla prossima. Nel frattempo per dubbi, congratulazioni, correzioni, insulti & altro scrivete a Nando Santagata. -------------------------------------------------------------------

CPAN (Comprehensive Perl Archive Network)

Questi sono alcuni dei siti che partecipano al CPAN: qui potrete trovare sia il modulo che ho citato nell'articolo, che molti altri (ormai sono decine e decine).

I link qui di seguito vi porteranno nella directory base del CPAN, che può essere diversa da sito a sito.
Il modulo CGI è (a partire dalla directory di base):

modules/by-module/CGI/CGI-modules-2.74.tar.gz

ftp.delphi.com
ftp.cis.ufl.edu
ftp.cs.ruu.nl
ftp.demon.co.uk
orpheu.ci.uminho.pt
janus.sedl.org
ftp.sterling.com
ftp.pasteur.fr
ftp.is.co.za

--------------------------------------------------------------------------- Nando Santagata: Telemastica & infornatica