/*  emailer 1.0, copyright 1998, zap technologies  */
/*  updated Aug 22  */

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

#define POINTERWIDTH 4
#define COMMANDSIZE 256 
#define FORMCACHESIZE 1024
#define TEST "ZAPTECH_CGITEST"


/*  data tytpes  */
struct cgiarg {
  struct cgiarg *next;
  char *name;
  char *value;
  };


/*  prototypes  */
struct cgiarg *cacheformargs();
char *findformarg(char *arg, struct cgiarg *head);


/*  global variables  */
char formargscache[FORMCACHESIZE];  /*  warning, this must start on longword boundary  */
char cgitest = 0;


main(int argc, char *argv[]) {
  char command[COMMANDSIZE];
  FILE *fp;
  struct cgiarg *harg, *temparg;
  char *flagstr, *email, *subody, *suffix;
  int c;
  time_t tm;

  time(&tm);
  srand(tm);
  printf("Content-type: text/html\n\n");
  if (harg = cacheformargs()) {
    flagstr = findformarg("flags", harg);
    email= findformarg("prefix", harg);
    suffix = findformarg("suffix", harg);
    }
  if (!harg || !email || !suffix) {
    /*  warning strcmp will crash if given a null ptr  */
    if (flagstr && !(strcmp(flagstr, "DEBUG"))) {
debugout:
      temparg = harg;
      while (temparg) {
        if (temparg != harg)
          printf("<BR>\n...<BR>\n");
        printf("%s ...<BR>\n%s", temparg->name, temparg->value);
        temparg = temparg->next;
        }
      }
    else {
      printf("<HTML>\n<BODY BGCOLOR=\"#FFFFFF\">\n\n");
      printf("<CENTER><BR>\n<B>insuffient arguments information</B>\n\n");
      printf("<P>Please go back to the previous page before continuing to browse.\n");
      printf("<P>real time: %X<br>\n", tm);
      printf("</CENTER>\n");
      printf("\n</BODY>\n");
      printf("</HTML>\n");
      }
    }
  else {
    printf("<!--  copyright 1998, zap technologies, http://www.zaptech.com  -->\n");
    printf("<!--  emailer 1.0  -->\n");
    if (flagstr && !(strcmp(flagstr, "DEBUG")))
      goto debugout;
    printf(email);
    if (email = findformarg("email_to", harg)) {
      if (!(subody = findformarg("subject", harg)))
        subody = "no subject";
      /*  need to check that command string won't overflow  */
      if ((strlen(email) + strlen(subody)) > (COMMANDSIZE - 16))
        goto noto;
      sprintf(command, "mail %s -s \"%s\"", email, subody);
      if (!(subody = findformarg("body", harg)))
        subody = "no body";
      if (!(email = findformarg("email_return", harg)))
        email = "no email_return";
      if (cgitest) {
        printf("CGITEST mode: no mail sent\n\n");
        printf("%s\n", command);
        printf("from: %s<BR>\n\n%s\n", email, subody);
        }
      else {
        if (fp = popen(command, "w"))
          fprintf(fp, "from: %s\n\n%s\n", email, subody);
        else
          printf("No pipe opened<BR>\n");
        pclose(fp);
        }
      subody = findformarg("acknowledge", harg);
      }
    else
noto: subody = findformarg("errormsg", harg);
    printf(subody);
    printf(suffix);
    }
  }


char *findformarg(char *arg, struct cgiarg *head) {
  struct cgiarg *carg;
  char *success = 0;

  carg = head;
  while (carg) {
    if (strcmp(arg, carg->name) == 0) {
      success = carg->value;
      break;
      }
    carg = carg->next;
    }
  return (success);
  }


/*  Note: Now checks for cacheformargs overrun.  Any arg  */
/*    overrun cancels all args that precede it ...        */
/*    may change this in future version?                  */
struct cgiarg *cacheformargs() {
  struct cgiarg *harg = 0;  /*  head  */
  struct cgiarg *parg = 0;  /*  prev  */
  struct cgiarg *carg;
  char *env;
  int c, i;
  int cachefree = sizeof(formargscache);
  char c2, parse_state = 0;

  carg = (struct cgiarg *) &formargscache;
  env = getenv(TEST);
  if (env) {
    cgitest = 1;
    printf("found environment variable: " TEST "<BR>\n");
    goto test;
    }
  env = getenv("REQUEST_METHOD");
  if (env && !(strcmp("POST",env))) {
    env = getenv("CONTENT_LENGTH");
    if (env) {
      i = atoi(env);  /*  get POST length, ignored  */
test: while ((c = getchar()) != EOF) {
        switch (parse_state) {
         case 0:
          i = 0;
          carg->next = 0;
          carg->name = (char *) carg + sizeof(struct cgiarg);
          carg->name[i++] = c;
          if ((cachefree -= 13) < 1) {
cacheout:   harg = 0;
            goto exit;
            }
          parse_state++;
          break;
         case 1:
          if ((cachefree -= 1) < 1)
            goto cacheout;
          if (c == '=') {
            carg->name[i++] = 0;
            carg->value = &carg->name[i];
            i = 0;
            parse_state++;
            }
          else
            carg->name[i++] = c;
          break;
         case 2:
          if (c == '&') {
            carg->value[i++] = 0;
            if (harg)
              parg->next = carg;
            else
              harg = carg;
            parg = carg;
            carg = (struct cgiarg *) &carg->value[i];
            c = (POINTERWIDTH - (unsigned long) carg % POINTERWIDTH) & (POINTERWIDTH - 1);
            carg = (struct cgiarg *) ((unsigned long) carg + c);
            if ((cachefree -= (c + 1)) < 0)
              goto cacheout;
            parse_state = 0;
            }
          else {
            if ((cachefree -= 1) < 1)
              goto cacheout;
            if (c == '%')
              parse_state = 3;
            else {
              if (c == '+') c = ' ';
                carg->value[i++] = c;
              }
            }
          break;
        case 3:
          if (c > '9') c = c - 'A' + 10;
          else c -= '0';
          c2 = c;
          parse_state++;
          break;
        case 4:
          if (c > '9') c = c - 'A' + 10;
          else c -= '0';
          carg->value[i++] = (c2 << 4) + c;
          parse_state = 2;
          }
        }  /*  while  */
      if (parse_state == 2) {
        /*  note trailing carriage return before EOF  */
        carg->value[i - 1] = 0;
        if (harg)
          parg->next = carg;
        else
          harg = carg;
        }
      }  /*  if env  */
    }  /*  if env  */
exit:
  return (harg);
  }

