Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

 << zurück
C von A bis Z von Jürgen Wolf
Das umfassende Handbuch für Linux, Unix und Windows
– 2., aktualisierte und erweiterte Auflage 2006
Buch: C von A bis Z

C von A bis Z
1.116 S., mit CD, Referenzkarte, 39,90 Euro
Galileo Computing
ISBN 3-89842-643-2
gp Kapitel 26 CGI mit C
  gp 26.1 Was ist CGI?
  gp 26.2 Vorteile von CGIs in C
  gp 26.3 Andere Techniken der Webprogrammierung
  gp 26.4 Das dreistufige Webanwendungsdesign
    gp 26.4.1 Darstellungsschicht
    gp 26.4.2 Verarbeitungsschicht
    gp 26.4.3 Speicherschicht
  gp 26.5 Clientseitige Programmierung
    gp 26.5.1 JavaScript
    gp 26.5.2 Java-Applets
  gp 26.6 Serverseitige Programmierung
  gp 26.7 Der Webserver
    gp 26.7.1 Das Client/Server-Modell des Internets
    gp 26.7.2 Serverimplementierung
    gp 26.7.3 Hosting-Services
    gp 26.7.4 Schlüsselfertige Lösung
    gp 26.7.5 Weitere Möglichkeiten
    gp 26.7.6 Apache
  gp 26.8 Das HTTP-Protokoll
    gp 26.8.1 Web-Protokolle
    gp 26.8.2 Wozu Protokolle?
    gp 26.8.3 Was ist ein Protokoll?
    gp 26.8.4 Normen für die Netzwerktechnik
    gp 26.8.5 Das OSI-Schichtenmodell
    gp 26.8.6 Die Elemente einer URL
    gp 26.8.7 Client-Anfrage – HTTP Request (Browser-Request)
    gp 26.8.8 Serverantwort (Server-Response)
    gp 26.8.9 Zusammenfassung
  gp 26.9 Das Common Gateway Interface (CGI)
    gp 26.9.1 Filehandles
    gp 26.9.2 CGI-Umgebungsvariablen
    gp 26.9.3 CGI-Ausgabe
  gp 26.10 HTML-Formulare
    gp 26.10.1 Die Tags und ihre Bedeutung
  gp 26.11 CGI-Eingabe
    gp 26.11.1 Die Anfrage des Clients an den Server
    gp 26.11.2 Eingabe parsen
  gp 26.12 Ein Gästebuch
    gp 26.12.1 Das HTML-Formular (guestbook.html)
    gp 26.12.2 Das CGI-Programm (auswert.cgi)
    gp 26.12.3 Das HTML-Gästebuch (gaeste.html)
    gp 26.12.4 Das Beispiel ausführen
  gp 26.13 Ausblick


Galileo Computing - Zum Seitenanfang

26.11 CGI-Eingabe  downtop


Galileo Computing - Zum Seitenanfang

26.11.1 Die Anfrage des Clients an den Server  downtop

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
Hier klicken, um das Bild zu Vergrößern

Abbildung 26.22   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.cgi" method=get>

umändern in:

<form action="http://.. /cgi-bin/auswert.cgi" 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 eine 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(void) {
   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 die GET- 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.


Galileo Computing - Zum Seitenanfang

26.11.2 Eingabe parsen  toptop

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.

gp  & – Die einzelnen Formularelemente (sofern es mehrere sind) werden mit diesem Zeichen getrennt.
gp  = – Mit diesem Zeichen werden die Variable/Wert-Paare voneinander getrennt.
Textfeld J%FCrgen+%22pronix%22+Wolf
gp  + – Damit werden die Leerzeichen der eingegebenen Daten getrennt.
Textfeld J%Fcrgen %22pronix%22 Wolf
gp  %XX – Bei einem Prozentzeichen gefolgt von zwei hexadezimalen Ziffern handelt es sich um ASCII-Zeichen mit dem dezimalen Wert von 128 bis 255. Diese hexadezimalen Ziffern müssen in ASCII-Zeichen dekodiert werden.
Textfeld Jürgen pronix Wolf

In der folgenden Tabelle finden Sie Steuer- und weitere Zeichen und ihre hexadezimale Darstellung im Überblick:


Tabelle 26.19   Kodierte Zeichen und Ihre Bedeutung

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

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

gp  Kodierte Zeichen (Prozentzeichen, gefolgt von zwei Hexzahlen) dekodieren.
gp  Alle Leerzeichen, welche mit + angegeben sind, in ein echtes Leerzeichen konvertieren.

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_anfang->variable, ptr_anfang->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.

 << zurück
  
  Zum Katalog
Zum Katalog: C von A bis Z
C von A bis Z
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Shell-Programmierung






 Shell-Programmierung


Zum Katalog: Linux-UNIX-Programmierung






 Linux-UNIX-Programmierung


Zum Katalog: C/C++






 C/C++


Zum Katalog: UML 2.0






 UML 2.0


Zum Katalog: Reguläre Ausdrücke






 Reguläre Ausdrücke


Zum Katalog: Linux






 Linux


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Galileo Press 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de