#!/usr/bin/perl # Display all players rated or interested in a game. # Bruno Wolff III # Last revised July 12, 2014 # While areaid and gameid can't currently contain any uri specials, I want # assume they can here, so I if tweak the database schema, things don't # break here. The problem characters that might eventually be allowed # are '#', '/' and '?'. Also note that '/'s don't really work at all # since the server has a special check for / (encoded as %2F) in a component. # Use unbuffered output as there are some long delays while doing queries select(STDOUT); $| = 1; $" = ''; use Pg; $time = time; # Use this to clean stuff extracted from the database for html output sub clean(@) { local $str = "@_"; $str =~ s/&/&/g; $str =~ s//>/g; $str =~ s/"/"/g; return $str; } # Use this to convert REQUEST_URI to unescaped string sub urldecode(@) { local $str = "@_"; $str =~ s/\%[0-9a-fA-F]{2}/$urldhash{$&}/eg; return $str; } # Use this to make sure urls don't contain url specials sub urlencode(@) { local $str = "@_"; $str =~ s/[^-_A-Za-z0-9.$+!*'(),]/$urlehash{$&}/eg; return $str; } # One time build of data used by url decode sub urlinit() { my $c; my $f; my $i; for ($i=0; $i<=255; $i++) { $c = chr($i); $f = sprintf('%%%.2X', $i); $urldhash{$f} = $c; $urldhash{lc($f)} = $c; $urlehash{$c} = $f; } } urlinit; # +s in the query part are really spaces $uri = $ENV{REQUEST_URI}; if ($uri =~ m/^([^?]*\?)(.*)$/) { $uri = $1; $temp = $2; $temp =~ s/\+/ /g; $uri .= $temp; } $uri = urldecode($uri); if ($ENV{REQUEST_METHOD} ne '' && $ENV{REQUEST_METHOD} ne 'GET' && $ENV{REQUEST_METHOD} ne 'HEAD') { $meth = clean($ENV{REQUEST_METHOD}); $uri = clean($ENV{REQUEST_URI}); print <<"EOF"; content-type: text/html; charset=UTF-8 status: 501 Method not implemented 501 Method Not Implemented

Method Not Implemented

Method '$meth' not implented for '$uri'. EOF exit; } $ENV{SCRIPT_NAME} =~ s%\?.*$%%; $ENV{SCRIPT_NAME} =~ s%[^/]*$%%; $script = $ENV{SCRIPT_NAME}; $script = urldecode($script); # The length check is to protect against buffer overrun attacks. if (length($uri) > 1000 || $uri !~ m/^\Q$script\E(G|T|W|A)_([\040-\176]*)\.html$/) { $uri = clean($ENV{REQUEST_URI}); print <<"EOF"; content-type: text/html; charset=UTF-8 status: 404 File Not Found 404 File Not Found

404 File Not Found

'$uri' not found. EOF exit; } $type = $1; $index = $2; if ($type eq 'T') { $index =~ s/[^-_A-Za-z0-9.]+/_/g; $index =~ s/^_+//; $index =~ s/_+$//; } elsif ($type eq 'A' || $type eq 'G') { $index = uc($index); } # Check if encoding is safe, otherwise browsers might mess up relative links $check = $ENV{REQUEST_URI}; for ($i=0; $i 301 moved permanently

301 moved permanently

Improperly escaped URL. Use $cloc instead. EOF exit; } # Quotes and backslashes need to be quoted before using in a pg string constant $qindex = $index; $qindex =~ s/\\|'/\\$&/g; # First try to connect $conn = Pg::connectdb('dbname=area'); if ($conn->status != PGRES_CONNECTION_OK) { $uri = clean($ENV{REQUEST_URI}); print <<"EOF"; content-type: text/html; charset=UTF-8 status: 404 File Not Found 404 File Not Found

404 File Not Found

'$uri' not found. Unable to connect to the database. EOF exit; } $result = $conn->exec("begin"); if ($result->resultStatus != PGRES_COMMAND_OK) { $uri = clean($ENV{REQUEST_URI}); print <<"EOF"; content-type: text/html; charset=UTF-8 status: 404 File Not Found 404 File Not Found

404 File Not Found

'$uri' not found. Unable to start a transaction. EOF exit; } $result = $conn->exec("set transaction isolation level serializable"); if ($result->resultStatus != PGRES_COMMAND_OK) { $uri = clean($ENV{REQUEST_URI}); print <<"EOF"; content-type: text/html; charset=UTF-8 status: 404 File Not Found 404 File Not Found

404 File Not Found

'$uri' not found. Unable to serialize transaction. EOF exit; } if ($type eq 'G' || $type eq 'A') { $result = $conn->exec("select gameid, title, to_char(touched, 'YYYY-MM-DD'), sol from games where gameid = '$qindex'"); } elsif ($type eq 'T') { $qindex = $index; $qindex =~ s/_/[^A-Za-z0-9]+/g; $qindex =~ s/\\|'/\\$&/g; $result = $conn->exec("select gameid, title, to_char(touched, 'YYYY-MM-DD'), sol from games where title ~* '^[^A-Za-z0-9]*${qindex}[^A-Za-z0-9]*\$'"); } elsif ($type eq 'W') { $result = $conn->exec("select games.gameid, games.title, to_char(games.touched, 'YYYY-MM-DD'), sol from games, wbcgames where wbcgames.gameid = games.gameid and lower(wbcgames.code) = lower('$qindex')"); } else { $uri = clean($ENV{REQUEST_URI}); $type = clean($type); print <<"EOF"; content-type: text/html; charset=UTF-8 status: 404 File Not Found 404 File Not Found

404 File Not Found

'$uri' not found. Unknown index type '$type'. EOF exit; } if ($result->resultStatus != PGRES_TUPLES_OK) { $uri = clean($ENV{REQUEST_URI}); print <<"EOF"; content-type: text/html; charset=UTF-8 status: 404 File Not Found 404 File Not Found

404 File Not Found

'$uri' not found. Unable to access the game table. EOF exit; } if ($result->ntuples <= 0) { $uri = clean($ENV{REQUEST_URI}); print <<"EOF"; content-type: text/html; charset=UTF-8 status: 404 File Not Found 404 File Not Found

404 File Not Found

'$uri' not found. No such game. EOF exit; } elsif ($result->ntuples > 1) { print << "EOF"; content-type: text/html; charset=UTF-8 Select a Specific Game

Select a Specific Game

The following games matched your initial selection criteria. EOF exit; } if (!(@row = $result->fetchrow)) { print <<"EOF"; content-type: text/html; charset=UTF-8 status: 404 File Not Found 404 File Not Found

404 File Not Found

'$uri' not found. No such game. EOF exit; } $gameid = $row[0]; $gameid = clean($row[0]); $lgameid = clean(urlencode($row[0])); $qgameid = $row[0]; $qgameid =~ s/\\|'/\\$&/g; $title = clean($row[1]); $gdate = clean($row[2]); $sol = clean($row[3]); print << "EOF"; content-type: text/html; charset=UTF-8 AREA Ratings for $title

AREA Ratings for $title

EOF if ($type eq 'A') { print "Switch to rank ordering.\n"; } else { print "Switch to alphabetical ordering.\n"; } $result = $conn->exec("select rtypes.rtype, descr from rtypes, games where gameid = '$qgameid' and rtypes.rtype = games.rtype"); # Only try to print one row as that is all there should be. if ($result->resultStatus == PGRES_TUPLES_OK && $result->ntuples > 0) { @row = $result->fetchrow; if ($row[0] !~ m/^\s*$/ && $row[1] !~ m/^\s*$/) { $rtype = clean(urlencode($row[0])); $descr = clean($row[1]); print "

\nThis game is rated as a $descr.\n"; } } if ($sol eq 't') { print "

It is possible for the game itself to win.\n"; } $email = 'area-game-' . lc($gameid) . '-' . $time . '@wolff.to'; print "

\nTo report results for this game you can send email to \n"; print "$email.\n"; print "(Note as a messure to reduce spam, this address will only work for 8 hours.)\n"; print "

Last updated $gdate.\n"; $result = $conn->exec("select pubname, puburl from gamepubs, publishers where gameid = '$qgameid' and publishers.pubid = gamepubs.pubid"); if ($result->resultStatus == PGRES_TUPLES_OK && $result->ntuples > 0) { $s = ''; $s = 's' if $result->ntuples > 1; print << "EOF";

Publisher$s

\n"; } $result = $conn->exec("select url, comment from gameurls where gameid = '$qgameid' and expires >= 'now' union select url, 'WBC Event: (' || wbc.code || ') ' || coalesce(event, '') from wbc, wbcgames where wbc.code = wbcgames.code and gameid = '$qgameid' and url is not null"); if ($result->resultStatus == PGRES_TUPLES_OK && $result->ntuples > 0) { $s = ''; $s = 's' if $result->ntuples > 1; print << "EOF";

Related Web Page$s

\n"; } $result = $conn->exec("select avg(rate)::bigint, case when count(rate) > 1 then stddev(rate)::bigint end, count(rate), case when count(case when frq >= 10 then rate end) > 1 then stddev(case when frq >= 10 then rate end)::bigint end, count(case when frq >= 10 then rate end) from crate where gameid = '$qgameid' and frq > 0"); undef $row[0]; undef $row[1]; $row[2] = 0; undef $row[3]; $row[4] = 0; if ($result->resultStatus == PGRES_TUPLES_OK && $result->ntuples > 0) { @row = $result->fetchrow; } $row[0] = 'Undefined' unless defined $row[0]; $row[1] = 'Undefined' unless defined $row[1]; $row[3] = 'Undefined' unless defined $row[3]; $mean = clean($row[0]); $stddev = clean($row[1]); $count = clean($row[2]); $stddev10 = clean($row[3]); $count10 = clean($row[4]); print << "EOF";

Game skill information

Skill MetricValuePopulation count
Standard Deviation for 10+ rated games$stddev10$count10
Standard Deviation for any rated games$stddev$count
Mean for any rated games$mean$count
EOF if ($type ne 'A') { print "

Active Player List

\n"; $result = $conn->exec("select cname_web.areaid, lname, fmname, aname, coalesce(genlab, to_char(gen, 'FMRN')), rate, frq, opp, rmp, trn, rmc, to_char(crate.touched,'YYYY-MM-DD'), displayid from cname_web, crate where cname_web.areaid = crate.areaid and gameid = '$qgameid' and frq > 0 and crate.touched > ((timestamp 'epoch' + '$time second') + '4 year ago') order by rate desc, lower(lname), lower(coalesce((aname || ' ') || fmname, fmname, aname)), gen, genlab, cname_web.areaid"); if ($result->resultStatus != PGRES_TUPLES_OK) { print "Unable to retrieve active player information from database.\n"; } elsif ($result->ntuples <= 0) { print "There are no active players for this game.\n"; } else { print << "EOF"; EOF $rank = 0; $oldrate = -1; $oldrank = 0; while (@row = $result->fetchrow) { $rank++; $id = clean(urlencode($row[0])); $areaid = clean($row[0]); if (!defined($row[1])) { $name = '* Name Withheld *'; } else { $name = $row[1]; if (defined($row[4])) { $name .= ' ' . $row[4]; } if (defined($row[2])) { if (defined($row[3])) { $name .= ', ' . $row[3] . ' (' . $row[2] . ')'; } else { $name .= ', ' . $row[2]; } } elsif (defined($row[3])) { $name .= ', ' . $row[3]; } } $name = clean($name); $oldrank = $rank if $row[5] != $oldrate; $oldrate = $row[5]; print "
AREA IDNameRankRatingFrequencyOpponentsRemote playTournamentsRemote CompetitionsActive date
$row[12]$name$oldrank$row[5]$row[6]$row[7]$row[8]$row[9]$row[10]$row[11]\n"; } print "
\n"; } print "

Inactive Player List

\n"; $result = $conn->exec("select cname_web.areaid, lname, fmname, aname, coalesce(genlab, to_char(gen, 'FMRN')), rate, frq, opp, rmp, trn, rmc, to_char(crate.touched,'YYYY-MM-DD'), displayid from cname_web, crate where cname_web.areaid = crate.areaid and gameid = '$qgameid' and frq > 0 and crate.touched <= ((timestamp 'epoch' + '$time second') + '4 year ago') and crate.touched > ((timestamp 'epoch' + '$time second') + '12 year ago') order by rate desc, lower(lname), lower(coalesce((aname || ' ') || fmname, fmname, aname)), gen, genlab, cname_web.areaid"); if ($result->resultStatus != PGRES_TUPLES_OK) { print "Unable to retrieve inactive player information from database.\n"; } elsif ($result->ntuples <= 0) { print "There are no inactive players for this game.\n"; } else { print << "EOF"; EOF while (@row = $result->fetchrow) { $id = clean(urlencode($row[0])); $areaid = clean($row[0]); if (!defined($row[1])) { $name = '* Name Withheld *'; } else { $name = $row[1]; if (defined($row[4])) { $name .= ' ' . $row[4]; } if (defined($row[2])) { if (defined($row[3])) { $name .= ', ' . $row[3] . ' (' . $row[2] . ')'; } else { $name .= ', ' . $row[2]; } } elsif (defined($row[3])) { $name .= ', ' . $row[3]; } } $name = clean($name); print "
AREA IDNameRatingFrequencyOpponentsRemote playTournamentsRemote CompetitionsActive date
$row[12]$name$row[5]$row[6]$row[7]$row[8]$row[9]$row[10]$row[11]\n"; } print "
\n"; } print "

Other Interested Player List

\n"; $result = $conn->exec("select cname_web.areaid, lname, fmname, aname, coalesce(genlab, to_char(gen, 'FMRN')), to_char(crate.touched,'YYYY-MM-DD'), displayid from cname_web, crate where cname_web.areaid = crate.areaid and gameid = '$qgameid' and frq <= 0 and crate.touched > ((timestamp 'epoch' + '$time second') + '4 year ago') order by lower(lname), lower(coalesce((aname || ' ') || fmname, fmname, aname)), gen, genlab, cname_web.areaid"); if ($result->resultStatus != PGRES_TUPLES_OK) { print "Unable to retrieve other interested player information from database.\n"; } elsif ($result->ntuples <= 0) { print "There are no other interested players for this game.\n"; } else { print << "EOF"; EOF while (@row = $result->fetchrow) { $id = clean(urlencode($row[0])); $areaid = clean($row[0]); if (!defined($row[1])) { $name = '* Name Withheld *'; } else { $name = $row[1]; if (defined($row[4])) { $name .= ' ' . $row[4]; } if (defined($row[2])) { if (defined($row[3])) { $name .= ', ' . $row[3] . ' (' . $row[2] . ')'; } else { $name .= ', ' . $row[2]; } } elsif (defined($row[3])) { $name .= ', ' . $row[3]; } } $name = clean($name); print "
AREA IDNameDate
$row[6]$name$row[5]\n"; } print "
\n"; } } else { print "

Player List

\n"; $result = $conn->exec("select cname_web.areaid, lname, fmname, aname, coalesce(genlab, to_char(gen, 'FMRN')), rate, frq, opp, rmp, trn, rmc, to_char(crate.touched,'YYYY-MM-DD'), displayid from cname_web, crate where cname_web.areaid = crate.areaid and gameid = '$qgameid' and crate.touched > ((timestamp 'epoch' + '$time second') + '12 year ago') order by lower(lname), lower(coalesce((aname || ' ') || fmname, fmname, aname)), gen, genlab, cname_web.areaid"); if ($result->resultStatus != PGRES_TUPLES_OK) { print "Unable to retrieve player information from database.\n"; } elsif ($result->ntuples <= 0) { print "There are no players for this game.\n"; } else { print << "EOF"; EOF while (@row = $result->fetchrow) { $id = clean(urlencode($row[0])); $areaid = clean($row[0]); if (!defined($row[1])) { $name = '* Name Withheld *'; } else { $name = $row[1]; if (defined($row[4])) { $name .= ' ' . $row[4]; } if (defined($row[2])) { if (defined($row[3])) { $name .= ', ' . $row[3] . ' (' . $row[2] . ')'; } else { $name .= ', ' . $row[2]; } } elsif (defined($row[3])) { $name .= ', ' . $row[3]; } } $name = clean($name); if ($row[6] > 0) { print "
AREA IDNameRatingFrequencyOpponentsRemote playTournamentsRemote CompetitionsActive date
$row[12]$name$row[5]$row[6]$row[7]$row[8]$row[9]$row[10]$row[11]\n"; } else { print "
$row[12]$nameInterested$row[11]\n"; } } print "
\n"; } } print << "EOF"; EOF