$msg
END;
}
// Open a form which we will process. Creates the form tag with the
// correct action.
function start_form() {
$me = $_SERVER['SCRIPT_NAME'];
return "
LTAB;
exit;
}
// Tell if it's name-ish (letter then and alpha, _ is a flat letter).
function goodname($n) {
return preg_match('/^[A-Za-z_][A-Za-z_0-9]*$/', $n);
}
// Tell if it's numeric.
function numeric($n) {
return preg_match('/^[0-9]*$/', $n);
}
// Tell if it contains no quotes or backtic and all printable.
function quoteless($n) {
return preg_match('/^[ !#-&(-\[\]-~]*$/', $n);
}
// Get the query variables for creating a new user. Checks them and
// returns a non-empty string error message if there is a problem.
function getnewudata()
{
$vars = array('newacct' => 'Account name',
'passwd' => 'Password',
'newname' => 'Human name',
'newdust' => 'New dustbunny balance',
'newjelly' => 'New jellybean balance');
foreach($vars as $v => $d) {
global ${$v};
${$v} = $_REQUEST[$v];
if(!quoteless($v)) return "$d contains quotes.";
}
if(!goodname($newacct)) return "Bad account name.";
if(!numeric($newdust) || !numeric($newjelly))
return "Balances must be numeric and non-negative.";
return '';
}
// Return a string which is a correct link back to ourselves, with the
// operation set to $func.
function gen_link($func, $content, $sel='') {
$me = $_SERVER['SCRIPT_NAME'];
if($sel) $sel = "&sel=$sel";
return "$content";
}
// This class represents the set of records which is the main part of the
// displayed page. The class keeps track of the current location in the
// database, scroll direction, number of records shown, and other such
// things. It will generate the HTML table when needed.
class recd_viewer {
// Table display settings and flags. Mostly pass info from the
// database search to the HTML display of the results.
private $up_arrow = 1; // Display the up arrow.
private $down_arrow = 1; // Display the down arrow.
private $do_plus = 1; // Display the plus sign.
private $do_minus = 1; // Display minus sign.
private $no_records = 0; // No records in search.
private $num_recs; // Number of records to display.
private $db_error = 0; // Database query error.
// Other flags are meaningless.
private $search_handle; // Refers to the search result.
private $rev = 0; // Reverse row fetch numbers.
private $top_line_rec_no = 0; // Index of recd on top line.
private $handle; // Database handle.
private $dir; // Display direction, up or down.
private $message = ''; // Error message.
// Information kept in the session.
private $firstdisp; // First key displayed last time.
private $lastdisp; // Last key displayed last time.
private $dispwidth; // Number of items to display.
private $restriction; // Display search restriction.
// Construct. Load information from the session, or defaults.
function __construct($handle, $dir)
{
$this->handle = $handle;
$this->dir = $dir;
// If this is not a new session, set defaults.
if(array_key_exists('firstdisp', $_SESSION)) {
// Get stuff from the session.
foreach(array('firstdisp', 'lastdisp',
'dispwidth', 'restriction') as $v)
$this->{$v} = $_SESSION[$v];
} else {
// Defaults.
$this->restriction = $this->firstdisp =
$this->lastdisp = '';
$this->dispwidth = 5;
}
//echo "FROOB:";
//foreach(array('firstdisp', 'lastdisp',
// 'dispwidth', 'restriction') as $v)
// echo " $v = [" . $this->{$v} . "]";
}
// Put the session things back. This should be done in the destructor,
// but that doesn't work. Don't know if this is a bug or a
// feature camouflaged as a bug.
function over()
{
foreach(array('firstdisp', 'lastdisp', 'dispwidth',
'restriction') as $v)
$_SESSION[$v] = $this->{$v};
}
// Get record $i from the top of the table.
private function get_rec($i)
{
if($this->rev) $i = $this->top_line_rec_no - $i;
return pg_fetch_row($this->search_handle, $i);
}
// Find the restriction.
function getres()
{
return $this->restriction;
}
// Modify the restriction.
function setres($r)
{
$this->restriction = $r;
}
// Get the ilike restriction. Adds the indication conjunction, but
// just returns empty string if there is no restriction
private function get_restr($conn)
{
if(!$this->restriction) return '';
return "$conn humanname ILIKE '%" . $this->restriction . "%'";
}
// Get the error message.
function message()
{
return $this->message;
}
// Set the search from the $firstdisp in $dir
private function set_search()
{
if($this->dir == 'up')
{
// Settings for upwards search.
$sortdir = 'desc';
$selcomp = '<=';
$this->rev = 1;
}
else
{
$sortdir = 'asc';
$selcomp = '>=';
$this->rev = 0;
}
// Find the first (last) account name.
$dat = @pg_exec($this->handle,
"SELECT acctname FROM accounts " .
$this->get_restr('WHERE') .
" ORDER BY acctname $sortdir LIMIT 1");
if(!$dat) {
$this->db_error = 1;
return;
}
if(pg_numrows($dat) == 0) {
$this->no_records = 1;
$this->up_arrow = $this->down_arrow =
$this->do_plus = $this->do_minus = 0;
return;
}
// Save the first (last) key.
list($extreme_key) = pg_fetch_row($dat, 0);
// If there is no position specified, set it to the
// top of the file.
if(!$this->firstdisp) $this->firstdisp = $extreme_key;
// Now, do a search for the records we will display,
// plus one more.
$dispp = $this->dispwidth + 1;
// Do the select first time.
$dat = @pg_exec($this->handle,
"SELECT * FROM accounts " .
"WHERE acctname $selcomp '" . $this->firstdisp . "' " .
$this->get_restr('AND') . ' ' .
"ORDER BY acctname $sortdir " .
"LIMIT $dispp");
if(!$dat) {
$this->db_error = 1;
return;
}
// Set the flags based on the query results.
// Number of records and flags based thereon.
$numrec = pg_numrows($dat);
$this->no_records = ($numrec == 0);
$this->num_recs = min($numrec, $this->dispwidth);
// See where to top of the display is, and get the key for
// that record.
if($this->dir == 'up')
$this->top_line_rec_no = $this->num_recs - 1;
else
$this->top_line_rec_no = 0;
// Check for the up and down arrows.
list($search_start) = pg_fetch_row($dat, 0);
if($this->dir == 'up')
{
// If we started at the bottom, can't go down.
$this->down_arrow = ($extreme_key != $search_start);
// If we couldn't fill quota, don't go up.
$this->up_arrow = ($numrec >= $dispp);
}
else
{
// If we started at top, can't go up.
$this->up_arrow = ($extreme_key != $search_start);
// If we couldn't fill quota, don't go up.
$this->down_arrow = ($numrec >= $dispp);
}
$this->search_handle = $dat;
list($this->firstdisp) = $this->get_rec(0);
list($this->lastdisp) = $this->get_rec($this->num_recs - 1);
}
// Clear position.
function clearpos()
{
$this->firstdisp = '';
}
// Scroll down
function down()
{
$this->firstdisp = $this->lastdisp;
}
// Adjust the display size.
function adjwidth($n)
{
$this->dispwidth += $n;
if($this->dispwidth < 1) $this->dispwidth = 1;
}
// Generate the restriction message, if any.
private function gen_restr()
{
if($this->restriction) {
echo "
", gen_link('clrres', '[Unrestricted]'),
' Display ',
'restricted to names like ',
htmlspecialchars($this->restriction),
"
\n";
}
}
// Find an indicate account and move the displayed location there.
function find($srcacct) {
// Find the name.
$dat = @pg_exec($this->handle,
"SELECT acctname FROM accounts " .
"WHERE acctname <= '$srcacct' " .
$this->get_restr('AND') .
" ORDER BY acctname DESC LIMIT 1");
// See what happened
if(!$dat)
// Didn't work.
$this->message = "Search for account $srcacct ".
"failed: ".
htmlspecialchars(pg_errormessage($this->handle));
else {
// Did we find anything?
$n = pg_numrows($dat);
if($n == 0) {
// We didn't
$this->message = "Account $srcacct not found.";
$this->firstdisp = '';
}
else {
// Move to the record, or near it.
list($k1) = pg_fetch_row($dat, 0);
if($k1 != $srcacct)
$this->message = "Account $srcacct ".
"not found.";
$this->firstdisp = $k1;
}
}
}
// Plus or minus link for the table.
private function pml($sign)
{
return gen_link($sign, "');
}
// Show a table of the indicated display search.
function gen() {
// Perform the serch.
// Set display in the indicated direction. If that doesn't
// fill the display, we'll try it again other way.
$this->set_search();
if($this->num_recs < $this->dispwidth)
{
if($this->dir == 'up')
$this->dir = 'down';
else {
$this->firstdisp = $this->lastdisp;
$this->dir = 'up';
}
$this->set_search();
}
$this->do_minus = ($this->num_recs > 3);
$this->do_plus = ($this->num_recs >= $this->dispwidth);
if($this->db_error)
$this->message = "Query error: " .
htmlspecialchars
(pg_errormessage($this->handle));
else if($this->no_records) {
if($this->restriction)
$this->message = "No accounts contain " .
htmlspecialchars($this->restriction) .
'.';
else
$this->message = "No accounts.";
}
echo "
SFORM;
}
// Generate the add user form. If Update is true, it attempts to load that
// record and generates a replace form rather than an insert.
function add_usr_form($handle, $update)
{
if($update)
{
// Create value text for each thingie.
$dat = pg_exec($handle,
"SELECT * FROM accounts WHERE acctname = '$update'");
list($acct, $pwd, $hname, $dust, $jelly) =
@pg_fetch_row($dat, 0);
if($acct != $update) $update = '';
$button = 'UPDATE';
foreach(array('pwd', 'hname', 'dust', 'jelly') as $n)
${$n} = " value = \"${$n}\"";
$acct = htmlspecialchars($acct) . "";
$cancel = " ";
$title = '