Tutorial CGI I II III
Introducción a SQL
Tutorial VBScript

Fao.gif (4164 bytes)

Tutorial de CGI III

Imagenes Clikeables.

A pesar que  este tipo de elementos  no este conectado directamente con los scripts CGI pense en incluirlos dado que tambien son una de las formas de incrementar la interactividad entre los usuarios y el web site.

Basicamente una imagen clikeable (es una acepcion que invente, ya fue, la que me parecio mas correcta en castellano) es una imagen, un grafico que cuando es clikeada en alguna de sus partes envia las coordenadas X e Y de la posicion del Mouse en ese momento y el server a partir de ellas genera un enlace con otras paginas.

El uso mas comun  de este tipo de imagenes es crear barras de herramientas personalizadas o regiones en un grafico que permitan al usuario navegar hasta un nuevo documento.

En teoria existen tres formas de lograr este tipo de imágenes;

Server-Side Image Maps (antigua)
Es la forma más antigua, utilizaba un Form para enviar la información y habia que programar un script en el server que procesara dicha información.
Server-Side Image Maps (Moderna)
Es la forma mas común y más ampliamaente utilizada, utiliza el tag ISMAP y la información es procesada por el web server.
Clien-Side Image Maps
Es la forma más moderna  ( y menos difundida) sólo soportad por los browsers en sus últimas versiones, y la información es procesada por el mismo browser.

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

Partiendo de una imagen (un .GIF) elegiremos dentro de ella las areas que funcionaran como links a otros documentos.
Dichas areas pueden ser definidas en base a uno omás de  los siguientes elementos.

RECT(x1,y1)(x2,y2) Determina un area rectangular en base a los vértices de su diagonal principal.
CIRCLE(a,b,r) Determina un circulo de centro (a,b) y de radio r.
POLY(x1,y1)(x2,y2)....(xn,yn) Determina un poligono cuyos vértices (x,y) son dispuestos secuencialmente uno detrás de otro.

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

default/default.htm

RECTANGULE(475,157)(611,344)/principal.htm
RECTANGULE(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 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 clikeemos en ninguna de las areas especificadas por los elementos que definimos. Si omitimos el tipo de elemento el server tomara por default el RECT.
Por convención, agregaremos el parametro ISMAP y haremos un hyperlink al .MAP de esta manera.

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

Entonces una vez cargada pla página en nuestro browser, cad vez que clikeemos en alguna parte de la imagen, el browserenviara las coordenadas de 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, este ultimo nos devolvera 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 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 sera realizada por el browser.
El .MAP que definiamos en el metodo anterior AHORA debe ser incluido DENTRO del codigo HTML con el siguiente formato:

<map name="barradetaresas">
<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="/imagenes/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 pagina en cuestion. Si utilizamos nombres de pagina significativos esto puede ser de gran ayuda para el usuario.

Este metodo obviamente es mucho más rapido (no implica un intercambio de informacion con el server) pero tiene la desventaja de que no esta implementado en todos los browsers todavia, a peras de que sta propuesto como estándar para la definicion del HTML 3.0.
Para cubrir este aspecto podemos utilizar los dos metodos simultaneamente de esta manera.

<a href="/maps/dibujo.map">
<img src="/imagenes/barrita.gif" usemap="#barradetareas" border=0 ismap></a>

Si el browser no llegara a entender el usemap, procesara nuestro pedido en base al link del ismap.

Desempaquetando Información.

Aunque este tema puede resultar algo "tecnico" 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 metodo POST toda la información de un form es "empaquetada" en una variable llamada QUERY_STRING, pero... en que 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
Edad: 22
Simpatizante de : Boca Juniors

Como deciamos estos datos deben de ser empaquetados en un único string, estyo se hace poniendo un "&" (ampersand) entre cada variable y reemplazando cada esepacio por un signo "+", con lo cual conseguimos que la variable quede asÍ:

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

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

Parsers CGI

La rutina escrita en 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 indices no son numericos, sino alfanumericos.
Como es esto?. En esta rutina, todos los campos que se separan son almacenadosen un array asociativo con el nombre @in. Si queremos acceder al contenido del campo edad, por ejemplo, este se encuentra en la posicion  "edad" y el codigo empleado es el siguiente.
@in{'nombre'};

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

#Perl Routines to Manipulate CGI input
#S.E.Brenner@bioc.cam.ac.uk
#ReadParse
#Lee los datos enviados con POST o GET, los convierte a texto simple, y pone
#un campo =valor en cada componente de la lista "@in"
#Tambien crea pares de campo/valores en %in, usando '\0' para separar
#selecciones multiples

sub ReadParse{
#Definicion de las variables a utilizar
  local (*in)=@_ if @_;
  local($i,$loc.$key,$val);

#De acuerdo al metodo 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 alfanumericos
$key=~s/%(..)/pack("c",hex($l))/ge;
$val=~s/%(..)/pack("c",hex($l))/ge;

#Asocia cada campo con su valor
#usando \0 como separador de elementos múltiples(checkbox por ejemplo)
$in{$key},="\0"if(defined($in{$key}));
$in{$key},=$val;

}
return1;
}

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

Locking de archivos

En la segunda parte de este documento plante, el hecho problematico 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.

Cuan dos usuarios quieren leer/escribir un mismo archivo (en realidad los scripst llamados por ellos)se produce un conflicto, el de saber quien tien 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 espera un segundo y volvera a intentar y en caso de que no exista lo creara y procedera a la consulta.
Un ejemplo comentado en Perl nos mostrara esta técnica:

$salir='no';
while($salir!='si'){
  if(-e "mini-gb.lock"){
    #Si el archivo existe, esperamos un segundo.
    sleep(1);
   }
   else{
     #al no existir el archivo, 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 dinamicos, profundizando en el conocimiento de la forma de trabajo del protocolo HTTP y los mensajes MIME.

Documentos Dinamicos

Para introducirnos en el tema de los documentos dinamicos nos basaremos en una aplicación practica, la animación, es decir imágenes en movimiento sin dejar de lado el hecho de que esta ultima es solo una de las multiples posibilidades que nos brinda el manejo dinamico de la información.

Actualmente existen tres metodos (si incluimos el gif multipart) para poder manejar información dinamicament, 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 " vuelve a cargar estos datos en 5 segundos" o "vaya y carge esta url en 10 segundos". Despues de que se cumple el tiempo especificado, el browser hace lo que se le indico , osea recargar la pagina actual o conseguir una nueva pagina.
Server Push
El server envía un "strem" 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 metodo mas avanzado (lo vierno 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 a partir de la version 2.0

Analicemos en detalle cada uno de los metodos.

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

<META HTTP-EQUIV="Refresh" Content="1">
<title> Primera pagina</title>
<H1>Hola!</h1>
Una demostración de documentos dinamicos<p>

Y???Que, paso?, 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 paginas HTML) que le dice al browser que el server le esta 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 pagina No seraá refrescada cada un segundo eternamente, sino una unica vez y que al tratarse de una respuesta HTTP (simulada) la misma debe figurar al comienzo de nuestra pagina .
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.html>

Que hara que el browser luego de diez segundos de haber cargado la primera pagina vaya y traiga la que se encuentre 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 parsaría si hacemos lo siguiente:

<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 habra dado cuenta de que estos documento se llaman el uno al otro cada tres segundos, generando una especie de loop infinito .
Aprtir de este ejemplo, podemos inducir varias otras conbinaciones tales como la de un documento que se llama a si mismo cada 0 segundos provocando adí tambien ese tipo de "loops sin fin"-
O podriamos hacer una cadena mas larga como la que sigue:

Pagina 1
  Pagina 2
    Pagina 3
      Pagina 4
        pagina 5

Un ejemplo de utilización practica de está tecnica ocurre en aquellas paginas que nos indican que la pagina original ha cambiado de lugar y que n cinco segundos seremos automaticamente redireccionados a la nueva ubicación.

Server Push

En contraste con el Client Pull, este mecanismo aprovecha que el serer puede mantenerse una conexion 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 aun como estandar)  llamado "multipart/x-mixed-replace.

Mensaje 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 de que van llegando. Con el tipo "multipart/alternative", la informacion en cada parte es idéntica, pero estan 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/mixed-replace" que sera el que utilizaremos, cada parte del mensaje se superpone a la parte anterior; el cliente sobrescrito 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 procesadaslas distintas partes del mensajey cuales son los separadores de las mismas, por ejemplo:

Conten-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, sobreescribiendo 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 grafico GIF o HTML por ejemplo. El header de cada parte siempre se encuentra debajo del separador que hallamos especificado(en la linea que vimos le pusimos "SeparaPartes" de nombre al separador, pero podriamos 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;

--Separa Partes

El script que sigue es una version reducida del NPH 1.2 creado por Matt Wright y en el veremos una implementadión comentada del mecanismo que acabamos de describir.

#!/usr/bin/perl
# Variables
$veces ="1";
$dirbase= "/WWW/imagenes/animation/";
@archivos = ("primero.gif","segundo.gif","tercero.gif","primero.gif");
$tipo="gif";
#Hacemos que el stream de datosfluya sin un buffer para hacerlo mas rapico
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 seguencia, la envia, manda un separador
#y luego envia la siguiente, repitiendolo 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 unico que me quedaría por aclarar es que ya el tipo "x-mixed/replace" fue definido por Netscape, solamente funcionara con su browser version 1.1 y posteriores

GIF multipart

En este caso no tendremos que programar nada y es por lejos la forma mas simple de armar una animación, aunque mantiene la desventaja de ser visualizados unicamente por Netscape 2.0 o posteriores.
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 Setampliaremos este header e incluiremos lineas con la secuencia de imagenes y comandos de control. El header de una animación simple quedaria 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 desidimos por usar este metodotengamos en cuenta lo siguiente:
La mayoría de los browsers (descartando Netscape 2.0) mostraran unicamente el primer cuadro de la animacióny algunos pocos solo el ultimo.
De manera que si queremos  que tratar que la animación luzca bienen cualquier browser, la solución más simple es hacer que el primer cuadro sea el más presentable (nada de logos al reves) y que el ultimo sea igual al primero.

Counters

Implementar un "counter" no es una tarea muy dificil. Basicamente se graba en un archivo el valor del contador, y cada vez que se hace un nuevo acceso a la pagina controlada, se lee este archivo, se incrementa en uno el valor leido y se vuelve a grabar. Veremos un fragmento de script que hace esta tarea.
Por otro lado, no queria 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 digitos que componen dicho numero y lo envian
Si seguimos avanzando tambien encontraremos scripts que pueden controlar mas de una pagina, ya que trabajan en base a un archivo indice que almacena el valor de los contadores para cada pagina en particular.
A los fines de este tutorial, el eljemplo presentado es lo suficientemente entendible como para poder implementarlo y practicar sin mucho trabajo

#!/usr/locaal/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(CONTADRO,">$counter.dat")||die "Error al cerrar el archivo: $!\n";
print CONTADOR "$contador";
close(CONTADOR);

Paginas 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.
Necesitamos nuevamente tener algunos conocimientos previos, en este caso el manejo de archivos UNIX.
Si utilizamos otro tipo de plataformas para correr el Web Server, como Windows NT, tendremos este tema solucionado con una muy sencilla configuración a traves del uso de menues (por lo menos con el website).
Lo principal para aclarar antes de comenzar es que cuando utilizamos este sistema (segun su mecanica interna) restringimos el acceso a un directorio, no a una pagina en particular.

El metodo se basa principalmente en poner a cada directorio a proteger un archivo de nombre ".htaccess" que contiene los parametros de configuración de la protección implementada y el nombre del archivo que contiene los passwordsque normalmente es el ".htpasswd".

El archivo ".htaccess"

Este archivo debe estar ubicado en el directorio que se desea proteger y dicha protección afectara 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
AutGroupFile /dev/null
AuthName Ingrese su user ID y su Password.
AuthTypeBasic

<Limit Get>
requiere user usuario1 usuario2 usuario3 ...........
</Limit>

Descripcion de los parametros

AutUserFile: Es el nombre del archivo que contiene los nombres de los usuarios (Users ID) y sus respectivos Passwords. Este archivo debe estar ubicado en el mismo directorio que ".htaccess" pero puede llevar cualquier nombre. por convencion le daremos el nombre de ".htpasswd".

AuthGroupFile: Este es el nombre del archivo de grupo. Podemos setear grupos de usuarios para que tengan acceso a un directorio, en el caso del ejemplo no tenemos un archivo de estos armado, por lo que direccionamos a null que significa que no existe un archivo de grupos.

AuthName: Es el strig que aparecera en la ventanita que levanta el browser , cuando se le pregunte al usuario su ID y Password.

AuthType: Es la clase de seguridad que implementamos . "Basic" significa Autentificación Basica HTTP, es decir que la información enviada a travez de la red no sera encriptada sino que sera "uuencodeada" (uuencoded), lo cual nos da un nivel de proteccion similar al de una sesion de telnet.
Existen otros metodos ademas de este tales como PEM, PGP, Kerberos V5 y Digest.

En el ejemplo que vimos solamente el metodo GET esta restringido usando la directiva <limit>. Para limitar otras metodos (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>
order deny,allow
deny from all
allow from .mencor.ar
</limit>

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

El archivo .httpasswd
Este archivo consite en una lista de los user ID y sus passwords encriptados, 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 un password encriptado 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 el password de USUARIO en otro momento usamos la misma sintaxis . Para eliminar usuarios simplemente se borra la linea correspondiente dentro del archivo.

Proteccion a nivel de grupo
Cuando tenemos un gran numero de usuarios que mantener podemos facilitar el manejo de los mismos separandolos en "grupos" creando un archivo de nombre ".htgroup" (por convencion) con el siguiente formato:

nombregrupo1:primeruser segundouser................nuser
nombregrupo2:primeruser segundouser................nuser
nombregrupo3:primeruser segundouser................nuser

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

< Anterior