#!/usr/bin/perl
use CGI;
use DBI;
use Digest::MD5 qw(md5 md5_hex md5_base64);
$DB = "dbi:mysql:HomeworkFive:silo.cs.indiana.edu:port=47066";
$username = "lbird";
$password = "dribl";
$DB_TABLE = "quiz";
$SECRET = "something secret";
$EXPIRE = 30 * 60 * 60 * 24; # one month
$MAX_TRIES = 10;
$ID_LENGTH = 8;
$q = new CGI;
# Open the database --------------------------------------------------------
$DBH = DBI->connect($DB, $username, $password, {PrintError => 0}) || die "Couldn't open database: ", $DBI::errstr;
# get the current session ID, or make one ----------------------------------
my ($session_id, $note) = &get_session_id();
print $q->header, $q->start_html;
my $state = &get_state($session_id);
$message = $state->{'message'};
$n1 = $state->{'n1'};
$n2 = $state->{'n2'};
$correct = $state->{'correct'};
$total = $state->{'total'};
$elbat = $state->{'elbat'}; # all of the above are state
$answer = $q->param('answer'); # input
if ($message) {
if ($answer == $n1 + $n2) {
$message = "$n1 + $n2 is INDEED equal to $answer ";
$correct += 1;
} else {
$message = "$n1 + $n2 is NOT equal to $answer ";
}
$total += 1;
$message .= "(score now $correct/$total) ";
# store in the table
# table has the format: sdjkfgsd:sfgesdf:agdsdjhfd:kjsdagfsd
if ($elbat) { $elbat .= ":" . $message; }
else { $elbat = $message; }
$n1 = int(rand(100)) - 50;
$n2 = int(rand(100)) - 50;
# $question is an output variable not part of the state
$question = "What is $n1 + $n2? Answer: ";
} else {
# initialize state
$correct = 0;
$total = 0;
$message = "Welcome the game starts at $correct out of $total ";
$elbat = "";
$n1 = int(rand(100)) - 50;
$n2 = int(rand(100)) - 50;
# $question is an output variable not part of the state
$question = "What is $n1 + $n2? Answer: ";
}
$state->{'message'} = $message; # state variables
$state->{'questions'} = $questions;
$state->{'n2'} = $n2;
$state->{'n1'} = $n1;
$state->{'elbat'} = $elbat;
$state->{'correct'} = $correct;
$state->{'total'} = $total;
&save_state($state, $session_id);
# save the state in the database ------------------------------save_state---
sub save_state {
my ($state, $id) = @_;
my $sth = $DBH->prepare(<errstr;
UPDATE $DB_TABLE
SET message=?,elbat=?,n1=?,n2=?,correct=?,total=?
WHERE session_id='$id'
END
$sth->execute(@{$state}{qw(message elbat n1 n2 correct total)})
|| die "execute: ", $DBH->errstr;
$sth->finish;
}
print qq{};
print "
";
print $q->end_html;
# get the state from the database ------------------------------get_state---
sub get_state {
my $id = shift;
my $query =
"SELECT * FROM $DB_TABLE WHERE session_id = '$id'";
my $sth = $DBH->prepare($query) || die "Prepare: ", $DBH->errstr;
$sth->execute || die "Execute: ", $sth->errstr;
my $state = $sth->fetchrow_hashref;
$sth->finish;
return $state;
}
# retrieve the session ID from the path info. if it's -----get_session_id---
# not already there, add it to the path info (more or less) with a redirect
sub get_session_id {
my (@result);
&expire_old_sessions();
my ($id) = $q->path_info() =~ m:^/([a-h0-9]{$ID_LENGTH}):o;
return @result if $id and @result = &check_id($id);
# if we get here, there's not already an ID in the path info
my $session_id = &generate_id();
die "Couldn't make a new session id" unless $session_id;
print $q->redirect($q->script_name() . "/$session_id");
exit 0;
}
# find a new unique ID and insert it into the database -------generate_id---
sub generate_id {
# create a new session id
my $tries = 0;
my $id = &hash($SECRET . rand());
while ($tries++ < $MAX_TRIES) {
last if $DBH->do("INSERT INTO $DB_TABLE (session_id) VALUES ('$id')");
$id = &hash($id);
}
return undef if $tries >= $MAX_TRIES; # we failed
return $id;
}
# check to see that an old ID is valid --------------------------check_id---
sub check_id {
my $id = shift;
return ($id, '')
if $DBH->do("SELECT 1 FROM $DB_TABLE WHERE session_id = '$id'") > 0;
return ($id, 'The record of your game may have expired. Restarting.')
if $DBH->do("INSERT INTO $DB_TABLE (session_id) VALUES ('$id')");
return ();
}
# generate a hash value ---------------------------------------------hash---
sub hash {
my $value = shift;
return substr(md5_hex($value), 0, $ID_LENGTH);
}
sub expire_old_sessions { # --------------------------expire_old_sessions---
$DBH->do(< $EXPIRE
END
}