PG2CGI databank toegang vanaf het Web

ArticleCategory:

Webdesign

AuthorImage:

[Luis Colorado]

TranslationInfo:

original in en Luis Colorado 

en to nl Tom Uijldert

AboutTheAuthor:

Luis Colorado werkt als beheerder voor Unix systemen en Internet toegang bij Telefónica Sistemas S.A. in Spanje. Hij is afgestudeerd natuurkundige van de Universidad Complutense van Madrid en heeft diverse open source hulpmiddelen ontwikkeld voor Unix.

Abstract:

We zullen hier een nieuw programma beschrijven dat de mogelijkheid tot databanktoegang biedt via HTML pagina's. Doelstellingen voor het programma-ontwerp waren de toegang tot data mogelijk te maken, flexibiliteit in het configureren en onafhankelijkheid van restricties in de opmaak. Het programma is het gevolg van ideeën die ik kreeg na het lezen van een aantal artikelen over M4 in recente edities van tijdschriften als Linux Journal en LinuxFocus van het afgelopen jaar.

ArticleIllustration:

[Illustration]

ArticleBody:

Inleiding

Dit artikel gaat in op mijn beweegredenen en ideeën voor het maken van een pakket dat databanktoegang vanaf het Web regelt. Bedoeling is niet om een handleiding te schrijven over PG2CGI (Een referentiehandleiding zit al in de distributie. De URL voor het pakket staat verderop in dit artikel) maar eerder om een kleine inleiding te geven op dit pakket om lezers aan te sporen het te gebruiken en gelegenheid te geven tot commentaar.

Motivatie

Ik schreef dit programma als een reactie op een aantal artikelen die verschenen in Linux Journal en LinuxFocus over het gebruik van M4 voor het beheren van HTML pagina's. Deze artikelen gingen in op de bruikbaarheid en voordelen van het inzetten van M4 als zelfstandig hulpmiddel voor het beheren en dynamisch genereren van Webpagina's.

Verder staat er een leger van webservers en databanken die, in schril contrast, uitblinken in de afwezigheid van hulpmiddelen om beide omgevingen te integreren (de meeste van dit soort hulpmiddelen zijn alleen commercieel te verkrijgen en hebben grote beperkingen voor wat betreft de formaten die het aankan).

Het hier besproken pakket verenigt databank- en web omgevingen. Het ontwerp diende aan de volgende voorwaarden te voldoen:

Het hier beschreven hulpmiddel voldoet aan de gestelde eisen ten nadele van de efficiency (M4 moet meerdere malen worden geactiveerd gedurende de uitvoering) maar de resultaten zijn in de meeste situaties voldoende (bedenk ook dat de aanvraag of query naar de databank meestal langer duurt dan de hele generatie van de dynamische HTML tekst).

M4

M4 is een macro processor die lang geleden is ontwikkeld. Onze software maakt hier zwaar gebruik van: Het herhaaldelijk gebruik van de macro processor kan verlies van efficiency tot gevolg hebben, hoewel onze tests met GNU M4 zeer bevredigend waren.

Reguliere expressies

Het pakket maakt grondig gebruik van reguliere expressies om de toe te passen configuratieregels te controleren. Reguliere expressies genieten de voorkeur boven simpele vergelijkingen omdat ze meer functionaliteit bieden. Reguliere expressies hebben de volgende voordelen:
PG2CGI gebruikt reguliere expressies voor het controleren van de toe te passen configuratieregel.
Het controleren van expressie syntaxis en het ontleden van de data voor de drivers vanuit de reguliere expressies kan eenvoudig worden gedaan met behulp van sub-expressies in een enkele reguliere expressie.

Voorbeeld: Stel de cliënt moet informatie doorgeven in de opvraagregel (query_string) en de data moet voldoen aan het volgende formaat:

  FIELD=waarde
Verder moet de query_string aan dit formaat voldoen en geen additionele informatie bevatten.

Het forceren van deze syntaxis kan eenvoudig worden gedaan met het opgeven van de volgende term in de selectieregel:

  QUERY_STRING: "^FIELD=[^&]*$";
Deze regel garandeert dat er alleen wat gebeurd als QUERY_STRING het juiste formaat heeft. Dat niet alleen, maar als we het nog eens tussen haakjes zetten dan kan het programma ook nog de bijbehorende waarde ontleden:
  QUERY_STRING: "^FIELD=([^&]*)$";
Tegelijkertijd zet het programma ook nog eens de escape codes van het type %xx om die uit de navigator komen.

Hoe werkt het?

Dit is een goed moment om te beschrijven hoe het programma werkt. Als het programma start krijgt het variabelen aangereikt van de web server omgeving en het configureert zichzelf aan de hand van die variabelen. De omgevingsvariabelen zijn het enige waar het programma achter moet zien te komen: hoe zit het met de cliënt? Waar begint de aanvraag? Wat is het aangevraagde? Wat voor informatie (MIME-type) wordt door de klant ondersteund? Enzovoorts. PG2CGI selecteert vervolgens de te gebruiken regel aan de hand van de linkerkant van de regel. Op dit moment zijn er drie typen regels: Met behulp van al deze mechanismen kunnen we nu een regel gaan bouwen. We zullen een aantal termen groeperen die de linkerkant van een regel specificeren. De termen staan tussen accolades ("{}").

De linker- en rechterkant van een regel worden afgebakend door accolades "{}" en van elkaar gescheiden door het symbool "->".

De rechterkant heeft hetzelfde formaat: de naam van een variabele, het teken ":", een aantal tekens en ";" als afsluiter. De rechterkant bevat alleen maar toewijzingen aan variabelen die M4 uit moet voeren:

Andere variabelen kunnen worden gebruikt door het template bestand of de betreffende driver.

Hoe bereikt de informatie van de gebruiker CGI?

Heel eenvoudig. De groepen van de expressies aan de linkerkant van de regel worden omgezet in variabelen (de namen daarvan hebben de vorm term_<i>_match_<j>, waarbij <i> het volgnummer is van de term binnen de regel (de eerste term heeft dus het volgnummer 1, de tweede 2 enzovoorts) en de <j> refereert aan het volgnummer van de groep, geteld vanaf links in de reguliere expressie. Als dan de query_string van de cliënt het volgende is:
      `NAME=JAN&FAMILIENAAM1=VAN+HET+HEK&FAMILIENAAM2=GOOR'
en de regel van de website is:
     QUERY_STRING: "NAAM=([^&]*)";
     QUERY_STRING: "FAMILIENAAM1=([^&]*)";
     QUERY_STRING: "FAMILIENAAM2=([^&]*)";
dan zou het resultaat zijn:
     term_0_match_0  <- "NAAM=JAN";
     term_0_match_1  <- "JAN";
     term_1_match_0  <- "FAMILIENAAM1=VAN HET HEK";  
           (de plustekens zijn vervangen door spaties)
     term_1_match_1  <- "VAN HET HEK";
     term_2_match_0  <- "FAMILIENAAM2=GOOR";
     term_2_match_1  <- "GOOR";
Drivers:

We gaan in dit artikel niet in op de drivers, info daarover is te vinden in de documentatie van de PG2CGI distributie. Geïnteresseerde lezers worden uitgenodigd om het referentiehandboek te lezen.

Op dit moment is er alleen een driver voor een connectie met PostgreSQL. De schrijver heeft echter plannen om een tweede te ontwikkelen voor een LDAP-achtige databank.

Een voorbeeld:

Laten we nu de sources bekijken van een volledig voorbeeld.

Neem de mededelingentabel in gedachten van de mededelingen databank bij slug.ctv.es (volg de AVISO A LOS NAVEGANTES link). Het is een zeer eenvoudig voorbeeld dat twee templates gebruikt voor het bekijken van één rij uit de tabel of het maken van een volledige overzichtslijst.

/etc/html2sql.cfg
{
  PATH_INFO: "^/avisos/?$"; # Selecteert door PATH_INFO
  [SERVER_ADMIN: ".*"];     # Haalt informatie op van SERVER_ADMIN. Optioneel
} -> {
  DRIVER:   "POSTGRESQL";
  PGTTY:    "/dev/console";  # Stuurt de logs naar de console
  PGDATABASE:     "postgres";
# We maken een query (schrijf altijd het oid dat het intern
# in het template bestand zal gebruiken om te linken naar de
# interne records).
  PGQUERY:  "select oid,ct,titulo,texto,mt"
      " from avisos"
      " where (dt is NULL or dt > 'now')"
      " order by mt desc";
# Het bestand dat het template bevat
  M4FILE:    "/usr/local/etc/httpd/plantillas_m4/avisos.m4";
  WEBMASTER: "term_1_match_0";
  #TESTMODE: "TRUE";
}

# De volgende regel laat ons toe om een aankondiging de selecteren (aviso)
# met een gekend OID (primaire sleutel). De informatie zit bij in de variabele
# PATH_INFO van de CGI.

{
  PATH_INFO: "^/avisos/([0-9]+)/?$";
  SERVER_ADMIN: ".*";
} -> {
  DRIVER:   "POSTGRESQL";
  PGTTY:    "/dev/console";  # zoals hierboven, write logt naar de console
  PGDATABASE:     "postgres";
  OID:      "term_0_match_1"; # wijs een OID toe

# Ook hier is de selectie belangrijk. We voegen de OID toe aan het
# begin van het veld voor het geval dat we een hyperlink willen schrijven
# om dit veld te wissen.

  PGQUERY:  "select oid,ct,titulo,texto,mt,dt,autor"
            " from avisos"
            " where (dt is NULL or dt > 'now') and oid=OID";
# Het template is nu anders
  M4FILE:    "/usr/local/etc/httpd/plantillas_m4/avisos_oid.m4";
  WEBMASTER: "term_1_match_0";
  #TESTMODE: "TRUE";
}

/usr/local/etc/httpd/plantillas_m4/avisos.m4
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>
  <BODY BGCOLOR="#ffffff">
    <CENTER>
      <H1>AVISOS A LOS NAVEGANTES QUE PASAN POR SLUG</h1>
    </center>
    <B>Nota:</b> Esta secci&oacute;n ha sido creada para dar a conocer
    cualquier noticia de inter&eacute;s relacionada con
    <A HREF="http://slug.ctv.es/">SLUG</a>,
    <A HREF="http://LuCAS.ctv.es/">LuCAS</a>,
    <A HREF="http://www.HispaLinux.ctv.es/">HispaLinux</a>
    y en general, cualquier servicio prestado por <B>slug.ctv.es</b>.<p>
    <CENTER><HR WIDTH=100></center>
ifelse(PGRES_RESULTSTATUS, <<<PGRES_TUPLES_OK>>>,<<<dnl
ifelse(PGRES_NTUPLES, 0,<<<dnl

      <!-- la tabla est\xe1 vac\xeda -->
      No hay avisos.<p>

>>>,<<<dnl /* PGRES_NTUPLES != 0 )( */
      <CENTER>
        <!-- contenido de la tabla -->
        <TABLE>
          <TR>
            <TH></th>
            <TH ALIGN="LEFT">Fecha-Hora&nbsp;</th>
            <TH ALIGN="LEFT">Asunto&nbsp;</th>
          </tr>

for(<<<i>>>,0,eval(PGRES_NTUPLES-1),<<<dnl
          <TR>
            <TD>
              <A HREF="/cgi-bin/pg2cgi/avisos/cell(i,0)">
                <IMG SRC="/icons/burst.gif">
              </a>
            </td>
            <TD><B>cell(i,1)&nbsp;</b></td>
            <TD>cell(i,2)&nbsp;</td>
          </tr>
>>>)dnl /* for */

        </table>
      </center>

>>>)dnl /* PGRES_NTUPLES */

>>>,<<<dnl /* ifelse PGRES_RESULTSTATUS )(*/

      Error en el resultado: <B>PGRES_RESULTSTATUS</b><BR>
      Mensaje del servidor: PGRES_ERRORMSG<P>

>>>)dnl

      <CENTER><HR WIDTH=100></center>
      <FONT SIZE=-2>
        <A HREF="mailto:WEBMASTER?subject=TABLON DE ANUNCIOS"><CODE>WEBMASTER</code></a>
      </font>
  </body>
</html>


/usr/local/etc/httpd/plantillas_m4/avisos_oid.m4
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>
  <BODY BGCOLOR="#ffffff">
    <CENTER>
      <H1>AVISO OID</h1>
    </center>
    <CENTER><HR WIDTH=100></center>

ifelse(PGRES_RESULTSTATUS, <<<PGRES_TUPLES_OK>>>,<<<dnl
ifelse(PGRES_NTUPLES, 0,<<<dnl

      <!-- la tabla est\xe1 vac\xeda -->
      No existe el aviso OID, o ha caducado.<p>

>>>,<<<dnl /* PGRES_NTUPLES != 0 )( */
      <CENTER>
        <!-- contenido de la tabla -->
        <TABLE>
          <TR VALIGN="BASELINE">
            <TD ALIGN="RIGHT" NOWRAP><font color="#808000"><B>Fecha de entrada:</b></font> </td>
            <TD width="80%">cell(0,1)</td>
          </tr><TR VALIGN="BASELINE">
            <TD ALIGN="RIGHT" NOWRAP><font color="#808000"><B>Fecha &uacute;ltima modif:</b></font> </td>
            <TD>cell(0,4)</td>
          </tr><TR VALIGN="BASELINE">
            <TD ALIGN="RIGHT" NOWRAP><font color="#808000"><B>Fecha eliminaci&oacute;n:</b></font> </td>
            <TD WIDTH=*>cell(0,5)</td>
          </tr><TR VALIGN="BASELINE">
            <TD ALIGN="RIGHT" NOWRAP><font color="#808000"><B>Autor:</b></font> </td>
            <TD><font size=+1><a href="mailto:cell(0,6)?subject=[TABLON-SLUG] cell(0,2)">cell(0,6)</a></font></td>
          </tr><TR VALIGN="BASELINE">
            <TD ALIGN="RIGHT" NOWRAP><font color="#808000"><B>Asunto:</b></font> </td>
            <TD><font size=+1><B>cell(0,2)<B></font></td>
          </tr><TR>
            <TD COLSPAN=2 BGCOLOR="#c0ffff"><font color="#404040">cell(0,3)</font></td>
          </tr>
        </table>
      </center>

>>>)dnl /* PGRES_NTUPLES */

>>>,<<<dnl /* ifelse PGRES_RESULTSTATUS )(*/

      Error en el resultado: <B>PGRES_RESULTSTATUS</b><BR>
      Mensaje del servidor: PGRES_ERRORMSG<P>

>>>)dnl

      <CENTER><HR WIDTH=100></center>
      <FONT SIZE=-2>
        <A HREF="mailto:WEBMASTER?subject=TABLON DE ANUNCIOS"><CODE>WEBMASTER</code></a>
      </font>
  </body>
</html>

Het resultaat kan worden bewonderd op de volgende URLs:
http://slug.ctv.es/cgi-bin/pg2cgi/avisos/
of
http://slug.ctv.es/cgi-bin/pg2cgi/avisos/20384

Ophalen

Het programma PG2CGI kan bij de volgende URLs op worden gehaald:

http://slug.ctv.es/~luis/utils/pg2cgi.tar.gz
http://slug.ctv.es/~luis/utils/pg2cgi.README
ftp://slug.ctv.es/pub/slug/luis/pg2cgi.tar.gz
ftp://slug.ctv.es/pub/slug/luis/pg2cgi.README