Luis Colorado Acerca del Author: Luis Colorado trabaja como administrador de sistemas UNIX y de acceso a Internet para la empresa Telefónica Sistemas, S.A. en España. Es Licenciado en CC. Físicas por la Universidad Complutense de Madrid y ha preparado varias utilidades de libre distribución para entornos UNIX. Contenidos:Introducción Motivación M4 ¿Cómo funciona? ¿Cómo pasa la información del usuario al CGI? Descarga (Download) |
Resumen:
En este artículo se describe un programa para permitir el
acceso a bases de datos desde consultas HTML. El programa tiene
como requisitos el control de accesos, la flexibilidad en la
configuración y la independencia del formato de presentación. El
programa es el resultado de una serie de ideas surgidas de la
lectura de una serie de artículos sobre M4, aparecidos en las
revistas Linux Journal y LinuxFocus (www.linuxfocus.org), en
los 12 últimos meses.
Este artículo pretende ser una descripción de los trabajos ---como dicen los ingleses, `a rationale'--- o de las ideas que me llevaron a realizar este programa.
No pretende ser un manual de usuario (un manual de referencia acompaña al software, cuya URL de obtención se presenta a lo largo del artículo) sino mas bien una presentación del mismo y una invitación a los usuarios a que lo empleen y comuniquen al autor cualquier problema que puedan encontrar con él.
El programa surgió como respuesta a una serie de artículos aparecidos en las revistas Linux Journal (*) y LinuxFocus (*) sobre el empleo de M4 en entornos de gestión de documentación HTML, ya que dichos artículos mostraban el potencial de M4, por sí solo, para el mantenimiento de documentos HTML y control de información insertable en el momento de generación de las páginas.
Por otro lado, la disponibilidad de numerosos programas servidores web y numerosas aplicaciones de base de datos, hacía chocante la ausencia de aplicaciones de interfaz entre los dos entornos (la mayoría de aplicaciones de este tipo son comerciales, o tienen grandes defectos respecto a la dependencia de los formatos de presentación).
Este programa trata de unificar los dos mundos con una aplicación que reuna los siguientes requisitos:
La mayor parte de aplicaciones que conozco que se emplean como pasarela entre HTML y bases de datos, componen la información extendiendo el conjunto de tags de HTML para que se puedan identificar las `macros'. Pero esto tiene un grave problema: La mayoría de las sustituciones que hay que hacer en un documento para conseguir el efecto deseado ocurren dentro de los propios tags HTML. Lo que hace a estos programas muy limitados (muchos de ellos `reinventan' tags solo con el fin de obtener un determinado efecto sobre un tag existente). Mi programa pretende emplear M4 a fondo con el fin de conseguir sustituciones de macro allá donde se necesiten. La independencia del formato de salida la impone el hecho de que el programa por sí no genera ningún texto formateable, sino solo una colección de macros con los resultados de la consulta, que luego una plantilla y M4 integran en un documento HTML.
La aplicación propuesta consigue de forma aceptable estos requisitos, a costa de un pequeño coste de eficiencia (la aplicación ha de llamar varias veces a M4 en el curso de una ejecución) pero los resultados son satisfactorios en la mayoría de los casos (normalmente, los accesos a bases de datos suelen provocar retardos superiores en las propias consultas).
M4 se creó hace ya mucho tiempo con la idea de tener un procesador de macros potente y totalmente general. La utilización de M4 por la aplicación es muy fuerte:
La aplicación fué creada inicialmente para enlazar el interface CGI (Common Gateway Interface, interface común de pasarela entre el servidor Web y los programas de aplicacion) con la base de datos PostgreSQL. Pronto se vió que el programa era lo suficientemente general como para poder incluir otras pasarelas (por ejemplo pasarela con un servidor LDAP, o con otras bases de datos como Informix u Oracle). Se estandarizó el interface recogiendo la funcionalidad necesaria y se reescribió el modulo PostgreSQL de acuerdo con este interface. Ahora es posible escribir nuevos drivers que permitan al programa enlazar con otros gestores de base de datos. |
Esto puede provocar perdidas de eficiencia, aunque las pruebas realizadas con la utilidad M4 de GNU, han sido bastante satisfactorias.
La aplicación hace uso de expresiones regulares a la hora de verificar la regla de configuración a emplear. El uso de comparaciones sencillas tiene como resultado una pérdida notable de funcionalidad y posibilidades. El empleo de expresiones regulares para comparar permite, entre otras:
|
Debido a este último punto se consigue una gran funcionalidad: El agrupamiento de sub-expresiones regulares permite la exigencia de que los datos pasados por el cliente respondan a un formato específico, y obtener la información que nos interesa para pasarla al driver que se encargará de realizar la consulta.
Pongamos un ejemplo: Supongamos que nos interesa que el cliente nos pase una información en la query_string, y esta información debe ajustarse al formato:
CAMPO=valor
Además nos interesa forzar que la query_string se ajuste a este formato y no haya información extra.
Nosotros podremos conseguir este propósito poniendo el siguiente término en la regla de selección:
QUERY_STRING: "^CAMPO=[^&]*$";
Con la línea anterior conseguiremos que la regla sólo se seleccione si la QUERY_STRING es de esta forma. Pero aún más, si en vez de poner la línea anterior, rodeamos la parte correspondiente al valor de unos paréntesis, el programa nos permitirá obtener, ya separada, la parte correspondiente al valor (además, en este proceso, el programa transforma las secuencias de escape de la forma %xx, introducidas por el navegador) con el siguiente término:
QUERY_STRING: "^CAMPO=([^&]*)$";
Ha llegado el momento de describir cómo funciona el programa. El programa, al arrancar, recibe del servidor web un conjunto de variables de entorno que transmiten información al mismo. Este conjunto de variables de entorno es todo lo que tiene el programa a su disposición para obtener datos de: ¿Quién es el cliente? ¿Desde donde hace la petición? ¿Qué consulta está haciendo? ¿Qué tipo de información (MIME type) puede soportar el cliente? etc. El programa permite seleccionar la regla que empleará basándose en los términos que aparecen en la parte izquierda de la regla. Estos son de tres tipos (actualmente):
QUERY_STRING: "^CAMPO=([^&]*)$";
! HTTP_ADDRESS: "^194\.142\.12\.";(Este ejemplo invalidaría la regla si la petición llegara de la red 194.142.12.xxx)
[ QUERY_STRING: "USUARIO=([^&]*)" ];Este término permitirá pasar el nombre del usuario, caso de pasarlo el cliente, pero no invalida la selección de la regla en caso de no aparecer.
Con toda esta artillería, ya tenemos las bases para empezar a construir reglas. A continuación agruparemos todos los términos que nos permitan construir una regla válida y los encerraremos entre llaves, para formar lo que llamaremos el lado izquierdo de una regla.
El lado izquierdo de una regla y el lado derecho se delimitan de igual modo (entre llaves) y se separan entre sí por el símbolo `->'.
El lado derecho contiene términos con la misma estructura: un nombre de variable, el carácter `:', una cadena de caracteres y el carácter `;'. Los términos del lado derecho son asignaciones de valores a variables, que se realizan, empleando los servicios de M4:
El resto de variables, pueden emplearse en el fichero plantilla, o serán empleadas por los drivers correspondientes.
Muy fácil. Los grupos creados con las expresiones regulares de los términos del lado izquierdo de la regla son definidos en variables con nombres específicos (los nombres de las variables son `term_<i>_match_<j>', donde <i> hace referencia al número de término en la regla y <j> hace referencia al número del grupo (contando por la izquierda) en la expresión regular). Así, suponiendo que la query_string pasada por el cliente es:
y que tenemos los siguientes términos en la regla:
QUERY_STRING: "NOMBRE=([^&]*)"; QUERY_STRING: "APELL1=([^&]*)"; QUERY_STRING: "APELL2=([^&]*)";Esto dará como resultado:
term_0_match_0 <- "NOMBRE=JOSE"; term_0_match_1 <- "JOSE"; term_1_match_0 <- "APELL1=DE LA FUENTE"; (obsérvese la transformación de caracteres + por ` ') term_1_match_1 <- "DE LA FUENTE"; term_2_match_0 <- "APELL2=LOPEZ"; term_2_match_1 <- "LOPEZ";Drivers:
No haremos una descripción de cómo se emplean los drivers en este artículo, dejando al lector interesado la posibilidad de leer la documentación del manual de referencia que acompaña la distribución.
Sólo diremos que en estos momentos tan sólo hay un driver, para conexión a bases de datos tipo POSTGRESQL (el nombre del driver es este, precisamente) aunque el autor planea realizar uno para acceso a bases de datos LDAP.
Ejemplo:
Veamos un ejemplo completo para ilustrar el uso del programa:
Valga la información de la Tabla de consultas a la base de datos de `AVISOS A LOS NAVEGANTES' de slug.ctv.es. Es un ejemplo muy sencillo, que permite ver como emplear dos plantillas para ver registros individuales de una tabla, o un listado completo.
/etc/html2sql.cfg
{ PATH_INFO: "^/avisos/?$"; # Selecciona por PATH_INFO [SERVER_ADMIN: ".*"]; # Obtiene la info de SERVER_ADMIN. Opcional } -> { DRIVER: "POSTGRESQL"; PGTTY: "/dev/console"; # Envía trazas a consola. PGDATABASE: "postgres"; # Hacemos una consulta (siempre ponemos el oid, que lo # empleará internamente el fichero con la plantilla para # enlazar con los registros individuales). PGQUERY: "select oid,ct,titulo,texto,mt" " from avisos" " where (dt is NULL or dt > 'now')" " order by mt desc"; # Fichero que contiene la plantilla. M4FILE: "/usr/local/etc/httpd/plantillas_m4/avisos.m4"; WEBMASTER: "term_1_match_0"; #TESTMODE: "TRUE"; } # Esta regla de selección permite seleccionar un aviso en el que se ha # elegido un OID (clave primaria) La información va incluida en la # variable PATH_INFO del CGI. { PATH_INFO: "^/avisos/([0-9]+)/?$"; SERVER_ADMIN: ".*"; } -> { DRIVER: "POSTGRESQL"; PGTTY: "/dev/console"; # igual que antes, trazas por consola. PGDATABASE: "postgres"; OID: "term_0_match_1"; # damos un nombre al OID. # Nuevamente, la selección es importante. Incluimos el OID al # comienzo de los campos, por si quisieramos poner un hiperenlace para # borrar este registro. PGQUERY: "select oid,ct,titulo,texto,mt,dt,autor" " from avisos" " where (dt is NULL or dt > 'now') and oid=OID"; # La plantilla es diferente. M4FILE: "/usr/local/etc/httpd/plantillas_m4/avisos_oid.m4"; WEBMASTER: "term_1_match_0"; #TESTMODE: "TRUE"; } |
define(<<<for>>>, <<<dnl
ifelse(eval((<<<$2>>>) <= (<<<$3>>>)), 1, <<<define(<<<$1>>>,<<<$2>>>)$4<<<>>>dnl for(<<<$1>>>,eval(<<<$2>>>+1),<<<$3>>>, <<<$4>>>)dnl >>>)dnl >>>)dnl divert(0)dnl Mime-Version: 1.0 Content-type: text/html <HTML>
<!-- la tabla est\xe1 vac\xeda -->
>>>,<<<dnl /* PGRES_NTUPLES != 0 )( */
for(<<<i>>>,0,eval(PGRES_NTUPLES-1),<<<dnl
</table>
>>>)dnl /* PGRES_NTUPLES */ >>>,<<<dnl /* ifelse PGRES_RESULTSTATUS )(*/ Error en el resultado: <B>PGRES_RESULTSTATUS</b><BR>
>>>)dnl <CENTER><HR WIDTH=100></center>
|
divert(-1)
$Id: generic_list.m4,v 1.1 1998/07/06 17:13:33 luis Exp $ define(<<<cell>>>, <<<PGRES_CELL_$1_$2>>>) define(<<<field>>>, <<<PGRES_FNAME_$1>>>) define(<<<for>>>, <<<dnl ifelse(eval((<<<$2>>>) <= (<<<$3>>>)), 1, <<<define(<<<$1>>>,<<<$2>>>)$4<<<>>>dnl for(<<<$1>>>,eval(<<<$2>>>+1),<<<$3>>>, <<<$4>>>)dnl >>>)dnl >>>)dnl divert(0)dnl Mime-Version: 1.0 Content-type: text/html <HTML>
ifelse(PGRES_RESULTSTATUS, <<<PGRES_TUPLES_OK>>>,<<<dnl
<!-- la tabla est\xe1 vac\xeda -->
>>>,<<<dnl /* PGRES_NTUPLES != 0 )( */
>>>)dnl /* PGRES_NTUPLES */ >>>,<<<dnl /* ifelse PGRES_RESULTSTATUS )(*/ Error en el resultado: <B>PGRES_RESULTSTATUS</b><BR>
>>>)dnl <CENTER><HR WIDTH=100></center>
|
Los resultados se pueden ver, consultando, por ejemplo:
http://slug.ctv.es/cgi-bin/pg2cgi/avisos/
ó
http://slug.ctv.es/cgi-bin/pg2cgi/avisos/20384
Descarga (Download)
El programa puede hallarse en la siguiente dirección del web:
http://slug.ctv.es/~luis/utils/pg2cgi-0.1.tar.gzTexto original en Castellano
Páginas web mantenidas por Miguel Ángel Sepúlveda © Luis Colorado 1998 LinuxFocus 1998 |