$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 "
\n"; } session_start(); // Take care of a logout first. if($_REQUEST['oper'] == 'logout') $_SESSION['loggedin'] = 0; // Check for a login request, and verify the password. $badlog = 0; if($_REQUEST['LOGIN']) { if($_REQUEST['apass'] == $admin_pwd) $_SESSION['loggedin'] = 1; else { $_SESSION['loggedin'] = 0; $badlog = 1; } } // See that the user is logged in, and generate a login page otherwise. if(!$_SESSION['loggedin']) { if($badlog) frogstart('Password Incorrect', TRUE); else frogstart('Please Log In'); echo start_form(), <<
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 "\n"; // Headers. echo ""; foreach(array("Operations", "Account", "Passwd", "Name", "Dustbunnies", "Jellybeans") as $head) echo ""; echo "\n"; if($this->up_arrow) { echo ''; for($i = 12; $i--;) echo ''; echo '"; } // Data. $num = $this->num_recs; for($i = 0; $i<$num; ++$i) { $row = $this->get_rec($i); $ncol = count($row); echo ""; echo "", ''; for($j = 0; $j < $ncol; ++$j) { $align = $j >= 3 ? 'right' :'left'; echo "", ""; } if($i == 1) { echo ''; } if($i == $num - 1 && $this->down_arrow) echo ''; echo "\n"; } echo "
$head
', gen_link('up', ''), "
", gen_link('del', '[Delete]', $row[0]), ' ', gen_link('upd', '[Update]', $row[0]), "", htmlspecialchars($row[$j]), "'; if($this->do_plus) echo $this->pml('plus'), ' '; if($this->do_minus) echo ' ', $this->pml('minus'); echo '', gen_link('down', ''), '
\n"; // Restriction message. $this->gen_restr(); } } // Generate the search form. function gen_search($restriction) { if($_REQUEST['srcacct']) $srcacct = 'value="' . $_REQUEST['srcacct'] . '"'; else $srcacct = ''; if($restriction) $resdef = "value=\"$restriction\""; else $resdef = ''; //$rset = gen_link('top', '[Top]'); //$rset = gen_link('top', ''); $rset = ''; echo start_form(), <<Search
Move To Top:$rset
Search For Account:
Show Only Names Containing:
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 = '

Update User
'; } if(!$update) { // Create empty/default values. $button = 'CREATE'; $acct = ""; $cancel = $pwd = $hname = ''; $dust = ' value = 0'; $jelly = ' value = 0'; $title = 'Add User
'; } echo start_form(), <<Account Name:$acct Human Name: Password: Dustbunnies: Jellybeans: $cancel ADDFORM; } // Open the database. $handle = @pg_connect("user=bennet password=XXXX " . "host=localhost dbname=bennet"); if(!$handle) { whap("DB Open Failed", "Sorry, database won't open. Probably gremlins."); exit; } // Get the operation and selected item. $oper = $_REQUEST['oper']; $sel = $_REQUEST['sel']; if($sel && !goodname($sel)) $sel = ''; // Create the search object. $show = new recd_viewer($handle, $oper == 'up' ? 'up' : 'down'); $message = ''; // Move down. if($oper == 'down') { $show->down(); } // Change number of records displayed. if($oper == 'plus') { $show->adjwidth(1); } // Change number of records displayed. if($oper == 'minus') { $show->adjwidth(-1); } // Top of list. if($oper == 'Top') { $show->clearpos(); } // Look for a delete operation. if($oper == 'del' && $sel) { $dat = pg_exec($handle, "DELETE FROM accounts WHERE acctname = '$sel'"); if(!$dat) $message = "Delete of account $sel failed."; } // Search for a specific name. if($oper == 'FIND') { $show->find($_REQUEST['srcacct']); } // Apply the filter. if($oper == 'APPLY') { $show->setres($_REQUEST['newname']); } if($oper == 'clrres') { $show->setres(''); } // Look for an add operation. if($oper == 'CREATE') { $message = getnewudata(); if(!$message) { // Insert the new account, then search it in the // display so it shows the new account. $res = @pg_exec($handle, "INSERT INTO accounts VALUES " . "('$newacct', '$passwd', '$newname', ". "$newdust, $newjelly)"); if(!$res) $message = "Add of account $sel failed."; else $show->find($newacct); } } // Look for an add operation. if($oper == 'UPDATE') { $message = getnewudata(); if(!$message) { // Update the account, then search it in the // display so it shows the new account. $res = @pg_exec($handle, "UPDATE accounts SET " . "password = '$passwd', ". "humanname = '$newname', " . "dust = '$newdust', jelly = '$newjelly' " . "WHERE acctname = '$newacct'"); if(!$res) $message = "Update of account $sel failed."; else $show->find($newacct); } } if(!$message) $message = $show->message(); if($message) frogstart($message, TRUE); else frogstart('Administrative Console'); echo '

[', gen_link('logout', 'logout'), ']
'; $show->gen(); if($oper != 'upd') gen_search(); add_usr_form($handle, $oper == 'upd' && $sel ? $sel : ''); $show->over(); ?>