[successivo] [precedente] [inizio] [fine] [indice generale] [violazione GPL] [translators] [docinfo] [indice analitico] [volume] [parte]
In questo capitolo si introduce la programmazione per la realizzazione di programmi gateway in Perl. Il primo problema che si incontra quando si realizzano programmi del genere è l'analisi delle stringhe di richiesta, per arrivare alla loro scomposizione in modo da poterne gestire i dati. Per questo si utilizzano frequentemente librerie già pronte e ben collaudate, ma in questo capitolo si vuole mostrare come lavorare partendo da zero.
Prima di iniziare a realizzare programmi CGI, occorre fare mente locale alla situazione in cui si trova il programma, specialmente per la verifica del funzionamento dello stesso. Il programma viene eseguito attraverso una forma di intermediazione: è il servente HTTP a metterlo in funzione ed è sempre il servente a ricevere l'output che poi viene restituito al programma cliente.
In questa situazione, lo standard error del programma viene perduto, assieme alle eventuali segnalazioni di errore di qualunque tipo.
Prima di provare il funzionamento di un programma del genere, per quanto banale sia, occorre averlo analizzato sintatticamente attraverso gli strumenti che mette a disposizione il compilatore o l'interprete. L'utilizzo di Perl come linguaggio di programmazione, non richiedendo una fase di compilazione, tende a fare dimenticare che è necessaria un'analisi sintattica. Se non si verifica il programma, magari solo per un punto e virgola fuori posto, ci si trova di fronte al solito messaggio: «500 Errore interno del servente».
Nello stesso modo, sarebbe bene che il programma che si realizza sia in grado di funzionare in qualche modo anche al di fuori dell'ambiente creato dal servente HTTP.
È il caso di ricordare che il controllo sintattico di un programma Perl si ottiene nel modo seguente:
perl -c programma_perl
oppure ancora meglio con:
perl -c -w programma_perl
Si è accennato al fatto che un programma gateway non può fare a meno di occuparsi della decodifica delle stringhe di richiesta. Questo problema si scompone almeno nelle fasi seguenti:
la suddivisione delle coppie nome=valore;
la separazione delle coppie;
la decodifica URI.
I dati provenienti da un modulo FORM sono uniti assieme attraverso l'uso del simbolo e-commerciale (&). Per suddividerli si può creare un array dei vari elementi utilizzando la funzione split
@coppia = split ('&', $richiesta); |
Le coppie nome=valore sono stringhe unite assieme attraverso il simbolo di assegnamento (=). La suddivisione avviene agevolmente attraverso la scomposizione in un array di due soli elementi. Solitamente si utilizza la scorciatoia seguente:
($nome, $valore) = split ('=', $coppia[$i]); |
In pratica, si scompone il contenuto di un elemento dell'array @coppia, visto nella sezione precedente.
La decodifica URI si compone di due fasi:
sostituzione del simbolo + con lo spazio;
sostituzione dei codici %hh con il carattere corrispondente.
$valore =~ tr/+/ /; $nome =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack('c',hex($1))/ge; $valore =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack('c',hex($1))/ge; |
Quello che segue è un esempio molto semplificato di due subroutine in grado, rispettivamente, di estrapolare le informazioni da una richiesta in modalità GET e in modalità POST. Le due subroutine restituiscono un hash (l'array associativo di Perl) corrispondente alle coppie di dati.
#====================================================================== # mini-lib.pl # Routine Perl utilizzabili da un programma gateway. #====================================================================== #====================================================================== # &Decodifica_GET () # Decodifica il contenuto della variabile $QUERY_STRING e lo # restituisce in un hash. #---------------------------------------------------------------------- sub Decodifica_GET { local ($richiesta) = $ENV{'QUERY_STRING'}; local (@coppia) = (); local ($elemento) = ""; local ($nome) = ""; local ($valore) = ""; local (%DATI) = (); #------------------------------------------------------------------ # Suddivide la richiesta in un array di coppie «nome=valore». #------------------------------------------------------------------ @coppia = split ('&', $richiesta); #------------------------------------------------------------------ # Elabora ogni coppia contenuta nell'array. #------------------------------------------------------------------ foreach $elemento (@coppia) { #-------------------------------------------------------------- # Scompone la coppia. #-------------------------------------------------------------- ($nome, $valore) = split ('=', $elemento); #-------------------------------------------------------------- # Trasforma «+» in spazio. #-------------------------------------------------------------- $valore =~ tr/+/ /; #-------------------------------------------------------------- # Trasforma «%hh» nel carattere corrispondente. #-------------------------------------------------------------- $nome =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack('c',hex($1))/ge; $valore =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack('c',hex($1))/ge; #-------------------------------------------------------------- # Aggiunge la coppia decodificata in un hash. #-------------------------------------------------------------- $DATI{$nome} = $valore; } #------------------------------------------------------------------ # Restituisce l'hash delle coppie ( nome => valore ). #------------------------------------------------------------------ return (%DATI); } #====================================================================== # &Decodifica_POST () # Decodifica quanto proveniente dallo standard input e lo # restituisce in un hash. #---------------------------------------------------------------------- sub Decodifica_POST { local ($richiesta) = ""; local (@coppia) = (); local ($elemento) = ""; local ($nome) = ""; local ($valore) = ""; local (%DATI) = (); #------------------------------------------------------------------ # Legge lo standard input. #------------------------------------------------------------------ read (STDIN, $richiesta, $ENV{CONTENT_LENGTH}); #------------------------------------------------------------------ # Suddivide la richiesta in un array di coppie «nome=valore». #------------------------------------------------------------------ @coppia = split ('&', $richiesta); #------------------------------------------------------------------ # Elabora ogni coppia contenuta nell'array. #------------------------------------------------------------------ foreach $elemento (@coppia) { #-------------------------------------------------------------- # Scompone la coppia. #-------------------------------------------------------------- ($nome, $valore) = split ('=', $elemento); #-------------------------------------------------------------- # Trasforma «+» in spazio. #-------------------------------------------------------------- $valore =~ tr/+/ /; #-------------------------------------------------------------- # Trasforma «%hh» nel carattere corrispondente. #-------------------------------------------------------------- $nome =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack('c',hex($1))/ge; $valore =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack('c',hex($1))/ge; #-------------------------------------------------------------- # Aggiunge la coppia decodificata in un hash. #-------------------------------------------------------------- $DATI{$nome} = $valore; } #------------------------------------------------------------------ # Restituisce l'hash delle coppie ( nome => valore ). #------------------------------------------------------------------ return (%DATI); } #====================================================================== # Trattandosi di una libreria, l'ultima riga deve restituire un # valore equiparabile a TRUE. #---------------------------------------------------------------------- 1; #====================================================================== |
Un programma banale che potrebbe fare uso di questa libreria, è il seguente. Si occupa solo di restituire i dati ottenuti dall'hash contenente le coppie nome=>valore.
#!/usr/bin/perl #====================================================================== # form.pl #====================================================================== require ('mini-lib.pl'); print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Metodo $ENV{REQUEST_METHOD}</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Metodo $ENV{REQUEST_METHOD}</H1>\n"); print STDOUT ("<PRE>\n"); if ($ENV{REQUEST_METHOD} eq 'GET') { %DATI = &Decodifica_GET; } elsif ($ENV{REQUEST_METHOD} eq 'POST') { %DATI = &Decodifica_POST; } else { print STDOUT ("Il metodo della richiesta non è gestibile.\n"); } @nomi = keys (%DATI); foreach $nome (@nomi) { print STDOUT ("$nome = $DATI{$nome}\n"); } print STDOUT ("</PRE>\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); #====================================================================== |
Il programma form.pl, appena mostrato, incorpora inizialmente la libreria presentata prima, mini-lib.pl, quindi, a seconda del metodo utilizzato per la richiesta, chiama la subroutine adatta. Al termine, restituisce semplicemente l'elenco dei dati ottenuti.
In questa sezione si vogliono mostrare alcuni esempi elementari di applicazioni CGI. Si tratta dell'accesso pubblico alla documentazione interna del sistema operativo attraverso apropos, whatis e man.
Per questi tre tipi di interrogazioni si prepara un solo file HTML di partenza, contenente tre moduli FORM distinti, ognuno dei quali invia una richiesta a un diverso programma gateway specializzato.
Segue il sorgente del file manuali.html
contenente i tre moduli FORM necessari per richiamare i programmi gateway in grado di fornire documentazione interna.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <!-- manuali.html --> <HTML> <HEAD> <TITLE>Manualistica</TITLE> </HEAD> <BODY> <H1>Manualistica</H1> <FORM ACTION="/cgi-bin/apropos.pl" METHOD="GET"> <P>apropos <INPUT NAME="apropos" SIZE="30"> <INPUT TYPE="submit" VALUE="Invio"></P> </FORM> <FORM ACTION="/cgi-bin/whatis.pl" METHOD="GET"> <P>whatis <INPUT NAME="whatis" SIZE="30"> <INPUT TYPE="submit" VALUE="Invio"></P> </FORM> <FORM ACTION="/cgi-bin/man.pl" METHOD="GET"> <P>man <SELECT NAME="sezione"> <OPTION VALUE="" SELECTED="selected">predefinito <OPTION VALUE="1">comandi utente <OPTION VALUE="2">chiamate di sistema <OPTION VALUE="3">chiamate di libreria <OPTION VALUE="4">dispositivi <OPTION VALUE="5">formati dei file <OPTION VALUE="6">giochi <OPTION VALUE="7">varie <OPTION VALUE="8">comandi di sistema <OPTION VALUE="9">routine del kernel </SELECT> <INPUT NAME="man" SIZE="30"> <INPUT TYPE="submit" VALUE="Invio"></P> </FORM> </BODY> </HTML> |
La figura 163.1 mostra in che modo appaia questo modulo.
Ognuno dei tre moduli FORM permette di indicare una stringa da utilizzare per ottenere informazioni. Ogni modulo FORM ha il proprio tasto di invio indipendente con il quale si decide implicitamente il tipo di informazione che si vuole avere: apropos, whatis o man. Dei tre tipi di modulo FORM, quello della richiesta per i file delle pagine di manuale è un po' diverso, dal momento che potrebbe essere necessario indicare la sezione.
Segue il sorgente del programma apropos.pl, che si occupa di interrogare il sistema attraverso il comando apropos e di restituire un file HTML con la risposta.
#!/usr/bin/perl #====================================================================== # apropos.pl #====================================================================== #---------------------------------------------------------------------- # Incorpora la libreria di decodifica dei dati. #---------------------------------------------------------------------- require ('mini-lib.pl'); #====================================================================== # &Metodo_non_gestibile () #---------------------------------------------------------------------- sub Metodo_non_gestibile { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Errore</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Metodo $ENV{REQUEST_METHOD} non gestibile.</H1>\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== # Inizio del programma. #====================================================================== local (%DATI) = (); local ($risposta) = ""; #---------------------------------------------------------------------- # Decodifica i dati in funzione del tipo di metodo della richiesta. #---------------------------------------------------------------------- if ($ENV{REQUEST_METHOD} eq 'GET') { %DATI = &Decodifica_GET; } elsif ($ENV{REQUEST_METHOD} eq 'POST') { %DATI = &Decodifica_POST; } else { &Metodo_non_gestibile; } #---------------------------------------------------------------------- # Rinvia la richiesta a apropos e ne restituisce l'esito. #---------------------------------------------------------------------- if (open (APROPOS, "apropos $DATI{apropos} |")) { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>apropos $DATI{apropos}</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>apropos $DATI{apropos}</H1>\n"); print STDOUT ("<PRE>\n"); while ($risposta = <APROPOS>) { print $risposta; } print STDOUT ("</PRE>\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } else { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Errore</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Errore</H1>\n"); print STDOUT ("Si è manifestato un errore durante l'inoltro "); print STDOUT ("della richiesta.\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== 1; #====================================================================== |
Il programma è molto semplice: interpreta la richiesta ottenuta e ne estrae solo il valore abbinato all'informazione apropos; quindi esegue il comando apropos leggendone l'output che viene restituito in una pagina HTML molto semplice. Il punto più delicato di questo programma sta quindi nell'istruzione seguente:
open (APROPOS, "apropos $DATI{apropos} |") |
Con questa viene abbinato un flusso di file a un comando il cui standard output verrà letto successivamente e riemesso all'interno di una pagina HTML con il ciclo seguente:
while ($risposta = <APROPOS>) { print STDOUT ($risposta); } |
Segue il sorgente del programma whatis.pl, che si occupa di interrogare il sistema attraverso il comando whatis e di restituire un file HTML con la risposta. È molto simile a apropos.pl appena mostrato, per cui qui alcune parti vengono tralasciate (in corrispondenza dei puntini di sospensione).
#!/usr/bin/perl #====================================================================== # whatis.pl #====================================================================== #---------------------------------------------------------------------- # Incorpora la libreria di decodifica dei dati. #---------------------------------------------------------------------- require ('mini-lib.pl'); #====================================================================== # &Metodo_non_gestibile () #---------------------------------------------------------------------- sub Metodo_non_gestibile { ... } #====================================================================== # Inizio del programma. #====================================================================== local (%DATI) = (); local ($risposta) = ""; #---------------------------------------------------------------------- # Decodifica i dati in funzione del tipo di metodo della richiesta. #---------------------------------------------------------------------- if ($ENV{REQUEST_METHOD} eq 'GET') { %DATI = &Decodifica_GET; } elsif ($ENV{REQUEST_METHOD} eq 'POST') { %DATI = &Decodifica_POST; } else { &Metodo_non_gestibile; } #---------------------------------------------------------------------- # Rinvia la richiesta a man e ne restituisce l'esito. #---------------------------------------------------------------------- if (open( WHATIS, "whatis $DATI{whatis} |")) { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>whatis $DATI{whatis}</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>whatis $DATI{whatis}</H1>\n"); print STDOUT ("<PRE>\n"); while ($risposta = <WHATIS>) { print STDOUT ($risposta); } print STDOUT ("</PRE>\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } else { ... } #====================================================================== 1; #====================================================================== |
Come si vede, si tratta della stessa cosa già vista nell'altro programma, con la differenza che la richiesta viene fatta al comando whatis invece che a apropos.
Segue il sorgente del programma man.pl, che si occupa di interrogare il sistema operativo attraverso il comando man e di restituire un file HTML con la risposta. È molto simile agli altri due appena mostrati, per cui, anche in questo caso, alcune parti vengono tralasciate.
#!/usr/bin/perl #====================================================================== # man.pl #====================================================================== #---------------------------------------------------------------------- # Incorpora la libreria di decodifica dei dati. #---------------------------------------------------------------------- require ('mini-lib.pl'); #====================================================================== # &Metodo_non_gestibile () #---------------------------------------------------------------------- sub Metodo_non_gestibile { ... } #====================================================================== # Inizio del programma. #====================================================================== local (%DATI) = (); local ($risposta) = ""; #---------------------------------------------------------------------- # Decodifica i dati in funzione del tipo di metodo della richiesta. #---------------------------------------------------------------------- if ($ENV{REQUEST_METHOD} eq 'GET') { %DATI = &Decodifica_GET; } elsif ($ENV{REQUEST_METHOD} eq 'POST') { %DATI = &Decodifica_POST; } else { &Metodo_non_gestibile; } #---------------------------------------------------------------------- # Rinvia la richiesta a man e ne restituisce l'esito. #---------------------------------------------------------------------- if (open (MAN, "man $DATI{sezione} $DATI{man} | col -bx |")) { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>man $DATI{sezione} $DATI{man}</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>man $DATI{sezione} $DATI{man}</H1>\n"); print STDOUT ("<PRE>\n"); while ($risposta = <MAN>) { print STDOUT ($risposta); } print STDOUT ("</PRE>\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } else { ... } #====================================================================== 1; #====================================================================== |
La differenza fondamentale sta nel fatto che qui si utilizzano due informazioni: il nome del comando di cui si vuole ottenere la pagina di manuale e il numero della sezione. Un'altra cosa da osservare è il modo in cui è stato predisposto il comando: attraverso una pipeline necessaria a eliminare i caratteri di controllo che non potrebbero essere visualizzati nella pagina HTML.
open (MAN, "man $DATI{sezione} $DATI{man} | col -bx |")
La situazione più comune in cui sono utili i moduli HTML, è quella in cui si vuole guidare l'inserimento di dati che poi generano un messaggio di posta elettronica: l'utente potrebbe scrivere un messaggio senza passare per la compilazione del modulo, ma in tal modo non ci sarebbe nessun controllo interattivo.
Viene mostrato un sistema molto semplice attraverso cui un utente può ordinare un prodotto, detto Articolo x, indicando il proprio recapito e i dati della propria carta di credito. Tutto quanto viene mostrato semplificando il procedimento al massimo, per esempio si presume che venga ordinata una sola unità dell'articolo prescelto. Le fasi dell'ordinazione possono distinguersi nel modo seguente:
invio del modulo compilato da parte dell'utente;
verifica da parte del programma gateway e richiesta di conferma dei dati introdotti;
conferma da parte dell'utente;
invio dei dati in forma di messaggio di posta elettronica all'utente root;
avviso del completamento dell'operazione.
La prima fase viene svolta utilizzando un file HTML, ordine.html
, che richiama il programma ordine.pl; tutte le altre fasi sono svolte direttamente dal programma.
Segue il sorgente del file ordine.html
.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <!-- ordine.html --> <HTML> <HEAD> <TITLE>Ordine attraverso FORM</TITLE> </HEAD> <BODY> <H1>Ordine attraverso FORM</H1> <FORM ACTION="/cgi-bin/ordine.pl" METHOD="POST"> <P><INPUT TYPE="hidden" NAME="modulo" VALUE="ordine base"> <P> Articolo ordinato: <SELECT NAME="articolo"> <OPTION VALUE="0" SELECTED="selected">Nessuno <OPTION VALUE="A">Articolo A <OPTION VALUE="B">Articolo B <OPTION VALUE="C">Articolo C <OPTION VALUE="D">Articolo D </SELECT> </P> <H2>Dati dell'ordinante</H2> <P> nome: <INPUT NAME="nome" SIZE="25"> cognome: <INPUT NAME="cognome" SIZE="25"> </P> <P> via: <INPUT NAME="via" SIZE="20"> n.: <INPUT NAME="n" SIZE="5"><BR> c.a.p.: <INPUT NAME="cap" SIZE="5"> città: <INPUT NAME="citta" SIZE="15"><BR> e-mail: <INPUT NAME="email" SIZE="30"> </P> <P> carta: VISA <INPUT TYPE="radio" NAME="carta" VALUE="VISA" CHECKED="checked"> American Express <INPUT TYPE="radio" NAME="carta" VALUE="American Express"> <INPUT NAME="carta_num" SIZE="20" MAXLENGTH="19"> </P> <P> <INPUT TYPE="submit" VALUE="Invio dell'ordine"> </P> </FORM> </BODY> </HTML> |
La figura 163.5 mostra un esempio di compilazione del modulo.
Segue il sorgente del programma ordine.pl che svolge tutte le fasi di controllo, invio e conferma dell'ordine inserito a partire dal file ordine.html
. La descrizione del suo comportamento è inserita nei commenti del sorgente stesso. In particolare, all'inizio sono riportate le subroutine, mentre l'inizio vero e proprio del programma è nella parte finale.
#!/usr/bin/perl #====================================================================== # ordine.pl #====================================================================== #---------------------------------------------------------------------- # Incorpora la libreria di decodifica dei dati. #---------------------------------------------------------------------- require ('mini-lib.pl'); #====================================================================== # &Metodo_non_gestibile () #---------------------------------------------------------------------- sub Metodo_non_gestibile { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Errore</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Metodo $ENV{REQUEST_METHOD} non gestibile.</H1>\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== # &Verifica_dati () #---------------------------------------------------------------------- sub Verifica_dati { if ($DATI{articolo} eq "0") { return (0); } if ($DATI{nome} eq "") { return (0); } if ($DATI{cognome} eq "") { return (0); } if ($DATI{via} eq "") { return (0); } if ($DATI{cap} eq "") { return (0); } if ($DATI{citta} eq "") { return (0); } if ($DATI{email} eq "") { return (0); } if ($DATI{carta_num} eq "") { return (0); } return (1); } #====================================================================== # &Dati_nascosti () #---------------------------------------------------------------------- sub Dati_nascosti { print STDOUT ("<INPUT TYPE=\"hidden\" NAME=\"articolo\" VALUE=\"$DATI{articolo}\">\n"); print STDOUT ("<INPUT TYPE=\"hidden\" NAME=\"nome\" VALUE=\"$DATI{nome}\">\n"); print STDOUT ("<INPUT TYPE=\"hidden\" NAME=\"cognome\" VALUE=\"$DATI{cognome}\">\n"); print STDOUT ("<INPUT TYPE=\"hidden\" NAME=\"via\" VALUE=\"$DATI{via}\">\n"); print STDOUT ("<INPUT TYPE=\"hidden\" NAME=\"n\" VALUE=\"$DATI{n}\">\n"); print STDOUT ("<INPUT TYPE=\"hidden\" NAME=\"cap\" VALUE=\"$DATI{cap}\">\n"); print STDOUT ("<INPUT TYPE=\"hidden\" NAME=\"citta\" VALUE=\"$DATI{citta}\">\n"); print STDOUT ("<INPUT TYPE=\"hidden\" NAME=\"email\" VALUE=\"$DATI{email}\">\n"); print STDOUT ("<INPUT TYPE=\"hidden\" NAME=\"carta\" VALUE=\"$DATI{carta}\">\n"); print STDOUT ("<INPUT TYPE=\"hidden\" NAME=\"carta_num\" VALUE=\"$DATI{carta_num}\">\n"); } #====================================================================== # &Richiedi_conferma () #---------------------------------------------------------------------- sub Richiedi_conferma { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Conferma</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Conferma dati dell'ordinazione.</H1>\n"); print STDOUT ("Si prega di controllare i dati e di confermare se tutto "); print STDOUT ("appare in ordine.\n"); print STDOUT ("<PRE>"); print STDOUT ("Nominativo: $DATI{nome} $DATI{cognome}\n"); print STDOUT ("Indirizzo: $DATI{via} $DATI{n}\n" ); print STDOUT (" $DATI{cap} $DATI{citta}\n" ); print STDOUT (" $DATI{email}\n" ); print STDOUT ("Carta: $DATI{carta} $DATI{carta_num}\n"); print STDOUT ("Articolo ordinato: $DATI{articolo}\n"); print STDOUT ("</PRE>"); print STDOUT ("<FORM ACTION=\"/cgi-bin/ordine.pl\" METHOD=\"POST\">\n"); print STDOUT ("<INPUT TYPE=\"hidden\" NAME=\"modulo\" VALUE=\"ordine conferma\">\n"); &Dati_nascosti; print STDOUT ("<INPUT TYPE=\"submit\" VALUE=\"Conferma i dati e l'ordine\">\n"); print STDOUT ("</FORM>\n"); print STDOUT ("Se i dati non sono come desiderato, si prega di ritornare\n"); print STDOUT ("alla <A HREF=\"/ordine.html\">compilazione del modulo</A>.\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== # &Dati_insufficienti () #---------------------------------------------------------------------- sub Dati_insufficienti { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Errore</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>I dati inseriti nel modello sono insufficienti.</H1>\n"); print STDOUT ("Si prega di controllare e aggiungere i dati mancanti.\n"); print STDOUT ("<P><A HREF=\"/ordine.html\">"); print STDOUT ("Ritorna al modulo di ordinazione</A>\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== # &Modulo_errato () #---------------------------------------------------------------------- sub Modulo_errato { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Errore</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Il modulo inviato non è previsto.</H1>\n"); print STDOUT ("Si prega di utilizzare il \n"); print STDOUT ("<A HREF=\"/ordine.html\">"); print STDOUT ("modulo d'ordine standard</A>.\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== # &E_mail_errore () #---------------------------------------------------------------------- sub E_mail_errore { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Errore</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Impossibile inviare l'ordine</H1>\n"); print STDOUT ("Si prega di scusare l'inconveniente.\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== # &E_mail ( <destinatario>, <oggetto>, <contenuto> ) #---------------------------------------------------------------------- sub E_mail { local ($destinatario) = $_[0]; local ($oggetto) = $_[1]; local ($contenuto) = $_[2]; local ($sendmail) = "/bin/mail $destinatario"; unless (open (EMAIL, "| $sendmail ")) { return (0); } print EMAIL ("$oggetto\n"); print EMAIL ("\n\n"); print EMAIL ("$contenuto\n"); print EMAIL (".\n"); close( EMAIL ); } #====================================================================== # &Invio_ordine () #---------------------------------------------------------------------- sub Invio_ordine { local ($ordine) = ""; $ordine = $ordine . "Nominativo: $DATI{nome} $DATI{cognome}\n" ; $ordine = $ordine . "Indirizzo: $DATI{via} $DATI{n}\n" ; $ordine = $ordine . " $DATI{cap} $DATI{citta}\n"; $ordine = $ordine . " $DATI{email}\n" ; $ordine = $ordine . "Carta: $DATI{carta} $DATI{carta_num}\n" ; $ordine = $ordine . "Articolo ordinato: $DATI{articolo}\n" ; if (&E_mail ('root@localhost', 'Ordine da modulo FORM ordine.pl', $ordine)) { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Conferma invio</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Conferma invio</H1>\n"); print STDOUT ("Il Vostro ordine è stato inviato.\n"); print STDOUT ("Grazie.\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } else { &E_mail_errore; } } #====================================================================== # Inizio del programma. #====================================================================== local (%DATI) = (); #---------------------------------------------------------------------- # Decodifica i dati in funzione del tipo di metodo della richiesta. #---------------------------------------------------------------------- if ($ENV{REQUEST_METHOD} eq 'GET') { %DATI = &Decodifica_GET; } elsif ($ENV{REQUEST_METHOD} eq 'POST') { %DATI = &Decodifica_POST; } else { &Metodo_non_gestibile; } #---------------------------------------------------------------------- # Attraverso il dato memorizzato con il nome «modulo» si determina # a che punto sia la compilazione. # «ordine base» è il modulo di partenza, mentre «ordine conferma» # è quello generato da questo programma per conferma. #---------------------------------------------------------------------- if ($DATI{modulo} eq 'ordine base') { #------------------------------------------------------------------ # Prima fase: si verificano i dati e si chiede conferma all'utente. #------------------------------------------------------------------ if (&Verifica_dati) { &Richiedi_conferma; } else { &Dati_insufficienti; } } elsif ($DATI{modulo} eq 'ordine conferma') { #------------------------------------------------------------------ # Seconda fase: si verificano i dati e si invia l'ordine. #------------------------------------------------------------------ if (&Verifica_dati) { &Invio_ordine; } else { &Dati_insufficienti; } } else { #------------------------------------------------------------------ # È stato indicato un modulo non previsto. #------------------------------------------------------------------ &Modulo_errato; } #====================================================================== 1; #====================================================================== |
La figura 163.6 mostra in che modo viene richiesta la conferma dei dati inseriti come dall'esempio della figura precedente.
Lo scopo di questo programma è generare e inviare un messaggio di posta elettronica all'utente root. Quello che segue è il messaggio generato dall'esempio mostrato sopra.
Date: Sun, 1 Feb 1998 08:04:30 +0100 From: Nobody <nobody@localhost> Message-Id: <199802010704.IAA00463@localhost> To: root@localhost Ordine da modulo FORM ordine.pl Nominativo: Pinco Pallino Indirizzo: Biglie 1 99999 Sferopoli ppinco@palloni.com Carta: VISA 1234-5678-9012-3456 Articolo ordinato: A |
Il programma ordine.pl si occupa solo di registrare un ordine attraverso l'invio di un messaggio di posta elettronica. Lo si potrebbe modificare in modo da aggiungere una registrazione su un file. Basta modificare la subroutine Invio_ordine().
#====================================================================== # ordine2.pl #====================================================================== use Fcntl ':flock'; # Importa le costanti di gestione dei file. #---------------------------------------------------------------------- # Incorpora la libreria di decodifica dei dati. #---------------------------------------------------------------------- require ('mini-lib.pl'); ... ... #====================================================================== # &Invio_ordine () #---------------------------------------------------------------------- sub Invio_ordine { local ($ordine) = ""; $ordine = $ordine . "Nominativo: $DATI{nome} $DATI{cognome}\n" ; $ordine = $ordine . "Indirizzo: $DATI{via} $DATI{n}\n" ; $ordine = $ordine . " $DATI{cap} $DATI{citta}\n"; $ordine = $ordine . " $DATI{email}\n" ; $ordine = $ordine . "Carta: $DATI{carta} $DATI{carta_num}\n" ; $ordine = $ordine . "Articolo ordinato: $DATI{articolo}\n" ; if (&E_mail ('root@localhost', 'Ordine da modulo FORM ordine.pl', $ordine)) { #-------------------------------------------------------------- # Memorizza l'ordine. #-------------------------------------------------------------- if (open (ORDINI, ">> /var/log/ordini")) { if (flock (ORDINI, LOCK_EX)) { seek (ORDINI, 0, 2); print ORDINI ("$ordine\n"); } close (ORDINI); } #-------------------------------------------------------------- # Avvisa l'utente. #-------------------------------------------------------------- print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Conferma invio</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Conferma invio</H1>\n"); print STDOUT ("Il Vostro ordine è stato inviato.\n"); print STDOUT ("Grazie.\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } else { &E_mail_errore; } } ... ... |
In pratica, l'ordine viene registrato nel file /var/log/ordini
, che viene bloccato (lock) in modo esclusivo per evitare sovrascritture simultanee da parte di altri processi.
open (ORDINI, ">> /var/log/ordini"); if (flock (ORDINI, LOCK_EX)) { seek (ORDINI, 0, 2); print ORDINI ("$ordine\n"); } close (ORDINI); |
Per poter utilizzare la costante LOCK_EX, all'inizio del programma è stata inserita l'istruzione seguente:
use Fcntl ':flock'; |
Il programma proposto per la gestione di ordini a distanza è troppo semplice per poter essere utilizzato come esempio reale di un sistema del genere. Il punto debole più grave è l'assenza di controlli dettagliati sui dati. Per renderlo più efficace occorrerebbe modificare la gestione degli errori, per informare l'utente in modo più preciso di un eventuale errore commesso nella compilazione di un modulo.
In pratica, occorre entrare nella logica della programmazione di procedure aziendali vere e proprie, con tutta la cura che è necessario dare alle maschere di inserimento dei dati e alle segnalazioni di errore relative, in modo da guidare facilmente l'utente nel loro utilizzo.
Il problema che si avverte immediatamente dopo aver compreso il meccanismo della programmazione CGI è quello dell'interfacciamento con una base di dati. A partire dal capitolo 324 è descritto PostgreSQL e a questo DBMS si vuole fare riferimento negli esempi di questa sezione.
Un programma CGI che debba accedere a dati attraverso un DBMS deve essere predisposto per un certo protocollo di comunicazione con il DBMS stesso. Generalmente si tratta di incorporare una libreria adatta e di utilizzare le sue funzioni. Nel caso di Perl si tratta di utilizzare un modulo adatto e per la connessione con PostgreSQL si usa il modulo Pg.
Se si intendono eseguire solo delle interrogazioni elementari, può darsi che basti utilizzare un programma cliente elementare attraverso una pipeline. PostgreSQL offre il programma cliente psql che può essere usato anche per questo scopo.
Per introdurre il problema con un esempio pratico, si suppone di disporre di una base di dati con una tabella contenente il listino di alcuni prodotti. Il programma che si vuole scrivere deve essere in grado di ricevere una stringa di ricerca e di passarla al cliente psql, in modo che questo restituisca gli articoli che corrispondono al modello.
Dalla descrizione fatta, potrebbe sembrare che dal punto di vista della programmazione il problema sia molto semplice. In realtà, tutto il lavoro lo deve fare il programma psql.
È bene ricordare che un DBMS deve gestire in proprio gli utenti per poter definire le politiche di accesso ai dati che vengono amministrati. I programmi CGI che vengono proposti interagiscono con un servente PostgreSQL locale, utilizzando i privilegi dell'utente nobody, ovvero l'utente anonimo del sistema operativo.(1)
Perché tali programmi possano funzionare occorre che questo utente sia aggiunto anche nel DBMS; nel caso di PostgreSQL si tratta di usare il programma createuser (vedere 324.2.3). Inoltre, è necessario che le tabelle che si utilizzano permettano l'accesso da parte di questo utente, attraverso una politica opportuna di REVOKE e GRANT.
Per facilitare la lettura, vengono riassunte di seguito le azioni da compiere per aggiungere l'utente nobody attraverso il programma createuser.
#
su postgres
[Invio]
postgres$
createuser
[Invio]
Enter name of user to add--->
nobody
[Invio]
Enter user's postgres ID or RETURN to use unix user ID: 65534 ->
[Invio]
Is user "nobody" allowed to create databases (y/n)
n
[Invio]
Is user "nobody" allowed to add users? (y/n)
n
[Invio]
createuser: nobody was successfully added
La tabella contenente il listino da interrogare deve essere costruita attraverso gli strumenti di PostgreSQL. Dovendo realizzare qualcosa che deve essere accessibile a tutti gli utenti HTTP, occorre organizzare le cose opportunamente. Si procede con la creazione di una base di dati adatta a contenere dati pubblici; si sceglie il nome: pubblico.
#
su postgres
[Invio]
$
createdb pubblico
[Invio]
Per preparare ciò che serve si utilizza psql specificando di voler accedere alla base di dati appena creata.
$
psql pubblico
[Invio]
Attraverso psql si crea la tabella denominata Listino e gli si inseriscono dei dati. Le istruzioni possono essere simili a quelle seguenti.
CREATE TABLE Listino ( Codice char(7), Descrizione varchar(160), Prezzo integer ); INSERT INTO Listino VALUES ( 'resis1k', 'Resistenze 1kOhm', 100 ); INSERT INTO Listino VALUES ( 'resis2k', 'Resistenze 2kOhm', 100 ); INSERT INTO Listino VALUES ( 'resis3k', 'Resistenze 3kOhm', 100 ); ... INSERT INTO Listino VALUES ( 'con10kp', 'Condensatore 10000 pf', 200 ); INSERT INTO Listino VALUES ( 'con20kp', 'Condensatore 20000 pf', 200 ); INSERT INTO Listino VALUES ( 'con30kp', 'Condensatore 30000 pf', 200 ); ... INSERT INTO Listino VALUES ( 'mo09pm', 'Monitor mono 9 pollici', 200000 ); ... INSERT INTO Listino VALUES ( 'mo09pc', 'Monitor colore 9 pollici', 400000 ); ... REVOKE ALL ON Listino FROM PUBLIC; GRANT ALL ON Listino TO postgres; GRANT SELECT ON Listino TO PUBLIC; |
Come si può osservare, prima viene creata la tabella con sole tre colonne: codice, descrizione e prezzo. Successivamente vengono inserite le varie righe contenenti ognuna l'informazione di un certo articolo. Infine, anche se potrebbe non essere indispensabile, è il caso di regolare i permessi di utilizzo di questa tabella: vengono revocati tutti i privilegi; quindi viene permesso qualunque intervento da parte dell'utente postgres (il DBA predefinito); infine viene concessa la lettura a tutti.
pubblico=>
\q
[Invio]
La soluzione proposta del problema è molto semplice: il programma listino.pl fa tutto da solo. Se viene avviato senza informazioni, restituisce un modulo da compilare; quindi, da quel punto in poi è comunque tutto sotto il suo controllo.
#!/usr/bin/perl #====================================================================== # listino.pl #====================================================================== #---------------------------------------------------------------------- # Incorpora la libreria di decodifica dei dati. #---------------------------------------------------------------------- require ('mini-lib.pl'); #====================================================================== # &Metodo_non_gestibile () #---------------------------------------------------------------------- sub Metodo_non_gestibile { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Errore</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<H1>Metodo $ENV{REQUEST_METHOD} non gestibile.</H1>\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== # &Verifica_dati () #---------------------------------------------------------------------- sub Verifica_dati { if ($DATI{ricerca} eq "") { return (0); } return (1); } #====================================================================== # &Ricerca_listino () #---------------------------------------------------------------------- sub Ricerca_listino { local ($query_sql) = "SELECT * FROM Listino WHERE descrizione LIKE '$DATI{ricerca}';"; local (@risposta) = (); if (open (LISTINO, "psql -d pubblico -H -q -c \"$query_sql\" |")) { @risposta = <LISTINO>; } else { @risposta = { "La stringa richiesta è incomprensibile\n" }; } print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Consultazione di un listino attraverso PostgreSQL</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Consultazione del listino</H1>\n"); print STDOUT ("<FORM ACTION=\"/cgi-bin/listino.pl\" METHOD=\"GET\">\n"); print STDOUT ("<P>\n"); print STDOUT ("Inserire una stringa di ricerca per ottenere gli articoli la\n"); print STDOUT ("cui descrizione coincide: ``%'' corrisponde a una stringa\n"); print STDOUT ("indefinita; ``_'' corrisponde a un singolo carattere\n"); print STDOUT ("indefinito.</P>\n"); print STDOUT ("<P>\n"); print STDOUT ("<INPUT NAME=\"ricerca\" SIZE=\"25\">\n"); print STDOUT ("<INPUT TYPE=\"submit\" VALUE=\"Cerca\"></P>\n"); print STDOUT ("</FORM>\n"); print STDOUT ("<P><HR></P>\n"); print STDOUT ("<H3>Risultato della ricerca con il modello: ``$DATI{ricerca}''</H3>\n"); print STDOUT ("\n"); print STDOUT ("@risposta"); print STDOUT ("\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== # &Dati_insufficienti () # In pratica, invia il FORM da compilare. #---------------------------------------------------------------------- sub Dati_insufficienti { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Consultazione di un listino attraverso PostgreSQL</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Consultazione del listino</H1>\n"); print STDOUT ("<FORM ACTION=\"/cgi-bin/listino.pl\" METHOD=\"GET\">\n"); print STDOUT ("<P>\n"); print STDOUT ("Inserire una stringa di ricerca per ottenere gli articoli la\n"); print STDOUT ("cui descrizione coincide: ``%'' corrisponde a una stringa\n"); print STDOUT ("indefinita; ``_'' corrisponde a un singolo carattere\n"); print STDOUT ("indefinito.</P>\n"); print STDOUT ("<P>\n"); print STDOUT ("<INPUT NAME=\"ricerca\" SIZE=\"25\">\n"); print STDOUT ("<INPUT TYPE=\"submit\" VALUE=\"Cerca\"></P>\n"); print STDOUT ("</FORM>\n"); print STDOUT ("\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== # Inizio del programma. #====================================================================== local (%DATI) = (); #---------------------------------------------------------------------- # Decodifica i dati in funzione del tipo di metodo della richiesta. #---------------------------------------------------------------------- if ($ENV{REQUEST_METHOD} eq 'GET') { %DATI = &Decodifica_GET; } elsif ($ENV{REQUEST_METHOD} eq 'POST') { %DATI = &Decodifica_POST; } else { &Metodo_non_gestibile; } #---------------------------------------------------------------------- # Prima fase: si verificano i dati. #---------------------------------------------------------------------- if (&Verifica_dati) { &Ricerca_listino; } else { &Dati_insufficienti; } #====================================================================== 1; #====================================================================== |
Vale la pena di analizzare la subroutine Ricerca_listino, in cui si svolge l'interrogazione della tabella del listino. L'istruzione SQL per la richiesta è la seguente:
SELECT * FROM Listino WHERE descrizione LIKE '$DATI{ricerca}'; |
In pratica, $DATI{ricerca} viene sostituito con una stringa fornita attraverso il modulo HTML.
Per eseguire la richiesta viene utilizzato psql in una pipeline, fornendo l'istruzione di interrogazione attraverso la riga di comando (opzione -c), specificando che si vogliono ottenere tabelle organizzate attraverso la struttura HTML 3.0 (opzione -H).
open (LISTINO, "psql -d pubblico -H -q -c \"$query_sql\" |") |
La figura 163.7 mostra un possibile risultato di una ricerca fatta con la stringa %sato%, corrispondente a tutto ciò che contiene la sequenza «sato» (per esempio i condensatori).
Quando le esigenze di programmazione diventano più complesse è bene accedere direttamente attraverso il programma che si scrive al servizio di PostgreSQL. Ciò può essere fatto attraverso un programma che incorpori la libreria LibPQ; nel caso di Perl si tratta di utilizzare il modulo Pg (che deve essere stato installato opportunamente).
Per iniziare a comprendere l'utilizzo di questo componente di Perl, viene mostrato l'esempio del listino proposto nella sezione precedente, con le dovute modifiche. Qui vengono mostrate solo le differenze.
#!/usr/bin/perl #====================================================================== # listino2.pl #====================================================================== #---------------------------------------------------------------------- # Utilizza il modulo Pg, per l'utilizzo delle librerie LibPQ di # PostgreSQL. #---------------------------------------------------------------------- use Pg; #---------------------------------------------------------------------- # Incorpora la libreria di decodifica dei dati. #---------------------------------------------------------------------- require ('mini-lib.pl'); ... |
Nella prima parte deve essere inserita l'istruzione con cui si dichiara l'utilizzo di Pg: use Pg.
... #====================================================================== # &Ricerca_listino () #---------------------------------------------------------------------- sub Ricerca_listino { local ($query_sql) = "SELECT * FROM Listino WHERE descrizione LIKE '$DATI{ricerca}'"; local (@tabella) = (); local ($PGconnessione); local ($i); local ($j); #------------------------------------------------------------------ # Apre la connessione con il servente PostgreSQL locale, # utilizzando la base di dati «pubblico». #------------------------------------------------------------------ $PGconnessione = Pg::connectdb ("dbname = pubblico"); #------------------------------------------------------------------ # Verifica che la connessione sia avvenuta e quindi esegue # l'interrogazione. #------------------------------------------------------------------ if ($PGconnessione->status == PGRES_CONNECTION_OK) { #-------------------------------------------------------------- # Invia la richiesta utilizzando la funzione Pg::doQuery che # fa tutto da sola (non occorre eseguire PQclear). #-------------------------------------------------------------- Pg::doQuery ($PGconnessione, "$query_sql", \@tabella); } #------------------------------------------------------------------ # La connessione non ha bisogno di essere chiusa. #------------------------------------------------------------------ #------------------------------------------------------------------ # Procede con la restituzione del risultato. #------------------------------------------------------------------ print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Consultazione di un listino attraverso PostgreSQL</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Consultazione del listino</H1>\n"); print STDOUT ("<FORM ACTION=\"/cgi-bin/listino2.pl\" METHOD=\"GET\">\n"); print STDOUT ("<P>\n"); print STDOUT ("Inserire una stringa di ricerca per ottenere gli articoli la\n"); print STDOUT ("cui descrizione coincide: ``%'' corrisponde a una stringa\n"); print STDOUT ("indefinita; ``_'' corrisponde a un singolo carattere\n"); print STDOUT ("indefinito.</P>\n"); print STDOUT ("<P>\n"); print STDOUT ("<INPUT NAME=\"ricerca\" SIZE=\"25\">\n"); print STDOUT ("<INPUT TYPE=\"submit\" VALUE=\"Cerca\"></P>\n"); print STDOUT ("</FORM>\n"); print STDOUT ("<P><HR></P>\n"); print STDOUT ("<H3>Risultato della ricerca con il modello: ``$DATI{ricerca}''</H3>\n"); print STDOUT ("\n"); print STDOUT ("<table>\n"); print STDOUT ("<TR>"); print STDOUT ("<TH>Codice</TH>"); print STDOUT ("<TH>Descrizione</TH>"); print STDOUT ("<TH>Prezzo unitario</TH>"); print STDOUT ("</TR>\n"); for ($i = 0; $i <= $#tabella; $i++) { print STDOUT ("<TR>"); for ($j = 0; $j <= $#{$tabella[$i]}; $j++) { print STDOUT ("<TD>$tabella[$i][$j]</TD>"); } print STDOUT ("</TR>\n"); } print STDOUT ("</table>\n"); print STDOUT ("\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } ... |
Evidentemente, la differenza sostanziale sta nella subroutine Ricerca_listino(), dove al posto di psql, si utilizzano le funzioni di Pg.
La prima cosa da fare è instaurare una connessione con il servizio PostgreSQL, specificando la base di dati con cui si intende interagire. Si ottiene questo attraverso Pg::connectdb() che restituisce un riferimento alla connessione instaurata, cosa che rappresenta un canale di comunicazione per l'invio di istruzioni SQL.
$PGconnessione = Pg::connectdb ("dbname = pubblico"); |
L'argomento di questa funzione (o meglio di questo metodo) è una stringa contenente una serie di assegnamenti a delle parole chiave che rappresentano delle opzioni. In questo caso, le opzioni che non sono state indicate, fanno riferimento a valori che vanno bene al loro stato predefinito.
Prima di utilizzare il riferimento alla connessione è bene controllare che questa sia stata instaurata:
if ($PGconnessione->status == PGRES_CONNECTION_OK) { ... } |
L'istruzione da inviare è un SELECT, ma per questo viene in aiuto una funzione speciale predisposta all'interno di Pg, per facilitare i programmatori. Si tratta di Pg::doQuery() che restituisce un array bidimensionale contenente il risultato dell'interrogazione.
Pg::doQuery ($PGconnessione, "$query_sql", \@tabella); |
Come si può osservare, la funzione utilizza il riferimento alla connessione, rappresentato dalla variabile $PGconnessione, una stringa contenente l'istruzione SELECT opportuna e un riferimento all'array che verrà riempito con i dati del risultato.
Il risultato dell'interrogazione viene quindi tradotto in modo da poter essere incluso nella pagina HTML. Dall'esempio si può osservare che la tabella ottenuta dall'interrogazione non contiene le intestazioni, per cui queste vengono inserite prima della sua scansione.
print STDOUT ("<TABLE>\n"); print STDOUT ("<TR>"); print STDOUT ("<TH>Codice</TH>"); print STDOUT ("<TH>Descrizione</TH>"); print STDOUT ("<TH>Prezzo unitario</TH>"); print STDOUT ("</TR>\n"); for ($i = 0; $i <= $#tabella; $i++) { print STDOUT ("<TR>"); for ($j = 0; $j <= $#{$tabella[$i]}; $j++) { print STDOUT ("<TD>$tabella[$i][$j]</TD>"); } print STDOUT ("</TR>\n"); } print STDOUT ("</TABLE>\n"); |
Nelle sezioni seguenti viene proposto un esempio attraverso cui gli utenti possono eseguire sia inserimenti che interrogazioni dalla stessa tabella. Si tratta di un sistema elementare per la gestione di annunci (gratuiti), senza controlli umani di alcun tipo (probabilmente si tratta di qualcosa giuridicamente sconsigliabile).
Il sistema in questione viene realizzato con un solo programma Perl, senza pagine iniziali di ingresso. Quando possibile vengono utilizzati metodi GET, in modo da permettere agli utenti di registrare le posizioni nel segnalibro del loro navigatore.
La tabella utilizzata per memorizzare gli annunci deve essere costruita attraverso gli strumenti di PostgreSQL. Negli esempi precedenti è già stato mostrato in che modo intervenire per creare una base di dati. Qui si intende utilizzare la stessa base di dati, pubblico, aggiungendo la tabella necessaria.
Attraverso psql si crea la tabella denominata Annunci senza bisogno di aggiungerci dati. Le istruzioni possono essere simili alle seguenti.
CREATE TABLE Annunci ( Data integer, Cognome varchar(60), Nome varchar(60), Telefono varchar(40), Email varchar(60), Rubrica integer, Annuncio varchar(1000) ); REVOKE ALL ON Annunci FROM PUBLIC; GRANT ALL ON Annunci TO postgres; GRANT INSERT ON Annunci TO PUBLIC; GRANT SELECT ON Annunci TO PUBLIC; |
È da osservare il fatto che per la data viene utilizzato il tipo integer. Ciò è necessario perché nel programma Perl si utilizzerà la funzione time() per riempire questo campo, dove la funzione restituisce un numero intero che rappresenta la quantità di secondi trascorsi da una data di riferimento.
Gli utenti che vogliono aggiungere un'inserzione attraverso il programma CGI, dovranno fornire tutti i dati, a esclusione della data che viene fornita dal sistema operativo. Durante l'interrogazione verranno mostrati solo il testo dell'inserzione e l'indirizzo di posta elettronica di chi lo ha fatto.
Il programma attraverso cui si gestisce tutto è annunci.pl. Questo organizza un sistema molto semplice, con pochi controlli di sicurezza. Nonostante ciò, si tratta comunque di un esempio molto lungo. Come al solito, l'inizio si trova verso la fine del sorgente.
#!/usr/bin/perl #====================================================================== # annunci.pl #====================================================================== #---------------------------------------------------------------------- # Utilizza il modulo Pg, per l'utilizzo delle librerie LibPQ di # PostgreSQL. #---------------------------------------------------------------------- use Pg; #---------------------------------------------------------------------- # Incorpora la libreria di decodifica dei dati. #---------------------------------------------------------------------- require ('mini-lib.pl'); #====================================================================== # &Metodo_non_gestibile () #---------------------------------------------------------------------- sub Metodo_non_gestibile { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Errore</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Metodo $ENV{REQUEST_METHOD} non gestibile.</H1>\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== # &Verifica_dati_annuncio () #---------------------------------------------------------------------- sub Verifica_dati_annuncio { if ($DATI{rubrica} eq "0") { return (0); } if ($DATI{testo} eq "") { return (0); } if ($DATI{email} eq "") { return (0); } if ($DATI{cognome} eq "") { return (0); } if ($DATI{nome} eq "") { return (0); } if ($DATI{telefono} eq "") { return (0); } return (1); } #====================================================================== # &Verifica_dati_consultazione () #---------------------------------------------------------------------- sub Verifica_dati_consultazione { if ($DATI{rubrica} eq "0") { return (0); } if ($DATI{modello} eq "") { $DATI{modello} = "%"; } return (1); } #====================================================================== # &Dati_insufficienti () #---------------------------------------------------------------------- sub Dati_insufficienti { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Errore</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>I dati inseriti nel modello sono insufficienti.</H1>\n"); print STDOUT ("Si prega di controllare e aggiungere i dati mancanti.\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== # &Modulo_iniziale () #---------------------------------------------------------------------- sub Modulo_iniziale { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Annunci on-line</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Annunci on-line</H1>\n"); print STDOUT ("<P>\n"); print STDOUT ("Selezionare una delle due voci seguenti:</P>\n"); print STDOUT ("<P>\n"); print STDOUT ("<A HREF=\"/cgi-bin/annunci.pl?modulo=preannuncio\">"); print STDOUT ("inserimento di un nuovo annuncio</A></P>\n"); print STDOUT ("<P>\n"); print STDOUT ("<A HREF=\"/cgi-bin/annunci.pl?modulo=prericerca\">"); print STDOUT ("ricerca tra gli annunci</A></P>\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== # &Modulo_nuovo_annuncio () #---------------------------------------------------------------------- sub Modulo_nuovo_annuncio { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Annunci on-line: inserimento</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Inserimento di un annuncio</H1>\n"); print STDOUT ("<P>\n"); print STDOUT ("Si prega di inserire tutti i dati richiesti.</P>\n"); print STDOUT ("<P>\n"); print STDOUT ("<FORM ACTION=\"/cgi-bin/annunci.pl\" METHOD=\"POST\">\n"); print STDOUT ("<INPUT TYPE=\"hidden\" NAME=\"modulo\" VALUE=\"annuncio\">\n"); print STDOUT ("<P>\n"); print STDOUT ("Rubrica: <SELECT NAME=\"rubrica\">\n"); print STDOUT (" <OPTION VALUE=\"0\" SELECTED=\"selected\">Nessuna\n"); print STDOUT (" <OPTION VALUE=\"1\">Compro\n"); print STDOUT (" <OPTION VALUE=\"2\">Vendo\n"); print STDOUT (" <OPTION VALUE=\"3\">Messaggi\n"); print STDOUT (" <OPTION VALUE=\"4\">Varie\n"); print STDOUT ("</SELECT></P>\n"); print STDOUT ("<P>\n"); print STDOUT ("Testo dell'annuncio:<BR>\n"); print STDOUT (" <INPUT NAME=\"testo\" SIZE=\"80\"></P>\n"); print STDOUT ("<P>\n"); print STDOUT ("e-mail: <INPUT NAME=\"email\" SIZE=\"40\"></P>\n"); print STDOUT ("\n"); print STDOUT ("<H2>Dati che non vengono pubblicati</H2>\n"); print STDOUT ("<P>\n"); print STDOUT ("Cognome: <INPUT NAME=\"cognome\" SIZE=\"25\"></P>\n"); print STDOUT ("<P>\n"); print STDOUT ("Nome: <INPUT NAME=\"nome\" SIZE=\"25\"></P>\n"); print STDOUT ("<P>\n"); print STDOUT ("Telefono: <INPUT NAME=\"telefono\" SIZE=\"25\"></P>\n"); print STDOUT ("<P>\n"); print STDOUT ("<INPUT TYPE=\"submit\" VALUE=\"Invia l'inserzione\"></P>\n"); print STDOUT ("</FORM></P>\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== # &Modulo_ricerca () #---------------------------------------------------------------------- sub Modulo_ricerca { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Annunci on-line: ricerca annunci</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Ricerca tra gli annunci</H1>\n"); print STDOUT ("<P>\n"); print STDOUT ("Si deve indicare la rubrica e un modello di ricerca.</P>\n"); print STDOUT ("<P>\n"); print STDOUT ("Il modello è sensibile alla differenza tra maiuscole \n"); print STDOUT ("e minuscole, si può utilizzare il simbolo `%' per \n"); print STDOUT ("indicare una stringa di caratteri indefinita.\n</P>"); print STDOUT ("<P>\n"); print STDOUT ("<FORM ACTION=\"/cgi-bin/annunci.pl\" METHOD=\"GET\">\n"); print STDOUT ("<INPUT TYPE=\"hidden\" NAME=\"modulo\" VALUE=\"ricerca\">\n"); print STDOUT ("<P>\n"); print STDOUT ("Rubrica: <SELECT NAME=\"rubrica\">\n"); print STDOUT (" <OPTION VALUE=\"0\" SELECTED=\"selected\">Nessuna\n"); print STDOUT (" <OPTION VALUE=\"1\">Compro\n"); print STDOUT (" <OPTION VALUE=\"2\">Vendo\n"); print STDOUT (" <OPTION VALUE=\"3\">Messaggi\n"); print STDOUT (" <OPTION VALUE=\"4\">Varie\n"); print STDOUT ("</SELECT></P>\n"); print STDOUT ("<P>\n"); print STDOUT ("Modello di ricerca: "); print STDOUT ("<INPUT NAME=\"modello\" SIZE=\"30\" VALUE=\"%\"></P>\n"); print STDOUT ("<P>\n"); print STDOUT ("<INPUT TYPE=\"submit\" VALUE=\"Inizia la ricerca\"></P>\n"); print STDOUT ("</FORM></P>\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== # &Ricerca_annunci () #---------------------------------------------------------------------- sub Ricerca_annunci { local ($PGquery) = " SELECT Annuncio, Email FROM Annunci WHERE Rubrica = $DATI{rubrica} AND Annuncio LIKE '$DATI{modello}' ORDER BY Annuncio " ; local (@tabella) = (); local ($PGconnessione); local ($i); local ($j); #------------------------------------------------------------------ # Apre la connessione con il servente PostgreSQL locale, # utilizzando la base di dati «pubblico». #------------------------------------------------------------------ $PGconnessione = Pg::connectdb ("dbname = pubblico"); #------------------------------------------------------------------ # Verifica che la connessione sia avvenuta e quindi esegue # l'interrogazione. #------------------------------------------------------------------ if ($PGconnessione->status == PGRES_CONNECTION_OK) { #-------------------------------------------------------------- # Invia la richiesta utilizzando la funzione Pg::doQuery che # fa tutto da sola (non occorre eseguire PQclear). #-------------------------------------------------------------- Pg::doQuery ($PGconnessione, $PGquery, \@tabella); } else { #-------------------------------------------------------------- # Per qualche motivo la connessione con la base di dati non # funziona e si avvisa l'utente di conseguenza. #-------------------------------------------------------------- &Database_inaccessibile (); } print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Annunci on-line: rubrica n. $DATI{rubrica}</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Consultazione della rubrica n. $DATI{rubrica}</H1>\n"); for ($i = 0; $i <= $#tabella; $i++) { #-------------------------------------------------------------- # Emette il testo dell'annuncio. #-------------------------------------------------------------- print STDOUT ("<P>\n"); print STDOUT ("$tabella[$i][0]</P>\n"); #-------------------------------------------------------------- # Emette l'indirizzo e-mail dello scrivente. #-------------------------------------------------------------- print STDOUT ("<P>\n"); print STDOUT ("<A HREF=\"mailto:$tabella[$i][1]\">$tabella[$i][1]</A></P>\n"); print STDOUT ("<HR>\n"); } print STDOUT ("\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== # &Database_inaccessibile () #---------------------------------------------------------------------- sub Modulo_errato { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Errore</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Problemi di accesso alla base di dati.</H1>\n"); print STDOUT ("Per qualche motivo non è possibile accedere alla base di dati "); print STDOUT ("degli annunci. Si prega di perdonare l'inconveniente.\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== # &Istruzione_errata () #---------------------------------------------------------------------- sub Istruzione_errata { local ($errore) = $_[0]; local ($istruzione) = $_[1]; print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Errore</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Problemi di accesso alla base di dati.</H1>\n"); print STDOUT ("<P>\n"); print STDOUT ("Il comando richiesto ha generato l'errore seguente,</P>"); print STDOUT ("<P>\n"); print STDOUT ("<CODE>${errore}</CODE></P>"); print STDOUT ("<P>\n"); print STDOUT ("a seguito di questa istruzione SQL:</P>"); print STDOUT ("<P>\n"); print STDOUT ("<CODE>${istruzione}</CODE></P>"); print STDOUT ("Si prega di avvisare l'amministratore del servizio."); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== # &Annuncio_memorizzato () #---------------------------------------------------------------------- sub Annuncio_memorizzato { print STDOUT ("Content-type: text/html\n"); print STDOUT ("\n"); print STDOUT ("<HTML>\n"); print STDOUT ("<HEAD>\n"); print STDOUT ("<TITLE>Annuncio memorizzato</TITLE>\n"); print STDOUT ("</HEAD>\n"); print STDOUT ("<BODY>\n"); print STDOUT ("<H1>Annuncio memorizzato</H1>\n"); print STDOUT ("L'annuncio è stato memorizzato. Grazie.\n"); print STDOUT ("</BODY>\n"); print STDOUT ("</HTML>\n"); } #====================================================================== # &Memorizza_annuncio () #---------------------------------------------------------------------- sub Memorizza_annuncio { local ($PGconnessione); local ($PGrisultato); local ($PGistruzione); local ($PGstatus); local ($data) = time(); #------------------------------------------------------------------ # Apre la connessione con il servente PostgreSQL locale, # utilizzando la base di dati «pubblico». #------------------------------------------------------------------ $PGconnessione = Pg::connectdb ("dbname = pubblico"); #------------------------------------------------------------------ # Verifica che la connessione sia avvenuta. #------------------------------------------------------------------ if ($PGconnessione->status == PGRES_CONNECTION_OK) { #-------------------------------------------------------------- # La connessione è avvenuta e si procede con l'inserimento # dell'annuncio. #-------------------------------------------------------------- $PGistruzione = " INSERT INTO Annunci ( Data, Cognome, Nome, Telefono, Email, Rubrica, Annuncio ) VALUES ( ${data}, '$DATI{cognome}', '$DATI{nome}', '$DATI{telefono}', '$DATI{email}', $DATI{rubrica}, '$DATI{testo}' ) "; $PGrisultato = $PGconnessione->exec ("$PGistruzione"); #--------------------------------------------------------------- # Verifica il risultato dell'esecuzione dell'istruzione. #--------------------------------------------------------------- $PGstatus = $PGrisultato->resultStatus; if (PGRES_COMMAND_OK == $PGstatus ) { &Annuncio_memorizzato(); } else { &Istruzione_errata( $PGconnessione->errorMessage, $PGistruzione ); } } else { #-------------------------------------------------------------- # Per qualche motivo la connessione con la base di dati non # funziona e si avvisa l'utente di conseguenza. #-------------------------------------------------------------- &Database_inaccessibile(); } } #====================================================================== # Inizio del programma. #====================================================================== local (%DATI) = (); #---------------------------------------------------------------------- # Decodifica i dati in funzione del tipo di metodo della richiesta. #---------------------------------------------------------------------- if ($ENV{REQUEST_METHOD} eq 'GET') { %DATI = &Decodifica_GET; } elsif ($ENV{REQUEST_METHOD} eq 'POST') { %DATI = &Decodifica_POST; } else { &Metodo_non_gestibile; } #---------------------------------------------------------------------- # Attraverso il dato memorizzato con il nome «modulo» si determina # l'operazione da compiere. #---------------------------------------------------------------------- if ($DATI{modulo} eq 'preannuncio') { &Modulo_nuovo_annuncio (); } elsif ($DATI{modulo} eq 'prericerca') { &Modulo_ricerca (); } elsif ($DATI{modulo} eq 'annuncio') { #------------------------------------------------------------------ # L'utente ha inviato un annuncio. #------------------------------------------------------------------ if (&Verifica_dati_annuncio) { &Memorizza_annuncio (); } else { &Dati_insufficienti (); } } elsif ($DATI{modulo} eq 'ricerca') { #------------------------------------------------------------------ # L'utente ha eseguito una ricerca tra gli annunci. #------------------------------------------------------------------ if (&Verifica_dati_consultazione) { &Ricerca_annunci (); } else { &Dati_insufficienti (); } } else { #------------------------------------------------------------------ # Si comincia dalla presentazione. #------------------------------------------------------------------ &Modulo_iniziale (); } #====================================================================== 1; #====================================================================== |
Il programma contiene dentro di se tutte le pagine HTML e i moduli FORM necessari per interagire. Per distinguere il contesto per il quale vengono eseguite le richieste del protocollo HTTP si utilizzano dei moduli FORM contenenti un campo nascosto: modulo. Quando questo campo contiene un valore non previsto, oppure è assente del tutto, viene presentata la pagina di ingresso, attraverso cui si deve specificare l'azione che si vuole compiere.
La pagina di ingresso, generata dalla funzione Modulo_iniziale(), contiene due riferimenti che puntano allo stesso programma, ma che incorporano una richiesta con il metodo GET, in modo da distinguere l'azione da compiere.
<A HREF="/cgi-bin/annunci.pl?modulo=preannuncio"> inserimento di un nuovo annuncio</A> <A HREF="/cgi-bin/annunci.pl?modulo=prericerca"> ricerca tra gli annunci</A> |
Selezionando la funzione di inserimento di un nuovo annuncio, si ottiene un modulo attraverso cui poterlo inserire, generato dalla funzione Modulo_nuovo_annuncio().
Se invece si seleziona la funzione di ricerca, si ottiene un modulo attraverso cui si può specificare la rubrica e una stringa di ricerca. Questo modulo è generato dalla funzione Ricerca_annunci().
Per completare in modo ragionevole l'esempio proposto di gestione di inserzioni automatiche, bisogna prevedere anche un meccanismo di eliminazione automatica degli annunci dopo un certo tempo. Per questo si può usare un programma separato, che utilizzi privilegi maggiori di quelli dell'utente nobody, eseguito periodicamente dal sistema Cron.
#!/usr/bin/perl #====================================================================== # annunci-elimina.pl #====================================================================== #---------------------------------------------------------------------- # Utilizza il modulo Pg, per l'utilizzo delle librerie LibPQ di # PostgreSQL. #---------------------------------------------------------------------- use Pg; #====================================================================== # Inizio del programma. #====================================================================== if ($#ARGV >= 0) { $giorni = $ARGV[0]; } else { $giorni = 7; } $adesso = time(); $data_minima = $adesso - ($giorni * 24 * 60 * 60); #---------------------------------------------------------------------- # Apre la connessione con il servente PostgreSQL locale, utilizzando # la base di dati «pubblico». #---------------------------------------------------------------------- $PGconnessione = Pg::connectdb ("dbname = pubblico"); #---------------------------------------------------------------------- # Verifica che la connessione sia avvenuta. #---------------------------------------------------------------------- if ($PGconnessione->status == PGRES_CONNECTION_OK) { #------------------------------------------------------------------ # La connessione è avvenuta e si procede con l'eliminazione # degli annunci vecchi. #------------------------------------------------------------------ $PGistruzione = " DELETE FROM Annunci WHERE Data < $data_minima "; $PGrisultato = $PGconnessione->exec ("$PGistruzione"); #------------------------------------------------------------------ # Verifica il risultato dell'esecuzione dell'istruzione. # Attualmente sembra che il valore dello stato restituito # sia invertito. #------------------------------------------------------------------ $PGstatus = $PGconnessione->status; if ($PGstatus == PGRES_COMMAND_BAD) { print STDOUT ("$PGerrore\n"); } } else { #------------------------------------------------------------------ # Per qualche motivo la connessione con la base di dati non # funziona e si avvisa l'utente di conseguenza. #-------------------------------------------------------------- print STDOUT ("La base di dati ``pubblico'' è inaccessibile.\n"); } #====================================================================== 1; #====================================================================== |
Per avviare questo programma conviene ottenere i privilegi dell'utente postgres. Si può inserire il suo avvio all'interno del file /etc/crontab
come nell'esempio seguente:
30 1 * * * postgres /usr/sbin/annunci-elimina.pl 10 |
L'esempio mostra l'avvio del programma ogni giorno alle ore 01:30 (della notte), per eliminare le inserzioni più vecchie di 10 giorni.
Di solito, quando si parte da zero, conviene evitare di reinventarsi le subroutine necessarie a gestire i moduli HTML. Attraverso la rete si possono ottenere molti validi esempi già pronti e collaudati da più tempo.
Tra tutte, la libreria di subroutine Perl più diffusa per la gestione di moduli HTML sembra essere cgi-lib.pl di Steven Brenner.
Ian Graham, Web/HTML Documentation and Developer's Resource
Jacqueline D. Hamilton, CGI Programming 101
Christian Neuss, Johan Vromans, Perl, guida pratica, Apogeo, 1996
perlWWW development, raccolta di riferimenti a librerie Perl per la programmazione CGI
Steven Brenner, cgi-lib.pl, libreria standard per la creazione di script CGI in Perl
daniele @ swlibero.org
1) Eventualmente, in base alla configurazione del servente HTTP, può trattarsi di un altro utente specifico.
Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome programmazione_cgi_in_perl.html
[successivo] [precedente] [inizio] [fine] [indice generale] [violazione GPL] [translators] [docinfo] [indice analitico]