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);
}