Curso de CGI


Introducción
El Web no es sólo un medio para colocar información de variados tipos y formatos a disposición de los usuarios de Internet. Utilizando el Web es posible también interactuar con el usuario de forma que ‚l pueda, por ejemplo, encargar un producto, suscribirse a algún servicio, reservar un pasaje o hacer una consulta a una base de datos. La intención de este texto es acercar a todos los que se han iniciado en HTML
hace poco tiempo, al diseño de páginas interactivas y así avanzar un poco más en sus conocimientos. Estimo que no debe ser para nada perfecto ni mucho menos, quizás podría decir sí, es bastante "entendible" por eso espero que sirva de iniciación en el tema, cualquier corrección, duda o sugerencia pueden mandarla a dquir@indec.mecon.ar  donde tratare de contestar en la medida de lo posible. Pido disculpas por los toscos gráficos ASCII (fue‚ mi mejor esfuerzo), pero les aseguro que la versión HTML va a estar mucho mejor, lo prometo. (con los dedos cruzados.... :D! ) provistos por el usuario.


Cómo funciona la cosa.
En el momento en que accedemos a una página del tipo que mencionamos, podemos
apreciar distintos elementos que permitirán que nuestros deseos/necesidades puedan ser transmitidos a las personas que manejan esa página. La mecánica es simple, la información es enviada a un programa, el script CGI, procesada y en consecuencia es generada una salida.

Los datos que ingresemos serán encapsulados en una variable de entorno denominada QUERY_STRING, y en el momento de apretar el botón de "submit" será
leída e interpretada por nuestro script. Dentro de ella irán todos los valores correspondientes a los datos que ingresemos, de manera que el script tendrá que separarlos uno por uno para su proceso. Esto se logra fácilmente mediante el uso de funciones que vienen incluidas en librerías especializadas para cada lenguaje en particular.

Existen otras variables de entorno que pueden ser accedidas por un script CGI
cuando éste se ejecuta tales como:

El script eventualmente retornar  una página HTML o una imagen que es mostrada como resultado de la ejecución. Este último caso es el de los counters, que a través de un pequeño programa en C, "pegan" los distintos dígitos que representan la cantidad de accesos que ha tenido esa página en un UNICO .GIF .
Para diferenciar estos dos tipos de salida se envía un "header" al comienzo de la transmisión de la respuesta que las identifica según la siguiente especificación (extensiones MIME).

Tipo de Informaci¢n retornada Texto:

{{ a propósito de los counters, quisiera hacer un comentario personal sobre la utilidad de los mismos, ya que en mi opinión la confiabilidad de éstos es como mínimo dudosa, como ejemplo basta mencionar una página llamada "The Terrorist Counter" que contiene links a muchos counters diferentes en distintas páginas, de todo el mundo y que al ser cargada los hace aumentar a todos al mismo tiempo, pero bueno, esa es otra historia..}}

Los scripts normalmente están ubicados en el directorio cgi-bin del web-server o bien en alguno similar (CGI-WIN,CGI-DOS). La ubicación del directorio cgi-bin es determinada por el administrador del Web, o "webmaster" como se lo llama comúnmente, y esta puede ser una ubicación física real o bien una lógica.
Es importante mencionarlo ya que la mayoría de los web-servers sólo permiten que se ejecuten programas desde estos directorios en particular.

Para finalizar este breve vistazo del tema basta con mencionar que los scripts CGI pueden ser escritos en cualquier lenguaje. Comúnmente se utiliza PERL o C, aunque contando con las librerías adecuadas se pueden escribir también en Turbo Pascal (mi caso particular), Visual Basic y hasta Delphi.

 

Por donde empezar.
Para comenzar analizaremos la creación de "forms", nuestro front-end / interfaz
gráfica que permitirá a los usuarios el ingreso de la información deseada. Estos "forms" hacen uso de elementos comunes para aquellos familiarizados con las GUI modernas, Windows o X-Windows, como son los buttons, check boxes, radio buttons, pulldown menus, text boxes, etc... Una vez que hayamos dominado bien este tema podremos pasar a la diagramación / programación de los scripts CGI, es decir los programas que procesarán los datos ingresados por los usuarios. Creo que la mejor manera de aprender este tema, es ir leyendo el documento y practicando en el browser (bastará con usar Netscape y el Edit de DOS) de todos modos cada cual elegirá el sistema que le resulte más cómodo.

Forms

Forms simples.
Comenzaremos con una página tipo que tiene incluido el tag correspondiente al inicio de un form;

<HTML>
<HEAD>
<TITLE> Página de Prueba </TITLE>
</HEAD>
<BODY>
<H1>Titulo Prueba de Forms </H1>

<FORM METHOD="post" ACTION="http://alguna.direccion/cgi-bin/prog.exe">
<-- aquí comenzaría el código para los elementos del form --!>
. . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . 
</FORM> <-- y aquí terminaría --!>

</BODY>
</HTML>

Evidentemente "prog.exe" es el nombre del ejecutable (script CGI) que correrá en el web-server una vez que el usuario envíe la información requerida, y POST es el método que se utilizará para realizar dicha acción. Existe otro método llamado GET que hace exactamente lo mismo, la diferencia radica en que este último envía la data dentro de la variable de entorno que habíamos mencionado (QUERY_STRING) "pegada" al URL del script y POST lo hace como un "stream" continuo de caracteres. Es preferible utilizar POST ya que el mismo no tiene limitaciones de tamaño y en cambio GET sí puede tenerlas en el server.

Text Line
El elemento más simple que podemos utilizar en un FORM es el "Text line" que le permite al usuario ingresar texto en una línea. La sintaxis utilizada para este tag es la siguiente;

<FORM METHOD="post" ACTION="http://alguna.direccion/cgi-bin/text.exe">
Escriba su nombre<BR>
<INPUT TYPE="text" NAME="nom_var" SIZE=20 >
</FORM>

En el browser se verá:



Cuando el usuario oprima la tecla "Enter" lo que haya escrito ser  enviado al web-server. Veamos los parámetros de este tag. En NAME va el nombre de la variable que almacenará el texto que ingrese el usuario y en SIZE tenemos 20 que es la longitud en caracteres que va a tener la caja. Por medio de un script-CGI "capturaremos" la variable "nom_var" y leeremos su contenido, el cual podremos procesar posteriormente.

Password Text.
En adición a Text Line existe el tipo "password" que sirve para ingresar claves o textos secretos, cuya sintaxis es muy parecida. No se trata para nada de un medio seguro para enviar información a través de la Net, pero bueno, puede servirnos para este fin didáctico.


<FORM METHOD="post" ACTION="http://alguna.direccion/cgi-bin/pass.exe">
Ingrese su nombre y password.<BR>
<INPUT TYPE="text" NAME="nom_var" SIZE=20 > <BR>
<INPUT TYPE="password" NAME="pass_var" SIZE=20 > <BR>
</FORM>

Obtendremos la siguiente salida :



Obviamente lo que escribamos en la caja de password no aparecerá como texto normal sino con una "*" representando cada carácter.

Text Boxes
Si necesitamos recibir más de una línea podemos utilizar los tags <TEXTAREA ..> </TESTAREA> de la siguiente manera;


<FORM METHOD="post" ACTION="http://alguna.direccion/cgi-bin/text.exe">
Ingrese su comentario<BR>
<TEXTAREA NAME="caja" ROWS=5 COLS=40> Este texto aparecerá en la caja por default. El mismo puede estar separado por "ENTERs".</TEXTAREA> <BR>
<INPUT TYPE="submit" NAME="boton_env" VALUE="Enviar">
</FORM>




El significado de cada parámetro es el siguiente:

Submit Button
En el caso que acabamos de ver, el "Enter" sirve para pasar a la línea siguiente mientras se está escribiendo, de manera que implementamos otro m‚todo para enviar la información, que es el "Submit Button", cuando el mismo sea oprimido el texto que hayamos cargado será enviado.

Estos son los parámetros del "Submit Button":

El parámetro "NAME" no es necesario en el caso anterior, pero es interesante citarlo ya que podemos llegar a utilizarlo en el siguiente situación;

<FORM METHOD="post" ACTION="http://alguna.direccion/cgi-bin/text.exe">
Le gustó la página<BR>
<INPUT TYPE="submit" NAME="boton_env" VALUE="Si">
<INPUT TYPE="submit" NAME="boton_env" VALUE="No">


Se obtendría algo como:



Aquí la variable boton_env contendrá los valores "Si" o "No" según cual de los dos botones oprima el usuario.

Radio Buttons
Los Radio Buttons permiten que el usuario elija una única opción entre varias. Son esos pequeños círculos que aparecen cuando tenemos que indicar el sexo, o bien un rango de edades o una opinión (muy bueno, bueno, malo). Veamos un ejemplo del código.

<FORM METHOD="post" ACTION="http://alguna.direccion/cgi-bin/radio.exe">
Cual es tu edad?<BR>
<INPUT TYPE="radio" NAME="edad" VALUE="a"> Menos de 18<BR>
<INPUT TYPE="radio" NAME="edad" VALUE="b" CHECKED> Entre 18 y 24<BR>
<INPUT TYPE="radio" NAME="edad" VALUE="c"> Entre 25 y 45<BR>
<INPUT TYPE="radio" NAME="edad" VALUE="d"> 46 o más<BR>
<INPUT TYPE="submit" NAME="boton_env" VALUE="Enviar">
</FORM>

La pantalla mostraría algo como esto:



El form retornar  dentro de la variable "edad" el valor a,b,c o d según corresponda la opción que haya sido elegida por el usuario. La cláusula CHECKED permite tener un ítem seleccionado por default.

Check Boxes
Los Check Boxes sirven cuando necesitamos recibir un input con más de una opción seleccionada, se utilizan por ejemplo para marcar las características que más nos agradan de un determinado producto. La sintaxis es bastante parecida al anterior.

<FORM METHOD="post" ACTION="http://alguna.direccion/cgi-bin/check.exe">
Marque aquellos temas que sean de su interes:<BR>
<INPUT TYPE="checkbox" NAME="temas" VALUE="ntecs"> Nuevas Tecnologías<BR>
<INPUT TYPE="checkbox" NAME="temas" VALUE="inves" CHECKED> Investigación<BR>
<INPUT TYPE="checkbox" NAME="temas" VALUE="grafs" CHECKED> Grá ficos / CAD<BR>
<INPUT TYPE="checkbox" NAME="temas" VALUE="redes"> Redes / Comunicaciones<BR>
<INPUT TYPE="submit" NAME="boton_env" VALUE="Enviar">
</FORM>

En el browser tendríamos:



Igual que con los radio buttons, la cláusula CHECKED permite tener marcados algunos cuadraditos por default. En la variable "temas" van a ir a parar aquellos opciones que sean marcadas por el usuario.

POP UP list
Podemos también utilizar menús descolgantes como manera de elegir una opción entre varias mediante el tag <SELECT> como podemos ver más abajo.

<FORM METHOD="post" ACTION="http://alguna.direccion/cgi-bin/popup.exe">
Que sistema operativo usas?<BR>
<SELECT NAME="sistema">
<OPTION SELECTED> DOS 
<OPTION> Windows 3.1 
<OPTION> Windows 95
<OPTION> OS/2 Warp
<OPTION> Linux
<OPTION> Otro
</SELECT> <BR>
<INPUT TYPE="submit" NAME="boton_env" VALUE="Enviar">
</FORM>


Si presionamos con el mouse sobre la flechita la lista desplegable se verá así:



Al igual que en los otros ejemplos, la variable "sistema" almacenará el ítem elegido.

Forms Multiples y el Reset Button
Todos los elementos que hemos mencionado pueden ser utilizados individualmente con un submit button (o sea con un form para cada uno) o bien pueden ser empleados varios de ellos en una misma página. 

Nota: Se estila en la creación de un buen form, el poner líneas horizontales arriba y abajo del form (<HR> o alguna línea gráfica) y proveer instrucciones de como se deben llenar los blancos.

Para finalizar construiremos un ejemplo que contenga todos los tags vistos hasta ahora. Este form múltiple introduce el uso de un nuevo tipo de botón, el Reset Button, que en resumida cuentas borra los datos que hayamos ingresado y deja los elementos en su opción de default.


<FORM METHOD="post" ACTION="http://alguna.direccion/cgi-bin/popup.exe">
Ingrese su información:<BR>

Nombre: <INPUT TYPE="text" NAME="nombre" SIZE=30 > <BR>
Email: <INPUT TYPE="text" NAME="email " SIZE=30 > <BR>
Comentarios.<BR>
<TEXTAREA NAME="caja" rows=5 cols=40></TEXTAREA> <BR>
<BR>
Que lenguaje prefiere?<BR>
<SELECT NAME="lenguaje">
<OPTION SELECTED> Turbo Pascal
<OPTION> Turbo Pascal 
<OPTION> Delphi
<OPTION> Visual Basic
<OPTION> Smalltalk/V
<OPTION> Cobol
</SELECT> <BR>
<BR>
Que le pareció la guía? <BR>
<INPUT TYPE="radio" NAME="opin" VALUE="a">Mala <BR>
<INPUT TYPE="radio" NAME="opin" VALUE="b">Regular<BR>
<INPUT TYPE="radio" NAME="opin" VALUE="c" CHECKED>Buena<BR>
<INPUT TYPE="radio" NAME="opin" VALUE="d">Muy Buena<BR>
<INPUT TYPE="radio" NAME="opin" VALUE="d">Excelente!<BR>
<BR>
<INPUT TYPE="checkbox" NAME="email" VALUE="si">
Marque esta casilla si quiere recibir información vía email. <BR>
<BR>
<INPUT TYPE="submit" NAME="boton_env" VALUE="Enviar">
<INPUT TYPE="reset" NAME="boton_res" VALUE="Borrar">
</FORM>



Salida en Pantalla.



Todo lo visto hasta ahora es posible practicarlo sin necesidad de un server, hasta un cut&paste de los ejemplos funcionaría, por lo tanto ..... Manos a la obra!!

Que necesitamos para empezar.
Para empezar necesitamos tener un poco de conocimiento de algún lenguaje de programación (el que sea). La idea de esta guía es utilizar como base Perl, aunque serán dados ejemplos en C y també‚n en Pascal (vamos Turbo todavía). Creo que con eso bastará para dar una idea general, (que por otra parte es lo que pretendo), y las adaptaciones necesarias para cualquier otro lenguaje con un poco de ingenio se podrán hacer sin problemas.

Qué son los scripts CGI?
Normalmente cuando un browser de Web (como por ejemplo el Netscape) llama a una URL en particular, sucede lo siguiente. Primero la computadora contacta el HTTP server con dicha URL (HTTP es el protocolo que se utiliza en las comunicaciones en la Web entre el server y el browser). El server HTTP revisa si el archivo requerido por nuestra computadora se encuentra en su sistema, en caso afirmativo envía el archivo como respuesta. Nuestra computadora entonces, muestra el archivo en el formato apropiado.

Además de todo esto, los servers de Web están configurados de tal manera que cada vez que se requiere un archivo de un directorio determinado (usualmente el "cgi-bin"), dicho archivo no es enviado; sino que es ejecutado como un programa y la salida de este programa es enviada a nuestra máquina para que ésta la muestre. Esta función es conocida como "Common Gateway Interface" y los programas a los que nos referimos son llamados scripts CGI.

Como dijimos, el directorio en el cual usualmente se encuentran los scripts CGI es el cgi-bin, de manera que los scripts que armemos deberán ser situados en el mismo. Si no tenemos control de la administración del server, debemos solicitar que se les dé permiso de ejecución a todos los archivos que pongamos en este directorio (cgi-bin). 

En esta segunda parte veremos scripts que funcionan en base a los datos y variables que les son enviados a través de un FORM en una página HTML, y dejaremos para la siguiente parte de aquellos que no necesitan de esta input, por ejemplo los counters, las animaciones y otras cosas un poco más complicadas.

Nuestro Primer Script.
Para empezar por lo más simple y poder apreciar la funcionalidad de este sistema veremos un script muy fácil de entender, el cual no necesita que les sean suministrados datos de ningún tipo para funcionar. Pero sigamos con lo nuestro;

Perl
!#/usr/bin/perl

print "Content-type: text/html\n\n"; 
print "<HTML>\n"; 
print "<HEAD>\n"; 
print "<TITLE>Aprendiendo CGI</TITLE>\n"; 
print "</HEAD>\n"; 
print "<BODY>\n"; 
print "Hola Intertips!\n"; 
print "</BODY></HTML>\n"; 

En el programa anterior la primera línea se encarga de llamar al intérprete de Perl

Pascal
begin
writeln "Content-type: text/html"; 
writeln; 
writeln "<HTML>"; 
writeln "<HEAD>"; 
writeln "<TITLE>Aprendiendo CGI</TITLE>"; 
writeln "</HEAD>"; 
writeln "<BODY>"; 
writeln "Hola Intertips!"; 
writeln "</BODY></HTML>"; 
end;

 

C
#include <stdio.h> 

main() 

  printf ("Content-type: text/html\n\n"); 
  printf ("<HTML>\n"); 
  printf ("<HEAD>\n"); 
  printf ("<TITLE>Aprendiendo CGI</TITLE>\n"); 
  printf ("</HEAD>\n"); 
  printf ("<BODY>\n"); 
  printf ("Hola Intertips\n"); 
  printf ("</BODY></HTML>\n"); 
}


Es así de fácil!! Y lo mejor de todo es que funciona!

Observen que lo primero que retornamos es un header indicando de que tipo es la información que enviamos, en este caso se trata de una página HTML, por lo tanto el header apropiado es: Content-type: text/html

Tomemos en cuenta también que los primeros dos saltos de línea que introducimos son significativos. Los código de retorno MIME, a los cuales pertenece "Content-type..", necesitan dos "newlines" siguiéndolos para delimitar su fin. No enviar este segundo newline nos traerá bastantes problemas. Los demás "newline" incluidos en la página que generamos no son realmente necesarios, pero facilitarán la lectura de alguien que haga un "View Source".

Como vemos en los ejemplos tuvimos que generar todos los tags de la página, pero eso resultó muy simple, no es así?

Ahora vamos a hacer algo un poco más interesante, vamos a solicitar algunos datos con un FORM apropiado, y vamos a retornar una página que contenga los nombres de las variables utilizadas junto con el contenido de cada una ingresado por el usuario.

Un script que hace Eco de un Form.
En este punto comenzaremos a hacer uso de librerías especializadas (los sitios donde conseguir cada una de las librerías figuran al final de este texto). Quisiera recalcar que estas librerías no son únicas, existen muchas variaciones sobre el mismo tema, y cada una de ellas tiene su propia forma de manejarse, por lo tanto es conveniente revisar cuidadosamente la documentación que las acompaña en caso de que se utilice alguna otra que no figure aquí.

Estas librerías nos ahorrarán el trabajo de separar los distintos componentes que son encapsulados en la variable de entorno QUERY_STRING (recuerdan lo que mencionamos en la primera parte?). En el caso que estudiaremos, la llamada la hacemos al script en Perl, pero obviamente cambiando el nombre del ejecutable de acuerdo al lenguaje que estemos utilizando esto se soluciona.

Veamos el form que vamos a utilizar:

<HTML>
<HEAD>
<TITLE>Prueba con un FORM</TITLE>
</HEAD>
<BODY>
<H1>Ingresa los datos que corresponden para hacer la prueba;</H1>
<BR><HR>
<FORM METHOD="POST" ACTION="http://mimaquina.midominio/cgi-bin/prueba.pl">
<UL>
<LI>Nombre: <INPUT TYPE="text" NAME=nombre SIZE=20>
<LI>Edad: <INPUT TYPE="text" NAME=edad SIZE=2>
<LI>Hincha de:
<SELECT NAME="equipo">
<OPTION SELECTED> Boca Juniors
<OPTION> River Plate
<OPTION> San Lorenzo
<OPTION> Independiente
<OPTION> Racing Club.
<SELECT>
<LI>Email: <INPUT TYPE="text" NAME=email SIZE=20>
</UL>
<INPUT TYPE="submit" VALUE="Ok"><BR>
<INPUT TYPE="reset" VALUE="Borrar"><BR>
</FORM>
<HR>
</BODY>
</HTML>


El script que utilizaremos será muy simple y nos va a servir de base para futuros desarrollos. Veamos paso a paso su estructura:

  1. Primero llama al intérprete en caso de ser necesario (Perl).

  2. Luego de incluir/cargar la librería, de alguna manera corre la función que separa QUERY_STRING en sus componentes individuales.

  3. Asignamos dichos componentes a las variables que vamos a utilizar.

  4. Imprimimos una salida, como en cualquier programa y..

  5. Listo!

Aquí van los fuentes en cada uno de los lenguajes.

Perl
!#/usr/bin/perl

push(@INC,"/var/opt/ncsa/httpd/cgi-bin"); 
require("cgi-lib.pl"); <-- Incluimos la librería. 

&ReadParse; <-- Separamos las variables. 

$nombre = $in{'nombre'}; <-- Asignamos los valores que 
$edad = $in{'edad'};      nos enviaron a las variables
$equipo = $in{'equipo'};  para utilizarlos. 
$email = $in{'email'}; 

print &PrintHeader; <-- Imprimimos el Header. 
print "<HTML>\n"; 
print "<HEAD>\n"; 
print "<TITLE>Aprendiendo CGI-Segundo Ejercicio</TITLE>\n"; 
print "</HEAD>\n"; 
print "<BODY>\n"; 
print "<HR>\n"; 
print "Tu nombre es ",$nombre,"<BR>\n"; 
print "Tenés ",$edad," años y sos simpatizante de ",$equipo,"<BR>\n"; 
print "Si alguien quisiera escribirte tu email es: ",$email,"<BR>\n"); 
print "<HR>\n"; 
print "</BODY></HTML>\n"; 

 

Pascal
Uses TPWCGI;               <-- Incluimos la librería. 

Var nombre, edad,          <-- Definimos las variables 
  equipo, email :string;     que vamos a usar. 

Begin 
StartCGI; 

nombre := Getvalue ('nombre',1);    <-- Asignamos los edad := Getvalue ('edad',1);          valores que nos 
equipo := Getvalue ('equipo',1);      enviaron a las 
email := Getvalue ('email',1);        variables.

MakeHeader; <ÄÄÄ Imprimimos el Header. 
Send ('<HTML>'); 
Send ('<HEAD>'); 
Send ('<TITLE>Aprendiendo CGI-Segundo Ejercicio</TITLE>'); 
Send ('</HEAD>'); 
Send ('<BODY>'); 
Send ('<HR>'); 
Send ('Tu nombre es '+nombre+'<BR>'); 
Send ('Tenés '+edad+'años y sos simpatizante de '+equipo+'<BR>'); 
Send ('Si alguien quisiera escribirte tu email es: '+email+'<BR>'); 
Send ('<HR>'); 
Send ('</BODY></HTML>'); 

EndCGI;
end.

 

C
#include <stdio.h>
#include <errno.h>
#include <uncgi.h>         <-- Incluimos la librería.
char *getenv();

main()
{
  char *nombre;           <-- Definimos las variables
  char *edad;               que vamos a usar.
  char *equipo;
  char *email;

  uncgi();               <-- Separamos las variables.

  nombre = getenv("WWW_nombre"); <-- Asignamos los
  edad = getenv("WWW_edad");       valores que nos
  equipo = getenv("WWW_equipo");   enviaron las 
  email = getenv("WWW_email");     variables.

  printf ("Content-type: text/html\n\n"); <-- Header 
  printf ("<HTML>\n"); 
  printf ("<HEAD>\n"); 
  printf ("<TITLE>Aprendiendo CGI-Segundo Ejercicio</TITLE>\n"); 
  printf ("</HEAD>\n"); 
  printf ("<BODY>\n"); 
  printf ("<HR>\n"); 
  printf ("Tu nombre es %s <BR>\n",nombre); 
  printf ("Tenés %s años y sos simpatizante de %s <BR>\n",edad,equipo); 
  printf ("Si alguien quisiera escribirte tu email es: %s <BR>\n",email); 
  printf ("<HR>\n"); 
  printf ("</BODY></HTML>\n"); 

Es evidente que a partir de este punto podríamos abordar cosas más complicadas si quisiéramos, ya que es posible utilizar todos los recursos de cada lenguaje para efectuar las acciones y procesos que queramos. Como sugerencia, la estructura que propongo para un script más o menos decente es la siguiente (de todas maneras igual que en matemáticas de primer grado, "el orden de los factores no altera el producto", aunque sí lo hace más difícil de mantener :)!

Lo mejor es comenzar a practicar con las estructuras más comunes, decisión (IF)
e iteración (FOR), haciendo pequeños programas y probando las salidas obtenidas.
Por ejemplo, un script que dependiendo del contenido de la variable "nombre" retorne una página con distintos mensajes según sea quien ha completado el form.

Los scripts pueden hacer cosas más complicadas, como verificar que los campos sean llenados correctamente, modificar archivos y hasta retornar una página que no haya sido elaborada por ellos. En el siguiente y último punto veremos como implementar estos métodos en la forma de un 'mini-guestbook'.

Un Mini-GuestBook
Vamos a armar una especie de Mini-Libro de Firmas, una aplicación bastante difundida, que nos servirá de ejemplo para mostrar los puntos que mencioné más arriba. Esto comienza con un form muy chiquito que solicita los siguientes datos:

A partir de allí, lo que el usuario ingrese será grabado en otra página, la página del guestbook más precisamente, y luego finalmente se presentará una pantalla agradeciendo el comentario e invitando a mirar el Libro.

Para poder conseguir lo que queremos, utilizaremos un método muy común, estableceremos una marca invisible (un comentario) en la página en cuestión y tomaremos dicha marca como punto de partida para cada nuevo comentario. Los pasos a seguir en este caso son los siguientes:

  1. Verificar la entrada del usuario, para que no existan campos vacíos, retornando un mensaje de error si existe algún problema.

  2. Abrir y leer la página del mini-guestbook línea por línea (grabando cada una de ellas en un archivo temporal) hasta encontrar la marca que indica el comienzo de los comentarios (o sea el último ingresado), grabar después de la misma el input del usuario y terminar de copiar el resto del archivo.

  3. Cerrar los archivos, borrar el original y renombrar el temporal.

  4. Devolver un código de retorno "Location.." que llevar  al usuario a una página de agradecimiento que tendrá un link al guestbook.

Muchos se preguntarán, Y que pasa si dos usuarios acceden al mismo tiempo al archivo? Cómo hacen para leerlo/grabarlo? Quién tiene prioridad? 
Creo que sería intentar avanzar demasiado en este momento, a manera de comentario (de todas maneras les contaré como hacerlo mas adelante) lo que se necesita es una manera de trabar el acceso al archivo mientras se esté trabajando con él.

Como siempre, veremos el form que permite al usuario agregar su nombre y su comentario a nuestro Libro de Firmas.


<HTML>
<HEAD>
<TITLE>Firme nuestro Libro</TITLE>
</HEAD>
<BODY>
<H1>Ingrese sus datos y el comentario que desee hacernos:</H1>
<BR><HR>
<FORM METHOD="POST" ACTION="http://mimaquina.midominio/cgi-bin/ingreso.pl">
Nombre:<BR><INPUT TYPE="text" NAME=nombre SIZE=20><BR>
Email: <BR><INPUT TYPE="text" NAME=email SIZE=20><BR>
Su comentario: <BR> <TEXTAREA NAME="comentario" ROWS=5 COLS=40> Escriba aqui
su comentario.</TEXTAREA> <BR>
<INPUT TYPE="submit" VALUE="Ok"><BR>
<INPUT TYPE="reset" VALUE="Borrar"><BR>
</FORM>
<HR>
</BODY>
</HTML>


Código fuente en Perl:

!#/usr/bin/perl

push(@INC,"/var/opt/ncsa/httpd/cgi-bin");
require("cgi-lib.pl");         <-- Incluimos la librería.

&ReadParse;                    <-- Separamos las variables.

$nombre = $in{'nombre'};       <-- Asignamos los valores que
$email = $in{'email'};          nos enviaron a las variables
$comentario = $in{'comentario'};           para utilizarlos.

Verifica si algún campo está vacío y retorna la siguiente página con mensaje de error.---

if (($nombre eq "") || ($email eq "") || ($comentario eq ""))
  {
  print &PrintHeader;
  print "<HTML>\n";
  print "<HEAD>\n";
  print "<TITLE> Error en el Ingreso</TITLE>\n";
  print "</HEAD>\n";
  print "<BODY>\n";
  print "<HR>\n";
  print "<H1>Alguno de los campos que ingresó no contenía datos </H1>";
  print "Por favor apriete este botón para ingresar sus datos nuevamente";
  print "<AHREF="/images/boton.gif">
  print "<HR>\n";
  print "</BODY></HTML>\n";
  exit;
  }

Arriba, la sentencia exit; hace una llamada para finalizar el script luego del mensaje de error.---

Abrimos y leemos el original guardando su contenido un el array "lineas".---

open(GB,"</htdocs/mini-gb.html");
@lineas = <GB>;
close GB;

Agregamos los "breaks" en cada newline del contenido de la TEXTAREA.---

$comentario =~ s/\n/<BR>\n/go;

open(GB,">/htdocs/mini-gb.htm");
foreach $linea (@lineas)
  {
  $linea =~ s/<!--ULTIMO COMENTARIO-->/<!--ULTIMO COMENTARIO-->\nNombre:
  $nombre<BR>\n Email: <A HREF=\"mailto:$email\">$email<\A><BR>\n Comentarios:<BR>\n$comentario<HR>\n/o;

Aquí grabamos cada línea reemplazando con el input del usuario en el lugar donde encontremos la marca.---

  print GB $line;
  }
close GB;

Vamos a la página de agradecimiento.---

print "Location: http://mimaquina.midominio/httdocs/gracias.htm\n\n";

Código fuente en Pascal:

Uses TPWCGI;                   <-- Incluimos la librería

Var nombre, edad, linea        <-- Definimos las variables 
 equipo, email :string;            que vamos a usar. 
Arch-In, Arch-Out :text; 

assign (Arch-In,'D:\WEBSITE\HTDOCS\MINI-GB.HTM');
assign (Arch-Out,'D:\WEBSITE\HTDOCS\MINI-GB.TMP');

reset (Arch-in);
rewrite (Arch-Out);
linea := '';

Begin
StartCGI;

Asignamos los valores que nos enviaron a las variables para utilizarlos.---

nombre := Getvalue ('nombre',1);
email := Getvalue ('email',1);
comentario := Getvalue ('comentario',1);

Si alguno de los campos está vacío retornaremos la siguiente página con este mensaje de error.---

if (( nombre = '') or (email = '') or (comentario = '') ) then begin
  MakeHeader;
  Send ('<HTML>'); 
  Send ('<HEAD>'); 
  Send ('<TITLE> Error en el Ingreso</TITLE>'); 
  Send ('</HEAD>'); 
  Send ('<BODY>'); 
  Send ('<HR>'); 
  Send ('<H1>Alguno de los campos que ingresó no contenía datos </H1>'); 
  Send ('Por favor apriete este botón e ingrese sus datos nuevamente'); 
  Send ('<A HREF="/images/boton.gif"> 
  Send ('<HR>\n'); 
  Send ('</BODY></HTML>'); 
  EndCGI; 
  end; 
 
else begin 

Aquí grabamos cada línea reemplazando con el input del usuario el lugar donde encontremos la marca.---

  Readln (Arch-In, linea); 
  while and NOT EOF (Arch-In) do 
    begin 
    Writeln (Arch-Out, linea);
    Readln (Arch-In, linea);
 
    If (linea = '<!--ULTIMO COMENTARIO-->') then begin 
    Writeln (Arch-Out, linea);
    linea := 'Nombre: '+nombre+'<BR>';
    Writeln (Arch-Out, linea);
    linea := 'Email:<A HREF="mailto:'+email+'">+email+'</A><BR>';
    Writeln (Arch-Out, linea);
    linea := 'Comentario: <BR>';
    Writeln (Arch-Out, linea);
    for i := 1 to GetLinNum ('comentario') do
      begin
      linea := GetValue ('comentario',1)+'<BR>';
      Writeln (Arch-Out, linea);
      end;
    end;
  end;

Close (FI);
Close (FO);

erase (FI);
rename (FO,'D:\WEBSITE\HTDOCS\DOCUMEN\MINI-GB.HTM');

Vamos a la página de agradecimiento.---

Send ('Location: http://mimaquina.midominio/httdocs/gracias.htm');
Send ('');
EndCGI
end. 


Me pareció redundante incluir la versión en C ya que esta puede ser fácilmente inducida a partir del fuente en Pascal. Como el TEXTAREA que utilizamos para cargar el comentario entrega sus datos sólo separados por newlines, tuvimos que hacer un pequeño proceso en cada uno de los programas para generar un <BR> por cada uno de ellos en la página definitiva. Notarán que existen diferencias en la forma de hacer el "update" del archivo en cada una de las versiones. En el fuente Perl aproveché una función de sustitución bastante potente que simplifica las cosas, en cambio en la versión en Pascal hubo que hacer dicha sustitución "a mano". Para finalizar esta segunda parte veremos una especie de lista de cosas a tomar en cuenta la hora de programar scripts CGI.

Un script CGI debe:

Imágenes clikeables.
A pesar que este tipo de elementos no está conectado directamente con los scripts CGI pensé en incluirlos dado que también son una de las formas de incrementar la interactividad entre el usuario y el web site. Básicamente una imagen clikeable (es una acepción que inventé, y fue la que me pareció más correcta en castellano) es una imagen, un gráfico que cuando es clickeada en alguna de sus partes envía las coordenadas X e Y de la posición del mouse en ese momento y el server a partir de ellas genera un enlace con otras páginas. 

El uso más común de este tipo de imágenes es crear barras de herramientas personalizadas, o regiones en un gráfico que permitan al usuario navegar hasta un nuevo documento.

En teoría existen tres formas de lograr este tipo de imágenes: 

Como todos los Web servers de la actualidad soportan la segunda forma, descartaremos la revisión de la primera y comenzaremos directamente a usar el tag ISMAP.

Partiendo de una imagen (un .GIF) elegiremos dentro de ella las áreas que funcionarán como links a otros documentos. Dichas áreas pueden ser definidas en base a uno o más de los siguientes elementos.

RECT (x1,y1) (x2,y2) 
Determina un área rectangular en base a los vértices de su diagonal principal. 

CIRCLE (a,b,r) 
Determina un círculo de centro (a,b) y de radio r. 

POLY (x1,y1) (x2,y2) .... (x1,y1) 
Determina un polígono cuyos vértices (x,y) son dispuestos secuencialmente uno detrás de otro.

Almacenaremos estos elementos en un archivo que tendrá  más o menos el siguiente formato:

default /default.htm 
RECTANGLE (475,157) (611,344) /principal.htm  
RECTANGLE (233,182) (447,359) /buscar.htm  
CIRCLE (130,54,20) /circulo.htm  
POLY (46,27) (57,33) (58,23) (68,35) (46,27) /carita.htm 

Usando un paquete gráfico cualquiera y revisando la posición del cursor podremos construir este tipo de archivos, el único problema es que nos puede resultar bastante tedioso el hacerlo "a mano". Existe un freeware realmente excelente llamado MapThis! que hace todo el trabajo por nosotros que puede ser bajado de cualquier repositorio de shareware como OAK, Garbo, CICA, SimTel, etc..

Veamos entonces la estructura del archivo. La primera línea linkea el documento que ser  cargado en caso de que no clickeemos en ninguna de las áreas especificadas por los elementos que definimos. Si omitimos el tipo de elemento el server tomar  por default el RECT. Por convención, grabaremos este archivo con la extensión .MAP en el directorio que más nos convenga.

Cuando tengamos que poner el tag IMG correspondiente a la imagen en la página en cuestión, agregaremos el par metro ISMAP y haremos un hyperlink al .MAP de esta manera.

<A HREF="/maps/dibujo.map"><IMG SRC="/images/barrita.gif" BORDER=0 ISMAP></A>

Entonces una vez cargada la página en nuestro browser, cada vez que clickeemos en alguna parte de la imagen , el browser enviar  las coordenadas de la ubicación en la que nos encontrábamos (esos numeritos que aparecen en la barra de status) al server, y luego de esperar su proceso, éste último nos devolverá la respuesta correspondiente.

Este mecanismo (Server-side), induce un cierto retardo (el proceso por parte del server de nuestro pedido) por lo cual recientemente Netscape introdujo un un nuevo mecanismo conocido como Client-Side Image Maps el cual veremos a continuación.

Client-Side Image Maps. 
En vez de realizar la conversión coordenadas/hyperlink en el server, esta será realizada por el browser. El .MAP que definíamos en el método anterior AHORA debe estar incluido DENTRO del c¢digo HTML con el siguiente formato.

<MAP NAME="barradetareas"> <AREA SHAPE="RECT" COORDS="475,157,611,344" HREF="principal.htm"> <AREA SHAPE="RECT" COORDS="233,182,447,359" HREF="buscar.htm"> <AREA SHAPE="CIRCLE" COORDS="130,54,20" HREF="circulo.htm"> <AREA SHAPE="POLY" COORDS="46,27,57,33,58,23,68,35,46,27" HREF="acarita.htm"> </MAP>

Y el link en el tag IMG quedaría así: 

<IMG SRC="/images/barrita.gif" USEMAP="#barradetareas">

En MAPNAME definimos el nombre del mapa y en USEMAP lo referenciamos. Al probarlo podremos ver que en la barra de status no aparecen las coordenadas sino directamente el nombre de la página en cuestión. Si utilizamos nombres de página significativos esto puede ser de gran ayuda para el usuario.

Este método obviamente es mucho más rápido (no implica un intercambio de información con el server) pero tiene la desventaja de que no está implementado en todos los browsers todavía, a pesar de que está propuesto como estándar para la definición de HTML 3.0. Para cubrir este aspecto podemos utilizar los dos métodos simultáneamente de esta manera.

<A HREF="/maps/dibujo.map"> <IMG SRC="/images/barrita.gif" USEMAP="#barradetareas" BORDER=0 ISMAP></A>

Si el browser no llegara a entender el USEMAP, procesar  nuestro pedido en base al link del ISMAP.

Desempaquetando información.
Aunque este tema puede resultar algo "técnico" creo que resulta interesante incorporarlo al tutorial, ya que nos permite conocer el funcionamiento de esas "cajas negras" que son las librerías, las cuales hemos utilizado al programar nuestros scripts. Sabemos que cuando usamos el método POST toda la información de un form es "empaquetada" en una variable llamada QUERY_STRING, pero... en qué forma?

La variable QUERY_STRING puede almacenar solamente un string continuo sin espacios, de manera que para enviar varios campos (y espacios) debe existir algún tipo de codificación de los mismos. De hecho, podemos verla en el siguiente ejemplo. Supongamos que en nuestro form solicitamos los siguientes datos: 

Nombre: Sebastian  (para variar :) 
Edad: 22 
Simpatizante de: Boca Juniors  (que grande boquita:-> 

Como decíamos estos datos deben ser empaquetados en un único string, esto se hace poniendo un "&" (ampersand) entre cada variable y reemplazando cada espacio por un signo "+", con lo cual conseguimos que la variable quede así;

QUERY_STRING = "nombre=Sebastian&edad=22&equipo="Boca+Juniors"

Adicionalmente, ciertos caracteres son reemplazados por su valor ASCII en un formato hexadecimal (%xx). Como vemos, esta ensalada de letras es imposible de manejar tal como está, por lo tanto recurrimos a los "parsers" cuya función es la de separar estos valores en cómodas y agradables variables.

Parsers CGI. La rutina escrita para Perl es un poco (sólo un poco) más clara que su correspondiente equivalente en C, por lo tanto la utilizaremos para la explicación del funcionamiento de un parser. Perl utiliza los llamados "arrays asociativos" que son muy similares a los arrays que todos conocemos, excepto por la salvedad (y a la vez gran ventaja) de que los índices no son numéricos, sino alfanuméricos. Cómo es esto? En esta rutina, todos los campos que se separan son almacenados en un array asociativo con el nombre @in. Si queremos acceder al contenido del campo edad, por ejemplo, éste se encuentra en la posición "edad" y el código empleado es el siguiente. 

@in { 'nombre'};

Lo que sigue es la rutina &ReadParse (que figura en la librería "cgi-lib.pl" de S.E.Brenner) comentada ampliamente para su mejor entendimiento.

# Perl Routines to Manipulate CGI input ³ ³
# S.E.Brenner@bioc.cam.ac.uk ³ 
# ReadParse 
# Lee datos enviados con POST o GET,

# los convierte a texto
simple, y pone 
# un campo=valor en cada componente de la lista "@in" 
# También crea pares de campo/valores en %in, 

#
usando '\0' para separar 
# selecciones múltiples 

sub ReadParse { 
  # Definición de las variables a utilizar. 
  local (*in) = @_ if @_; 
  local ($i, $loc, $key, $val); 
  # De acuerdo al método de envío 
  # almacena en la variable $in el texto. 
  if ($ENV{'REQUEST_METHOD'} eq "GET")
  { 
    $in = $ENV{'QUERY_STRING'}; 
  } elsif ($ENV{'REQUEST_METHOD'} eq "POST")
    {  
      read(STDIN,$in,$ENV{'CONTENT_LENGTH'}); 
    } 
  @in = split(/&/,$in);
  # Separa en un array los contenidos de $in que se
  # encuentran separados por "&" 
  foreach $i (0 .. $#in)
  { 
    # Convierte los "+" en espacios. 
    $in[$i] =~ s/\+/ /g; 
    # Separa el campo y su contenido . 
    ($key, $val) = split(/=/,$in[$i],2);
    # corta en el primer =. 
    # Convierte %XX de n£meros hexadecimales a 
    # alfan£mericos. 
    $key =~ s/%(..)/pack("c",hex($1))/ge; 
    $val =~ s/%(..)/pack("c",hex($1))/ge; 
    # Asocia cada campo con su valor. 
    # usando \0 como separador de elementos 

    #
múltiples(checkbox por ej) 
    $in{$key} .= "\0" if (defined($in{$key}));
    $in{$key} .= $val; 
    }
  return 1;
  } 

Esta no es la única librería que existe para manejar CGI bajo Perl , pero sí la más utilizada. Según el autor nuevas versiones incorporarán más facilidades, tales como file uploading y otras.

Locking de archivos.
En la segunda parte de este documento planteé el hecho problemático de que dos usuarios pretendan acceder al mismo tiempo a un mismo archivo, como es el caso del mini-guestbook. En Unix tenemos recursos que nos permiten realizar este tipo de bloqueos con una llamada al sistema, pero bajo otros sistemas (y para mantener la "transportabilidad" de nuestros scripts, veremos una técnica muy simple que puede ser implementada en cualquier plataforma y que puede ser utilizada sin problemas.

Cuando dos usuarios quieren leer/escribir un mismo archivo (en realidad los scripts llamados por ellos) se produce un conflicto, el de saber quien tiene la prioridad al hacerlo. Una manera de resolver este conflicto es crear un archivo "lock" cuya sola presencia nos indique que el archivo esta siendo accedido en ese momento. Así cuando nuestro script pretenda leer/escribir el archivo, primero consultará la existencia del "lock", si existe esperar  un segundo y volver  a intentar y en caso de que no exista lo crear  y proceder  a la consulta. Un ejemplo comentado en Perl nos mostrará esta técnica:

$salir = 'no'; 
while ($salir != 'si') { 
  if (-e "/mini-gb.lock") { 
    # Si el archivo existe, esperamos 1 segundo. 
    sleep(1); 
    }
  else { 
    # Si no existe, lo creamos y empezamos el proceso.   
    open(LOCK,">/mini-gb.lock"); 
    close LOCK; 

     . . . . . .Proceso de nuestro script . . . . . . . 

    # Destrabamos el archivo (lo eliminamos) 
    unlink("/guestlock"); 
    #Salimos de nuestra rutina 
    $salir = si; 
    }
  }
 

Scripts avanzados.
A partir de este punto analizaremos en detalle los requisitos para el manejo de documentos dinámicos, profundizando en el conocimiento de la forma de trabajo del protocolo HTTP y los mensajes MIME.

Documentos Dinámicos
Para introducirnos en el tema de los documentos dinámicos nos basaremos en una aplicación práctica, la animación, es decir imágenes en movimiento sin dejar de lado el hecho de que esta última es sólo una de las múltiples posibilidades que nos brinda el manejo dinámico de la información.

Actualmente existen tres métodos (si incluimos el gif multipart) para poder manejar información dinámicamente, en orden de eficiencia estos son:

Client Pull 
El server env¡a un "stream" de información, incluyendo una directiva (en la respuesta HTTP) que dice "vuelva a cargar estos datos en 5 segundos" o "vaya y cargue esta URL en 10 segundos". Después que se cumple el tiempo especificado, el browser hace lo que se le indicó, o sea recargar la página actual o conseguir una nueva página.

Server Push
El server envía un "stream" de información y el browser la muestra, pero sigue manteniendo la conexión abierta; cuando el server lo requiera, puede continuar enviando más información para que el browser la muestre, y así sucesivamente.

Gif multipart
Se trata del método más avanzado (lo vieron en WebTV?) que utiliza un nuevo formato de imagen GIF en donde se incluyen múltiples cuadros separados por comandos de animación, los cuales pueden ser vistos con el browser de Netscape partir de la versión 2.0 

Analicemos en detalle cada uno de los métodos.

Client Pull: Para muestra basta un botón, así que de entrada prueben el siguiente código:

<META HTTP-EQUIV="Refresh" CONTENT=1> <TITLE> Primera página</TITLE> <H1> HOLA! </H1> Una demostración de documentos dinámicos<P>

Y? Qué pasó?, por el bien de este tutorial espero que el documento haya sido recargado por el browser al pasar un segundo.

Esto lo logramos al agregar el tag "META" (el cual permite simular respuestas HTTP en páginas HTML) que le dice al browser que el server le está enviando un header con la indicación "Refresh: 1". Cambiando el valor, cambiamos el tiempo de retardo obviamente. Observemos que cada directiva "Refresh" es única y por tanto la página NO ser  refrescada cada un segundo eternamente, sino una única vez y que al tratarse de una respuesta HTTP (simulada) la misma debe figurar al comienzo de nuestra página. Otra cosa que podemos lograr con este tag es cargar otro URL luego de una determinada cantidad de tiempo. La sintaxis es la siguiente:

<META HTTP-EQUIV="Refresh" CONTENT=10; URL=http://mimaq.midominio/dinamic.htm>

Que hará que el browser luego de diez segundos de haber cargado la primera página vaya y traiga la que se encuentra referenciada por la variable URL. Un detalle importante es que debemos utilizar URLs absolutos, es decir incluir en la definición el "http://..." y no emplear URLs relativos.

Y que pasaría si hacemos lo siguiente: 

pri.html 
seg.html 
<META HTTP-EQUIV="Refresh" CONTENT=3; 
<META HTTP-EQUIV="Refresh" CONTENT=3; URL=http://mimaq.midominio//seg.html> 
URL=http://mimaq.midominio//pri.html 

Más de uno se habrá dado cuenta de que estos dos documentos se llaman el uno al otro cada tres segundos, generando una especie de "loop" infinito. A partir de este ejemplo, podemos inducir varias otras combinaciones tales como la de un documento que se llame a si mismo cada 0 segundos provocando así también este tipo de "loops sin fin". O podríamos hacer una cadena más larga, como la que sigue;

Página 1 »  Página 2 / Con la cual obtendríamos una especie 
Página 3 / de animación aunque por cierto, 
Página 4 / bastante pobre. 
Página 5

Un ejemplo de utilización práctica de esta técnica ocurre en aquellas páginas que nos indican que la página original ha cambiado de lugar y que en cinco segundos seremos automáticamente redireccionados a la nueva ubicación.

Server Push
En contraste con el Client Pull, este mecanismo aprovecha que el server puede mantener una conexión abierta por un tiempo indefinido, dejando espacio para que sean enviadas varias respuestas secuencialmente. Para lograrlo se utiliza un tipo MIME experimental (nuevo, no registrado aún como estándar) llamado "multipart/x-mixed-replace".

Mensajes MIME multipart. Existen varios tipos diferentes de mensajes "multipart"MIME. Cada uno de ellos le indica al cliente como debe procesar las diferentes partes que le serán enviadas. Con el tipo "multipart/mixed", la información de cada parte es independiente de las otras, y el cliente debería mostrar una tras otra a medida que van llegando. Con el tipo "multipart/alternative", la información de cada parte es idéntica, pero están formateadas de una manera distinta de manera que el cliente determina cual es el "mejor" formato que puede mostrar (por ejemplo elige "rich text" en vez de plain text) y lo muestra. Con el tipo "multipart/parallel", el cliente debería mostrar cada parte simultáneamente, si le fuera posible. Con el tipo "multipart/mixed-replace" que ser  el que utilizaremos, cada parte del mensaje se superpone a la parte anterior; el cliente sobrescribe la parte vieja con la más nueva.

Un mensaje MIME multipart está compuesto de un header y una o más partes que componen la totalidad del mensaje. El header indica como deben ser procesadas las distintas partes del mensaje y cuales son los separadores de las mismas, como ejemplo:

Content-Type: multipart/x-mixed-replace;boundary=SeparaPartes

Cuando el cliente encuentra este tipo en el header, hace un refresh del sector correspondiente cada vez que una nueva parte del mensaje llega, sobrescribiendo de esta manera la anterior.

Cada parte del mensaje tiene su propio header, el cual indica el tipo de datos que esa parte contiene, "plain text", un gráfico GIF o HTML por ejemplo. El header de cada parte siempre se encuentra debajo del separador que hallamos especificado (en la línea que vimos le pusimos "SeparaPartes" de nombre al separador , pero podríamos haber utilizado cualquier otro).

El server indica el fin de un mensaje multipart enviando un separador con el agregado de dos guiones, por ejemplo nuestro separador quedaría:

--SeparaPartes

El script que sigue es una versión reducida del NPH 1.2 creado por Matt Wright y en él veremos una implementación comentada del mecanismo que acabamos de describir. 

#!/usr/bin/perl
# Variables
$veces = "1";
$dirbase = "/WWW/images/animation/";
@archivos = ("primero.gif","segundo.gif","tercero.gif","primero.gif");
$tipo = "gif";

# Hacemos que el stream de datos fluya sin un buffer para hacerlo mas rápido
select (STDOUT);
$| = 1;

# Comienzo del contenido multipart.
print "Content-Type: multipart/x-mixed-replace;boundary=separador\n\n";
print "--separador\n";

# Este for toma cada imagen de la secuencia, la envía, manda un separador
#y luego envía la siguiente, repitiéndolo las veces que indique $veces.
for ($num=1;$num<=$veces;$num++) { 
  foreach $archi (@archivos) {
    print "Content-Type: image/$tipo\n\n";
    open(GIF,"$dirbase$archi"); 
    print <GIF>;
    close(GIF);
    print "\n--separador\n";
    }
  } 

Lo único que me quedaría por aclarar es que ya que el tipo "x-mixed/replace" fué definido por Netscape, solamente funcionará con su browser versión 1.1 y posteriores.

GIF multipart.
En este caso no tendremos que programar nada y es por lejos la forma más simple de armar una animación, aunque mantiene la desventaja de ser visualizados únicamente por el Netscape 2.0 en adelante. En definitiva se trata de ampliar las posibilidades del formato GIF89A (aquel que nos permite utilizar backgrounds transparentes). Los archivos .GIF tienen al comienzo un "header" que indica su tamaño y otros datos, con programas como el Gif Construction Set ampliaremos este header e incluiremos líneas con la secuencia de imágenes y comandos de control. El header de una animación simple quedaría de la siguiente manera: 

HEADERGIF89AScreen(320x200)
LOOP
CONTROL
IMAGE320x200,256colours
CONTROL
IMAGE320x200,256colours
CONTROL
IMAGE320x200,256colours

Ya que el Gif Construction Set trae un help bastante completo y es sencillo de usar no quisiera extenderme más en el tema. Sugerencia: Leer con el GifCon los .GIF que trae de ejemplo.

Si nos decidimos por utilizar este método tengamos en cuenta lo siguiente. La mayoría de los browsers (descartando Netscape 2.0) mostrarán únicamente el primer cuadro de la animación y algunos pocos sólo el último. De manera que si queremos que tratar de que la animación luzca bien en cualquier browser, la solución más simple es hacer que el primer cuadro sea el más presentable (nada de logos al revés) y que el último sea igual al primero.

Counters.
Implementar un "counter" no es una tarea muy difícil. Básicamente se graba en un archivo el valor del contador, y cada vez que se hace un nuevo acceso a la página controlada se lee este archivo, se incrementa en uno el valor leído y se vuelve a grabar. Veremos un fragmento de script que hace esta tarea. Por otro lado no quería dejar de comentar que existen scripts más complicados que pueden generar una salida en la forma de un archivo .GIF. Estos scripts lo que hacen es leer el valor a representar, "pegan" en una misma imagen los dígitos que componen dicho número y la envían. Si seguimos avanzando también encontraremos scripts que pueden controlar más de una página, ya que trabajan en base a un archivo índice que almacena el valor de los contadores para cada página en particular. A los fines de este tutorial el ejemplo presentado es lo suficientemente entendible como para poder implementarlo y practicar sin mucho trabajo. 

#!/usr/local/bin/perl

open(CONTADOR,"$counter.dat") || die "Error al abrir el archivo: $!\n";
$contador = <CONTADOR>;
close(CONTADOR);
if ($contador =~ /\n$/) {
  chop($contador);
  }

$contador++;

print "<HTML><HEAD><TITLE>Bla,bla,bla..</TITLE></HEAD>" 

open(CONTADOR,">$counter.dat") || die "Error al cerrar el archivo: $!\n";
print CONTADOR "$contador";
close(CONTADOR); 

 

Páginas Web con protección por password.
Para no dejar de lado el tema de la seguridad abordaremos el uso de passwords en el acceso a un servidor Web. Necesitaremos nuevamente tener algunos conocimientos previos, en este caso del manejo de archivos UNIX. Si utilizamos otro tipo de plataforma para correr el Web Server, como Windows NT, tendremos este tema solucionado con una muy sencilla configuración a través del uso de menús (por lo menos con el Website). Lo principal para aclarar antes de comenzar es que cuando utilizamos este sistema (según su mecánica interna) restringimos el acceso a un directorio, no a una página en particular.

El método se basa principalmente en poner en cada directorio a proteger un archivo de nombre ".htaccess" que contiene los par metros de configuración de la protección implementada y el nombre del archivo que contiene las passwords que normalmente es el ".htpasswd".

Y se acabó el misterio. Eso es todo. Cuando se le solicita al server una página que tiene en su mismo directorio un ".htaccess", éste se encarga de enviar los códigos HTTP necesarios para que el browser levante la ventanita de autentificación. Para explotar este recurso completamente pasaremos a analizar en detalle los parámetros que pueden ser utilizados en el ".htaccess" y la forma en que podemos mantener actualizado el ".htpasswd" .

El archivo ".htaccess".
Este archivo debe estar ubicado en el directorio que se desea proteger y dicha protección afectará a todos los subdirectorios del mismo, a menos que posean sus propios archivos ".htaccess". Dado que se trata de un archivo de texto común podremos actualizarlo con cualquier editor del que dispongamos.

AutUserFile .htpasswd
AuthGroupFile /dev/null
AuthName Ingrese su user ID y su password.
AuthType Basic

<Limit GET>
require user usuario1 usuario2 usuario3 .....
</Limit>

Descripción de los Parámetros.

En el ejemplo que vimos solamente el método GET está restringido usando la directiva <Limit>. Para limitar otros métodos (particularmente en los directorios CGI-BIN) se puede especificar los mismos separados por espacios dentro de la directiva LIMIT. Por ejemplo:

<Limit GET POST PUT> require user usuario1 usuario2 usuario3 ..... </Limit>

El comando "require" le dice al server cuales son los usuarios que están autorizados en ese directorio. Este comando puede tomar dos formas, "require user" para limitar el acceso según usuarios o bien "require group" para limitar el acceso según grupos de usuarios. Existen otros comandos que pueden ser implementados en este nivel, como "allow" y "deny" que permiten otorgar o denegar (respectivamente) el acceso a un dominio en particular.

<Limit GET POST PUT> order deny,allow deny from all allow from .mecon.ar </Limit>

Restringiendo el acceso a los documentos de ese directorio a aquellas máquinas que se encuentren en el dominio .mecon.ar y excluyendo a las restantes. Cabe destacar que los ID utilizados son completamente arbitrarios (es decir no tienen relación con los usuarios del server).

El archivo .htpasswd . Este archivo consiste en una lista de los user ID y sus password encriptadas, es un archivo de texto común por lo que puede ser movido, copiado o borrado como cualquier otro.

Por ejemplo:

pedro:WkSK1DE7N8.9. pablo:hQty39EV1.g56 diego:JeB31vf9PSTgw susana:EDRfRrfrw43q

Como no podemos crear una password encriptada "a mano" usaremos una herramienta llamada htpasswd que permite construir este tipo de archivos. Esta herramienta se usa de la siguiente manera:

Server> htpasswd -c .htpasswd USUARIO Adding password for USUARIO New password:******** Re-type new password: *******

Si queremos modificar la password de USUARIO en otro momento usamos la misma sintaxis. Para eliminar usuarios simplemente se borra la línea correspondiente dentro del archivo.

Protección a nivel de grupo Cuando tenemos un gran número de usuarios que mantener podemos facilitar el manejo de los mismos separándolos en "grupos" creando un archivo de nombre ".htgroup" (por convención) con el siguiente formato:

nombregrupo1: primeruser segundouser .... £ltimouser nombregrupo2: primeruser segundouser .... £ltimouser nombregrupo3: primeruser segundouser .... £ltimouser

y debemos cambiar el require por un "require group nombregrupo ".

Final del tutorial (por ahora).
Con el tema de las "passwords" damos por terminada la tercera parte de este documento, lo que sigue es un apéndice con links que me fueron bastante útiles al momento armarlo, la bibliografía utilizada y algo de vocabulario. Terminar esta tercera parte fue algo más que un triunfo, (pensar que había planeado tenerla lista para fines de marzo:), cada vez que agarraba el doc surgía un tema nuevo, y así. Releyendo las otras partes me doy cuenta que faltan algunos ítems (oh no..más laburo :P) o que por lo menos están un poco incompletos, paciencia en cuanto pueda sale una nueva versión corregida y aumentada. Lo próximo que tengo en mente en cuanto a tutoriales estará basado en el uso de frames y JavaScript en páginas HTML, una tecnología interesante pero no madura del todo en este momento, así como también una mini-introducción al lenguaje Perl, de manera que en algunos meses más empezaré por ese lado. A propósito, ustedes notarán que desde que empezó esto del tutorial mis preferencias programáticas han ido cambiando a lo largo del tiempo. Estos cambios tienen su fundamento; Perl es portable, es fácil de usar y de aprender, es freeware y además es interpretado de manera que el ciclo edición/ testeo se hace mucho más corto. Escribir este tutorial fué una tarea bastante grata, aprendí muchísimo tratando de explicar algunos conceptos, y como siempre para hacer de esta una tarea completa espero sus comentarios (de los buenos y de los otros ;), en mi email: dquir@indec.mecon.ar , los cuales serán de gran ayuda en mis proyectos futuros.

Suerte y buenos scripts! (frase afanada de algún lado) Sebass.

APENDICE: Librerías para la Programación de scripts CGI.

Estos son los URL de las librerías utilizadas en los ejemplos de este documento.

Librería C / UnCGI  http://www.hyperion.com/~koreth/uncgi.html 

Librería Pascal / TPWCGI / http://141.2.61.48/tpwcgi/tpwcgi.htm 

Librería Perl / CGI-LIB.PL / http://www.bio.cam.ac.uk/cgi-lin/1.14/cgi-lib.pl.txt 

Sitios con información acerca de CGI. Estos sitios contienen mucha de la información que fue utilizada para crear este documento y son realmente muy interesantes.

http://shani.net:80/~tls/guide/index.html 

http://www.stars.com/Vlib/Providers/CGI.html

http://super.sonic.net/ann/delphi/cgicomp/detail.html

http://www.city.net/win-httpd/httpddoc/wincgi.htm

http://www.aspectse.com/Product/dbWeb/dbWeb.html

http://128.172.69.106:8080/cgi-bin/cgis.html

http://users.aol.com/thingtone/workshop/index.htm 

Vocabulario - Siglas.
URL: Universal Resource Locator Una nomenclatura que describe en forma compacta la ubicación de cada recurso en la Internet y el protocolo utilizado para acceder al mismo.

MIME: Multipurpose Internet Mail Extensions Una nomenclatura que permite identificar correctamente el tipo de datos que se esta enviando a través de una conexión.

HTML: HyperText Markup Languaje Un lenguaje para el "armado" de documentos de hypertexto incluyendo "links" y algunas otras características adicionales.

CGI: Common Gateway Interface Un mecanismo que permite a los browser de Web ejecutar programas en el server Web y recibir la "salida" de estos programas.

HTTP: HyperText Transport Protocol Un protocolo para la transferencia de documentos de hipertexto y otro tipo de archivos.

VRML:Virtual Reality Modeling Languaje Un lenguaje que mediante "modelos" permite generar visiones 3D de un universo con "links" y otras características adicionales.

Bibliografía.
An exploration of Dynamic Documents - Netscape Corporation. Very Limited Guide to HTML - Kevin Werbach. 

CGI tutor - Bex Lanner. HTML-based Interfaces - Nik Swoboda. 

CGI Tutorial - Agora. An guide to HTML and CGI scripts - Mike Smith. 

Mosaic User Authentication Tutorial - NCSA. 

Web Page Password Protecion - CNC.

 

FIN DEL TUTORIAL 01/05/96
Autor: Sebastián Quiroga. diego@comunicaciones.mecon.ar  

Este documento puede ser reproducido en su totalidad o en parte siempre que se conserve esta posdata o bien se haga clara mención de la fuente.