#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define SMAX 100          /* maximale Schluessellaenge */
#define ALPHA 96          /* Alphabetlaenge */

/* Debug-Modus */
#define DEBUG

/* Debug-Helfer-Makro */
#ifdef DEBUG
#define DEBUG_PRINT printf
#else
#define DEBUG_PRINT(...)
#endif

/* --------------- Konfiguration -------------------- */
/* Port fuer Clientzugriff */
#define PORT 7777

/* Puffergroesse */
#define BUFSIZE 4096

/* Max. Laenge einer Nachricht vom Client (< BUFSIZE) */
#define MAXLEN 10

/* Codeschluessel, wird im Praktikum bekannt gegeben */
char secret[] = "Mirakulix";

/* Pfad zur Datei mit Bewegungsinfo */
char DATA[] = "/tmp/motion";

/* Pfad zum LED-Steuerprogramm */
char CMD[]  = "/home/local/bin/LED.py ";
/* --------------- Konfiguration -------------------- */

int keylen;               /* Laenge des Schluesselwortes */
char key[SMAX];           /* Schluesselwort in Form von Offsets */

/* Funktionsprototypen */
void do_dialog (int sock);
void err_exit(char *message);
void encode(char *key, char *buffer);
void decode(char *key, char *buffer);
void mkkey(char *secret, char *key, int *keylen);
split(char *str, char delim, char **res, int *length);


int main(int argc, char *argv[])
  {
  int sockfd, newsockfd, c_len;
  struct sockaddr_in serv_addr, c_addr;

  /* Schluessel erzeugen */
  mkkey(secret, key, &keylen);

  /* Socket erzeugen */
  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (sockfd < 0)
    err_exit("ERROR opening socket");

  /* Socket-Struktur initialisieren */
  memset( &serv_addr, 0, sizeof (serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = INADDR_ANY;
  serv_addr.sin_port = htons(PORT);

  /* Port an die Host-Adresse binden */
  if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
    err_exit("ERROR on binding");

  /* Auf Verbindungsanfragen warten */
  listen(sockfd,5);

  for(;;)
    {
    printf("Server bereit ...\n");
    /* Verbindung akzeptieren */
    c_len = sizeof(c_addr);
    newsockfd = accept(sockfd, (struct sockaddr *)&c_addr, (socklen_t *)&c_len);
    if (newsockfd < 0)
      err_exit("ERROR on accept");

    /* Verbindung steht -> Kommunizieren */
    printf("... connect von: %s\n", inet_ntoa(c_addr.sin_addr));
    do_dialog(newsockfd);
    close(newsockfd);
    }
  return 0;
  }

/* Kommunikation ueber den Socket durchfuehren */
void do_dialog (int sock)
  {
  char buffer[BUFSIZE] = {0};  /* Textpuffer */
  char *param[20];             /* Parameterarray */
  int count;                   /* Anzahl Parameter */
  FILE *dat;                   /* motion-Datei */
  char onoff;                  /* Optionen SET-Befehl */
  int led;
  int n, k;                    /* Hilfsvariable */

  memset(buffer, 0, BUFSIZE);      /* Puffer loeschen */
  n = read(sock,buffer,BUFSIZE-1); /* Clientdaten lesen */
  if (n < 0)
    err_exit("ERROR reading from socket");
  for(k = 0; k < n; k++)           /* cr, lf etc. entfernen */
    if (buffer[k] < ' ') buffer[k] = ' ';
  buffer[BUFSIZE] = '\0';          /* letzes Byte = 0 */

  DEBUG_PRINT("... coded Message: %s\n", buffer);
  decode(key, buffer);
  DEBUG_PRINT("... decoded Message: %s\n", buffer);

  split(buffer, ' ', param, &count);
#ifdef DEBUG
  printf("... Param-Array:\n");
  for(k = 0; k < count; k++)
    { printf("...       %2d: [%s]\n", k, param[k]); }
#endif

  if (strncmp(param[0], "GET", 3) == 0)
    { /* GET-Befehl, Datei laden */
    DEBUG_PRINT("... processing GET\n");
    dat = fopen(DATA, "r");
    if (dat == NULL)
      {
      DEBUG_PRINT("... %s not found\n", DATA);
      n = write(sock, "NAK\n", 4);
      }
    else
      {
      /* "ACK " vor den Puffer zaubern */
      strcpy(buffer, "ACK ");
      if(fgets(buffer+4, BUFSIZE-4, dat) != NULL)
        {
        fclose(dat);
        DEBUG_PRINT("... sending %s\n", buffer);
        strcat(buffer,"\n");
        n = write(sock, buffer, strlen(buffer));
        }
      else
        n = write(sock, "NAK\n", 4);
      }
    }

  else if (strncmp(param[0], "SET", 3) == 0)
    { /* SET-Befehl, LED ansteuern */
    DEBUG_PRINT("... processing SET\n");
    onoff = param[2][0];       /* erstes Zeichen vom String */
    led = atoi(param[1]);      /* LED-Nummer (1..4) */
    /* Parameter pruefen */
    DEBUG_PRINT("... SET: [%d] [%c]\n", led, onoff);
    if (led >= 1 && led <= 4 && (onoff == '0' || onoff == '1'))
      { /* LED setzen, Kommando zusammenbauen */
      sprintf(buffer, "%s %d %c\n", CMD, led, onoff);
      DEBUG_PRINT("... executing %s\n", buffer);
      system(buffer);
      n = write(sock, "ACK\n", 4);
      }
    else
      n = write(sock, "NAK\n", 4);
    }

  else
    n = write(sock, "NAK\n", 4);
  }


/* Fehlermeldung ausgeben und exit */
void err_exit(char *message)
  {
  perror(message);
  exit(1);
  }

/*
 * Verschluesseln des Textes im buffer
 * Es werden nur Zeichen im Bereich von 0x32 bis 0x128
 * verschluesselt. Andere Zeichen werden einfach
 * unveraendert durchgereicht.
 * Der buffer enthaelt nach Ende der Funktion den
 * codierten Text --> der urspruenglieche Inhalt
 * wird ueberschrieben.
 */
void encode(char *key, char *buffer)
  {
  int c;
  int n = 0;
  int j = 0;
  while (buffer[j] != 0 )
    {
    if (' ' <= buffer[j] && buffer[j] <= 128)
      {
      c = (buffer[j] - ' ' + key[n]) % ALPHA;
      buffer[j] = c + ' ';
      n = (n + 1) % keylen;
      }
    j++;
    }
  }

/*
 * Entschluesseln des Textes im buffer
 * Es werden nur Zeichen im Bereich von 0x32 bis 0x128
 * entschluesselt. Andere Zeichen werden einfach
 * unveraendert durchgereicht.
 * Der buffer enthaelt nach Ende der Funktion den
 * decodierten Text --> der urspruenglieche Inhalt
 * wird ueberschrieben.
 */
void decode(char *key, char *buffer)
  {
  int c;
  int n = 0;
  int j = 0;
  while (buffer[j] != 0 )
    {
    if (' ' <= buffer[j] && buffer[j] <= 128)
      {
      c = (buffer[j] - ' ' - key[n]);
      if (c < 0) c = c + ALPHA;
      buffer[j] = c + ' ';
      n = (n + 1) % keylen;
      }
    j++;
    }
  }

/* key erzeugen:
 * In key werden die Offsets gespeichert: 'a' --> 0, 'b' --> 1 usw.
 * Es wird nicht zwischen Gross- und Kleinbuchstaben unterschieden.
 * nicht-alphabetische Zeichen werden ignoriert (--> 0).
 * Da das key-array Nullen enthaelt, muss dessen Laenge getrennt
 * in der Variable keylen gespeichert werden.
 */
void mkkey(char *secret, char *key, int *keylen)
  {
  int len = 0;
  while (secret[len] != '\0')
    {
    if (isupper(secret[len])) key[len] = secret[len] - 'A';
    else if (islower(secret[len])) key[len] = secret[len] - 'a';
    else key[len] = 0;
    len++;
    }
  *keylen = len;
  }


/* String in Teilstrings aufspalten:
 * Der Parameter str wird jeweils an den Stellen, an denen der Delimiter
 * delim auftaucht, aufgeteilt. Zurueckgegeben wird ein Array res vom Typ
 * char **, das Pointer auf die Teilstrings enthaelt. Der Parameter
 * length enthaelt die Anzahl der erzeugten Teilstrings.
 * Aufrufbeispiel:
 * split(str, ',', res, &count);
 * for(k = 0; k < count; k++)
 *   { printf("%2d: %s\n", k, res[k]); }
 */

void split(char *str, char delim, char **res, int *length)
  {
  char *p;        /* pointer for scanning of string */
  int count = 0;  /* number of parts */
  int k = 0;      /* loop counter */

  /* count occurance of delim in string */
  p = str;
  while((p = strchr(p,delim)) != NULL)
    {
    *p = 0;            /* null terminate the delim */
    p++;               /* skip past the new null */
    count++;
    }

  p = str;
  for(k = 0; k <= count; k++)
    {
    if(*p) res[k] = p; /* copy start of string */
    p = strchr(p, 0);  /* look for next null */
    p++;               /* start of next string */
    }

  *length = count + 1; /* number of strings (not last index) */
  }
