#!/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{
$message

$question

Press to move on.

}; print ""; @lines = split(/:/, $elbat); foreach $line (@lines) { ($a, $b) = split(/ \(/, $line); print "
$a ($b"; } 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 }