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

Inhaltsverzeichnis
Vorwort
Vorwort des Gutachters
1 Einstieg in C
2 Das erste Programm
3 Grundlagen
4 Formatierte Ein-/Ausgabe mit »scanf()« und »printf()«
5 Basisdatentypen
6 Operatoren
7 Typumwandlung
8 Kontrollstrukturen
9 Funktionen
10 Präprozessor-Direktiven
11 Arrays
12 Zeiger (Pointer)
13 Kommandozeilenargumente
14 Dynamische Speicherverwaltung
15 Strukturen
16 Ein-/Ausgabe-Funktionen
17 Attribute von Dateien und das Arbeiten mit Verzeichnissen (nicht ANSI C)
18 Arbeiten mit variabel langen Argumentlisten – <stdarg.h>
19 Zeitroutinen
20 Weitere Headerdateien und ihre Funktionen (ANSI C)
21 Dynamische Datenstrukturen
22 Algorithmen
23 CGI mit C
24 MySQL und C
25 Netzwerkprogrammierung und Cross–Plattform-Entwicklung
26 Paralleles Rechnen
27 Sicheres Programmieren
28 Wie geht’s jetzt weiter?
A Operatoren
B Die C-Standard-Bibliothek
Stichwort

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
C von A bis Z von Jürgen Wolf
Das umfassende Handbuch
Buch: C von A bis Z

C von A bis Z
3., aktualisierte und erweiterte Auflage, geb., mit CD und Referenzkarte
1.190 S., 39,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1411-7
Pfeil 23 CGI mit C
Pfeil 23.1 Was ist CGI?
Pfeil 23.2 Vorteile von CGIs in C
Pfeil 23.3 Andere Techniken der Webprogrammierung
Pfeil 23.4 Das dreistufige Webanwendungsdesign
Pfeil 23.4.1 Darstellungsschicht
Pfeil 23.4.2 Verarbeitungsschicht
Pfeil 23.4.3 Speicherschicht
Pfeil 23.5 Clientseitige Programmierung
Pfeil 23.5.1 JavaScript
Pfeil 23.5.2 Java-Applets
Pfeil 23.6 Serverseitige Programmierung
Pfeil 23.7 Der Webserver
Pfeil 23.7.1 Das Client/Server-Modell des Internets
Pfeil 23.7.2 Serverimplementierung
Pfeil 23.7.3 Hosting-Services
Pfeil 23.7.4 Schlüsselfertige Lösung
Pfeil 23.7.5 Weitere Möglichkeiten
Pfeil 23.7.6 Apache
Pfeil 23.8 Das HTTP-Protokoll
Pfeil 23.8.1 Web-Protokolle
Pfeil 23.8.2 Wozu dienen Protokolle?
Pfeil 23.8.3 Was ist ein Protokoll?
Pfeil 23.8.4 Normen für die Netzwerktechnik
Pfeil 23.8.5 Das OSI-Schichtenmodell
Pfeil 23.8.6 Die Elemente einer URL
Pfeil 23.8.7 Client-Anfrage – HTTP-Request (Browser-Request)
Pfeil 23.8.8 Serverantwort (Server-Response)
Pfeil 23.8.9 Zusammenfassung
Pfeil 23.9 Das Common Gateway Interface (CGI)
Pfeil 23.9.1 Filehandles
Pfeil 23.9.2 CGI-Umgebungsvariablen
Pfeil 23.9.3 CGI-Ausgabe
Pfeil 23.10 HTML-Formulare
Pfeil 23.10.1 Die Tags und ihre Bedeutung
Pfeil 23.11 CGI-Eingabe
Pfeil 23.11.1 Die Anfrage des Clients an den Server
Pfeil 23.11.2 Eingabe parsen
Pfeil 23.12 Ein Gästebuch
Pfeil 23.12.1 Das HTML-Formular (»guestbook.html«)
Pfeil 23.12.2 Das CGI-Programm (»auswert.cgi«)
Pfeil 23.12.3 Das HTML-Gästebuch (»gaeste.html«)
Pfeil 23.13 Ausblick


Rheinwerk Computing - Zum Seitenanfang

23.12 Ein Gästebuch Zur nächsten ÜberschriftZur vorigen Überschrift

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:

  • das HTML-Formular, in das sich der Besucher einträgt
  • die CGI-Anwendung, die diesen Eintrag einliest, auswertet und bearbeitet
  • die HTML-Datei, mit der das Gästebuch angezeigt wird

In diesem Beispiel wurde auch gleich die HTML-Datei, die 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.


Rheinwerk Computing - Zum Seitenanfang

23.12.1 Das HTML-Formular (»guestbook.html«) Zur nächsten ÜberschriftZur vorigen Überschrift

<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 23.21 Das HTML-Formular


Rheinwerk Computing - Zum Seitenanfang

23.12.2 Das CGI-Programm (»auswert.cgi«) Zur nächsten ÜberschriftZur vorigen Überschrift

/* 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 den Aufrufer */
            return puffer;
      }
   }

   /* Weder die 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: beispielsweise 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 oeffnen\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+");

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


Rheinwerk Computing - Zum Seitenanfang

23.12.3 Das HTML-Gästebuch (»gaeste.html«) topZur vorigen Überschrift

<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, das Sie mit chmod nachträglich vergeben können (chmod a+rwx gaeste.html).

Das Beispiel ausführen

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:

Jetzt sollte die HTML-Seite erscheinen, und zwar mit dem Formular aus Abbildung 23.22.

Abbildung 23.22 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 das CGI-Programm Sie 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 23.23 Einträge im Gästebuch



Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
  
  Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: C von A bis Z

 C von A bis Z
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchtipps
Zum Rheinwerk-Shop: C/C++






 C/C++


Zum Rheinwerk-Shop: Einstieg in C






 Einstieg in C


Zum Rheinwerk-Shop: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Rheinwerk-Shop: C++ Handbuch






 C++ Handbuch


Zum Rheinwerk-Shop: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2009
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.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern