/*
 * Proccgi
 *
 * Reads form variables and dumps them on standard output.
 * Distributed by the GNU General Public License. Use and be happy.
 *
 * Frank Pilhofer
 * fp@informatik.uni-frankfurt.de
 *
 * Last changed 11/06/1997
 *
 * Modified by Bob Puff 03/18/01 to convert "-" to "_" for form field names
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <memory.h>

/*
 * Duplicate string
 */

char *
FP_strdup (char *string)
{
  char *result;

  if (string == NULL)
    return NULL;

  if ((result = (char *) malloc (strlen (string) + 1)) == NULL) {
    fprintf (stderr, "proccgi -- out of memory dupping %d bytes\n",
	     (int) strlen (string));
    return NULL;
  }

  strcpy (result, string);
  return result;
}

/*
 * Read CGI input
 */

char *
LoadInput (void)
{
  char *result, *method, *p;
  int length, ts;

  if ((method = getenv ("REQUEST_METHOD")) == NULL) {
    return NULL;
  }

  if (strcmp (method, "GET") == 0) {
    if ((p = getenv ("QUERY_STRING")) == NULL)
      return NULL;
    else
      result = FP_strdup (p);
  }
  else if (strcmp (method, "POST") == 0) {
    if ((length = atoi (getenv ("CONTENT_LENGTH"))) == 0)
      return NULL;

    if ((result = malloc (length + 1)) == NULL) {
      fprintf (stderr, "proccgi -- out of memory allocating %d bytes\n",
	       length);
      return NULL;
    }

    if ((ts = fread (result, sizeof (char), length, stdin)) < length) {
      fprintf (stderr, "proccgi -- error reading post data, %d bytes read, %d expedted\n",
	       ts, length);
    }
    result[length] = '\0';
  }
  else {
    return NULL;
  }

  return result;
}

/*
 * Parse + and %XX in CGI data
 */

char *
ParseString (char *instring)
{
  char *ptr1=instring, *ptr2=instring;

  if (instring == NULL)
    return instring;

  while (isspace (*ptr1))
    ptr1++;

  while (*ptr1) {
    if (*ptr1 == '+') {
      ptr1++; *ptr2++=' ';
    }

    else if (*ptr1 == '%' && isxdigit (*(ptr1+1)) && isxdigit (*(ptr1+2))) {
      ptr1++;
      *ptr2    = ((*ptr1>='0'&&*ptr1<='9')?(*ptr1-'0'):((char)toupper(*ptr1)-'A'+10)) << 4;
      ptr1++;
      *ptr2++ |= ((*ptr1>='0'&&*ptr1<='9')?(*ptr1-'0'):((char)toupper(*ptr1)-'A'+10));
      ptr1++;
    }
    else 
      *ptr2++ = *ptr1++;
  }
  while (ptr2>instring && isspace(*(ptr2-1)))
    ptr2--;

  *ptr2 = '\0';

  return instring;
}

/*
 * break into attribute/value pair. Mustn't use strtok, which is
 * already used one level below. We assume that the attribute doesn't
 * have any special characters.
 */

void
HandleString (char *input)
{
  char *data, *ptr, *p2;

  if (input == NULL) {
    return;
  }

  data = FP_strdup   (input);
  ptr  = ParseString (data);

  /*
   * Security:
   *
   * only accept all-alphanumeric attributes, and don't accept empty
   * values
   */

  if (!isalpha(*ptr) && *ptr != '_' && *ptr != '-') {free (data); return;}
  ptr++;
  while (isalnum(*ptr) || *ptr == '_' || *ptr == '-') {
     if (*ptr == '-') *ptr = '_';
     ptr++; }
  if (*ptr != '=') {free (data); return;}

  *ptr = '\0';
  p2 = ptr+1;

  fprintf (stdout, "FORM_%s=\"", data);

  /*
   * escape value
   */

  while (*p2) {
    switch (*p2) {
    case '"': case '\\': case '`': case '$':
      putc ('\\', stdout);
    default:
      putc (*p2,  stdout);
      break;
    }
    p2++;
  }
  putc ('"',  stdout);
  putc ('\n', stdout);
  *ptr = '=';
  free (data);
}

int
main (int argc, char *argv[])
{
  char *ptr, *data = LoadInput();
  int i;

  /*
   * Handle CGI data
   */

  if (data) {
    ptr = strtok (data, "&");
    while (ptr) {
      HandleString (ptr);
      ptr = strtok (NULL, "&");
    }
    free (data);
  }

  /*
   * Add Path info
   */

  if (getenv ("PATH_INFO") != NULL) {
    data = FP_strdup (getenv ("PATH_INFO"));
    ptr = strtok (data, "/");
    while (ptr) {
      HandleString (ptr);
      ptr = strtok (NULL, "/");
    }
    free (data);
  }

  /*
   * Add args
   */

  for (i=1; i<argc; i++) {
    HandleString (argv[i]);
  }

  /*
   * done
   */

  return 0;
}
