#ifndef CGI_H
#define CGI_H

/* ===========================================
 * A class to encapsulate the CGI decoding
 * proccess for the GET and POST methods.  The 
 * class also encapsulates a generic container
 * used to hold the decoded form data.
 *
 * (C) 1998 Matthew W. Coan
 *          mcoan@slack.net
 *          http://slack.net/~mcoan
 *
 *       You are free to use this,
 *       just as long as the above
 *       copywright info. stays 
 *       intact and. Any changes 
 *       made must be reported
 *       to me.  Help me make this
 *       the best C++ CGI system
 *       out there.
 */

#include <map>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

/**
 * A compare class for the form vars.
 */
class form_var_comp {
public:
   bool operator()(const char * p1, const char * p2) const {
      return strcmp(p1,p2) < 0 ? true : false;
   }
};

/** 
 * Default container.
 */
typedef std::map< char*, char*, form_var_comp > cgi_container;

/**
 * The cgi class parses and decodes the form data 
 * storing it in the char* to char* associative container.
 */
template< class container = cgi_container >
class cgi {
public:
	typedef container container_type;
private:
   container form_map;
   char * qs;
   bool didPost;

   class key_data_pair { 
   public:
      char * name, * val; 
   } temp;                  
      
   /**
    * Unescape a url encoded string.
    */
   void unescape_url(char *url) {
      register int i,j; 
      for(i = 0,j=0; url[i]; i++,j++) {
         if((url[i]=url[j]) == '%') {
            url[i] = (url[j+1] >= 'A' ? ((url[j+1] & 0xdf) - 'A') + 10 : (url[j+1] - '0'));
            url[i] *= 16;
            url[i] += (url[j+2] >= 'A' ? ((url[j+2] & 0xdf) - 'A') + 10 : (url[j+2] - '0'));
            j += 2;
         }
         else if (url[i] == '+') {
            url[i] = ' ';
         }
      }
      url[i] = '\0';
   }

public:

   /**
    * Constructor. 
    */
   cgi( const int & MAX_INPUT_SIZE = 3000 ) {
      register char * cl,                      // Content length.
                    * method;                  // The request method.
      register int icl,                        // The content length in int form.
                   rc;                         // Error code.

      register char * p,
                    * qsPtr;

      method = getenv("REQUEST_METHOD");
      if ( ! method ) {
         fprintf(stderr,"\nMust run as a cgi...\n");
         fflush(stderr); 
         exit(EXIT_FAILURE); 
      }

      //
      // Proccess the post method.
      //
      if (strncmp(method, "POST", 4) == 0) {
         didPost = true;
         cl = getenv("CONTENT_LENGTH");
         if ( ! cl ) {
            fprintf(stderr,"CONTENT_LENGTH = null");
            fflush(stderr);
            exit(EXIT_FAILURE);
         }
         icl = atoi ( cl );
         if ( icl > MAX_INPUT_SIZE ) {
            fprintf(stdout,"Your message is too long.."
                           " please shorten it. Thanks!");
            fflush(stdout);
            exit(EXIT_FAILURE);
         }
         qs = new char[ icl + 1 ];
         if( ! qs ) {
            fprintf(stdout,"Unable to allocate memory "
                           "for the querrey string.\n</BODY></HTML>");
            fflush(stdout);
            exit(EXIT_FAILURE);
         }
         if ((rc = fread(qs, icl, 1, stdin)) != 1) {
            fprintf(stdout,"Unable to read from the input stream.");
            fflush(stdout);
            exit(EXIT_FAILURE);
         }
         qs[icl] = '\0';

	 qsPtr = qs;
         for(register unsigned int i = 0; i<icl;) { 
            temp.name = qs;
            for ( p = qs; *p != '=' && i < icl; p++, i++ ) {
               ;
            }
            *p++ = '\0';
            i++;
            temp.val = p;
            for ( ; *p != '&' && i < icl && *p != '\0'; p++, i++ ) {
               ;
            }
            *p++ = '\0';
            i++;
            unescape_url( temp.name );
            unescape_url( temp.val );
            form_map[temp.name] = temp.val;
            qs = p;
         }
         qs = qsPtr;
      }

      //
      // Proccess the get method.
      //
      else if (strncmp(method, "GET", 3) == 0) {
         didPost = false;
         qs = getenv("QUERY_STRING");
         if ( ! qs )
	        qs = "";
         if ( (icl=strlen(qs)) > MAX_INPUT_SIZE ) {
            fprintf(stdout,"\nYour message is too long.. please shorten it. Thanks!");
            fflush(stdout);
            exit(EXIT_FAILURE);
         }

	 qsPtr = qs;
         for(register unsigned int i = 0; i<icl;) { 
            temp.name = qs;
            for ( p = qs; *p != '=' && i < icl; p++, i++ ) {
               ;
            }
            *p++ = '\0';
            i++;
            temp.val = p;
            for ( ; *p != '&' && i < icl && *p != '\0'; p++, i++ ) {
               ;
            }
            *p++ = '\0';
            i++;
            unescape_url( temp.name );
            unescape_url( temp.val );
            form_map[temp.name] = temp.val;
            qs = p;
         }
         qs = qsPtr;
      }
   }

   /**
    * Deconstructor.
    */
   ~cgi() {
      if ( didPost )
        delete [] qs;
   }

   /**
    * Subscripts are overloaded for lookup.
    */
   char * operator[](const char * n) {
      return form_map[(char * const &)n];
   } 

   /**
    * Return the form map container.
    */
   container & get_container() { return form_map; }   
};

/**
 * The default CGI type.
 */
typedef cgi< > CGI;

#endif /* End of CGI_H. */

