/* Implementierung eines einfachen HTTP-Servers,
 * der ausschließlich GET-Requests bearbeiten kann */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>


/* Port fuer die HTTP-Requests */
#define HTTP_PORT 8080

static void serv_request(int client_fd, char* rootpath);
static void err_exit(char *error_message);

int main( int argc, char **argv)
  {
  struct sockaddr_in server, client;
  int sock, clientd;
  int len;

  /* Die "Document Root" wird per Kommadozeilenargument
   * uebergeben */
  if (2 != argc) 
    err_exit("Angabe von document_root fehlt\n");
    
  /* Erzeuge das Socket */
  sock = socket( PF_INET, SOCK_STREAM, 0);
  if (sock < 0) 
    err_exit("Kann Socket nicht anlegen");

  /* Erzeuge die Socketadresse des Servers */ 
  memset(&server, 0, sizeof (server));
  server.sin_family = AF_INET;
  server.sin_addr.s_addr = htonl(INADDR_ANY);
  server.sin_port = htons(HTTP_PORT);

  /* Binde Port an die Serveradresse  */
  if (bind(sock, (struct sockaddr*)&server, sizeof( server)) < 0) 
    err_exit("Kann Socket nicht an Port binden");
  listen(sock, 5);

  /* Bearbeite die Verbindungswünsche von Clients 
   * in einer Endlosschleife  */
  for (;;) 
    {
    printf("Server wartet auf Anfrage ...\n");
    len = sizeof(client);
    clientd = accept(sock, (struct sockaddr*)&client, (socklen_t*)&len);
    if (clientd < 0) 
      err_exit("accept failed");
    printf("client address: %s\n", inet_ntoa(client.sin_addr));
    /* Bearbeite den HTTP-Request */
    serv_request(clientd, argv[1]);
    /* Schließe Verbindung */
    close(clientd);
    }
  }
        
/* Bearbeite den ankommenden HTTP-Request */
static void serv_request(int client_fd, char* rootpath)
  {
  char delimiter[] = "\r\n";  /* Zeilentrenner im Puffer */
  char buffer[16384];         /* Empfangspuffer */
  char *ptr = NULL;           /* Hilfspointer */
  struct stat info;           /* fileinfo */    
  char url[1024];             /* URL */
  char path[1024];            /* Dateipfad */
  int count;                  /* Bytezaehler */
  int fd;                     /* Filedesc. fuer Ausgabedatei */
  int eoh = 0;                /* End of Header Flag */

  *url = '\0';
  /* HTTP-Request einlesen */
  count = recv(client_fd, buffer, sizeof(buffer), 0);
  eoh = (count > 0)? 0 : 1;
  buffer[count] = '\0'; /* Stringterminator setzen */
  ptr = strtok(buffer,delimiter);
  while ((ptr != NULL) /* && ! eoh */ )
    {
    printf ("Headerzeile: %s\n", ptr);
    /* GET-Request? Dann URL extrahieren */
    sscanf(ptr, "GET %1023s HTTP/", url);
    /* naechste Zeile nehmen */
    ptr = strtok(NULL,delimiter);
    }

  if (url[strlen(url)-1] == '\r') url[strlen(url)-1] = '\0';
  if (strlen(url) > 1) 
    { /* URL im Header gefunden */
    printf( "--- Request: GET %s ", url);
    sprintf(path, "%s/%s", rootpath, url); 
    printf( "--- Path: %s ", path);
    }
  else
    { /* als default "index.html" nehmen */
    printf( "--- Request: GET /index.html ");
    sprintf(path, "%s/index.html", rootpath); 
    printf( "--- Path: %s ", path);
    }
  if (stat(path, &info) == 0 && S_ISDIR(info.st_mode))
    { /* bei Directory "/index.html" anhaengen */
    sprintf(path, "%s/%s/index.html", rootpath, url); 
    printf( "--- Path: %s ", path);
    }

  /* gewuenschte Datei oeffnen */
  fd = open(path, O_RDONLY);
  if (fd > 0) 
    { /* Datei vorhanden, also ausliefern */
    sprintf(buffer, "HTTP/1.0 200 OK\nContent-Type: text/html\n\n");
    send(client_fd, buffer, strlen(buffer), 0);
    do 
      {
      count = read(fd, buffer, sizeof(buffer));
      send(client_fd, buffer, count, 0);
      } 
    while (count > 0);
    close(fd);
    }
  else 
    { /* Datei nicht vorhanden - Fehler senden */
    sprintf(buffer, "HTTP/1.0 404 Not Found\n\n");
    send(client_fd, buffer, strlen(buffer), 0);
    }
  printf(" --- done!\n");
  }


/* Funktion gibt aufgetretenen Fehler aus und
 * beendet das Programm */
static void err_exit(char *error_message)
  {
  fprintf(stderr, "%s: %s\n", error_message, strerror(errno));
  exit(EXIT_FAILURE);
  }


