//*****************************************************************************
// Simple ReadParse.cc for CGI-Programming.
// Distribute freely for non-commercial use.
//
// E-Mail: Gottfried.Rudorfer@wu-wien.ac.at
//
// Bugfixes: 
//   01/98 integrated CERT security fix.
//         
// (c) 1999 by Gottfried Rudorfer
// All rights reserved.
//*****************************************************************************

// Documentation: *************************************************************
// 1. Evaluates the strings passed by the GET and POST method. Uses a STL-Map.
// An Example: This code expands Key1=Value1&Key3&Key2=Value2 pairs or singles
// into the map "in".
/*
#include <iostream>
using namespace std;
#include "ReadParse.h"

int main()
{
  ReadParse mycgi;
  mycgi.PrintHeader();
  map<string,string>::iterator pos;
  for(pos=mycgi.in.begin();  pos != mycgi.in.end(); ++pos) {
    cout << "Name: <B>" << pos->first << "</B>,"
         << "Value: " << pos->second << "<BR>" << endl; 
    cout << "<HR>\n";
  }
}
*/
// 2. The method EvalFile(string) opens the file named in string
// and parses the contents of that file for $vars. If such a string is
// found, it is replaced with the value behind the key stored in the map "in".
// You may have more than one variable substiution i.e.:
// <INPUT type=hidden name="$h_user " value="$$h_user ">
//*****************************************************************************

#include"ReadParse.h"

ReadParse::ReadParse() 
{
  _env_string = _html_string = NULL;
  _has_parsed = 0;
  Parse();
}

ReadParse::~ReadParse() 
{
  if (_html_string)
    delete _html_string;
  if (_env_string)
    delete _env_string;
}


void ReadParse::PrintHeader()
{
  cout << "Content-type: text/html\n" << endl;
}

int ReadParse::strnicmp(const char *s1, const char *s2, const int count) {
  char a='\0', b='\0';
  
  for(int n=0; n<count; n++)
  {
    a = toupper(*s1++);
    b = toupper(*s2++);
    if ((a=='\0')||(b=='\0')||(a!=b))
      break;
  }
  return(a - b);
}

char *ReadParse::CertFix(char *s) 
{
  // security fix see ftp.cert.org
  for (char *cp = s; *(cp += strspn(cp, _ok_chars)); /* */)
    *cp = '_';
  return s;
}

void ReadParse::Mygetenv(char *s)
{
  char *strptr;

  if (_env_string) {
    delete _env_string;
    _env_string = NULL;
  }
    
  if ((strptr=getenv(s)))
  {
    _env_string = new char [strlen(strptr)+1];
    strcpy(_env_string, strptr);
  } 
}

//*****************************************************************************

void CgiNoSpace()
{ 
  cerr << "Not enough free memory; "
       << "new failed" <<endl;
  exit(1);  
}

int ReadParse::HexToChar(char *strptr, char &thechar)
{

  char value=0, c, w[]={1, 16};
  
  for(int j=1;j>=0;j--)
  {
    c=*++strptr; //skip %
    if ((c >= '0') && (c <= '9'))
    {
      value+=w[j]*(c-'0');
    }
    else if ((c >= 'a') && (c <= 'f'))
    {
      value+=w[j]*(10+(c-'a'));
    }
    else if ((c >= 'A') && (c <= 'F'))
    {
      value+=w[j]*(10+c-'A');
    }
    else
      return 0;
  }
  thechar=value;
  return 1;
}

void ReadParse::Parse()
{
  
  int html_length;
  set_new_handler(CgiNoSpace);
  char *sg = NULL, *sp = NULL;
  
  if(_has_parsed)
  {
    cerr << "Don't Call ReadParse twice !!!!\n";
    return;
  }
  _has_parsed=1;
	
  Mygetenv("QUERY_STRING");
  if (_env_string) {
    int l = strlen(_env_string);
    sg=new char[l+2];
    strcpy(sg,_env_string);
  }
    
  Mygetenv("CONTENT_LENGTH");
  if (_env_string) {
    int l = atoi(_env_string);
    if (l > 0) {
      sp=new char[l+2];
      cin.get(sp,l+1);
    }
  }

  if(!sg && !sp)
  {
    cerr << "ReadParse: Hey boy ... we understand METHOD GET & POST\n";
    cerr << "ReadParse: Define at least the environment variable QUERY_STRING\n";
    
    exit(1);
  }
  
  html_length = 0;
  if (sg) 
    html_length += strlen(sg);
  if (sp) 
    html_length += strlen(sp);
  if(sg && sp)
    html_length++; // '&'

  _html_string = new char [html_length+2];
  _html_string[0] = '\0';
  if (sg) {
    strcat(_html_string, sg);
    delete sg;
  }
  if (sp) {
    if (_html_string[0] != '\0')
      strcat(_html_string, "&");
    strcat(_html_string, sp);
    delete sp;
  }
  
  int rows=html_length+1, cols=2, html_bufcount[] = {0,0}, readparse_flag=0;
  char *ptr, *html_arr=new char[rows*cols], c;
  html_length=strlen(_html_string);
  ptr=_html_string;
  while (true)
    {
      switch(*ptr)
        {
          case '=':
            readparse_flag=1;
            break;
          case '&':
          case '\0':
            *(html_arr+html_bufcount[0])='\0';
            *(html_arr+rows+html_bufcount[1])='\0';
            in[(string)CertFix(html_arr)] = (string)CertFix(html_arr+rows);
            if (*ptr == '\0')
              return;
            readparse_flag=0;
            html_bufcount[0] = 0;       
            html_bufcount[1] = 0;
            break;
          case '%':
            if (strlen(ptr) >=3)
              {
                if (HexToChar(ptr, c))
                  {
                    *(html_arr+readparse_flag*rows+html_bufcount[readparse_flag]++)=c;
                    ptr+=2;
                    break;
                  }
              }
          case '+':
            *ptr=' ';
          default:
            *(html_arr+readparse_flag*rows+html_bufcount[readparse_flag]++) = *ptr;
            break;
        }
      ptr++;
    }
  delete html_arr;
}

string ReadParse::Line(const string& text, const string::size_type pos)
{
  string::size_type nl_pos, cpos = 0;
  unsigned int line=1;
  char buf[10];
  
  while((cpos < pos) && (cpos != string::npos)) {
    nl_pos = text.find_first_of("\n", cpos);
    if (nl_pos == string::npos)
      break;
    if (nl_pos < pos) {
      line++;
      cpos = nl_pos + 1;
    } else
      break;
  }

  sprintf(buf, "%d", line);
  return string(buf);
}


string ReadParse::EvalString(const string text) 
{
  string::size_type dollar, space;
  const string trennzeichen(" \t\n");
  string neu="";
  unsigned int numsub;
  
  for (string::size_type cpos=0; cpos != string::npos; ) {
    dollar = text.find_first_of("$", cpos);
    numsub = 1;
    if (dollar != string::npos) {
      neu += text.substr(cpos, dollar-cpos);
      space=text.find_first_of(trennzeichen,dollar);
      while(text.at(dollar+numsub) == '$') 
        numsub++;
      string name=text.substr(dollar+numsub,space-dollar-numsub);
      
      if (space != string::npos)
        cpos = space + 1;
      else
        cpos = space;

      string subs="";
      bool ok = false;
      
      for(unsigned int j = 0; j < numsub; j++) {
        subs += name + " -> ";
        if ((in.find(name) != in.end()) && (name.length() > 0)) {
          name = in[name];
          ok = true;
        } else {
          neu += " Error: Variable \'" + name
            + "\' not found in line " + Line(text, dollar)
            + "! The division is: " + subs + ".";
          ok = false;
          break;
        }
      }
      if (ok)
        neu += name;
    } else {
      neu += text.substr(cpos, dollar-cpos);
      cpos = dollar;
    }
  }
  return neu;
}

string ReadParse::EvalFile(const string file) 
{
  ifstream in;
  string r="", s;
  
  in.open(file.c_str(), ios::in);
  if (!in) {
    cout << "Unable to load file \"" << file << "\"" << endl;
  } else {
    while(getline(in, s))
      r += s + '\n';
    in.close();
  }
  return EvalString(r);
}
