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.12 Ein Gästebuch  downtop

Um alle Funktionen jetzt zu demonstrieren, soll ein einfaches Gästebuch erstellt werden. Ich denke so etwas kennt jeder. Folgende Dateien benötigen Sie dafür:

gp  Das HTML-Formular, in das der Besucher seinen Eintrag macht.
gp  Die CGI-Anwendung, welche diesen Eintrag einliest, auswertet und bearbeitet.
gp  Die HTML-Datei, mit der das Gästebuch angezeigt wird.

In diesem Beispiel wurde auch gleich die HTML-Datei, welche die Einträge anzeigt, zur Datenspeicherung verwendet, um das Programm ein wenig kürzer zu halten. Bei einem umfangreichen Gästebuch würden sich dafür eine extra Datei oder eine Datenbank anbieten.


Galileo Computing - Zum Seitenanfang

26.12.1 Das HTML-Formular (guestbook.html)  downtop

<html>
<head>
<title>Eintrag ins Gästebuch</title>
</head>
<body text="#000000" bgcolor="#FFFFFF" link="#FF0000" alink="#FF0000" vlink="#FF0000">
<h3>Formular zum Eintragen ins Gästebuch</h3>
<pre>
<form action="http://localhost/cgi-bin/auswert.cgi" method=post>
<b>Name   : </b>
<input value="IhrName" name="Name" size="20">
<b>E-Mail : </b>
<input value="Mailadresse@mail" name="E-Mail" size="20">
<b>Bewertung dieser Webseite : </b>
<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><br>
<b>Ihr Eintrag :</b>
<textarea name="textform" cols="32" rows="6">Textinhalt
</textarea>
<b>Ihre Programmierkenntnis : </b>
<input type="checkbox" name="programmieren" value="C/C++">C/C++
<input type="checkbox" name="programmieren" value="Perl">Perl
<input type="checkbox" name="programmieren" value="Visual Basic">
Visual Basic
<input type="reset"><input type=submit value="Abschicken">
</form>
</pre>
</body>
</html>

Der Ort, wo Sie diese Datei speichern, ist in der Regel auf dem lokalen System nicht so wichtig. Wichtiger ist, dass die Angaben zum Aufrufen der CGI-Anwendung stimmen:

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

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 26.23   Das HTML-Formular


Galileo Computing - Zum Seitenanfang

26.12.2 Das CGI-Programm (auswert.cgi)  downtop

/* auswert.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_PAARE 255
void print_location(char *);
char *getdata();
char *Strdup(const char *);
void hex2ascii(char *);
char convert(char *);
struct CGI_DATEN *erstellen(char *);
void printf_error(char *);
struct CGI_DATEN {
   char *variable;
   char *wert;
   struct CGI_DATEN *next;
};
struct CGI_DATEN *ende = NULL;
/* Weiterleitung zu einer URL
 * url ist die URL, wohin Sie den User weiterleiten
 */
void print_location(char *url) {
   printf("Location: %s\n", url);
   /* Für den Fall, dass ein alter Browser keine
      automatische Weiterleitung unterstützt */
   printf("Content-Type: text/html\n\n");
   printf("<html><head>\n");
   printf("<title>Weiterleitung zu %s</title>\n",url);
   printf("</head><body>\n");
   printf("Weiter gehts <a href=\"%s\">hier</a>",url);
   printf("</body></html>\n");
}
/*
 *  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 dem Ausrufer */
            return puffer;
      }
   }
   /* Weder GET-Methode noch die POST-Methode wurden verwendet */
   else
      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 = (char *)malloc(strlen(str)+1);
      if(NULL == p)
         return NULL;
      else
         strcpy(p, str);
   }
   return p;
}
/* 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;
}
/* Liste aus Variable/Wert-Paaren erstellen
 * Rückgabewert: Anfangsadresse der Liste
 * Bei Fehler: NULL
 */
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], "=");
         if(res == NULL)
            return NULL;
         ptr_anfang->variable = (char *)
           malloc(strlen(res)+1);
         if( ptr_anfang->variable == NULL )
            return NULL;
         ptr_anfang->variable = res;
         res = strtok(NULL, "\0");
         if(res == NULL)
            return NULL;
         ptr_anfang->wert = (char *) 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 =(struct CGI_DATEN *)
           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], "=");
         if(res == NULL)
            return NULL;
         ptr_daten->variable =(char *)
           malloc(strlen(res)+1);
         if(ptr_daten->variable == NULL)
            return NULL;
         ptr_daten->variable = res;
         res = strtok(NULL, "\0");
         if(res == NULL)
            return NULL;
         ptr_daten->wert =(char *) 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 = (struct CGI_DATEN *)
           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;
}
void loeschen(struct CGI_DATEN *daten) {
   struct CGI_DATEN *next = NULL;
   while(daten != ende) {
      next = daten->next;
      if(daten->variable != NULL)
         free(daten);
      daten=next;
   }
}
void printf_error(char *str) {
   printf("Content-Type: text/html\n\n");
   printf("<html><head>\n");
   printf("<title>CGI-Fehlermeldung</title>\n");
   printf("</head><body>\n");
   printf("%s",str);
   printf("</body></html>\n");
}
int main(void) {
   char *str;
   struct CGI_DATEN *cgi;
   struct CGI_DATEN *free_cgi;
   FILE *f;
   /* Eingabe einlesen */
   str = getdata();
   if(str == NULL) {
      printf_error("Fehler beim Einlesen von der "
                   "Formulareingabe");
      return EXIT_FAILURE;
   }
   /* Hexzeichen in ASCII-Zeichen konvertieren und aus '+'
    * Leerzeichen machen */
   hex2ascii(str);
   /* Liste der Formualardaten erstellen */
   cgi = erstellen(str);
   free_cgi = cgi;
   if (cgi == NULL) {
      printf_error("Fehler beim Erstellen der "
                   "Variablen/Werte-Liste\n");
      return EXIT_FAILURE;
   }
   /* Datei zum Schreiben öffnen */
   /* Bitte den Pfad anpassen: bspw. unter SuSE Linux:
    * f = fopen("/srv/www/htdocs/gaeste.html", "r+");
    * und WICHTIG: Schreibrechte auf diese Datei vergeben
    */
   f = fopen("gaeste.html", "r+");
   if(f == NULL) {
      printf_error("Konnte Datei gaeste.html nicht zum "
                   "schreiben öffnen\n");
      return EXIT_FAILURE;
   }
   else {
      /* Stream vor </body></html> */
      fseek(f, –14, SEEK_END);
      fprintf(f, "<hr><br>"); /* Eine horizontale Linie */
      /* Name */
      if(cgi->wert != NULL)
         fprintf(f, "Name: %s E-Mail: ",cgi->wert);
      cgi = cgi->next;
      /* Mailadresse */
      if(cgi->wert != NULL)
         fprintf(f, "<a href=\"mailto:%s\">%s</a> ",
           cgi->wert,cgi->wert);
      cgi = cgi->next;
      /* Bewertung */
      if(cgi->wert != NULL)
         fprintf(f, "Bewertung : %s",cgi->wert);
      cgi = cgi->next;
      /* Eintrag */
      if( cgi->wert != NULL) {
         fprintf(f, "<p><b>Der Eintrag : </b>");
         fprintf(f, "%s",cgi->wert);
      }
      cgi = cgi->next;
      /* Programmierkenntnis(se) */
      if(cgi->wert != NULL) {
         fprintf(f, "<br><br>Programmierkenntnisse : ");
         while(cgi->wert != NULL  &&
           strcmp(cgi->variable,"programmieren") == 0 ) {
            fprintf(f, "%s ",cgi->wert);
            cgi = cgi->next;
         }
      }
      fprintf(f, "</p></body></html>");
      fclose(f);
   }
   /* Speicher wieder freigeben */
   loeschen(free_cgi);
   /* Auch hier müssen Sie die Pfadangabe ggf. anpassen */
   print_location("http://localhost/gaeste.html");
   return EXIT_SUCCESS;
}

Wichtig in diesem Listing ist der Pfad zum Öffnen der Datei gaeste.html

f = fopen("gaeste.html", "r+");

welchen Sie gegebenenfalls anpassen müssen. Bei dieser Angabe wird davon ausgegangen, dass sich das HTML-Dokument im cgi-bin-Verzeichnis befindet. Das funktioniert aber nicht auf jedem System. Beispielsweise bei SuSE 8.2 sieht der Pfad auf meinem System wie folgt aus:

f = fopen("/srv/www/htdocs/gaeste.html","r+");

Vorausgesetzt, die Datei gaeste.html befindet sich auch im entsprechenden Verzeichnis. Denken Sie auch an die Angabe und Änderung des Pfades, wenn Sie die CGI-Anwendung ins Web stellen.

Der zweite wichtige Pfad in dieser Anwendung ist der folgende:

print_location("http://localhost/gaeste.html");

Auch hier muss der Speicherort der Datei gaeste.html richtig angegeben werden. Im Beispiel bedeutet dies, dass sich die Datei im htdocs-Verzeichnis des Webservers befindet.

Dies sollte hier erwähnt werden, da ich aus Erfahrung weiß, dass dies eine der häufigsten Ursachen dafür ist, warum eine CGI-Anwendung nicht auf Anhieb funktioniert.


Galileo Computing - Zum Seitenanfang

26.12.3 Das HTML-Gästebuch (gaeste.html)  downtop

<html>
<head>
  <title>Gästebuch</title>
</head>
<body>
<center><h1> Einträge im Gästebuch</h1></center><br>
</body></html>

Dies ist die Datei, in der die CGI-Anwendung auswert.cgi neue Daten schreibt. Daher muss der Speicherort der Datei mit den Angaben der Funktionen fopen() und print_location() im Programm auswert.cgi übereinstimmen. Außerdem benötigen Sie das Schreibrecht auf diese Datei, welches Sie mit chmod nachträglich vergeben können (chmod a+rwx gaeste.html).


Galileo Computing - Zum Seitenanfang

26.12.4 Das Beispiel ausführen  toptop

In diesem Beispiel wird davon ausgegangen, dass sich die Dateien auswert.cgi und gaeste.html im cgi-bin-Verzeichnis befinden. Bitte passen Sie die Pfade im Listing auswert.c an Ihre Bedürfnisse an. Starten Sie als Erstes wieder Ihren Lieblingswebbrowser und öffnen Sie die HTML-Datei guestbook.html. Befindet sich diese beispielsweise im htdocs-Verzeichnis des Webservers, können Sie diese Datei mit folgender URL aufrufen:

http://localhost/guestbook.html

Jetzt sollte die HTML-Seite erscheinen, und zwar mit dem Formular:

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 26.24   Eingabeformular des Gästebuchs

Wenn Sie alle Daten eingetragen haben, klicken Sie auf den Abschicken-Button, und das CGI-Programm auswert.cgi verrichtet seine Arbeit. Gleich darauf leitet Sie das CGI-Programm zur Seite gaeste.html weiter, wo Sie sich den neuen Eintrag ansehen können.

Zugegeben, das Layout entspricht nicht mehr dem heutigen Standard, aber darum geht es hierbei nicht.

Abbildung
Hier klicken, um das Bild zu Vergrößern

Abbildung 26.25   Einträge im Gästebuch

 << 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