CGI's (Common Gateway Interface) ist einfach Ausgedrückt eine Schnittstelle, womit man Anwendungen für das Internet schreiben kann. Diese CGI-Anwendung laufen dabei auf einem (Web)Server (wie Beispielsweise dem Apache) und wird von einer HTML-Webseite angesteuert. Die Daten erhält diese CGI-Anwendung entweder von der HTML-Seite selbst (POST-Verfahren) oder direkt über die URL (GET-Verfahren). C C++ C/C++ CGI mit C CGI Skripte in C CGI mit C- CGI Skripte in C Kapitel 27: CGI in C

27.10. HTML-Formulare            zurück  Ein Kapitel tiefer  zum Inhaltsverzeichnis

Mit den Umgebungsvariablen allein könnten Sie schon ein ganzes Arsenal von CGI-Anwendungen schreiben. Aber irgendwann werden Sie auch Daten eines HTML-Formulars auswerten wollen. Und darum geht es in diesem Abschnitt, nämlich um das Erstellen eines solchen Formulars. Natürlich handelt es sich dabei nur um einen groben Überblick. Sollten Sie mehr Wissen zu den HTML-Tags von Webformularen oder HTML im Allgemeinen benötigen, finden Sie im Anhang einige Links und Bücherempfehlungen.

27.10.1 Die Tags und ihre Bedeutung
Jetzt lernen Sie einige HTML-Tags kennen, mit denen Sie so genannte Webformulare erstellen können.

Hinweis
 

Der Inhalt von HTML-Dateien befindet sich eingeschlossen in so genannten Tags. Ein Tag besteht zum einen aus dem HTML-Befehl selbst und zum anderen aus einer Kennzeichnung, ob es sich dabei um einen einführenden oder einen abschließenden HTML-Befehl handelt. Der Bereich zwischen dem einführenden und dem abschließenden Tag wird als Gültigskeitsbereich bezeichnet. Als Beispiel hier die Tags, die Ihren Webbrowser anweisen, dass angeforderte Dokument als HTML-Seite zu interpretieren und darzustellen: <html> Das sehen Sie im Browser </html>

<form>-Tag
Diesen Tag werden Sie bei jedem Formular verwenden. Alle anderen Formular-Tags stehen zwischen den Tags <form> und </form>:

<form>
...
Formularinhalt
...
</form>

Folgende zwei Attribute des <form>-Tags sind von besonderem Interesse:

Somit könnte der <form>-Tag folgendermaßen aussehen:

<font action="./cgi-bin/auswert.cgi" method="POST">
...
Formularinhalt
...
</form>

<input>-Tag
Mit dem <input>-Tag können Sie ein Menge Formularobjekte erzeugen. Unterschieden werden diese durch das type-Attribut. Besonders wichtig für CGI-Anwendungen ist außerdem das Attribut name. Damit können Sie beim Auswerten eines Formulars auf den übergebenen Wert zugreifen. Jetzt zu den einzelnen Typen des <input>-Tags.

<input type="text">
So sieht es aus:

Abbildung 27.xx: Einzeiliges Eingabefeld

Abbildung 27.xx: Einzeiliges Eingabefeld

So wird es gemacht:

<input type=text name="name" size=32
       maxlength=32 value="dein name">

Bedeutung der Attribute
size Gibt die Breite des Textfeldes an.
maxlength Anzahl der Zeichen, die eingegeben werden können.
value Wert, der standardmäßig im Text erscheint. Wird dieses Attribut nicht verwendet, bleibt das Textfeld leer.

Tabelle 27.11: <input> - Tag (Text)

<input type="password">
So sieht es aus:

Abbildung 27.xx: Einzeiliges Passwortfeld

Abbildung 27.xx: Einzeiliges Passwortfeld

So wird es gemacht:

<input type=password value="abcdefg" size=16, maxlength=16> 

Bedeutung der Attribute
Als Attribute werden dabei dieselben wie schon beim Textfeld (type=text) verwendet. Dieses Eingabefeld dient allerdings nur als Bildschirmschutz, denn bei der GET-Methode wird das Passwort unverschlüsselt im Query-String angezeigt.

Tabelle 27.12: <input> - Tag (Passwort)

<input type="radio">
So sieht es aus:

Abbildung 27.xx: Radiobuttons

Abbildung 27.xx: Radiobuttons

So wird es gemacht:

<input type="radio" name="essen" value="Pizza" checked> Pizza<br>
<input type="radio" name="essen" value="Pommes"> Pommes<br>
<input type="radio" name="essen" value="Salat"> Salat<br>

Bedeutung der Attribute
value Dieser Wert wird, falls aktiviert, beim Request an die CGI-Anwendung mitgeschickt.
checked Dieses Attribut können Sie nur an einen Radiobutton in der Gruppe übergeben. In diesem Beispiel ist das die Gruppe essen.

Tabelle 27.13: <input> -Tag (Radiobuttons)

<input type="checkbox">
So sieht es aus:

Abbildung 27.xx: Checkboxen

Abbildung 27.xx: Checkboxen

So wird es gemacht:

<input type="checkbox" name="zusatz" value="10000">
Nachnahme (+6 Euro)<br>
<input type="checkbox" name="zusatz" value="20000">
Express &nbsp;&nbsp;(+2 Euro)<br>
<input type="checkbox" name="zusatz" value="30000">
Versichert(+2 Euro)<br>

Bedeutung der Attribute
Die Checkboxen können ebenso verwendet werden wie schon die Radiobuttons, allerdings mit dem Unterschied, dass mit Checkboxen eine Mehrfachauswahl möglich ist. Natürlich kann man hier das Attribut checked ebenfalls mehrmals verwenden.

Tabelle 27.14: <input> - Tag (Checkboxen)

<input type="submit"> und <input type="reset">
So sieht es aus:

Abbildung 27.xx: Schaltflächen (Buttons)
Abbildung 27.xx: Schaltflächen (Buttons)

So wird es gemacht:

<input type="Submit" name="sub" value="Abschicken">
<input value="Zurücksetzen" type="reset">

Bedeutung der Attribute
Mit dem Button vom Typ Submit wird das Formular abgeschickt. Es wird also eine Anfrage an die URL gemacht, welche Sie im <form>-Tag beim action-Attribut angegeben haben. Das Attribut name macht beim Submit-Button eigentlich nur dann Sinn, wenn Sie mehrere Submit-Buttons für ein Formular verwenden. Mit dem Button vom Typ reset werden alle bisher veränderten Werte im Formular wieder auf ihren Ursprungszustand gesetzt. Die Beschriftung der beiden Buttons legen Sie mit dem Attribut value fest.

Tabelle 27.15: <input> - Tag (Schaltflächen)

<texarea>-Tag
So sieht es aus:

Abbildung 27.xx: Mehrzeiliges Textfeld

Abbildung 27.xx: Mehrzeiliges Textfeld

So wird es gemacht:

<textarea name="textform" cols="32" rows="6">Textinhalt
</textarea>

Bedeutung der Attribute
Damit haben Sie einen Textbereich mit einer Breite von 32 Zeichen und sechs Spalten erstellt. Den Text, den Sie zwischen den Tags <textarea>text</textarea> schreiben, finden Sie in dem Textbereich wieder. Außer den für sich selbst sprechenden Attributen cols und rows können Sie noch das Attribut wrap verwenden. Damit können Sie angeben, ob und wie der Text am Zeilenende gebrochen werden soll. Als Parameter können Sie dazu verwenden : virtual, physical und none.

Tabelle 27.16: <textarea> - Tag

<select>-Tag
Mit dem <select>-Tag können zwei unterschiedliche Arten von Listen erzeugt werden. Welche Art, das bestimmen Sie mit dem Attribut size. Geben Sie für dieses Attribut den Wert 1 an, erstellen Sie ein Aufklappmenü. Geben Sie diesem Attribut beispielsweise den Wert 5, so haben Sie eine Scroll-Liste, mit fünf sichtbaren Elementen. Als Beispiel:

size=1  

So sieht es aus:

Abbildung 27.xx: Listen (Aufklappmenü)

Abbildung 27.xx: Listen (Aufklappmenü)

So wird es gemacht:

Seite bewerten:
<select name="Bewertung" size="1">
<option>Note 1</option>
<option>Note 2</option>
<option>Note 3</option>
<option>Note 4</option>
<option>Note 5</option>
<option>Note 6</option>
</select>

Bedeutung der Attribute
Mithilfe des <option>-Tags können Sie die möglichen Werte im Klartext schreiben, da der Tag <select> kein value-Attribut besitzt. Setzen Sie beim <option>-Tag zusätzlich noch das Attribut selected, können Sie dieses Element in der Liste als ausgewählt anzeigen lassen.

Tabelle 27.17: <select> - Tag

Die zweite Möglichkeit:

size= (>1)

So sieht es aus:

Abbildung 27.xx: Listen (Scrollbox)

Abbildung 27.xx: Listen (Scrollbox)

So wird es gemacht:

Seite bewerten:
<select name="Bewertung" size="3">
<option>Note 1</option>
<option>Note 2</option>
<option selected>Note 3</option>
<option>Note 4</option>
<option>Note 5</option>
<option>Note 6</option>
</select>

Bedeutung der Attribute
Zusätzlich können Sie mit dem Attribut multiple beim <select>-Tag dafür sorgen, dass mehrere Optionen ausgewählt werden können. Dies funktioniert natürlich nur mit Scroll-Listen. Außerdem müssen Sie im Falle einer Mehrfachauswahl die STRG-Taste gedrückt halten.

Tabelle 27.18: <select> - Tag

Zu diesen Tags gibt es natürlich noch eine Menge weiterer Attribute und Optionen. Diese hier alle zu erwähnen, würde den Rahmen sprengen. Sollten Sie mehr darüber lernen wollen, habe ich Ihnen im Anhang Links und empfehlenswerte Literatur zusammengestellt. Oder noch besser, Sie sehen sich einfach den Quelltext diverser Webseiten an, deren Formulare Ihnen besonders gut gefallen. Aber auch hier gilt: nur inspirieren lassen und nicht klauen.

27.11. CGI-Eingabe            zurück  Ein Kapitel höher  zum Inhaltsverzeichnis

27.11.1 Die Anfrage des Clients an den Server
In den Abschnitten zur Client-Anfrage und den Formular-Tags wurden die zwei vorwiegend in der Praxis eingesetzten Methoden zum Empfangen von Daten schon erwähnt (POST, GET). Diese Methoden werden jetzt anhand des folgenden HTML-Formulars genauer erläutert:

<html>
<head>
<title>Eine einfache Auswertung</title>
</head>
<body>
<h1><center>
<b><u>Formular</u></b></center></h1><br><br>
<form action=http://localhost/cgi-bin/auswert.cgi method=get>
   <center><b>Bitte geben Sie Ihren Namen ein :</b><br>
   <input name="Textfeld" size="20"> </input>
   <input type=submit value="abschicken"></center>
</form>
</body>
</html>

So sieht es aus:

Abbildung 27.xx: Eingabeformular

Abbildung 27.xx: Eingabeformular

Von ganz besonderem Interesse ist hier der <form>-Tag. Sobald Sie den Button "Abschicken" drücken, wird mit dem Attribut action ein Prozess, nämlich die CGI-Anwendung auswert.cgi, auf dem Webserver gestartet. Darin unterscheiden sich beide Methoden nicht. Da im HTML-Beispiel hier die Methode GET (method=get) verwendet wurde, soll diese auch gleich als erste behandelt werden.

Vorgang von GET
Bei der GET-Methode hängt der Browser die Zeichenkette am Ende der URL an. Bei einer Eingabe der Zeichenkette "hallo" hat die URL folgendes Aussehen:

http://localhost/cgi-bin/auswert.cgi?Textfeld=hallo 

Der Webserver ist jetzt dafür verantwortlich, diese Zeichenkette wieder zu entfernen und übergibt diese an die Umgebungsvariable QUERY_STRING:

QUERY_STRING = Textfeld=hallo 

Jetzt sind Sie an der Reihe, mit dem CGI-Programm die Umgebungsvariable QUERY_STRING auszuwerten. Die GET-Möglichkeit stellt dabei die einfachste Art dar. Es herrscht hierbei immer noch der Irrglaube, dass mit der GET-Methode nur 255 bzw. 1024 Bytes verarbeitet werden können. Dies wird aber vom Webserver vorgegeben und beträgt mindestens 255 Bytes bis zu 8192 Bytes.

Vorgang von POST
Wollen Sie statt der Methode GET die Methode POST verwenden, müssen Sie nur im HTML-Formular die Zeile

<form action="http://... /cgi-bin/auswert.exe" method=get>

umändern in:

<form action="http://.. /cgi-bin/auswert.exe" method=post >

Bei der POST-Methode werden die Daten nicht in einer der Umgebungsvariablen abgelegt, sondern in die Standardeingabe (stdin). Sie können somit die CGI-Anwendung so schreiben, als würde die Eingabe von der Tastatur gemacht. Die Länge des kodierten Strings befindet sich in der Umgebungsvariablen CONTENT_LENGTH. Außerdem wird noch die Umgebungsvariable CONTENT_TYPE verwendet, damit die CGI-Anwendung weiß, um was für ein Art Dokument es sich handelt.

POST- oder GET-Methode programmtechnisch auswerten
Damit die CGI-Anwendung jetzt weiß, ob die Daten mit der Methode GET oder POST gesendet wurden, benötigen Sie eine Funktion, welche die Umgebungsvariable REQUEST_METHOD auswertet und den Inhalt (String), sei es nun vom QUERY_STRING oder von der Standardeingabe, an den Aufrufer zurückgibt.

/*
 *  Funktion liest Daten in der POST- oder GET-Methode ein.
 *  Rückgabewert: String puffer mit den Daten
 *  bei Fehler  : NULL
*/
char *getdata()
{
   unsigned long size;
   char *puffer = NULL;
   char *request = getenv("REQUEST_METHOD");
   char *cont_len;
   char *cgi_string;

   /* Zuerst die Request-Methode überprüfen */
   if(  NULL == request )
      return NULL;
   else if( strcmp(request, "GET") == 0 )
      {
         /* Die Methode GET -> Query String abholen */
         cgi_string = getenv("QUERY_STRING");
         if( NULL == cgi_string )
            return NULL;
         else
            {
               puffer =(char *) Strdup(cgi_string);
               return puffer; /* Rückgabewert an den Aufrufer */
            }
      }
   else if( strcmp(request, "POST") == 0 )
      {
         /* Die Methode POST -> Länge des Strings
            ermitteln (CONTENT_LENGTH) */
         cont_len = getenv("CONTENT_LENGTH");
         if( NULL == cont_len)
            return NULL;
         else
            {
               /* String CONTENT_LENGTH in
                  unsigned long umwandeln */
               size = (unsigned long) atoi(cont_len);
               if(size <= 0)
                  return NULL; /* Keine Eingabe!?!? */
            }
         /* Jetzt lesen wir die Daten von stdin ein */
         puffer =(char *) malloc(size+1);
         if( NULL == puffer )
            return NULL;
         else
            {
               if( NULL == fgets(puffer, size+1, stdin) )
                  {
                     free(puffer);
                     return NULL;
                  }
               else  /* Rückgabewerte an den Ausrufer */
                  return puffer;
            }
      }
   else /* Weder GET-Methode noch die POST-Methode
           wurden verwendet */
      return NULL;
}

/*
 *  Da die Funktion strdup() in der Headerdatei <string.h> keine
 *  ANSI C-Funktion ist, schreiben wir eine eigene
 */
char *Strdup(const char *str)
{
   char *p;
   if(NULL == str)
      return NULL;
   else
      {
         p = malloc(strlen(str)+1);
        if(NULL == p)
           return NULL;
        else
           strcpy(p, str);
      }
   return p;
}

Es wurde außerdem eine extra Funktion für Strdup() geschrieben, da die vorhandene strdup() in der Headerdatei <string.h> nicht zum ANSI C-Standard gehört. Sie können diese eben erstellten Funktionen gern in einer CGI-Anwendung testen und ausführen. Falls Sie die CGI-Anwendung getestet haben und Sie haben etwa folgende Eingabe in einem Webformular gemacht

Jürgen "pronix" Wolf 

erscheint anschließend im Webbrowser folgende Zeile:

Textfeld=J%FCrgen+%22pronix%22+Wolf 

Also noch ein wenig vom gewünschten Ergebnis entfernt.

27.11.2 Eingabe parsen
Sie können zwar jetzt die Eingabe vom Client empfangen, aber Sie müssen den String noch lesefreundlich dekodieren. Hier ein Überblick zu den Zeichen, welche eine besondere Bedeutung haben, und die Sie als Programmierer von CGI-Anwendungen berücksichtigen müssen.

In der folgenden Tabelle finden Sie Steuer- und weitere Zeichen und ihre hexadezimale Darstellung im Überblick:
Zeichen Hexadezimale Darstellung
Tab %09
Space %20
" %22
# %23
% %25
& %26
( %28
) %29
' %2C
. %2E
/ %2F
: %3A
; %3B
< %3C
= %3D
> %3E
? %3F
@ %40
[ %5B
\ %5C
] %5D
^ %5E
, %60
{ %7B
| %7C
} %7D
~ %7E

Tabelle 27.19: Kodierte Zeichen und Ihre Bedeutung

Jetzt wollen Sie natürlich auch wissen, wie Sie dies in der Praxis dekodieren können. Folgende Funktionen sind dazu zunächst nötig:

Alle anderen Schritte folgen in den nächsten Funktionen. Hierzu zunächst die Funktionen, mit denen die hexadezimalen Zahlen in ASCII-Werte und die +-Zeichen in echte Leerzeichen konvertiert werden.

/* Wandelt einzelne Hexzeichen (%xx) in ASCII-Zeichen
   und kodierte Leerzeichen (+) in echte Leerzeichen um */
void hex2ascii(char *str)
{
   int x,y;
   for(x=0,y=0; str[y] != '\0'; ++x,++y)
      {
         str[x] = str[y];
         /* Ein Hexadezimales Zeichen? */
         if(str[x] == '%')
            {
               str[x] = convert(&str[y+1]);
               y+=2;
            }
         /* Ein Leerzeichen? */
         else if( str[x] == '+')
            str[x]=' ';
      }
   /* Geparsten String sauber terminieren */
   str[x] = '\0';
}

/* Funktion konvertiert einen String von zwei hexadezimalen
  Zeichen und gibt das einzelne dafür stehende Zeichen zurück */
char convert(char *hex)
{
   char ascii;
   /* erster Hexawert */
   ascii =
   (hex[0] >= 'A' ? ((hex[0] & 0xdf) - 'A')+10 : (hex[0] - '0'));

   ascii <<= 4; /* Bitverschiebung schneller als ascii*=16 */
   /* zweiter Hexawert */
   ascii +=
   (hex[1] >= 'A' ? ((hex[1] & 0xdf) - 'A')+10 : (hex[1] - '0'));
   return ascii;
}

Sollten Sie die Funktionen wieder in der Praxis testen wollen, können Sie dies tun. Jetzt bekommen Sie die Eingabe des Formulars im Klartext ausgegeben:

Textfeld=Jürgen "pronix" Wolf

Im nächsten Schritt benötigen Sie eine Funktion, die diese beiden Werte auseinander hält und zwischenspeichert. In diesem Beispiel wurde nur eine Variable (Textfeld) mit dem Wert (Jürgen "pronix" Wolf) verwendet. Die nun folgende Funktion sollte in diesem Zusammenhang allgemein gültig und auch später wieder verwendbar sein. Daher kommen Sie nicht um die Programmierung einer dynamischen Liste herum. Folgende Struktur soll hierfür verwendet werden:

struct CGI_DATEN
{
  char *variable;
  char *wert;
  struct CGI_DATEN *next;
};

struct CGI_DATEN *ende = NULL;

Die Daten einer einzelnen CGI-Eingabe werden anhand des '='-Zeichens getrennt (variable=wert). Somit muss dieses Zeichen auch geparst werden. Und für den Fall (so ist es meistens), dass mehrere Variablen/Werte-Paare übermittelt werden, müssen Sie auch das Zeichen '&' parsen, welches die Paare voneinander trennt (variable1=wert&variable2=wert). Eine Menge Arbeit liegt damit vor Ihnen. Hier die komplette Funktion.

struct CGI_DATEN *erstellen(char *str)
{
   char* s;
   char* res;
   /* Irgendwo gibt es auch eine Grenze, hier sind
      MAX_PAARE erlaubt */
   char *paare[MAX_PAARE];
   struct CGI_DATEN *ptr_daten = NULL;
   struct CGI_DATEN *ptr_anfang = NULL;
   int i=0, j=0;
   /* Zuerst werden die Variablen/Werte-Paare anhand
      des Zeichens '&' getrennt, sofern es mehrere sind */
   s=str;
   res=strtok(s,"&");
   while( res != NULL && i < MAX_PAARE)
      {
         /* Wert von res dynamisch in char **pair speichern */
         paare[i] = (char *)malloc(strlen(res)+1);
         if(paare[i] == NULL)
            return NULL;
         paare[i] = res;
         res=strtok(NULL,"&");
         i++;
      }
   /* Jetzt werden die Variablen von den Werten getrennt und
      an die Struktur CGI_DATEN übergeben  */
   while ( i > j )
      {/* Das erste Element? */
         if(ptr_anfang == NULL)
            {
               ptr_anfang =(struct CGI_DATEN *)
                            malloc(sizeof (struct CGI_DATEN *));
               if( ptr_anfang == NULL )
                  return NULL;
               res = strtok( paare[j], "=");
               ptr_anfang->variable = malloc(strlen(res)+1);
               if( ptr_anfang->variable == NULL )
                  return NULL;
               ptr_anfang->variable = res;
               res = strtok(NULL, "\0");
               ptr_anfang->wert = malloc(strlen(res)+1);
               if( ptr_anfang->wert == NULL )
                  return NULL;
               ptr_anfang->wert = res;
               //printf("%s %s<br>",ptr_daten->variable,
                                    ptr_daten->wert);
               ptr_anfang->next =
                            malloc(sizeof (struct CGI_DATEN *));
               if(ptr_anfang->next == NULL)
                  return NULL;
               ptr_daten = ptr_anfang->next;
               j++;
            }
         else
            {/* Die restlichen Elemente */
               res = strtok( paare[j], "=");
               ptr_daten->variable = malloc(strlen(res)+1);
               if(ptr_daten->variable == NULL)
                  return NULL;
               ptr_daten->variable = res;
               res = strtok(NULL, "\0");
               ptr_daten->wert = malloc(strlen(res)+1);
               if(ptr_daten->wert == NULL)
                  return NULL;
               ptr_daten->wert = res;
               //printf("%s %s<br>",ptr_daten->variable,
                                    ptr_daten->wert);
               ptr_daten->next =
                           malloc(sizeof (struct CGI_DATEN *));
               if( ptr_daten->next == NULL )
                  return NULL;
               ptr_daten = ptr_daten->next;
               j++;
            }
      }
   ende = ptr_daten;
   /* Anfangsadresse der Liste struct CGI_DATEN zurückgeben */
   return ptr_anfang;
}

Hiermit haben Sie eine Funktion erstellt, die für alle Fälle gerüstet ist. Jetzt benötigen Sie nur noch eine Funktion, welche den Speicherplatz der dynamischen Liste wieder freigibt. Diese finden Sie im folgenden Programmbeispiel.

Weiter mit: 27.12. Ein Gästebuch          zum Inhaltsverzeichnis