![]() |
Algorithmen & Datenstrukturen Programmieren 1 von Prof. Jürgen Plate |
Die Realisierung erfolgt in kleinen Teilaufgaben. Teile des Web-Frontends in HTML werden fertig zur Verfügung gestellt. Der Webserver nimmt uns viel Arbeit ab. Dahinter steckt das Common Gateway Interface.
TestumgebungDamit Sie Ihre Programme auch testen können, wurde eine Testumgebung auf dem Praktikumsrechner eingerichtet. Es läuft ein Webserver, der ein sogenanntes "User-Directory" erlaubt. Jeder Benutzer kann damit Webseiten und auch CGI-Programme anbieten. Dazu wird im Heimatverzeichnis ein Unterverzeichnis www eingerichtet, in das alle Webseiten gelegt werden (Dateiendung .html). Das www-Verzeichnis enthält ein Unterverzeichnis namens cgi-bin, in dem alle Programme untergebracht sind (Dateiendung .cgi).Zur Einrichtung des Ganzen öffnen Sie ein Shell-Fenster und rufen Sie dann das Kommando machwww auf. Dieses Programm richtet die Verzeichnisse ein und erzeugt auch eine (sehr einfache) Datei index.html sowie ein ebenso einfaches CGI-Programm namens huhu.cgi (einschl. Quelltext). Zum Testen können im Browser dann die beiden Webadressen
http://localhost/~username/index.html
verwendet werden. Eigene CGI-Programme müssen aus dem Kdevelop-Projektverzeichnis ins CGI-Verzeichnis umkopiert und umbenannt werden. Das geschieht durch das Kommando (aus dem Heimatverzeichnis /home/Username heraus). Ausserdem müssen die Zugriffsrechte gesetzt werden:
cp Projektname/debug/src/Projektname
www/cgi-bin/Projektname.cgi
Für Projektname müssen Sie natürlich den von Ihnen verwendeten Namen des Projekts (in der Regel wiki) eingeben. Damit Sie sich leichter tun, gibt es zwei Hilfskommandos: copycgi Projektname kopiert Ihr Binärprogramm ins CGI-Verzeichnis und versieht es dort auch gleich mit der Endung .cgi. gibfrei setzt die Zugriffsrechte für alle Dateien in Ihrem www-Verzeichnis so, dass auf diese per Browser zugegriffen werden kann.
|
#include <sys/param.h> #include <dirent.h> DIR *opendir(const char *pfadname); struct dirent *readdir(DIR *dir); int closedir(DIR *dir);Die Funktion opendir() erhält als Parameter den Namen des Verzeichnisses. Der Rückgabewert ist ein Zeiger auf das Verzeichnis-Handle. Ein Fehler wird dadurch angezeigt, dass dieser Zeiger den Wert NULL besitzt.
Die Funktion readdir() liest den nächsten Eintrag im Verzeichnis und erhält als Rückgabewert einen Zeiger auf einen Wert vom Typ dirent:
struct dirent
{
long d_ino; /* Inode Nummer */
off_t d_off; /* Offset zum naechsten dirent */
unsigned short d_reclen;/* Laenge dieses Eintrags */
char d_name[MAXNAMELEN+1]; /* Dateiname */
};
Für das Anwenderprogramm ist eigentlich nur der Name des Eintrags interessant.
Wollen Sie mehr über diesen Eintrag erfahren, beispielsweise, ob es wieder ein
Verzeichnis ist, so brauchen Sie dazu andere Systemaufrufe (z. B. stat()).
Zuletzt wird das Verzeichnis mit closedir() wieder geschlossen.
Ein Beispielprogramm für das Auslesen eines Verzeichnisses sieht so aus:
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
// fuer Anzeige des Verzeichnis-Inhalts
#include <dirent.h>
int main(void)
{
DIR *dir;
struct dirent *dirzeiger;
char dateiname[MAXNAMELEN+1]; /* MAXNAMELEN definiert in param.h */
char dirname[MAXNAMELEN+1] = "."; /* --> Laenge reicht garantiert */
printf("Alle Dateien im aktuellen Verzeichnis: %s\n",dirname);
dir = opendir(dirname);
if(dir != NULL)
{
while((dirzeiger = readdir(dir)) != NULL)
{
strcpy(dateiname,(*dirzeiger).d_name);
printf("%s\n",dateiname);
}
}
closedir(dir);
return 0;
}
Das Programm können Sie unter dir.c herunterladen.
#define DATADIR "/home/wiki"definiert ist (Stringfunktionen strcat bzw. strcpy!).
<HTML> <HEAD> <TITLE>Lexikon-Ausgabeformular</TITLE> </HEAD> <BODY> <H1>Lexikon-Ausgabe</H1> <P> <FORM method=POST action="http://localhost/~testuser/www/cgi-bin/wiki.cgi"> <SELECT NAME="Datei" SIZE="5">Danach folgt ihre Dateiausgabe, wobei für jede Datei eine Zeile der Form
<OPTION>Dateiname</OPTION>ausgegeben werden soll. Die Ausgaben schliesst mit den Zeilen:
</SELECT> <P> <INPUT TYPE="SUBMIT" NAME="SUBMIT" VALUE="Absenden"> </FORM> </BODY> </HTML>
Dabei ist es nur nötig, die erste Zeile in die drei Felder "Begriff", "Autor" und "Datum" zu zerlegen. Der Rest der Datei kann an der oben genannten Stelle (Textarea) einfach auf die Standardausgabe kopiert werden. Beachten Sie dazu auch die Erläterungen in der Vorlesung.
Hinweis: Wir brauchen die Webseitensprache HTML zwar nur als Vehikel (weshalb die Sprachelemente in der Regel vorgegeben werden), aber wer sich dafür interessiert, findet eine knappe Einführung unter http://www.netzmafia.de/skripten/easyhtml/index.html.
Beim Programm aus Aufgabe 1 müssen Sie nur noch die #define-Zeilen ändern, indem Sie bei den folgenden Zeilen die Kommantare ("// ") entfernen:
// #define REQUEST_METHOD getenv("REQUEST_METHOD")
// #define CONTENT_LENGTH getenv("CONTENT_LENGTH")
// #define QUERY_STRING getenv("QUERY_STRING")
Die folgenden Test-Zeilen werden gelöscht.
/* Fuer's Testen definieren wir die Dinger selbst */ #define REQUEST_METHOD "GET" #define CONTENT_LENGTH "119" #define QUERY_STRING "Begriff=G%E4nsegeier&Autor=...Dann muss das CGI-Programm ja noch eine Antwort geben, z. B. "Ihre Eingabe wurde gespeichert". Das muss natürlich in einem HTML-Umfeld verpackt werden ("Content-Type"-Zeile nicht vergessen!). Das könnte etwa folgendermassen aussehen:
printf("Content-Type: text/html\r\n\r\n");
printf("<HTML><HEAD><TITLE>Wiki-Eingabe</TITLE></HEAD>\n");
printf("<BODY><H1>Wiki-Eingabe</H1>\n");
.
.
.
printf("Ihre Eingabe wurde gespeichert\n");
printf("</BODY></HTML>\n");
Nun können Sie das schon mal testen. Dazu übersetzen Sie das Programm und
kopieren dann den erzeugten Binärcode in Ihr cgi-bin-Verzeichnis (Programm
copycgi). Anschriessend sind die Execute-Rechte zu setzen (Programm gibfrei).
Bei eventuell fehlerhaften Verhalten können Sie die Fehlerausgabe des Webservers in
einem Shell-Fenster mithilfe des Kommandos tail -f /var/apache/logs/error_log
beobachten (Abbruch des Programms mit Strg-C).
Damit es beim Testen keine "Interferenzen" gibt (53 mal der "Gänsegeier"), nehmen Sie als Begriff Ihren Usernamen oder geben etwas Sinnvolles ein.
Das Programm aus Aufgabe 2 ist noch nicht fertig und wird nun fertiggestellt. Bisher gibt es zwei Funktionen, die bei mir die Namen
char x2c(char hex[]); void unescape(char *url); void CGIReadInput(void);heraus und hängen diese an Ihren Programmcode an. Die Funktion CGIReadInput() erweitern Sie diesmal so, dass der Formularwert "Datei" herausgeholt wird. Da aber nun die Möglichkeit besteht, dass CGIReadInput() auch ohne Parameter aufgerufen wird, ist noch eine weitere Änderung notwendig:
.
.
.
if (CONTENT_LENGTH == NULL)
{
/* Hier Dateiname auf "" setzen o. ä */
return; /* statt exit(1) */
}
Nun gibt es zwei Möglichkeiten:
int main(void)
{
char filename[MAXNAMELEN+1] = "";
printf("Content-Type: text/html\r\n\r\n");
CGIReadInput(filename);
if (strcpy(filename,"") == 0)
dirlist(dirname);
else
{
strcat(dirname,filename);
showform(dirname);
}
return EXIT_SUCCESS;
}
Anmerkung: Alle Programme im cgi-bin-Verzeichnis laufen mit der Benutzerberechtigung des Webserver-Programms. Das Verzeichnis /home/wiki hat Schreibberechtigung fü diesen Benutzer. Beim lokalen Test in der KDevelop-Umgebung laufen die Programme natürlich mit Ihrer Benuterberechtigung, weshalb Sie auf /home/wiki nicht problemlos zugreifen können. Es ist also sinnvoll diesen Pfad als Konstante zu definieren (#define ...) und vor dem Erzeugen der Endversion umzustellen.
int main(int argc, char *argv[])
{
char Dateiname[MAXNAMELEN+1] = "Textdatei";
char Wort[1000];
FILE *fp;
fp = fopen(Dateiname,"r");
if (fp == NULL)
{
printf("Kann Datei %s nicht oeffnen\n",Dateiname);
exit(1);
}
do
{
nextword(fp,Wort);
printf(">>>%s&tl;<<\n",Wort);
}
while (Wort[0] != '\0');
fclose(fp);
return EXIT_SUCCESS;
}
int main(int argc, char *argv[])
{
char Dateiname[MAXNAMELEN+1] = "Textdatei";
char Suchwort[1000] = "Desoxyribonukleinsäure";
int Erg;
Erg = scanfile(Dateiname,Suchwort);
if (Erg) printf("WORT GEFUNDEN!\n");
else printf("NICHTS GEFUNDEN!\n");
return EXIT_SUCCESS;
}
.
.
.
/* #define ... nicht vergessen */
.
.
.
#define FILEPATH "/home/wiki/"
.
.
int main(int argc, char *argv[])
{
DIR *dir;
struct dirent *dirzeiger;
char Dateiname[MAXNAMELEN+1] = "";
char Dateipfad[MAXNAMELEN+1] = "";
char Suchwort[1000] = "";
int Erg;
printf("Content-Type: text/html\r\n\r\n");
printf("<HTML>\n<HEAD>\n<TITLE>Lexikon-Suche</TITLE>\n</HEAD>\n<BODY>\n");
printf("<H1>Lexikon-Suche</H1>\n");
CGIReadInput(Suchwort);
if (strcmp(Suchwort,"") == 0)
{
printf("<FORM method=POST action=\"http://localhost/~testuser/www/cgi-bin/wiki.cgi\">\n");
printf("Suchbegriff: <INPUT TYPE=TEXT NAME=\"Suchwort\" SIZE=\"20\">\n");
printf("<INPUT TYPE=\"SUBMIT\" NAME=\"SUBMIT\" VALUE=\"Suchen\">");
printf("</FORM>\n</BODY>\n</HTML>");
}
else
{
dir = opendir(dirname);
if(dir != NULL)
{
printf("<UL>\n");
while((dirzeiger = readdir(dir)) != NULL)
{
strcpy(Dateiname,(*dirzeiger).d_name);
if (Dateiname[0] != '.')
{
strcpy(Dateipfad,FILEPATH);
strcat(Dateipfad,Dateiname);
if(scanfile(Dateipfad,Suchwort))
printf("<LI>%s\n",Dateiname);
}
}
printf("</UL>\n");
closedir(dir);
}
}
printf("</BODY>\n</HTML>\n");
return EXIT_SUCCESS;
}
In dieser Stufe werden nur die Dateinamen ausgegeben. In einer späteren
Erweiterung können dann auch noch die Dateiinhalte ausgegeben werden.
Im Praktikum und seinem abgeschlossenen System konnten Sicherheitsbetrachtungen zurückgestellt werden, um die Programmierung leichter und einfacher zu gestalten. Bei Verwendung der einzelnen Funktionen in der realen Welt sind aber noch weitere Sicherheitsbetrachtungen anzustellen.