<?php include '../util.inc'; // Names of the acounts. define('act_A', "jellybeans"); define('Act_A', "Jellybeans"); define('act_B', "dustbunnies"); define('Act_B', "Dustbunnies"); // Use a session. session_start(); // Tell if it's numeric. function numeric($n) { return preg_match('/^[0-9]+\s*$/', $n); } // 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 contains only characters we allow in a password. Mostly, // not quotes or backtic. function goodpw($n) { return preg_match('/^[ !#-&(-\[\]-~]*$/', $n); } // Open and return a connection to the database. function opendb() { return pg_connect("host=localhost dbname=bennet user=bennet password=XXXX"); } // Specialized start function function frogstart($msg, $red = FALSE) { if($red) $style = 'color: red; font-weight: bold;'; else $style = 'color: green; font-style: italic;'; start('Frogbreath'); echo <<<END <div style="margin-left: 20px; $style"> <img src="../imgs/frog.gif" style="vertical-align: middle;">$msg</div> END; } // Return a string which is a correct link back to ourselves, with the // operation set to $func. function gen_link($func, $content) { $me = $_SERVER['SCRIPT_NAME']; return "<a href=\"$me?oper=$func\">$content</a>"; } // Open a form which we will process. Creates the form tag with the // correct action, and generates a hidden field for the function. function gen_form($func) { $me = $_SERVER['SCRIPT_NAME']; return "<form action=\"$me\" method=\"POST\">\n" . "<input type=\"hidden\" name=\"oper\" value=\"$func\">\n"; } // Generate a form asking the amount and selecting an account. These are used // for adds, transfers, and purchases. First arg is the oper value for the // action URL, the rest fill in text: The label for the amount, text for each // radio button, text for the submit button. function amt_acct_form($act, $amtlab, $rad1, $rad2, $sub) { echo gen_form($act), <<<ENDTAB $amtlab: <input type="text" name="amt" size=6><br> <input type="radio" name="which" value="a" checked> $rad1<br> <input type="radio" name="which" value="b"> $rad2<br> <input type="submit" name="sub" value="$sub"> </form> ENDTAB; } // Update accounts. Used by the functions which change balances to check // and update. Args are the amount of transaction from the web page (must // be checked). $delta_a and $delta_b are factors. Each account is // incremented by the delta. function update_account($acct, $amt, $delta_a, $delta_b) { // Check the number. if(!numeric($amt)) return "Please Enter a Valid Number"; // Convert deltas to absolute. $delta_a = $delta_a * $amt; $delta_b = $delta_b * $amt; // Attempt to read the account entry. $dbm = opendb(); if(!$dbm) return "Database Update Failed"; $dat = pg_exec($dbm,"SELECT jelly, dust " . "FROM accounts WHERE acctname = '$acct'"); list($a, $b) = pg_fetch_row($dat, 0); // Check for sufficient funds. if($a + $delta_a < 0) { pg_close($dbm); return "Insufficient funds in ".act_A; } if($b + $delta_b < 0) { pg_close($dbm); return "Insufficient funds in ".act_B; } // Update $a += $delta_a; $b += $delta_b; pg_exec($dbm, "UPDATE accounts SET jelly = $a, dust = $b " . "WHERE acctname = '$acct'"); pg_close($dbm); return ''; } // Generate the start menu (the menu for logged out sessions). The title // can be sent, or defaults. function gen_start_menu($msg = 'Welcome to Frogbreath', $error = FALSE) { frogstart($msg, $error); echo gen_link('login', 'Log in'), " to Frogbreath<br>"; echo gen_link('create', 'Create'), " a Frogbreath account<br>"; global $oper; $oper = 'menu'; } // Generate the action menu (the menu for logged in sessions). Title will // default. function gen_do_menu($acct, $name, $msg = FALSE, $error = FALSE) { // Default message. Can't be the default 'cause it needs a variable. if(!$msg) $msg = "$name at Frogbreath"; frogstart($msg, $error); // Generate the links. echo gen_link('buy', 'Buy stuff'), " you can't see from Frogbreath<br>"; echo gen_link('add', 'Give'), " fake money to Frogbreath<br>"; echo gen_link('xfer', 'Transfer'), " fake money between accounts.<br>"; echo gen_link('query', 'Query'), " fake balances.<br>"; echo gen_link('logout', 'Log out'), " from Frogbreath<br>"; global $oper; $oper = 'menu'; } // Log out the user. Changes the session state. function do_logout($name) { // Change state and generate the login menu. $_SESSION['state'] = 'out'; gen_start_menu("$name has left Frogbreath"); } // Function for serious errors that we can't figure out. function gen_err($msg) { // Just in case. $_SESSION['state'] = 'out'; whap("Internal Error: $msg", "An internal error has occurred: $msg." . "This may indicate a problem with our server or your " . "network connection. You may want to try again later. " . "We regret any inconvenience this may cause.<p>" . "Thank you for visiting Frogbreath."); } // Output a table of the subaccount balances for the currently logged-on // account. Displays the header which defaults. If there is problem, // returns an error message. Otherwise, returns ''. function display_bal($acct, $msg = '', $error = FALSE) { $Act_A = Act_A; $Act_B = Act_B; // Attempt to read the balances. $dbm = opendb(); if(!$dbm) return 'Database open failed.'; $dat = pg_exec($dbm,"SELECT jelly, dust, humanname " . "FROM accounts WHERE acctname = '$acct'"); list($n, $m, $name) = pg_fetch_row($dat, 0); pg_close($dbm); // Display. if(!$msg) $msg = "Balances for $name"; frogstart($msg, $error); echo <<<DONE <p><table> <tr><td>$Act_A:</td><td>$n</td></tr> <tr><td>$Act_B:</td><td>$m</td></tr> </table><p> DONE; return ''; } // Generate the balance query page. function gen_query($acct, $name) { $str = display_bal($acct); if($str != '') gen_do_menu($acct, $name, $str, TRUE); } // Produce the add additional funds menu. function gen_add($acct, $name, $head, $error = FALSE) { // Generate the current balance table if(($m = display_bal($acct,$head)) != '') { gen_do_menu($acct, $name, $m, TRUE); return; } echo "<p>"; // Generate the transfer information form. amt_acct_form('doadd', 'Funds to Add', "Add to ".act_A, "Add to ".act_B, 'ADD'); } // Process additional funds response. function do_add($acct, $name) { $amt = $_REQUEST['amt']; $which = $_REQUEST['which']; // Compute the delta factors for update_account based on the // account selection from the form. $da = $db = 0; if($which == 'a') $da = 1; else $db = 1; // Attempt to update the account, then go to the index, or back to // account add form in case of error. if(($msg = update_account($acct, $amt, $da, $db)) != '') gen_add($acct, $name, $msg); else gen_do_menu($acct, $name, "Added $amt to " . ${"act_$which"} . " for $name"); } // Generate transfer menu. function gen_xfer($acct, $name, $head, $error = FALSE) { // Display current balances. if(($m = display_bal($acct, $head, $error)) != '') { gen_do_menu($acct, $name, $m); return; } echo "<p>"; // Generate transfer form. amt_acct_form('doxfer', 'Funds to Transfer', "Transfer from " . act_A . " to " . act_B, "Transfer from " . act_B . " to " . act_A, 'TRANSFER'); } // Process funds transfer. function do_xfer($acct, $name) { $amt = $_REQUEST['amt']; // Based on radio button results, choose deltas for update_account, // and a success message, in case we have some. if($_REQUEST['which'] == 'a') { $da = -1; $db = 1; $succ = "Transferred $amt from " . act_A . " to " . act_B . " for $name"; } else { $da = 1; $db = -1; $succ = "Transferred $amt from " . act_B . " to " . act_A . " for $name"; } // Update the account, menu or back to form in case of error. if(($msg = update_account($acct, $amt, $da, $db)) != '') gen_xfer($acct, $name, $msg, TRUE); else gen_do_menu($acct, $name, $succ); } // Generate the purchase menu. function gen_buy($acct, $name, $head, $error = FALSE) { // Display current balances. if(($m = display_bal($acct, $head, $error)) != '') { gen_do_menu($acct, $name, $m); return; } echo "<p>"; // The "market" prices set at random. (The market is rather volatile) if($error) { $pri_pop = $_SESSION['pri_pop']; $pri_fame = $_SESSION['pri_fame']; $pri_sawdust = $_SESSION['pri_sawdust']; } else { list($u, $s) = explode(' ', microtime()); srand((100000*$u) ^ $s); $_SESSION['pri_pop'] = $pri_pop = rand(1, 20); $_SESSION['pri_fame'] = $pri_fame = rand(15, 30); $_SESSION['pri_sawdust'] = $pri_sawdust = rand(6, 15); } // Generate purchase form. $Act_A = Act_A; $Act_B = Act_B; echo gen_form('dobuy'), <<<ENDTAB Which would you like to purchase?<br> <input type="text" size="2" name="sel_pop" value="0" align="right">Popularity @ $pri_pop<br> <input type="text" size="2" name="sel_fame" value="0" align="right">Fame @ $pri_fame<br> <input type="text" size="2" name="sel_sawdust" value="0" align="right">Sawdust @ $pri_sawdust<p> From which account will you pay?<br> <input type="radio" name="which" value="a" checked> $Act_A<br> <input type="radio" name="which" value="b"> $Act_B<br> <input type="submit" name="sub" value="PURCHASE"> </form> ENDTAB; } // Process purchase. function do_buy($acct, $name) { // Prices from session. $pri_pop = $_SESSION['pri_pop']; $pri_fame = $_SESSION['pri_fame']; $pri_sawdust = $_SESSION['pri_sawdust']; // Purchase selections from form. $sel_pop = $_REQUEST['sel_pop']; $sel_fame = $_REQUEST['sel_fame']; $sel_sawdust = $_REQUEST['sel_sawdust']; // Check that input counts are numeric. if(!numeric($sel_pop) || !numeric($sel_fame) || !numeric($sel_sawdust)) { gen_buy($acct, $name, "Please give positive numeric quantities.", TRUE); return; } // Compute price. $price = $sel_pop*$pri_pop; $price += $sel_fame*$pri_fame; $price += $sel_sawdust*$pri_sawdust; // Set account to debit. $da = $db = 0; if($_REQUEST['which'] == 'a') $da = -1; else $db = -1; // Update account, show menu or return to form on an error. if(($msg = update_account($acct, $price, $da, $db)) != '') gen_buy($acct, $name, $msg, TRUE); else gen_do_menu($acct, $name, "$name spent $price from " . ${"act_$which"}); } // Generate the create account page. function gen_create() { frogstart('Create A Frogbreath Account'); // Generate the form. echo gen_form('docreate'); ?> <table> <tr><td>Your Name:</td> <td><input type="text" name="newname" size="40"></td></tr> <tr><td>Account Name:</td> <td><input type="text" name="newacct" size="20"></td></tr> <tr><td>Password:</td> <td><input type="password" name="passwd" size="20"></td></tr> </table> <input type="submit" name="sub" value="CREATE"> </form> <?php } // Process the account creation page. function do_create() { $newacct = $_REQUEST['newacct']; $passwd = $_REQUEST['passwd']; $newname = $_REQUEST['newname']; // Vet the input. if(!goodname($newacct)) { gen_start_menu('User name must be alphanumeric and start' . ' with a letter.', TRUE); return; } if(!goodpw($passwd) || !goodpw($newname)) { gen_start_menu('Names and passwords may not use quotes.', TRUE); return; } if(strlen($passwd) < 5) { gen_start_menu('Passwords should be at least five characters.', TRUE); return; } // Attempt to open the database. $dbm = opendb(); if(!$dbm) { gen_start_menu('Database open failed.', TRUE); return; } // Account already exists? $dat = pg_exec($dbm,"select acctname from accounts " . "where acctname = '$newacct'"); if($dat && @pg_fetch_row($dat, 0)) { pg_close($dbm); gen_start_menu("Account $newacct exists.", TRUE); return; } // Create the account record. (May fail if account is suddenly // created by someone else.) $res = pg_exec($dbm, "INSERT INTO accounts VALUES " . "('$newacct', '$passwd', '$newname', 0, 0)"); pg_close($dbm); if($res) { // Log the session into the new account. $_SESSION['state'] = "in"; $_SESSION['acct'] = $newacct; $_SESSION['name'] = $newname; gen_do_menu($newacct, $newname, "Account $newacct created"); } else // Failed. gen_start_menu("Account $newacct not created", TRUE); } // Generate the login form. function gen_login() { frogstart('Login to Frogbreath'); echo gen_form('dologin'); ?> <table> <tr><td>Account Name:</td> <td><input type="text" name="newacct" size="20"></td></tr> <tr><td>Password: <td><input type="password" name="passwd" size="20"></td></tr> </table> <input type="submit" name="sub" value="LOGIN"> </form> <?php } // Process the login form. function do_login() { // Relevant form data. $newacct = $_REQUEST['newacct']; $passwd = $_REQUEST['passwd']; if(!goodname($newacct) || !goodpw($passwd)) { gen_start_menu('Invalid credentials.', TRUE); return; } // Attempt to open the database. $dbm = opendb(); if(!$dbm) { gen_start_menu('Database open failed.', TRUE); return; } // Get the human name. $dat = pg_exec($dbm,"select password, humanname from accounts " . "where acctname = '$newacct'"); // Missing? if(!$dat || !@pg_fetch_row($dat, 0)) { pg_close($dbm); gen_start_menu("Account $newacct not found.", TRUE); return; } // Read account information. list($pw, $name) = @pg_fetch_row($dat, 0); pg_close($dbm); // Check the password, whine if bad, otherwise update the session. if($pw == $passwd) { // Log in the session. $_SESSION['state'] = "in"; $_SESSION['acct'] = $newacct; $_SESSION['name'] = $name; gen_do_menu($newacct, $name, "$name logged on."); } else // Nope. gen_start_menu("Bad password for $newacct.", TRUE); } // If there is no state value set, initialize to a logged-out state, with // a request for the menu. $state = $_SESSION['state']; if(!$state) $_SESSION['state'] = $state = 'out'; $oper = $_REQUEST['oper']; if(!$oper) $oper = 'menu'; // Take appropriate action based on the session state and the request from // the url or form post. if($state == 'in') { // Logged in. Process appropriate requests. $name = $_SESSION['name']; $acct = $_SESSION['acct']; switch($oper) { case 'menu': gen_do_menu($acct, $name); break; case 'logout': do_logout($name); break; case 'buy': gen_buy($acct, $name, "$name: Purchase from Frogbreath"); break; case 'dobuy': do_buy($acct, $name); break; case 'add': gen_add($acct, $name, "$name: Add funds to Frogbreath"); break; case 'doadd': do_add($acct, $name); break; case 'xfer': gen_xfer($acct, $name, "$name: Transfer funds at Frogbreath"); break; case 'doxfer': do_xfer($acct, $name); break; case 'query': gen_query($acct, $name); break; default: gen_err('Illegal Request'); } } else { // Operations for a logged out session. switch($oper) { case 'menu': gen_start_menu(); break; case 'create': gen_create(); break; case 'docreate': do_create(); break; case 'login': gen_login(); break; case 'dologin': do_login(); break; default: gen_err('Illegal Request'); } } // For every page generate the logo image, and, if it is not a menu // reguest, create a link to one. if($oper != 'menu') echo "<p> ", gen_link('menu', '<i><font color="green">Menu</font></i></p>'); ?> </body> </html>