#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "global.h"

struct var_t {
  char *name,
       *data;

  struct var_t *next;
};

struct env_t {
  char *name,
       *data;

  struct env_t *next;
};

extern char **environ, allowpassword;

unsigned int content_length = 0;
unsigned char *gdata = NULL;
struct var_t *varlist = NULL;
struct env_t *envlist = NULL;

/*
   Determine what user is trying to do based on variables supplied
*/
void cgi_nav(void)
{
  char *r = NULL, i = 0;

  i = 0;

  r = cgi_is_var("Register");
  if (r) {
     go_register();
     return;
  }

  if (allowpassword)
     t_open(T_REGISTER_WITH_PASSWORD);
  else
     t_open(T_REGISTER);
}

/*
   Parse out information in stdin, if any

   gdata contains parsed out data, which is pointed to by
   the var_t linked list.
*/
void cgi_var(void)
{
  int ret = 0;
  struct var_t *v = NULL, *vt = NULL;
  unsigned char *h = NULL, *t = NULL, *n = NULL, *d = NULL;  

  gdata = (unsigned char *)malloc(content_length + 1);
  if (gdata == NULL)
     global_error("Out of memory", 1, 0);
 
  memset((unsigned char *)gdata, 0, (content_length + 1));

  ret = read(0, (unsigned char *)gdata, content_length);
  if (ret != content_length)
     global_error("Unable to read content", 1, 0);

  cgi_parse_hex();

  varlist = (struct var_t *)malloc(sizeof(struct var_t));
  varlist->next = NULL;
  vt = varlist;

  for (n = NULL, d = NULL, h = t = gdata;;) {
      if ((*h == '=') && (n == NULL)) {
         *h++ = '\0';
         n = t;
         t = h;
      }

      else if (((*h == '&') && (n != NULL) && (d == NULL)) ||
               ((!(*h)) && (n != NULL) && (d == NULL))) {
         if (*h)
            *h++ = '\0';
         else
            h = NULL;

         d = t;
         t = h;
       
         v = (struct var_t *)malloc(sizeof(struct var_t));
         if (v == NULL)
            global_error("Out of memory", 1, 0);
         
         v->name = n;
         v->data = d;
         v->next = NULL;

         vt->next = v;
         vt = v;

         n = d = NULL;         

         if (h == NULL)
            break;
      }

      else
         h++;
  }
}

/*
   Convert all hex codes in content to ascii
*/
void cgi_parse_hex(void)
{
  unsigned int len = 0;
  unsigned char r = 0;
  unsigned char *p = NULL;

  len = content_length;

  for (p = gdata; *p; p++) {
      len--;

      if (*p == '%') {
         if ((*(p + 1)) && (*(p + 2))) {
            r = hex2asc(*(p + 1), *(p + 2));
            
            *p = r;

            memcpy((char *)(p + 1), (char *)(p + 3), (len - 2));

            *(p + len - 1) = '\0';
         }         
      }

      else if (*p == '+')
	 *p = ' ';
  }
}

/*
   Fetch HTTP environment variables
*/
void cgi_env(void)
{
  struct env_t *et = NULL, *e = NULL;
  char *t = NULL, *n = NULL, *v = NULL;
  int i = 0, elen = 0, nlen = 0, vlen = 0;

  envlist = (struct env_t *)malloc(sizeof(struct env_t));
  if (envlist == NULL)
     global_error("Out of memory", 1, 0);

  envlist->next = NULL;
  et = envlist;  

  for (i = 0; environ[i]; i++) {
      for (t = environ[i]; *t != '='; t++);

      elen = strlen(environ[i]);
      nlen = (elen - strlen(t));
      vlen = (elen - nlen);

      e = (struct env_t *)malloc(sizeof(struct env_t));
      if (e == NULL)
         global_error("Out of memory", 1, 0);         
          
      n = (char *)malloc(nlen + 1);
      if (n == NULL)
         global_error("Out of memory", 1, 0);

      v = (char *)malloc(vlen + 1);
      if (v == NULL)
         global_error("Out of memory", 1, 0);

      e->name = n;
      e->data = v;
      e->next = NULL;

      memset(n, 0, (nlen + 1));
      memset(v, 0, (vlen + 1));

      memcpy(n, environ[i], nlen);
      memcpy(v, (++t), vlen);

      et->next = e;
      et = e;
  }
}

void cgi_init(void)
{
  int val = 0;
  char *env = NULL;

  cgi_env();

  env = cgi_is_env("REQUEST_METHOD");
  if (env == NULL)
     global_error("Unknown request method", 1, 0);

  if (strcasecmp(env, "POST")) {
     if (strcasecmp(env, "GET"))     
        global_error("Unsupported request method", 1, 0);  

     gdata = NULL;
     varlist = NULL;

     if (allowpassword)
        t_open(T_REGISTER_WITH_PASSWORD);
     else
        t_open(T_REGISTER);
  }
  
  env = cgi_is_env("CONTENT_LENGTH");
  if (env == NULL)
     global_error("Unknown content", 1, 0);
 
  val = atoi(env);
  if (val < 1)
     global_error("Invalid content length", 1, 0);

  if (val > MAX_CONTENT_LENGTH)
     val = (MAX_CONTENT_LENGTH - 1);

  content_length = val;

  cgi_var();
}

char *cgi_is_env(char *name)
{
  struct env_t *e = NULL;

  for (e = envlist; e->next; e = e->next) {
      if (!(strcmp(e->next->name, name)))
         return e->next->data;
  }

  return NULL;
}

char *cgi_is_var(char *name)
{
  struct var_t *v = NULL;

  if (varlist == NULL)
     return NULL;

  for (v = varlist; v->next; v = v->next) {
      if (!(strcmp(v->next->name, name)))
         return v->next->data;
  }

  return NULL;
}

char matoh(char x)
{
  char ret = 0;

  switch(x) {
    case '0':
             ret = 0;
             break;
    case '1':
             ret = 1;
             break;
    case '2':
             ret = 2;
             break;
    case '3':
             ret = 3;
             break;
    case '4':
             ret = 4;
             break;
    case '5':
             ret = 5;
             break;
    case '6':
             ret = 6;
             break;
    case '7':
             ret = 7;
             break;
    case '8':
             ret = 8;
             break;
    case '9':
             ret = 9;
             break;
    case 'A':
             ret = 10;
             break;
    case 'B':
             ret = 11;
             break;
    case 'C':
             ret = 12;
             break;
    case 'D':
             ret = 13;
             break;
    case 'E':
             ret = 14;
             break;
    case 'F':
             ret = 15;
             break;
    default:
             ret = -1;
             break;
  }

  return ret;
}

unsigned char hex2asc(char s, char f)
{
  char ret = 0;
  unsigned char val1 = 0, val2 = 0, val3 = 0;

  ret = matoh(f);
  if (ret == -1)
     return 0;

  val1 = ret;

  ret = matoh(s);
  if (ret == -1)
     return 0;

  val2 = ret;

  val3 = val1 + (val2 * 15) + val2;
  return val3;
}


syntax highlighted by Code2HTML, v. 0.9.1