CGI es una norma
para establecer comunicación entre un servidor web y un
programa, de tal modo que este último pueda interactuar con
internet. También se usa la palabra CGI para referirse al
programa mismo, aunque lo correcto debería ser script. De
todos modos en este tutorial nos interesa aprender a escribir
estos programas por lo cúal usaremos la palabra
indistintamente, como es costumbre en castellano.
Un CGI (Common
Gateway Interface) es un programa que se ejecuta en tiempo
real en un Web Server en respuesta a una solicitud de un
Browser. Cuando esto sucede el Web Server ejecuta un proceso
hijo queo recibirá los datos que envía el usuario (en caso de
que los haya), pone a diposición del mismo algunos datos en
forma de variables de ambiente y captura la salida del
programa para enviarlo como respuesta al Browser.
El
propósito de los CGI´s es proveer "inteligencia" e
interactividad a un sitio web, por ejemplo encontrar un sitio
en Yahoo utilizando
solo los links que se proveen puede ser una labor frustrante,
sin embargo usar el formulario y solicitar una busqueda
personalizada suele frustrarnos (un poco) menos, ya que un CGI
nos provee de una respuesta hecha a la medida (eso dice la
teoría) de nuestra consulta. Existen otras herramientas
para lograr esto en cierta medida, tales como Server
Side Includes o JavaScript,
no es el proposito de este tutorial tocar en profundidad estos
temas.
Lenguajes
Aunque un CGI se
puede programar en cualquier lenguaje soportado por el sistema
operativo del servidor, usaremos el lenguaje C para nuestros
ejemplos (excepcionalmente se tocarán algunos otros
lenguajes). Esto es porque un ejemplo en C podrá ser
fácilmente portado a casi cualquier sistema
operativo.
Nota: Si escribes
CGIs para IIS (Windows NT), debes asegurarte de compilarlos en
modo 32 bits, de otro modo no funcionarán. Además no es una
buena idea usar programas .exe sino más bien usar DLLs Dynamic
Link Libraries que corren mucho más rápido.
Otro lenguaje
popular para escribir CGI´s es el Perl, el
cúal es interpretado y al igual que el C es altamente portable
entre sistemas operativos.
Los ejemplos de
código los haré lo mas claros posibles, por ejemplo en vez de
usar un código como:
int main(){
FILE *fp; if ( (fp = fopen("algo.dat","rb"))==NULL )
exit(1) else algo(); return(0); }
preferiré uno algo más largo pero
también más claro como:.
int main(){
FILE *fp; fp = fopen("algo.dat","rb"); if( fp ==
NULL ) { exit(1); }else{ algo();
} return ( 0 ); }
Servidor
Este sitio se
encuentra funcionando sobre un servidor Apache cuya
configuración particular obliga a que la extensión de los cgis
sea .cgi, esto puede variar en el caso de tu webserver.
Te sugiero que contactes a tu webmaster para que te indique
que extensiones, lenguajes y directorios están a tu
disposicion para ejecutar CGI´s.
HolaMundo
Cómo tienes algunos
conocimientos de programación, haremos juntos el ejemplo
básico de todo curso, el programa "Hola Mundo".
Como
resultado obtendremos una página html que dirá solamente eso,
"Hola Mundo". Al igual que en casi todo lenguaje de
programación este programita es muy sencillo.
El código básico en lenguaje
C es: /* Programa Hola Mundo */
/* Autor: Juan Manuel Doren */
/* Compilar en unix con: gcc -i hola.c -o hola.cgi */
int main(){
printf("Content-Type: text/html\n\n");
printf("<html>Hola Mundo</html>\n");
return( 0 );
}
Ejecutando este programa desde un browser se obtendrá una página conteniendo. <html>Hola Mundo</html>
Sin embargo como puedes notar en el código hay una primera línea que debería imprimir: Content-Type: text/html
La salida de esta
primera línea de código es efectivamente recibida por el
browser y le indica que tipo de información viene enseguida.
Un CGI puede enviar código html, imágenes, sonido y en general
cualquier tipo de archivo que se usa en Internet y es esta
línea la que indica al browser como debe interpretar los datos
que recibirá.
Adicionalmente el
browser recibe otra información como largo de los datos o cookies
que se desean grabar, todo esto en forma de un encabezado que
no será visible. El final de este encabezado está marcado por
dos retornos de carro ("\n\n") siguiendole inmediatamente los
datos.
Un documento html típico recibido
por un browser puede verse así: HTTP/1.0 200 OK
Date: Fri, 08 May 1998 19:18:41 GMT
Server: Apache/1.2.5 mod_perl/1.07
Last-Modified: Tue, 07 Apr 1998 19:22:57 GMT
ETag: "7807b84a-1d52-352a7d11"
Content-Length: 7506
Accept-Ranges: bytes
Content-Type: text/html
X-Pad: avoid browser bug
<HTML>
[ ... etc. ]
Por el momento, al
construir un cgi solo será necesario preocuparse de la línea
destacada en rojo ya que las demás son colocadas por el
browser indicando entre otras cosas que protocolo usa, la
fecha del documento (así sabe el browser si bajarlo o usar
alguna versión guardada en el caché), el largo de los datos
(así el browser puede indicarnos el porcentaje que ya ha
bajado) y otra información que no nos interesa de momento.
Algunos de los encabezados mas
frecuentes son:
Encabezado |
Significado |
text/html |
Documento
html |
Location:
url |
Desvia al
visitante a otra dirección |
image/gif |
Imágen del tipo:
.gif |
text/plain |
Documento de
Texto |
image/jpeg |
Imágen del tipo:
.jpeg .jpg .jpe |
audio/x-wav |
Sonido
.wav |
audio/midi |
Sonido
.mid |
video/mpeg |
Video .mpeg .mpg
.mpe |
video/quicktime |
Video Quicktime
.qt .mov |
video/x-msvideo |
Video
.avi |
video/x-sgi-movie |
Video
.movie |
audio/basic |
Sonido .au
.snd |
application/zip |
Archivo
.zip |
application/msword |
Archivo Word
.doc |
application/msexcel |
Planilla Excel
.xls |
audio/x-pn-realaudio |
(Real)Audio .ram
.ra | | | "Espiando" al visitante
En este capítulo
analizaremos como puede un CGI recibir información desde el
mundo externo.
Aparte de lo que
tradicionalmente puede obtener un programa desde el sistema
operativo un CGI recibe información en dos formas
diferentes.
- i) Datos que el Browser
envía al Servidor Web y que a su vez tienen dos
componentes
- Datos de sí mismo y de la
conexión (son enviados siempre).
- Datos ingresados en un form
o pasados como parametros (Solo si existen).
- ii) Datos que el
Servidor Web pone por defecto a disposición del CGI con
información sobre sí mismo.
En este capítulo
nos concentraremos en los datos que siempre son pasados al CGI
y la manera de recogerlos.
Algunos de estos datos
son:
HTTP_REFERER |
Documento que está
solicitando el CGI. |
HTTP_USER_AGENT |
Browser que está usando el
visitante. |
HTTP_USER_OS |
Sistema operativo está
usando el visitante. |
REMOTE_HOST |
Nombre del equipo que esta
pidiendo el cgi (habitualmente el Módem del ISP que está
usando el visitante). |
REMOTE_ADDR |
Dirección IP del
visitante. |
|
Cuando un Servidor
Web ejecuta un CGI, levanta un proceso hijo con un juego
independiente de variables de entorno conteniendo estos
datos.
La manera de leer
estos datos en C es usando la función getenv( char *nombre),
en Perl la función $ENV{"NOMBRE"}, y con Server Sides Includes
nombre de la variable. Un ejemplo en C se implementa en el
programa espia cuyo código (Resumido) es el
siguiente:
/*
**************************** Programa
Espia Autor: Juan Manuel Doren
Compilar en unix con: gcc -i espia.c -o
espia.cgi ****************************
*/
#include <stdlib.h>
void
imprimeVariable( char *Nombre );
int
main(){ /*
*********************************** */
printf("Content-Type: text/html\n\n");
printf("<html><body bgcolor=#ffffff
text=#ffffff>\n");
printf("<p
align=center><table cellpadding=4
bgcolor=000000>\n");
printf("<tr><td color=000080
align=center>"); printf("<b>Variables
del
Sistema</b></td></tr>\n");
imprimeVariable("REMOTE_ADDR");
imprimeVariable("REMOTE_HOST");
printf("</table>\n");
printf("</body></html>");
return( 0 ); }
void imprimeVariable( char
*Nombre ){ /* ***********************************
*/
printf("<tr>\n"); printf("<td
bgcolor=\"#9999FF\"
valign=top>%s</td>\n",Nombre); printf("<td
bgcolor=\"#DDDDFF\" valign=top><font
color=#000000>");
printf("%s",getenv(Nombre));
printf("</td>\n"); printf("</tr>\n"); }
El programa utiliza el mismo
metodo visto en el Capítulo 1 para generar una página HTML. La
función imprimeVariable() recibe como parámetro el
nombre de la variable que se desea mostrar, imprime el código
HTML necesario para formatear la salida y mediante una
invocación a getenv(Nombre) recupera el valor que se
solicita. En algunos casos las variables no devuelven
nada.
Procesamiento de
Formularios Una de las
funciones más importantes de los cgis es el proceso datos
ingresados por el visitante mediante rellenar un formulario.
Es el propósito de este capítulo entregar las herramientas
necesarias para poder recibir estos datos.
El CGI puede recibir el contenido
de un formulario de dos maneras básicas:
- Mediante la variable de
ambiente QUERY_STRING
- Mediante la entrada éstandar
en cuyo caso la variable de ambiente CONTENT_LENGTH
nos indicará el largo del string que deberemos leer.
Cúal de las dos formas se usa
dependerá del método que se haya seleccionado en el parametro
method del tag form del documento
HTML.
Para enterarnos de cúal de método fúe usado se
deberá consultar la variable de ambiente REQUEST_METHOD
que contendrá GET o PUT según sea el
caso.
Nota:
Las principales diferencias entre
ambos metodos se ve en esta tabla:
GET |
POST |
El CGI lo recibe por medio de una variable
de entorno lo que significa una restriccion en el
tamaño de los datos. |
El CGI lo recibe por medio de la entrada
estandard (como si fuera teclado) lo que significa
virtualmente recibir cualquier tamaño de
datos. |
El Browser envía los datos visiblemente
haciendo una llamada de la forma:
.../cgi-bin/cgi.pl?campo=algo Es la forma como
se llaman cgis desde fuera de un formulario, por
ejemplo los contadores de visitas son
habitualmente llamados de la
forma: <img src="Count.cgi?df=cgimaster2"> siendo
el resultado:
 |
El Browser envia los datos directamente al
CGI por lo cual no se ven en el browser (ideal
para campos ocultos) |
En ambos
casos se recibe un string con pares ordenados en la
forma: nombre=valor separados por un signo
& los espacios serán pasados como un signo
más (+) y los caráctes especiales (incluyendo el
espacio) en la forma: %valor
hexadecimal
Por ejemplo
el string: mañana comeré será recibido
como: ma%F1ana+comer%E9
Para ilustrar
como procesar estos datos usaremos el programa que
muestra el contenido de los datos pasados desde el
browser.
#include <stdio.h> #include
"cgiforms.h" #define LARGO_MAXIMO
1024
void imprimeVariable( char *lpzNombre
);
main(){ /* **** */
printf("Content-Type:
text/html\n\n");
printf("<html><body bgcolor=#ffffff
text=#ffffff>\n");
printf("<p
align=center><table cellpadding=4
bgcolor=000000>\n");
printf("<tr><td color=000080
align=center>");
printf("<b>Campos recibidos desde el
Form</b></td></tr>\n");
imprimeVariable( "nombre" );
imprimeVariable( "color" );
imprimeVariable( "pais" );
imprimeVariable( "preferencia" );
imprimeVariable( "marca" );
printf("</table>\n");
printf("</body></html>");
return( 0 ); }
void imprimeVariable(
char *lpzNombre ){ /*
************************************* recibe el
nombre de la variable y la imprime formateada como
celdas de una
tabla *************************************
*/ char
lpzResultado[LARGO_MAXIMO];
printf("<tr>\n"); printf("<td
bgcolor=\"#9999FF\"
valign=top>%s</td>\n",Nombre);
printf("<td bgcolor=\"#DDDDFF\"
valign=top><font
color=#000000>");
/*
********************* aqui pide la
variable y luego la
imprime
********************* */
formPideVariable(
lpzNombre,lpzResultado,LARGO_MAXIMO);
printf(¨[%s]",lpzResultado);
printf("</td>\n");
printf("</tr>\n");
}
Generación de imágenes |
|
En este
capítulo se analizará como enviar una imagen desde
un cgi y posteriormente, como generarlas a
voluntad. El protocolo para el envío de
imágenes no varía notablemente en relación al
envío de otro tipo de documentos al browser.
Primero ha de enviarse un header indicando el tipo
de documento que enviaremos y luego byte a byte,
el documento (imagen) en cuestión.
A
continuación se muestra el código del programa
lanzagif.c el cual ilustra el uso de la función
lanzaGif que nuestra un gif almacenado en el
disco, la función no modifica para nada la imagen
y puede ser usado para mostrar alguna imagen
aleatoriamente (yo lo uso en Minibanner). |
#include <stdio.h>
void
lanzaGif( char *NombreGif
); main(){
lanzaGif("ejemplo.gif"); }
void lanzaGif(
char *NombreGif ){ /*
*************************************************************
envia una imagen leyendola del archivo
NombreGif
****************************************************************
*/
#define GIF_BUFF_SIZE
2048
FILE
*fp; int
buff[GIF_BUFF_SIZE]; int
leido = GIF_BUFF_SIZE;
fp =
fopen(NombreGif,"rb"); if ( fp !=
NULL ) { printf( "Content-type:
image/gif\n\n"); /* Este es el header de un
gif */
while ( leido ==
GIF_BUFF_SIZE ){
leido =
fread(&buff,1,GIF_BUFF_SIZE,fp);
if (leido)
{
fwrite(&buff,1,leido,stdout); /* lanza lo
leido
por
la salida standard
*/
} }
fclose(fp); } }
El resultado del programa
lo ves en la imagen a continuación. (El código html para
la inclusión del cgi es: <img
src=lanzagif.cgi>

Ahora tenemos dos formas de
generar un gif, una es estudiando la estructura interna
de un gif y lanzando 1 a 1 los bytes que lo compondrían
o utilizar alguna librería para lograr lo mismo. Como el
propósito de este tutorial no es profundizar el formato
de archivos gifs nos remitiremos a usar GD una popular
libreria disponible gratuitamente en internet. Para
eso desarrollaremos un pequeño
ejemplo que no pretende ahondar en el uso de la
mencionada librería sino mostrar la base de la
generación de imágenes.
Generación de imágenes
usando GD
Para ilustrar el uso de la
libreria GD construiremos un pequeño "juego",
llamado Kill Nikita
y que trata de disparar con el Mouse a la Femme
Nikita. El juego se basa en un CGI que recibe como
parametros las coordenadas del click y dibuja un círculo
rojo en aquellas coordenadas. El código del programa
se lista a continuación
#include
#include "gd.h" #include
"gdok.c" #include "cgiforms.h"
int
main(void) {
int i; FILE
*in;
/* Archivo Gif de Entrada */ gdImagePtr
im_out; /* Imagenes de entrada y salida
*/
char lpzX[5]; /*
coordenadas del disparo
*/ char
lpzY[5];
int X = 0; int Y =
0;
gdImagePtr brush; /* pincel y
colores */ int rojo; int
blanco;
formPideVariable( "x",lpzX,4); /* lee las
coordenadas
*/
formPideVariable(
"y",lpzY,4);
if
(lpzX[0])
{
X =
atoi(lpzX);
}
if
(lpzY[0])
{
Y =
atoi(lpzY);
}
in = fopen("nikita.gif", "rb"); /*
crea el gif a partir de uno existente */ im_out =
gdImageCreateFromGif(in);
rojo =
gdImageColorAllocate(im_out, 255, 0, 0); /* crea
los colores */ blanco = gdImageColorAllocate(im_out,
255, 255, 255);
if ( (X > 0) && (Y > 0) )
/* Si hay disparo
*/
{
gdImageArc(im_out, X, Y, 15, 15, 0, 360, rojo); /*
Dibuja el disparo
*/
gdImageArc(im_out, X, Y, 14, 14, 0, 360,
rojo); /* Lo pinta
*/
gdImageArc(im_out, X, Y, 13, 13, 0, 360,
rojo);
for (
i=12;i>10;i--)
{
gdImageArc(im_out, X, Y, i, i, 0, 360,
blanco);
}
for (
i=10;i>0;i--)
{
gdImageArc(im_out, X, Y, i, i, 0, 360,
rojo);
}
}
printf("Content-type: image/gif\n\n"); /*
Headers */ gdImageGif(im_out, stdout);
/* Salida Estandard */
fclose(in);
/* cierra archivos
*/ gdImageDestroy(im_out);
/* libera memoria */
return
0; }
Como se
observa se han usado las rutinas de la librería GD para
dibujar el disparo. Hemos usado además otro cgi que
lanza el có:digo HTML y usa un form para poder pasar las
coordenadas a una segunda llamda a sí mismo. La forma
de usar el form es <form method=get
action=nikita.cgi><input type=image
src="shownikita.cgi?x=&y="width=181 height=310
border=0>
No nos
detendremos a analizar las rutinas GD ya que estas
vienen bastante bien documentadas con la
librería | |