lotto-cgi.cc

#include <iostream>
#include <list>
#include <new>
#include <string>
#include <iomanip> // setfill
#include "ReadParse.h"

using namespace std;

extern "C" {
#include <stdlib.h>  // random() atoi()
#include <time.h>  // für time()
#include <string.h>   // für strcmp()
#include <sys/types.h> // für stat()
#include <sys/stat.h> // für stat()
#include <stdio.h> // für fopen()
#include <sys/file.h> // für flock()
#include <errno.h> // für perror()
#include <unistd.h> // für unlink()

  /* Unter DU 4.0E gibt es sonst Probleme.
     Die Deklaration von flock();
  */
  int flock(int filedes,int operation );
}

// Konstante schreiben wir nicht
// in die Deklaration einer Klasse
const int min_kugel = 1;
const int max_kugel = 45;
const int tip_size = 6;
const int ziehung_size = 7;

char h_newtip[] = "NEWTIP";
char h_compare[] = "COMPARE";
char h_init[] = "INIT";
char h_user[] = "USER";
char h_answer[] = "ANSWER";


const char h_url[] = "http://rudorfer.homedns.org/cgi-bin/lotto-cgi.rudorfer";

class Lottotip;
typedef list<Lottotip> LottotipList;

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

void Error(char *msg)
{
  //Wir wollen eine Fehlermeldung zum Browser schicken.
  cout << "<p>\nEs ist ein Fehler aufgetreten:<br>\n" 
       << msg << endl;
  exit(1);
}


// Deklaration der Klasse Lottotip
// (Bauplan)
class Lottotip
{
private:
  int tip[6];
  int richtige;
  void allocate(void);
  string filename;
public:
  void tippe();
  void ziehung_erfassen(ReadParse&, LottotipList&);
  void auswertung();
  void print_html_tip();
  void eingabe(int *,int);
  void vergleichen(int *);
  void frage(ReadParse&);
  void read_history(LottotipList &);
  void write_history(LottotipList &);
  void init_history(void);
  void print_history(LottotipList &);
  Lottotip(string);
  Lottotip(int *);
  friend ostream& operator << (ostream&, const Lottotip&);          
}; // hier brauchen wir den ;


void  Lottotip::print_history(LottotipList& t_list) 
{
  cout << "<BR>\n<H1>Vergangene Tips</H1><BR>\n" << endl;
  for (LottotipList::iterator i = t_list.begin(); i != t_list.end(); ++i) {
    cout << (*i) << "<BR>";
  }
}

Lottotip::Lottotip(string f)
{
  filename = f;

  for (int i=0; i < tip_size; i++) {
    tip[i] = 0;
  }
}


Lottotip::Lottotip(int t[])
{
  for (int i=0; i < tip_size; i++) {
    tip[i] = t[i];
  }
}

void Lottotip::eingabe(int *arr, int size)
{
  for (int i=0; i < size; i++)
    {
      cout << "Bitte geben Sie die " 
	   << i+1 << "-te Zahl ein: ";
      cin >> arr[i];
    }  
}

void Lottotip::vergleichen(int ziehung[])
{
  // int richtige ist eine private Variable dieser
  // Klasse
  richtige=0;

  for (int i=0; i < tip_size; i++)
    {
      for (int j=0; j < ziehung_size - 1; j++)
	{
	  if (tip[i] == ziehung[j]) richtige++;
	}
    }
  if (richtige == 5)
    {
      for (int i=0; i < tip_size; i++)
	{
	  if (tip[i] == ziehung[ziehung_size-1]) richtige = 7;
	}
    }
}

void Lottotip::auswertung()
{
  // 3 - 6 ... 3er bis 6er
  // 7 ... 5er plus zusatzzahl
  // sonst nichts gewonnen
  switch(richtige)
    {
      case 3:
        cout << "Immerhin, sie haben einen 3er"
             << endl;
        break;
      case 4:
      case 5:
        cout << "Nicht schlecht! Sie haben einen"
             << richtige << "er" << endl;
        break;
      case 6:
        cout << "Hurra! Sie haben einen 6er!!!!!"
	       << endl;
          break;
      case 7:
        cout << "Knapp daneben, aber doch ein 5er mit Zusatzzahl"
             << endl;
        break;
      default:
        cout << "Sorry, Sie haben keine uebereinstimmende Zahlen" 
             << endl;
    }
}

ostream& operator << (ostream& s, const Lottotip& p)
{
  s << "tip ";
  for (int j=0; j < tip_size; j++) {
    s << setfill('0') << setw(2) << p.tip[j];
    if ((j+1) < tip_size) 
      s << " ";
  }
  s << endl;
  return s;
}

void Lottotip::print_html_tip()
{
  cout << "<P>Der Tip lautet: ";
  for (int j=0; j < tip_size; j++) {
    cout << tip[j];
    if ((j+1) < tip_size) 
      cout << ", ";
  }
  cout << ".\n" << endl;
}

void Lottotip::tippe()
{
  long z;
  int j=0, found;

  while(j < tip_size)
    {
      z = min_kugel + random() % max_kugel;
      
      found = 0; // Annahme z kommt noch nicht vor
      for (int i=0; i < j; i++) // bereits verwendete Elemente
	if (tip[i] == z) found = 1; // z kommt vor
	
      tip[j] = z;
      if (!found) j++; // erhöhe j nur dann, wenn die Zahl nicht vorgekommen ist.
    }
}

void Lottotip::init_history(void)
{
  /* Löscht die Datei mit den Daten
     */
  cout << "Loesche Datei \"" << filename << "\"" << endl;
  unlink(filename.c_str());
}

void Lottotip::read_history(LottotipList& t_list)
{
  ifstream ifile;
  int t[tip_size];
  string name;
  bool te;
  int fd;

  // File-locking ist bei CGI-Programmen immer notwendig.
  // Wir verwenden eine eigene Lock-Datei
  string lockfile = filename + ".lck";
  if (-1 == (fd = open(lockfile.c_str(), O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR))) {
        perror(lockfile.c_str());
        exit(1);
  }
  // Beim Lesen reicht ein SHARED lock aus. 
  if ((flock(fd, LOCK_SH)) == -1) {
      perror(lockfile.c_str());
      exit(1);
  }

  ifile.open(filename.c_str(), ios::in);
  if (ifile) {
    ifile.seekg(0, ios::beg);
    te = false;
    while(!ifile.eof()) {
      ifile >> name;
  
      if (name == "tip") {
        for (int i=0; i < tip_size; i++) 
          ifile >> t[i];
        te = true;
      } 
      if (te) {
        te = false;
        t_list.push_back(Lottotip(t));
	name = "";
      }
    }
    ifile.close();  
 }

 if ((flock(fd, LOCK_UN)) == -1) {
   perror(lockfile.c_str());
   exit(1);
 }
 close(fd);
}

void Lottotip::write_history(LottotipList& t_list)
{
  ofstream ofile;
  int fd;

  // File-locking ist bei CGI-Programmen immer notwendig.
  // Wir verwenden eine eigene Lock-Datei
  string lockfile = filename + ".lck";
  if (-1 == (fd = open(lockfile.c_str(), O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR))) {
        perror(lockfile.c_str());
        exit(1);
  }
  // Beim Lesen schreiben: Exklusiver lock 
  if ((flock(fd, LOCK_EX)) == -1) {
      perror(lockfile.c_str());
      exit(1);
  }

  ofile.open(filename.c_str(), ios::out);
  if (ofile) {
    ofile.seekp(0,ios::beg);
    for (LottotipList::iterator i = t_list.begin(); i != t_list.end(); ++i) {
      ofile << *i;
    }
    ofile.close();  
  } else {
	cerr << "Kann die Datei " << filename << " nicht erstellen" << endl;
  } 

  if ((flock(fd, LOCK_UN)) == -1) {
    perror(lockfile.c_str());
    exit(1);
  }
  close(fd);
}

void Lottotip::ziehung_erfassen(ReadParse& mycgi, LottotipList& t_list)
{
  cout << "<H1>Ziehung erfassen und Tips auswerten</H1>" << endl;
  cout << "Zahlen der Ziehung: ";
  
  if (mycgi.in.find("Tips_auswerten") != mycgi.in.end()) {
    char buf[4];
    int z[ziehung_size];

    for (int i=0; i<(ziehung_size-1); i++) {
      sprintf(buf, "Z%d", i);
      z[i] = atoi(mycgi.in[buf].c_str());
      cout << z[i] << " ";
    }
    z[ziehung_size-1] = atoi(mycgi.in["ZS"].c_str());
    cout << z[ziehung_size-1] << "<BR>\n";

    for (LottotipList::iterator ti = t_list.begin(); ti != t_list.end(); ++ti) {
      ti->vergleichen(z);
      ti->print_html_tip();
      ti->auswertung();
      
    }    
  }else {
    cout << "<BR>\n<FORM METHOD=\"POST\" ACTION=\"" << h_url << "\">" << '\n';
    for (int i=0; i<(ziehung_size-1); i++) {
      cout  << "Z" << i+1 << ":<INPUT type=text name=\"Z" << i << "\" SIZE=2>" << " \n";
    }
    cout  << "Zusatzzahl: <INPUT type=text name=\"ZS\" SIZE=2>" << '\n'
          << "<INPUT type=hidden name=\"" << h_user << "\" value=\"" << mycgi.in[h_user] << "\">\n"
          << "<INPUT type=hidden name=\"" << h_answer << "\" value=\"" << h_compare << "\">" << '\n'
      
          << "<INPUT type=submit value=\"Tips_auswerten\" name=\"Tips_auswerten\">" << '\n'
          << "</FORM>" << endl;
  }
}

void Lottotip::frage(ReadParse& mycgi)
{
  cout << "<BR>\n<HR>\n<FORM METHOD=\"POST\" ACTION=\"" << h_url << "\">" << '\n'
       << "<INPUT type=radio name=\"" << h_answer << "\" value=\"" << h_newtip << "\" CHECKED>Neuen Tip erzeugen." << '\n'
       << "<INPUT type=radio name=\"" << h_answer << "\" value=\"" << h_compare << "\">Ziehung erfassen und Tips auswerten." << '\n'
       << "<INPUT type=radio name=\"" << h_answer << "\" value=\"" << h_init << "\">Historie mit Tips loeschen." << '\n';
  if (mycgi.in.find(h_user) != mycgi.in.end()) // Gebe den Benutzer in einer versteckten Variable aus, wenn er definiert ist.
    cout << "<INPUT type=hidden name=\"" << h_user << "\" value=\"" << mycgi.in[h_user] << "\">\n";
  cout << "<INPUT type=submit VALUE=\"OK\">" << '\n'
       << "</FORM>" << '\n'
       << "<A HREF=\"http://rudorfer.homedns.org/cppag\">Zurueck zur Homepage der C++ AG</A>" << endl;    
}

void print_userform()
{
  cout << "<FORM METHOD=\"POST\" ACTION=\"" << h_url << "\">" << '\n'
       << "<INPUT type=text name=\"" << h_user << "\">" << '\n'
       << "<INPUT type=submit NAME=\"Benutzer registrieren\" VALUE=\"Benutzer registrieren\">" << '\n'
       << "</FORM>" << '\n'
       << "<A HREF=\"http://rudorfer.homedns.org/cppag\">Zurueck zur Homepage der C++ AG</A>" << endl;    
}

int main(int argc, char *argv[])
{
  set_new_handler(NoSpace);
  LottotipList t_list;
  ReadParse mycgi;
  
  // Initialisiere den Zufallszahlengenerator
  // steht immer am Beginn der Funktion main()
  srandom(time(NULL));

  mycgi.PrintHeader(); //Gebe den Content-type aus: Benoetigt jede WEB-Seite
 

  if ((mycgi.in.find(h_user) == mycgi.in.end()) || (mycgi.in[h_user] == "")) {
    print_userform(); //Zuerst muß der Benutzername erfaßt werden.
  } else {
    cout << "<H1>Benutzer \"" << mycgi.in[h_user] << "\"</H1>" << endl;
    Lottotip mein_tip("/tmp/lotto-" + mycgi.in[h_user] + ".dat");
   
    if ((mycgi.in[h_answer] == h_newtip) || (mycgi.in[h_answer] == "")) {
      mein_tip.read_history(t_list);
      mein_tip.tippe();
      mein_tip.print_html_tip();
    
      t_list.push_back(mein_tip);
      mein_tip.write_history(t_list);    
      mein_tip.print_history(t_list);    
    } else if (mycgi.in[h_answer] == h_compare) {
      mein_tip.read_history(t_list);
      mein_tip.ziehung_erfassen(mycgi,t_list);
      mein_tip.print_history(t_list);
    } else if (mycgi.in[h_answer] == h_init) {
      mein_tip.init_history();
    }else {
      Error("Unbekannter Parameter");
    }
    mein_tip.frage(mycgi); // Gebe <FORM> aus, ob Benutzer nochmals tippen will.
  }
}


Makefile

# defines
CC=g++ 
#CC=cxx 
CXX= $(CC)
CFLAGS=-g -Wall
#CFLAGS=-g  -g -w0
#CFLAGS=-O -Wall
CXXFLAGS=$(CFLAGS)
LIBS=
BIN=lotto-cgi
BINDIR=/var/html/cgi-bin
USER=rudorfer

# Programs
SRC=	ReadParse.cc \
	${BIN}.cc


OBJS=	ReadParse.o \
	${BIN}.o

all:	${BIN}
	@echo "Now type make install"

clean:	
		-@rm -r *.o *.dat *~ *.bak ${BIN} cxx_repository 2> /dev/null
		@echo "The filesystem thanks you for your kindness"

test:
	@if [ "" = "${USER}" ]; \
	then \
	echo "Sie müssen zuerst die Umgebungsvariable USER setzen!"; \
	fi;

install: test ${BIN}
	@if [ ! -d ${BINDIR} ]; \
	then \
	echo "Erzeuge Verzeichnis ${BINDIR}..."; \
	mkdir -p ${BINDIR}; \
	fi; \
	cp -p ${BIN} ${BINDIR}/${BIN}.${USER} && \
	chmod 4711 ${BINDIR}/${BIN}.${USER} && \
	echo "Die Programm-Datei wurde in das Verzeichnis ${BINDIR} kopiert." && \
	echo "Über http://${HOSTNAME}:81/cgi-bin/${BIN}.${USER} kann nun das Programm aufgerufen werden."\

# dependencies
ReadParse.o: ReadParse.cc ReadParse.h
${BIN}.o: ReadParse.h ReadParse.cc ${BIN}.cc

# linking
${BIN}:	${OBJS} ${SRC}
		${CC} ${CFLAGS}	${OBJS} -o ${BIN} ${LIBS}

ReadParse.h

//******************************************************************************
// ReadParse.h
// Copyright 1999 by Gottfried Rudorfer
//
// E-Mail: Gottfried.Rudorfer@ai.wu-wien.ac.at
//
// All rights reserved.
//******************************************************************************

// Documentation: ***************************************************************
// VARIABLES:
// in .... map of key/value pairs
//******************************************************************************


#include <iostream>
#include <fstream>
#include <string>
#include <map>
#include <new>

using namespace std;

extern "C"
{
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
}

// characters that we accept (according to CERT)
const char _ok_chars[] = "abcdefghijklmnopqrstuvwxyz\
ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_-.@%=&";

typedef map<string,string> StringStringMap;

void CgiNoSpace();

class ReadParse 
{
public:
  ReadParse();
  ~ReadParse();
  void PrintHeader();
  string EvalString(const string);
  string EvalFile(const string);
  void Mygetenv(char*);
  StringStringMap in;
  int strnicmp(const char *s1, const char *s2, const int count);
  int HexToChar(char *strptr, char &thechar);
private:
  void Parse();
  char *_html_string;
  int _has_parsed;
  string Line(const string& text, const string::size_type pos);
  char *_env_string;
  char *CertFix(char*); 
};

ReadParse.cc

//*****************************************************************************
// Simple ReadParse.cc for CGI-Programming.
// Distribute freely for non-commercial use
// Copyright 1999 by Gottfried Rudorfer.
//
// E-Mail: Gottfried.Rudorfer@wu-wien.ac.at
//
// Bugfix: 01/98 check if environment variable exists
//         01/98 integrated CERT security fix
//         Gottfried.Rudorfer@wu-wien.ac.at
//         
// 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 != NULL)
    delete _html_string;
  if (_env_string != NULL)
    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 != NULL) {
    delete _env_string;
    _env_string = NULL;
  }
    
  if ((strptr=getenv(s)))
  {
    _env_string = new char [strlen(strptr)+1];
    strcpy(_env_string, strptr);
  } else {
    cerr << "Hey boy ... the environment variable \"" << s 
	 << "\" is not defined!" << endl;
    exit(1);
  } 
}

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

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=-1;
  set_new_handler(CgiNoSpace);

  if(_has_parsed)
  {
    cerr << "Don't Call ReadParse twice !!!!\n";
    return;
  }
  _has_parsed=1;
	
  // Method GET
  Mygetenv("REQUEST_METHOD");
  if(!strcmp(_env_string,"GET"))
  {
    Mygetenv("QUERY_STRING");
    html_length=strlen(_env_string);
    _html_string=new char[html_length+2];
    strcpy(_html_string,_env_string);
  }
  
  // Method POST
  Mygetenv("REQUEST_METHOD");
  if(!strcmp(_env_string,"POST"))
  {
    Mygetenv("CONTENT_LENGTH");
    html_length= atoi(_env_string);
    _html_string=new char[html_length+2];
    cin.get(_html_string,html_length+1);
  }

  if(html_length==-1)
  {
    cerr << "Hey boy ... we understand METHOD GET & POST\n";
    exit(1);
  }

  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 '+':
      *ptr=' ';
    case '%':
      if (strlen(ptr) >=3)
      {
	if (HexToChar(ptr, c))
	{
	  *(html_arr+readparse_flag*rows+html_bufcount[readparse_flag]++)=c;
	  ptr+=2;
	  break;
	}
      }
    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 += " Fehler: Variable \'" + name
            + "\' wurde nicht in der Zeile " + Line(text, dollar)
            + " gefunden! Die Ableitung ist: " + 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 << "Fehler beim Lesen der Datei \"" << file << "\"" << endl;
  } else {
    while(getline(in, s))
      r += s + '\n';
    in.close();
  }
  return EvalString(r);
}