4 // A Trubanc web client
6 require_once "../lib/weblib.php";
8 // Define $dbdir, $require_coupon, $ssl_domain
9 if (file_exists('settings.php')) require_once "settings.php";
10 if (!$template_file) $template_file = "template.php";
12 die_unless_client_properly_configured();
13 maybe_forward_to_ssl($ssl_domain);
15 require_once "../lib/fsdb.php";
16 require_once "../lib/ssl.php";
17 require_once "../lib/client.php";
18 require_once "../lib/timestamp.php";
19 require_once "../lib/perf.php";
22 if (get_magic_quotes_gpc()) return stripslashes($x);
27 return mq(@$_POST[$x]);
30 function mqrequest($x) {
31 return mq(@$_REQUEST[$x]);
35 return htmlspecialchars($x);
46 function appenddebug($x) {
51 // Add a string to the debug output.
52 // Does NOT add a newline.
53 // Use var_export($val, true) to dump arrays
54 function debugmsg($x) {
57 $client->debugmsg($x);
60 $cmd = @mq
($_REQUEST['cmd']);
62 $db = new fsdb($dbdir);
64 $client = new client($db, $ssl);
65 $timestamp = new timestamp();
66 $iphone = strstr($_SERVER['HTTP_USER_AGENT'], 'iPhone');
70 if (@$_COOKIE['debug']) {
71 $client->showprocess
= 'appenddebug';
73 $perf_idx = perf_start('The rest');
76 $default_menuitems = array('balance' => 'Balance',
77 'contacts' => 'Contacts',
80 //'admins' => 'Admin',
81 'logout' => 'Logout');
83 // Initialize (global) inputs to template.php
84 $title = "Trubanc Client";
89 $session = @$_COOKIE['session'];
91 $err = $client->login_with_sessionid($session);
93 setcookie('session', false);
94 $error = "Session login error: $err";
98 if (!$cmd) $cmd = 'balance';
103 $keephistory = $client->userpreference('keephistory');
104 // Default is to keep history
105 if (!$keephistory) $keephistory = 'keep';
106 $client->keephistory($keephistory == 'keep');
110 if (!$client->bankid
) {
111 if ($cmd && $cmd != 'logout' && $cmd != 'login' & $cmd != 'bank') {
115 } elseif ($cmd != 'login' && $cmd != 'register') $cmd = '';
117 if (!$cmd) draw_login();
119 elseif ($cmd == 'logout') do_logout();
120 elseif ($cmd == 'login') do_login();
121 elseif ($cmd == 'contact') do_contact();
122 elseif ($cmd == 'bank') do_bank();
123 elseif ($cmd == 'asset') do_asset();
124 elseif ($cmd == 'admin') do_admin();
125 elseif ($cmd == 'spend') do_spend();
126 elseif ($cmd == 'canceloutbox') do_canceloutbox();
127 elseif ($cmd == 'processinbox') do_processinbox();
128 elseif ($cmd == 'storagefees') do_storagefees();
129 elseif ($cmd == 'dohistory') do_history();
130 elseif ($cmd == 'togglehistory') do_togglehistory();
131 elseif ($cmd == 'toggleinstructions') do_toggleinstructions();
133 elseif ($cmd == 'register') draw_register();
134 elseif ($cmd == 'balance') draw_balance();
135 elseif ($cmd == 'rawbalance') draw_raw_balance();
136 elseif ($cmd == 'contacts') draw_contacts();
137 elseif ($cmd == 'banks') draw_banks();
138 elseif ($cmd == 'assets') draw_assets();
139 elseif ($cmd == 'admins') draw_admin();
140 elseif ($cmd == 'coupon') draw_coupon();
141 elseif ($cmd == 'history') draw_history();
142 elseif ($session) draw_balance();
146 // Use $title, $body, and $onload, $debug to fill the page template.
147 if ($debug) $debug = "<b>=== Debug log ===</b><br/><pre>$debug</pre>\n";
149 if ($client->showprocess
) {
150 perf_stop($perf_idx);
151 $client->debugmsg("<table><tr><td>\n");
152 $times = draw_times(null, '=== Client Timing ===');
153 $server_times = $client->server_times();
155 $client->debugmsg("</td><td>");
156 draw_times($server_times, '=== Server Timing ===');
157 $times = $client->accumulate_times($times, $server_times);
158 $client->debugmsg("</td><td>");
159 draw_times($times, '=== Totals ===');
161 $client->debugmsg("</tr></table>\n");
164 // Here's where the output happens
165 include $template_file;
168 function draw_times($times, $caption) {
171 $times = perf_times($times);
172 if (count($times) > 0) {
173 $client->debugmsg('<table border="1">
174 <caption><b>' . $caption . '</b></caption>
175 <tr><th>Function</th><th>Count</th><th>Time</th></tr>
177 foreach($times as $name => $stats) {
178 $cnt = $stats['cnt'];
179 $time = $stats['time'];
180 $client->debugmsg("<tr><td>$name</td><td>$cnt</td><td>$time</td></tr>\n");
182 $client->debugmsg("</table>\n");
187 function settitle($subtitle) {
190 $title = "$subtitle - Trubanc Client";
193 function menuitem($cmd, $text, $highlight) {
194 $res = "<a href=\"./?cmd=$cmd\">";
195 if ($cmd == $highlight) $res .= '<b>';
197 if ($cmd == $highlight) $res .= '</b>';
202 function setmenu($highlight=false, $menuitems=false) {
203 global $menu, $default_menuitems;
206 if (!$menuitems) $menuitems = $default_menuitems;
209 if ($highlight && $client->bankid
) {
210 foreach ($menuitems as $cmd => $text) {
211 if ($cmd != 'admins' ||
212 ($client->bankid
&& $client->id
== $client->bankid
)) {
213 if ($menu) $menu .= '  ';
214 $menu .= menuitem($cmd, $text, $highlight);
218 $menu .= menuitem('logout', 'Logout', false);
222 function do_logout() {
223 global $session, $client, $bankline, $error;
225 if ($session) $client->logout();
226 setcookie('session', false);
232 // Here from the login page when the user presses one of the buttons
233 function do_login() {
234 global $title, $body, $onload;
235 global $keysize, $require_coupon;
237 global $client, $ssl;
241 $passphrase = mqpost('passphrase');
242 $passphrase2 = mqpost('passphrase2');
243 $coupon = mqpost('coupon');
244 $name = mqpost('name');
245 $keysize = mqpost('keysize');
246 $login = mqpost('login');
247 $newacct = mqpost('newacct');
248 $showkey = mqpost('showkey');
251 $key = $client->getprivkey($passphrase);
252 if (!$key) $error = "No key for passphrase";
255 } elseif ($newacct) {
257 $privkey = mqpost('privkey');
259 $error = "Passphrase may not be blank";
260 } elseif (!$privkey && $passphrase != $passphrase2) {
261 $error = "Passphrase didn't match Verification";
264 // Support adding a passphrase to a private key without one
265 $pk = $ssl->load_private_key($privkey);
267 if ($passphrase != $passphrase2) {
268 $error = "Passphrase didn't match Verification";
272 openssl_pkey_export($pk, $privkey, $passphrase);
273 openssl_free_key($pk);
275 } else $privkey = $keysize;
278 $err = $client->parsecoupon($coupon, $bankid, $url, $coupon_number);
280 // See if "coupon" is just a URL, meaning the user
281 // already has an account at that bank.
282 $err2 = $client->verifybank($coupon, $bankid);
283 if ($err2) $error = "Invalid coupon: $err";
285 $error = $client->verifycoupon($coupon, $bankid, $url);
287 } elseif ($require_coupon && !privkey
) {
288 $error = "Bank coupon required for registration";
292 $error = $client->newuser($passphrase, $privkey);
293 if (!$error) $login = true;
299 $session = $client->login_new_session($passphrase);
300 if (is_string($session)) {
301 $error = "Login error: $session";
303 $session = $session[0];
304 if (!setcookie('session', $session)) {
305 $error = "You must enable cookies to use this client";
308 $error = $client->addbank($coupon, $name, true);
312 if ($client->bankid
) draw_balance();
323 // Here to change banks or add a new bank
330 $newbank = mqpost('newbank');
331 $selectbank = mqpost('selectbank');
336 $bankurl = trim(mqpost('bankurl'));
337 $name = mqpost('name');
338 $error = $client->addbank($bankurl, $name);
339 if (!$error) $client->userpreference('bankid', $client->bankid
);
340 } elseif ($selectbank) {
341 $bankid = mqpost('bank');
342 if (!$bankid) $error = "You must choose a bank";
343 else $client->userpreference('bankid', $bankid);
346 if ($error) draw_banks($bankurl, $name);
350 function do_contact() {
354 $addcontact = mqpost('addcontact');
355 $deletecontacts = mqpost('deletecontacts');
356 $chkcnt = mqpost('chkcnt');
360 $nickname = mqpost('nickname');
361 $notes = mqpost('notes');
363 for ($i=0; $i<$chkcnt; $i++
) {
364 $chki = mqpost("chk$i");
366 $id = mqpost("id$i");
372 if ($id) $err = $client->addcontact($id, $nickname, $notes);
373 else $error = "You must specify an ID, either explicitly or by checking an existing contact";
375 $error = "Can't add contact: $err";
376 draw_contacts($id, $nickname, $notes);
377 } else draw_contacts();
378 } elseif ($deletecontacts) {
379 for ($i=0; $i<$chkcnt; $i++
) {
380 $chki = mqpost("chk$i");
382 $id = mqpost("id$i");
383 $client->deletecontact($id);
387 } else draw_balance();
391 // Here to add a new asset
392 function do_asset() {
399 $newasset = mqpost('newasset');
400 $updatepercent = mqpost('updatepercent');
403 $scale = mqpost('scale');
404 $precision = mqpost('precision');
405 $assetname = mqpost('assetname');
406 $storage = mqpost('storage');
407 if (!((strlen($scale) > 0) && (strlen($precision) > 0) &&
408 (strlen($assetname) > 0))) {
409 $error = "Scale, Precision, and Asset name must all be specified";
410 } elseif (!(is_numeric($scale) && is_numeric($precision))) {
411 $error = "Scale and Precision must be numbers";
412 } elseif ($storage && !is_numeric($storage)) {
413 $error = "Storage fee must be a number";
415 $error = $client->addasset($scale, $precision, $assetname, $storage);
417 if ($error) draw_assets($scale, $precision, $assetname, $storage);
419 } elseif ($updatepercent) {
420 $percentcnt = mqpost('percentcnt');
421 for ($i=0; $i<$percentcnt; $i++
) {
422 $assetid = mqpost("assetid$i");
423 $opercent = mqpost("opercent$i");
424 $percent = mqpost("percent$i");
425 if (!($percent === $opercent)) { // Detect differences in trailing zeroes
426 $asset = $client->getasset($assetid);
427 if (is_string($asset)) {
428 $error = "Can't find assetid: $assetid";
430 $scale = $asset[$t->SCALE
];
431 $precision = $asset[$t->PRECISION
];
432 $assetname = $asset[$t->ASSETNAME
];
433 $error = $client->addasset($scale, $precision, $assetname, $percent);
439 } else draw_balance();
442 function do_admin() {
447 function do_spend() {
450 global $fraction_asset;
456 $amount = mqpost('amount');
457 $recipient = mqpost('recipient');
458 $mintcoupon = mqpost('mintcoupon');
459 $recipientid = mqpost('recipientid');
460 $allowunregistered = mqpost('allowunregistered');
461 $note = mqpost('note');
462 $nickname = mqpost('nickname');
463 $toacct = mqpost('toacct');
464 $tonewacct = mqpost('tonewacct');
469 $recipient = $recipientid;
470 if ($recipient && !$allowunregistered &&
471 $u->is_id($recipient) && !$client->get_id($recipient)) {
472 $error = 'Recipient ID not registered at bank';
476 if ($mintcoupon) $recipient = $t->COUPON
;
477 } elseif ($mintcoupon) $error = "To mint a coupon don't specify a recipient";
479 if (!($amount ||
($amount === '0'))) $error = 'Spend amount missing';
480 elseif ($id == $recipient ||
!$recipient) {
481 // Spend to yourself = transfer
484 if (!$acct2) $acct2 = $tonewacct;
485 elseif ($tonewacct) $error = 'Choose "Transfer to" from the selector or by typing, but not both';
486 if (!$acct2) $error = 'Recipient missing';
487 } elseif ($recipient != $t->COUPON
&& !$u->is_id($recipient)) {
488 $error = "Recipient ID malformed";
492 draw_balance($amount, $recipient, $note, $toacct, $tonewacct, $nickname);
494 // Add contact if nickname specified
496 $client->addcontact($recipient, $nickname);
499 // Find the spent asset
501 foreach ($_POST as $key => $value) {
502 $prefix = 'spentasset';
503 $prelen = strlen($prefix);
504 if (substr($key, 0, $prelen) == $prefix) {
505 $acctdotasset = substr($key, $prelen);
506 $acctdotasset = explode('|', $acctdotasset);
507 if (count($acctdotasset) != 2) {
508 $error = "Bug: don't understand spentasset";
509 draw_balance($amount, $recipient, $note, $toacct, $tonewacct, $nickname);
511 $acctidx = $acctdotasset[0];
512 $assetidx = $acctdotasset[1];
513 $acct = mqpost("acct$acctidx");
514 $assetid = mqpost("assetid$acctidx|$assetidx");
515 if (!$acct ||
!$assetid) {
516 $error = "Bug: blank acct or assetid";
517 draw_balance($amount, $recipient, $note, $toacct, $tonewacct, $nickname);
519 if ($acct2) $acct = array($acct, $acct2);
520 $error = $client->spend($recipient, $assetid, $amount, $acct, $note);
522 draw_balance($amount, $recipient, $note, $toacct, $tonewacct, $nickname);
523 } elseif ($mintcoupon) {
524 draw_coupon($client->lastspendtime
);
526 $fraction_asset = $assetid;
536 $error = "Bug: can't find acct/asset to spend";
537 draw_balance($amount, $recipient, $note, $toacct, $tonewacct, $nickname);
542 function do_canceloutbox() {
546 $cancelcount = mqpost('cancelcount');
547 for ($i=0; $i<$cancelcount; $i++
) {
548 if (mqpost("cancel$i")) {
549 $canceltime = mqpost("canceltime$i");
550 $error = $client->spendreject($canceltime, "Spend cancelled");
557 function do_processinbox() {
563 $spendcnt = mqpost('spendcnt');
564 $nonspendcnt = mqpost('nonspendcnt');
566 $directions = array();
567 for ($i=0; $i<$spendcnt; $i++
) {
568 $time = mqpost("spendtime$i");
569 $spend = mqpost("spend$i");
570 $note = mqpost("spendnote$i");
571 $acct = mqpost("acct$i");
572 if ($spend == 'accept' ||
$spend == 'reject') {
573 $dir = array($t->TIME
=> $time);
574 if ($note) $dir[$t->NOTE
] = $note;
575 $dir[$t->REQUEST
] = ($spend == 'accept') ?
$t->SPENDACCEPT
: $t->SPENDREJECT
;
576 if ($acct) $dir[$t->ACCT
] = $acct;
577 $directions[] = $dir;
579 $nickname = mqpost("spendnick$i");
580 $spendid = mqpost("spendid$i");
581 if ($nickname && $spendid) {
582 $client->addcontact($spendid, $nickname);
586 for ($i=0; $i<$nonspendcnt; $i++
) {
587 $time = mqpost("nonspendtime$i");
588 $process = mqpost("nonspend$i");
590 $dir = array($t->TIME
=> $time);
591 $directions[] = $dir;
593 $nickname = mqpost("nonspendnick$i");
594 $spendid = mqpost("nonspendid$i");
595 if ($nickname && $spendid) {
596 $client->addcontact($spendid, $nickname);
600 if (count($directions) > 0) {
601 $err = $client->processinbox($directions);
602 if ($err) $error = "error from processinbox: $err";
608 function do_storagefees() {
612 $error = $client->storagefees();
616 function do_history() {
617 $history = gethistory();
618 $history->do_history();
621 function do_togglehistory() {
622 global $client, $keephistory, $error;
624 $keephistory = ($keephistory == 'keep' ?
'forget' : 'keep');
625 $client->keephistory($keephistory == 'keep');
627 $client->userpreference('keephistory', $keephistory);
628 $error = ($keephistory == 'keep' ?
"History enabled" : "History disabled");
633 function hideinstructions($newvalue=false) {
636 $key = 'hideinstructions';
637 if ($newvalue === false) $newvalue = $client->userpreference($key);
638 else $client->userpreference($key, $newvalue);
642 function do_toggleinstructions() {
645 $page = mqrequest('page');
646 hideinstructions(hideinstructions() ?
'' : 'hide');
647 if ($page == 'history') draw_history();
651 function draw_login($key=false) {
652 global $title, $menu, $body, $onload;
655 $page = mqpost('page');
656 if ($page == 'register') return draw_register($key);
659 $onload = "document.forms[0].passphrase.focus()";
661 <form method="post" action="./" autocomplete="off">
662 <input type="hidden" name="cmd" value="login"/>
665 <td><b>Passphrase:</b></td>
666 <td><input type="password" name="passphrase" size="50"/>
667 <input type="submit" name="login" value="Login"/></td>
670 <td style="color: red">$error </td>
673 <a href="./?cmd=register">Register a new account</a>
679 function draw_register($key=false) {
681 global $title, $menu, $body, $onload;
682 global $keysize, $require_tokens;
687 settitle('Register');
689 $onload = "document.forms[0].passphrase.focus()";
691 if (!$keysize) $keysize = 3072;
692 $sel = ' selected="selected"';
693 $sel512 = ($keysize == 512) ?
$sel : '';
694 $sel1024 = ($keysize == 1024) ?
$sel : '';
695 $sel2048 = ($keysize == 2048) ?
$sel : '';
696 $sel3072 = ($keysize == 3072) ?
$sel : '';
697 $sel4096 = ($keysize == 4096) ?
$sel : '';
700 <form method="post" action="./" autocomplete="off">
701 <input type="hidden" name="cmd" value="login"/>
704 <td><b>Passphrase:</b></td>
705 <td><input type="password" name="passphrase" size="50"/>
706 <input type="submit" name="login" value="Login"/></td>
707 <input type="hidden" name="page" value="register"/>
710 <td style="color: red">$error </td>
712 <td><b>Verification:</b></td>
713 <td><input type="password" name="passphrase2" size="50"/>
715 <td><b>Coupon:</b></td>
716 <td><input type="text" name="coupon" size="64"/></td>
718 <td><b>Account Name<br/>(Optional):</b></td>
719 <td><input type="text" name="name" size="40"/></td>
721 <td><b>Key size:</b></td>
723 <select name="keysize">
724 <option value="512"$sel512>512</option>
725 <option value="1024"$sel1024>1024</option>
726 <option value="2048"$sel2048>2048</option>
727 <option value="3072"$sel3072>3072</option>
728 <option value="4096"$sel4096>4096</option>
730 <input type="submit" name="newacct" value="Create account"/>
731 <input type="submit" name="showkey" value="Show key"/></td>
735 To generate a new private key, leave the area below blank, enter a
736 passphrase, the passphrase again to verify, a bank coupon, an optional
737 account name, a key size, and click the "Create account" button. To
738 use an existing private key, paste the private key below, enter its
739 passphrase above, a bank coupon, an optional account name, and click
740 the "Create account" button. To show your encrypted private key,
741 enter its passphrase, and click the "Show key" button. Warning: if you
742 forget your passphrase, <b>nobody can recover it, ever</b>.
746 <td><textarea name="privkey" cols="64" rows="42">$key</textarea></td>
752 function bankline() {
756 $bankid = $client->bankid
;
760 $bank = $client->getbank($bankid);
763 $name = $bank[$t->NAME
];
764 $url = $bank[$t->URL
];
765 $bankline = "<b>Bank:</b> $name <a href=\"$url\">$url</a><br/>\n";
775 if ($client) $id = $client->id
;
777 $args = $client->get_id($id);
780 $name = $args[$t->NAME
];
781 if ($name) $res = "<b>Account name:</b> $name<br/>\n";
783 $res .= "<b>Your ID:</b> $id<br/>\n";
787 function setbank($reporterror=false) {
788 global $banks, $bank;
794 $banks = $client->getbanks();
796 $bankid = $client->userpreference('bankid');
798 $err = $client->setbank($bankid, false);
800 $err = "Can't set bank: $err";
801 $client->userpreference('bankid', '');
806 foreach ($banks as $bank) {
807 $bankid = $bank[$t->BANKID
];
808 $err = $client->setbank($bankid);
810 $err = "Can't set bank: $err";
814 $client->userpreference('bankid', $bankid);
819 $err = "No known banks. Please add one.";
823 if ($reporterror) $error = $err;
826 function namestr($nickname, $name, $id) {
829 if ($name != $nickname) $namestr = "$nickname ($name)";
830 else $namestr = $name;
831 } else $namestr = $nickname;
832 } elseif ($name) $namestr = "($name)";
833 else $namestr = "$id";
837 function contact_namestr($contact) {
842 $nickname = hsc(@$contact[$t->NICKNAME
]);
843 $name = hsc($contact[$t->NAME
]);
844 $recipid = hsc($contact[$t->ID
]);
845 return namestr($nickname, $name, $recipid);
848 function id_namestr($fromid, &$contact, $you=false) {
851 if ($fromid == 'coupon') return $fromid;
853 if ($you && $fromid == $client->id
) return $you;
855 $contact = $client->getcontact($fromid);
857 $namestr = contact_namestr($contact);
858 if ($namestr == $fromid) $namestr = "[unknown]";
859 $namestr = "<span title=\"$fromid\">$namestr</span>";
860 } else $namestr = hsc($fromid);
864 // Return the ready-for-html-output formatted date for a timestamp
865 function datestr($time) {
868 $unixtime = $timestamp->stripfract($time);
869 return hsc(date("j-M-y g:i:sa T", $unixtime));
872 function draw_balance($spend_amount=false, $recipient=false, $note=false,
873 $toacct=false, $tonewacct=false, $nickname=false) {
876 global $onload, $body;
877 global $iphone, $keephistory;
878 global $fraction_asset;
882 $bankid = $client->bankid();
883 $banks = $client->getbanks();
892 foreach ($banks as $bid => $b) {
893 if ($bid != $bankid) {
894 if ($client->userreq($bid) != -1) {
895 $bname = $b[$t->NAME
];
897 $bankopts .= "<option value=\"$bid\">$bname $burl</option>\n";
905 <form method="post" action="./" autocomplete="off">
906 <input type="hidden" name="cmd" value="bank">
908 <option value="">Choose a bank...</option>
911 <input type="submit" name="selectbank" value="Change Bank"/>
923 $contacts = $client->getcontacts();
924 $havecontacts = (count($contacts) > 0);
926 if (!$error && $client->bankid
) {
927 // Print inbox, if there is one
928 $inbox = $client->getinbox();
929 $outbox = $client->getoutbox();
930 $accts = $client->getaccts();
933 if (count($accts) > 1) {
935 foreach ($accts as $acct) {
937 $acctoptions .= <<<EOT
938 <option value="$acct">$acct</option>
944 // Try again, in case we just needed to sync.
945 // Maybe this should be hidden by getinbox()
946 if (is_string($inbox)) $inbox = $client->getinbox();
949 if (is_string($inbox)) {
950 $error = "Error getting inbox: $inbox";
953 elseif (count($inbox) == 0) $inboxcode .= "<b>=== Inbox empty ===</b><br/><br/>\n";
955 if ($acctoptions) $acctheader = "\n<th>To Acct</th>";
959 <caption><b>=== Inbox ===</b></caption>
963 <th colspan="2">Amount</th>
966 <th>Reply</th>$acctheader
972 <option value="accept">Accept</option>
973 <option value="reject">Reject</option>
974 <option value="ignore">Ignore</option>
978 if (is_string($outbox)) {
979 $error = "Error getting outbox: $outbox";
982 $nonspends = array();
984 $assets = $client->getassets();
985 foreach ($inbox as $itemkey => $item) {
987 $request = $item[$t->REQUEST
];
988 $fromid = $item[$t->ID
];
989 $time = $item[$t->TIME
];
990 $namestr = id_namestr($fromid, $contact);
992 if ($request != $t->SPEND
) {
993 $msgtime = $item[$t->MSGTIME
];
994 $outitem = $outbox[$msgtime];
995 // outbox entries are array($spend, $tranfee)
996 if ($outitem) $outitem = $outitem[0];
998 $item[$t->ASSETNAME
] = $outitem[$t->ASSETNAME
];
999 $item[$t->FORMATTEDAMOUNT
] = $outitem[$t->FORMATTEDAMOUNT
];
1000 $item['reply'] = $item[$t->NOTE
];
1001 $item[$t->NOTE
] = $outitem[$t->NOTE
];
1003 $nonspends[] = $item;
1006 $assetid = $item[$t->ASSET
];
1007 $assetname = hsc($item[$t->ASSETNAME
]);
1008 if (!@$assets[$assetid]) {
1009 $assetname .= ' <span style="color: red;"><i>(new)</i></span>';
1011 $amount = hsc($item[$t->FORMATTEDAMOUNT
]);
1012 $itemnote = hsc($item[$t->NOTE
]);
1013 if (!$itemnote) $itemnote = ' ';
1014 else $itemnote = str_replace("\n", "<br/>\n", $itemnote);
1015 $selname = "spend$spendcnt";
1016 $notename = "spendnote$spendcnt";
1017 $acctselname = "acct$spendcnt";
1018 if (!@$contact[$t->CONTACT
]) {
1021 <input type="hidden" name="spendid$spendcnt" value="$fromid"/>
1023 <input type="text" name="spendnick$spendcnt" size="10"/>
1027 <input type="hidden" name="spendtime$spendcnt" value="$time">
1032 <select name="$selname">
1040 <td><select name="$acctselname">
1045 $date = datestr($time);
1046 $inboxcode .= <<<EOT
1051 <td align="right" style="border-right-width: 0;">$amount</td>
1052 <td style="border-left-width: 0;">$assetname</td>
1055 <td><textarea name="$notename" cols="20" rows="2"></textarea></td>
1064 foreach ($nonspends as $item) {
1065 $request = $item[$t->REQUEST
];
1066 $fromid = $item[$t->ID
];
1067 $reqstr = ($request == $t->SPENDACCEPT
) ?
"Accept" : "Reject";
1068 $time = $item[$t->TIME
];
1069 $namestr = id_namestr($fromid, $contact);
1070 $assetname = hsc($item[$t->ASSETNAME
]);
1071 $amount = hsc($item[$t->FORMATTEDAMOUNT
]);
1072 $itemnote = hsc($item[$t->NOTE
]);
1073 if (!$itemnote) $itemnote = ' ';
1074 else $itemnote = $itemnote = str_replace("\n", "<br/>\n", $itemnote);
1075 $reply = hsc($item['reply']);
1076 if (!$reply) $reply = ' ';
1077 else $reply = str_replace("\n", "<br/>\n", $reply);
1078 $selname = "nonspend$nonspendcnt";
1079 if (!@$contact[$t->CONTACT
]) {
1082 <input type="hidden" name="nonspendid$nonspendcnt" value="$fromid"/>
1084 <input type="text" name="nonspendnick$nonspendcnt" size="10"/>
1088 <input type="hidden" name="nonspendtime$nonspendcnt" value="$time">
1092 <input type="checkbox" name="$selname" checked="checked">Remove</input>
1095 $date = datestr($time);
1097 if ($acctoptions) $acctcode = "\n<td> </td>";
1098 $inboxcode .= <<<EOT
1103 <td align="right" style="border-right-width: 0;">$amount</td>
1104 <td style="border-left-width: 0;">$assetname</td>
1107 <td>$reply</td>$acctcode
1115 <form method="post" action="./" autocomplete="off">
1116 <input type="hidden" name="cmd" value="processinbox"/>
1117 <input type="hidden" name="spendcnt" value="$spendcnt"/>
1118 <input type="hidden" name="nonspendcnt" value="$nonspendcnt"/>
1122 <input type="submit" name="submit" value="Process Inbox"/>
1128 // Index the spends in the inbox by MSGTIME
1129 $inboxspends = array();
1130 foreach ($inbox as $items) {
1132 $request = $item[$t->REQUEST
];
1133 $inboxspends[$item[$t->MSGTIME
]] = $item;
1136 // Prepare outbox display
1139 foreach ($outbox as $time => $items) {
1140 $timestr = hsc($time);
1141 $date = datestr($time);
1142 foreach ($items as $item) {
1143 $request = $item[$t->REQUEST
];
1144 if ($request == $t->SPEND
) {
1145 $recip = $item[$t->ID
];
1146 if (!$outboxcode) $outboxcode = <<<EOT
1148 <caption><b>=== Outbox ===</b></caption>
1152 <th colspan="2">Amount</th>
1157 $assetname = hsc($item[$t->ASSETNAME
]);
1158 $amount = hsc($item[$t->FORMATTEDAMOUNT
]);
1159 $not = hsc($item[$t->NOTE
]);
1160 if (!$not) $not = ' ';
1162 if ($recip == $t->COUPON
) {
1164 $recip = hsc($recip);
1165 $timearg = urlencode($time);
1167 <a href="./?cmd=coupon&time=$timearg">$recip</a>
1170 $namestr = id_namestr($recip, $contact);
1172 $cancelcode = ' ';
1173 if (!@$inboxspends[$time]) {
1174 $cancelcode = <<<EOT
1175 <input type="hidden" name="canceltime$cancelcount" value="$timestr"/>
1176 <input type="submit" name="cancel$cancelcount" value="$label"/>
1181 $outboxcode .= <<<EOT
1185 <td align="right" style="border-right-width: 0;">$amount</td>
1186 <td style="border-left-width: 0;">$assetname</td>
1188 <td>$cancelcode</td>
1195 $outboxcode .= "</table>\n";
1196 if ($cancelcount > 0) {
1197 $outboxcode = <<<EOT
1198 <form method="post" action="./" autocomplete="off">
1199 <input type="hidden" name="cmd" value="canceloutbox"/>
1200 <input type="hidden" name="cancelcount" value="$cancelcount"/>
1208 $balance = $client->getbalance();
1209 if (is_string($balance)) $error = $balance;
1210 elseif (count($balance) > 0) {
1211 $balcode = "<table border=\"1\">\n<caption><b>=== Balances ===</b></caption>
1214 foreach ($balance as $acct => $assets) {
1218 foreach ($assets as $asset => $data) {
1219 if ($data[$t->AMOUNT
] != 0) {
1221 $assetid = hsc($data[$t->ASSET
]);
1222 $assetname = hsc($data[$t->ASSETNAME
]);
1223 $formattedamount = hsc($data[$t->FORMATTEDAMOUNT
]);
1225 $newassetlist .= <<<EOT
1226 <input type="hidden" name="assetid$acctidx|$assetidx" value="$assetid"/>
1229 $submitcode = <<<EOT
1230 <input type="submit" name="spentasset$acctidx|$assetidx" value="Spend"/>
1234 $assetcode .= <<<EOT
1236 <td align="right"><span style="margin-right: 5px">$formattedamount</span></td>
1238 <td>$submitcode</td>
1247 $balcode .= "<tr><td colspan=\"3\"> </td></tr>\n";
1248 } else $firstacct = false;
1249 $balcode .= "<tr><th colspan=\"3\">- $acct -</th></tr>\n$assetcode";
1252 if ($newassetlist) {
1253 $assetlist .= <<<EOT
1254 <input type="hidden" name="acct$acctidx" value="$acct"/>
1260 $balcode .= "</table>\n</td></tr></table>\n";
1261 $enabled = ($keephistory == 'keep' ?
'enabled' : 'disabled');
1262 if ($fraction_asset && $_COOKIE['debug']) {
1263 $fraction = $client->getfraction($fraction_asset);
1264 $amt = $fraction[$t->AMOUNT
];
1265 $scale = $fraction[$t->SCALE
];
1266 $balcode .= "Fractional balance: $amt";
1267 if ($scale) $balcode .= " x 10<sup>-$scale</sup>";
1268 $balcode .= "<br/>\n";
1272 <a href="./?cmd=rawbalance">Show raw balance</a>
1274 <a href="./?cmd=history">Show history</a> ($enabled)
1284 $storagefeecode = '';
1286 $recipopts = '<select name="recipient">
1287 <option value="">Choose contact...</option>
1290 foreach ($contacts as $contact) {
1291 $namestr = contact_namestr($contact);
1292 $recipid = $contact[$t->ID
];
1293 if ($recipid != $client->id
) {
1295 if ($recipid == $recipient) {
1296 $selected = ' selected="selected"';
1299 $recipopts .= <<<EOT
1300 <option value="$recipid"$selected>$namestr</option>
1305 $recipopts .= "</select>\n";
1307 if ($recipient == $t->COUPON
) $selectmint = ' checked="checked"';
1309 if (!$found && $recipient != $t->COUPON
) $recipientid = $recipient;
1310 $openspend = '<form method="post" action="./" autocomplete="off">
1311 <input type="hidden" name="cmd" value="spend"/>
1315 if (count($accts) > 1) {
1317 foreach ($accts as $acct) {
1319 if ($acct == $toacct) $selcode = ' selected="selected"';
1321 $acctoptions .= <<<EOT
1322 <option value="$acct"$selcode>$acct</option>
1331 <td><select name="toacct">
1332 <option value="">Select or fill-in below...</option>
1337 <td><b> </b></td>
1342 $storagefeecode = '';
1343 $storagefees = $client->getstoragefee();
1344 if (is_string($storagefees)) {
1345 $error = $storagefees;
1346 $storagefees = array();
1348 $storagefeecode = '';
1349 if (count($storagefees) > 0) {
1350 $storagefeecode = <<<EOT
1351 <form method="post" action="./" autocomplete="off">
1352 <input type="hidden" name="cmd" value="storagefees"/>
1354 <caption><b>=== Storage Fees ===</b></caption>
1358 foreach ($storagefees as $assetid => $storagefee) {
1359 $formattedamount = $storagefee[$t->FORMATTEDAMOUNT
];
1360 $assetname = $storagefee[$t->ASSETNAME
];
1361 $time = $storagefee[$t->TIME
];
1362 $timestr = hsc($time);
1363 $date = datestr($time);
1364 $storagefeecode .= <<<EOT
1366 <td align="right"><span style="margin-right: 5px">$formattedamount</span></td>
1367 <td><span style="margin-right: 5px">$assetname</span></td>
1373 $storagefeecode .= <<<EOT
1376 <input type="submit" name="accept" value="Move to Inbox"/>
1383 if ($client->id
== $client->bankid
) {
1385 $disablemint = ' disabled="disabled"';
1391 <td><b>Spend amount:</b></td>
1392 <td><input type="text" name="amount" size="20" value="$spend_amount" style="text-align: right;"/>
1394 <td><b>Recipient:</b></td>
1396 <input type="checkbox" name="mintcoupon"$selectmint$disablemint>Mint coupon</input></td>
1398 <td><b>Note:</b></td>
1399 <td><textarea name="note" cols="40" rows="10">$note</textarea></td>
1401 <td><b>Recipient ID:</b></td>
1402 <td><input type="text" name="recipientid" size="40" value="$recipientid"/>
1403 <input type="checkbox" name="allowunregistered">Allow unregistered</input></td>
1405 <td><b>Nickname:</b></td>
1406 <td><input type="text" name="nickname" size="30" value="$nickname"/></td>
1408 <td><b>Transfer to:</b></td>$acctcode
1409 <td><input type="text" name="tonewacct" size="30" value="$tonewacct"/></td>
1413 $onload = "document.forms[0].amount.focus()";
1414 $closespend = "</form>\n";
1415 $historytext = ($keephistory == 'keep' ?
"Disable" : "Enable") . " history";
1416 $instructions = '<p><a href="./?cmd=togglehistory">' .
1417 $historytext . "</a>\n";
1418 if (hideinstructions()) {
1419 $instructions .= '<br>
1420 <a href="./?cmd=toggleinstructions">Show Instructions</a>
1424 $instructions .= <<<EOT
1427 To make a spend, fill in the "Spend amount", choose a "Recipient" or
1428 enter a "Recipient ID, enter (optionally) a "Note", and click the
1429 "Spend" button next to the asset you wish to spend.
1432 To transfer balances, enter the "Spend Amount", select or fill-in the
1433 "Transfer to" name (letters, numbers, and spaces only), and click
1434 the"Spend" button next to the asset you want to transfer from. Each
1435 storage location costs one usage token, and there is currently no way
1436 to recover an unused location. 0 balances will show only on the raw
1440 To mint a coupon, enter the "Spend Amount", check the "Mint coupon"
1441 box, and click the "Spend" button next to the asset you want to
1442 transfer to the coupon. You can redeem a coupon on the "Banks" page.
1445 Entering a "Nickname" will add the "Recipient ID" to your contacts
1446 list with that nickname, or change the nickname of the selected
1450 <a href="./?cmd=toggleinstructions">Hide Instructions</a>
1458 if ($error) $error = "$saveerror<br/>$error";
1459 else $error = $saveerror;
1462 $error = "<span style=\"color: red\";\">$error</span>\n";
1473 if ($iphone) $fullspend .= "</tr>\n<tr>\n";
1474 $fullspend .= <<<EOT
1481 $body = "$error<br/>$bankcode$inboxcode$fullspend$outboxcode$storagefeecode$instructions";
1484 function draw_coupon($time = false) {
1487 global $onload, $body;
1494 $outbox = $client->getoutbox();
1495 if (!$time) $time = mq($_REQUEST['time']);
1496 $items = $outbox[$time];
1497 $timestr = hsc($time);
1498 $datestr = datestr($time);
1500 foreach ($items as $item) {
1501 $request = $item[$t->REQUEST
];
1502 if ($request == $t->SPEND
) {
1503 $assetname = hsc($item[$t->ASSETNAME
]);
1504 $formattedamount = hsc($item[$t->FORMATTEDAMOUNT
]);
1505 $note = hsc($item[$t->NOTE
]);
1506 if ($note) $note = "<tr><td><b>Note:</b></td><td><span style=\"margin: 5px;\">$note</span></td></tr>\n";
1507 } elseif ($request == $t->COUPONENVELOPE
) {
1508 $coupon = hsc(trim($item[$t->COUPON
]));
1511 <b>Coupon for outbox entry at $datestr</b>
1513 <tr><td><b>Amount:</b></td><td><span style="margin: 5px;">$formattedamount $assetname</span></td></tr>
1514 $note<tr><td><b>Coupon:</b></td><td><span style="margin: 5px;">$coupon</span></td></tr>
1521 $error = "Couldn't find coupon: $timestr";
1525 function draw_raw_balance() {
1529 settitle('Raw Balance');
1536 $bankid = $client->bankid
;
1537 if (!($id && $bankid)) return;
1542 $key = $client->userbankkey($t->INBOX
);
1543 $inbox = $db->contents($key);
1544 if (count($inbox) == 0) {
1545 $body .= "<br/><b>=== Inbox empty ===</b><br/>\n";
1547 $body .= '<br/><b>=== Inbox ===</b><br/>
1551 foreach ($inbox as $file) {
1552 $msg = $db->get("$key/$file");
1555 <td valign="top">$file</td>
1556 <td><pre>$msg</pre></td>
1561 $body .= "</table>\n";
1564 $key = $client->userbankkey($t->OUTBOX
);
1565 $outbox = $db->contents($key);
1566 if (count($outbox) == 0) {
1567 $body .= "<br/><b>=== Outbox empty ===</b><br/>\n";
1569 $body .= '<br/><b>=== Outbox===</b><br/>
1573 foreach ($outbox as $file) {
1574 $msg = $db->get("$key/$file");
1577 <td valign="top">$file</td>
1578 <td><pre>$msg</pre></td>
1583 $body .= "</table>\n";
1586 $key = $client->userbankkey($t->BALANCE
);
1587 $accts = $db->contents($key);
1588 foreach ($accts as $acct) {
1589 $body .= '<br/><b>' . $acct . '</b><br/>
1593 $assets = $db->contents("$key/$acct");
1594 foreach ($assets as $assetid) {
1595 $asset = $client->getasset($assetid);
1596 $assetname = $asset[$t->ASSETNAME
];
1597 $msg = $db->get("$key/$acct/$assetid");
1600 <td valign="top">$assetname</td>
1601 <td><pre>$msg</pre></td>
1605 $body .= "</table><br/>\n";
1608 $key = $client->userfractionkey();
1609 $assetids = $db->contents($key);
1610 if (count($assetids) > 0) {
1611 $body .= '<br/><b>=== Fractional Balances ===</b><br/>
1614 foreach($assetids as $assetid) {
1615 $asset = $client->getasset($assetid);
1616 $assetname = $asset[$t->ASSETNAME
];
1617 $msg = $db->get("$key/$assetid");
1620 <td valign="top">$assetname</td>
1621 <td><pre>$msg</pre></td>
1625 $body .= "</table><br/>\n";
1630 function draw_banks($bankurl='', $name='') {
1631 global $onload, $body;
1637 $banks = $client->getbanks();
1639 $onload = "document.forms[0].bankurl.focus()";
1644 <span style="color: red;">$error</span><br/>
1645 <form method="post" action="./" autocomplete="off">
1646 <input type="hidden" name="cmd" value="bank"/>
1649 <td><b>Bank URL<br/>or Coupon:</b></td>
1650 <td><input type="text" name="bankurl" size="64" value="$bankurl"/>
1652 <td><b>Account Name<br/>(optional):</b></td>
1653 <td><input type="text" name="name" size="40" value="$name"/></td>
1656 <td><input type="submit" name="newbank" value="Add Bank"/>
1657 <input type="submit" name="cancel" value="Cancel"/></td>
1664 if (count($banks) > 0) {
1665 $body .= '<table border="1">
1673 foreach ($banks as $bid => $b) {
1674 if ($client->userreq($bid) != -1) {
1675 $name = hsc($b[$t->NAME
]);
1676 if (!$name) $name = "unnamed";
1677 $url = hsc($b[$t->URL
]);
1679 <form method="post" action="./" autocomplete="off">
1680 <input type="hidden" name="cmd" value="bank"/>
1681 <input type="hidden" name="bank" value="$bid"/>
1684 <td><a href="$url">$url</a></td>
1686 <td><input type="submit" name="selectbank" value="Choose"/></td>
1693 $body .= "</table>\n";
1698 function draw_contacts($id=false, $nickname=false, $notes=false) {
1699 global $onload, $body;
1705 $onload = "document.forms[0].id.focus()";
1706 settitle('Contacts');
1707 setmenu('contacts');
1710 $nickname = hsc($nickname);
1711 $notes = hsc($notes);
1714 <span style="color: red;">$error</span><br/>
1715 <form method="post" action="./" autocomplete="off">
1716 <input type="hidden" name="cmd" value="contact">
1719 <td align="right"><b>ID:</b></td>
1720 <td><input type="text" name="id" size="40" value="$id"/></td>
1722 <td><b>Nickname<br/>(Optional):</b></td>
1723 <td><input type="text" name="nickname" size="30" value="$nickname"/></td>
1725 <td><b>Notes<br/>(Optional):</b></td>
1726 <td><textarea name="notes" cols="30" rows="10">$notes</textarea></td>
1729 <td><input type="submit" name="addcontact" value="Add/Change Contact"/>
1730 <input type="submit" name="cancel" value="Cancel"/></td>
1736 $contacts = $client->getcontacts();
1737 $cnt = count($contacts);
1739 $body .= '<br/><form method="post" action="./" autocomplete="off">
1740 <input type="hidden" name="cmd" value="contact"/>
1741 <input type="hidden" name="chkcnt" value="' . $cnt . '"/>
1752 foreach ($contacts as $contact) {
1753 $id = hsc($contact[$t->ID
]);
1754 $name = trim(hsc($contact[$t->NAME
]));
1755 $nickname = trim(hsc($contact[$t->NICKNAME
]));
1756 $display = namestr($nickname, $name, $id);
1757 if (!$name) $name = ' ';
1758 if (!$nickname) $nickname = ' ';
1760 $note = hsc($contact[$t->NOTE
]);
1761 if (!$note) $note = " ";
1762 else $note = str_replace("\n", "<br/>\n", $note);
1771 <input type="hidden" name="id$idx" value="$id"/>
1772 <input type="checkbox" name="chk$idx"/>
1782 <input type="submit" name="deletecontacts" value="Delete checked"/>
1788 function draw_assets($scale=false, $precision=false, $assetname=false, $storage=false) {
1789 global $onload, $body;
1795 $onload = "document.forms[0].scale.focus()";
1800 $scale = hsc($scale);
1801 $precision = hsc($precision);
1802 $assetname = hsc($assetname);
1805 <span style="color: red;">$error</span><br/>
1806 <form method="post" action="./" autocomplete="off">
1807 <input type="hidden" name="cmd" value="asset"/>
1810 <td><b>Scale:</b></td>
1811 <td><input type="text" name="scale" size="3" value="$scale"/>
1813 <td><b>Precision:</b></td>
1814 <td><input type="text" name="precision" size="3" value="$precision"/></td>
1816 <td><b>Asset name:</b></td>
1817 <td><input type="text" name="assetname" size="30" value="$assetname"/></td>
1819 <td><b>Storage fee (%/year):</b></td>
1820 <td><input type="text" name="storage" size="5" value="$storage"/></td>
1823 <td><input type="submit" name="newasset" value="Add Asset"/>
1824 <input type="submit" name="cancel" value="Cancel"/></td>
1831 $assets = $client->getassets();
1832 if (count($assets) > 0) {
1833 $body .= '<form method="post" action="./" autocomplete="off">
1839 <th>Storage Fee<br/>(%/year)</th>
1845 foreach ($assets as $asset) {
1846 $ownerid = $asset[$t->ID
];
1847 $namestr = id_namestr($ownerid, $contact);
1848 $assetid = $asset[$t->ASSET
];
1849 $scale = $asset[$t->SCALE
];
1850 $precision = $asset[$t->PRECISION
];
1851 $assetname = $asset[$t->ASSETNAME
];
1852 $percent = $asset[$t->PERCENT
];
1853 if ($ownerid == $client->id
) {
1855 <input type="hidden" name="assetid$incnt" value="$assetid"/>
1856 <input type="hidden" name="opercent$incnt" value="$percent"/>
1857 <input type="text" name="percent$incnt" value="$percent" size="10" style="text-align: right;"/>
1861 if (!$percent) $percent = " ";
1865 <td align="right">$scale</td>
1866 <td align="right">$precision</td>
1867 <td align="right">$percent</td>
1874 $body .= "</table>\n";
1876 $body .= '<input type="hidden" name="percentcnt" value="' . $incnt . '"/>
1877 <input type="hidden" name="cmd" value="asset"/>
1878 <br/><input type="submit" name="updatepercent" value="Update Storage Fees"/>
1881 $body .= "</form>\n";
1885 function gethistory() {
1889 require_once "history.php";
1890 $history = new history();
1895 function draw_history() {
1898 $history = gethistory();
1899 return $history->draw_history();
1902 function draw_admin($name=false, $tokens=false) {
1903 global $onload, $body;
1910 $onload = "document.forms[0].name.focus()";
1912 $body = 'No admin stuff yet';
1915 // Copyright 2008 Bill St. Clair
1917 // Licensed under the Apache License, Version 2.0 (the "License");
1918 // you may not use this file except in compliance with the License.
1919 // You may obtain a copy of the License at
1921 // http://www.apache.org/licenses/LICENSE-2.0
1923 // Unless required by applicable law or agreed to in writing, software
1924 // distributed under the License is distributed on an "AS IS" BASIS,
1925 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1926 // See the License for the specific language governing permissions
1927 // and limitations under the License.