Start the location editing backend
[loomclient.git] / ip.php
blob4393b06c885b2a3bff2d4e1f396b07d9d10a3195
1 <?php
3 require_once "Socket.php";
4 require_once "LoomClient.php";
6 /*
7 * iPhone interface to the Loom folder.
8 * Don't run this unencrypted.
9 * See http://www.whoopis.com/howtos/apache-rewrite.html
10 * for unstructions on setting up a .htaccess file to rewrite
11 * http://... to https://...
14 function mq($x) {
15 if (get_magic_quotes_gpc()) return stripslashes($x);
16 else return $x;
19 $passphrase = mq($_POST['passphrase']);
20 $qty = mq($_POST['zip']);
21 $type = mq($_POST['type']);
22 $location = mq($_POST['location']);
23 $folderkv = mq($_POST['folderkv']);
24 $valueskv = mq($_POST['valueskv']);
25 $take = mq($_POST['take']);
26 $give = mq($_POST['give']);
27 $page = mq($_POST['page']);
28 $greendot = mq($_POST['greendot']);
29 $showfolder = mq($_POST['showfolder']);
31 $client = new LoomClient();
33 $onload = 'qty';
34 $folder = '';
35 $values = '';
36 $message = '';
38 if ($page == 'refresh') {
39 $folderkv = '';
40 $valueskv = '';
41 $page = 'main';
42 } else {
43 if ($folderkv != '') $folder = $client->parsekv($folderkv, TRUE);
44 if ($valueskv != '') $values = $client->parsekv($valueskv, TRUE);
47 if ($passphrase == '' || !login()) {
48 $onload = 'passphrase';
49 $page = 'login';
50 } else {
51 if ($values == '') {
52 $values = scanFolder($folder);
53 $valueskv = $client->array2kv($values);
57 ?><html>
58 <head>
59 <meta name="viewport" content="width=device-width" user-scalable="no" minimum-scale="1.0" maximum-scale="1.0"/>
60 <title>Loom Folder</title>
62 <script language="JavaScript">
63 function submitPage(page) {
64 document.forms["mainform"].page.value = page;
65 document.mainform.submit();
68 function greenDot(greendot) {
69 document.forms["mainform"].greendot.value = greendot;
70 document.mainform.submit();
72 </script>
74 <style type="text/css">
75 body { font-family: verdana, arial, sans-serif; font-size: 12pt }
76 div { font-size:12pt }
77 p { font-size:12pt }
78 h1 { font-size:14pt }
79 h2 { font-size:12pt }
80 h3 { font-size:10pt }
81 td { font-size:12pt }
82 ul { font-size:12pt }
83 li { padding-bottom: 7px }
84 pre { font-family: verdana, arial, sans-serif; }
85 A:link, A:visited { color:blue; text-decoration:none }
86 A:hover { color:blue; text-decoration:underline }
87 A:active { color:#FAD805; text-decoration:underline }
88 .tt { font-family: Courier; font-size:10pt }
89 .mono { font-family: monospace; font-size: 11pt }
90 .large_mono { font-family: monospace; font-size: 10pt }
91 .giant_mono { font-family: monospace; font-size: 14pt }
92 .tiny_mono { font-family: monospace; font-size: 6pt }
93 .normal { font-size:10pt }
94 .smaller { font-size:6pt }
95 .small { font-size:8pt }
96 .large { font-size:12pt }
97 .alarm { color:red }
98 .focus_value { background-color:#DDDDDD }
99 .color_heading { margin-top:12px; padding:1px; background-color:#DDDDDD; width:100% }
100 A.label_link { font-weight:bold; }
101 A.highlight_link { font-weight:bold; }
102 A.cancel { background-color:#FFDDDD }
103 A.plain:link, A.plain:visited { color:black; text-decoration:none }
104 A.plain:hover { color:blue; text-decoration:underline }
105 A.plain:active { color:#FAD805; text-decoration:underline }
106 A.name_dot { font-size:16pt; font-weight:bold; color:green; }
107 </style>
109 </head>
110 <body onload="document.forms[0].<? echo $onload; ?>.focus()">
111 <table width="320px">
112 <tr><td>
115 if ($page == 'main') doMain();
116 elseif ($page == 'locations') doLocations();
118 if ($page == 'login') drawLogin();
119 elseif ($page == 'main') drawMain();
120 elseif ($page == 'locations') drawLocations();
122 ?></td></tr>
123 </table>
124 </body>
125 </html>
128 function doMain() {
129 global $qty, $type, $location, $take, $give;
130 global $client, $folder, $folder_loc, $folder_name;
131 global $values, $valueskv;
132 global $message;
134 $id = '';
136 if ($type != '-- choose asset --') {
137 $t = $folder['types'][$type];
138 if ($t) {
139 $id = $t['id'];
140 $min_precision = $t['min_precision'];
141 if ($min_precision == '') $min_precision = 0;
142 $scale = $t['scale'];
143 if ($scale == '') $scale = 0;
146 if ($location != '-- choose location --') {
147 $loc = $folder['locs'][$location];
149 if ($id != '' && $loc != '') {
150 if ($scale != '') $count = bcmul($qty, bcpow(10, $scale), 0);
151 $transferred = TRUE;
152 if ($count != '' && $id != '' && $loc != '') {
153 if ($take != '') {
154 $res = $client->move($id, $count, $loc, $folder_loc, $url);
155 $loc_orig = $location;
156 $loc_dest = $folder_name;
157 } else if ($give != '') {
158 $res = $client->move($id, $count, $folder_loc, $loc, $url);
159 $loc_dest = $location;
160 $loc_orig = $folder_name;
161 } else $transferred = FALSE;
162 if ($transferred) {
163 $status = $res['status'];
164 if ($status == 'success') {
165 $value_orig = applyScale($res['value_orig'], $min_precision, $scale);
166 $value_dest = applyScale($res['value_dest'], $min_precision, $scale);
167 $values[$loc_orig][$type] = $value_orig;
168 ksort($values[$loc_orig]);
169 $values[$loc_dest][$type] = $value_dest;
170 ksort($values[$loc_dest]);
171 $valueskv = $client->array2kv($values);
172 } else $message = "Insufficient funds";
178 function doLocations() {
181 function drawLogin() {
184 <p>Welcome to Loom for iPhone. Note that in order to use this
185 confidently, you must trust that the PHP scripts at billstclair.com do
186 not steal your passphrase. I promise that unless somebody hacks my
187 site, the code running is what you can download from
188 <a href="index.html">here</a>, but don't trust that unless you know
189 me.</p>
190 <form method="post" action="" autocomplete="off">
191 <input type="hidden" name="page" value="main"/>
192 <table width="99%">
193 <tr>
194 <td>Passphrase:</td>
195 <td><input type="password" name="passphrase" size="35" /></td>
196 </tr>
197 <tr>
198 <td></td>
199 <td><input type="submit" name="login" text="Login" /></td>
200 </tr>
201 </table>
202 <p>It can take 10 or 20 seconds to read the initial values of all your
203 folder's locations, more if you have lots of locations and asset
204 types. Please be patient. After the initial load, things should be
205 pretty quick.</p>
206 </form>
210 function hsc($text) {
211 return htmlspecialchars($text);
214 function hiddenValue($name) {
215 eval('global $' . $name . ';');
216 echo '<input type="hidden" name="' . $name .
217 '" value="' . hsc(eval('return $' . $name . ';')) . '"/>' . "\n";
220 function drawValues($name, $typevalues) {
221 if (is_array($typevalues)) {
222 $str = '';
223 foreach ($typevalues as $type => $value) {
224 if ($value != 0) {
225 if ($str == '') {
226 $str .= "<b>$name</b>\n";
227 $str .= '<table border="0"><tr><td width="50px">&nbsp;</td><td>';
228 $str .= '<table border="0">' . "\n";
230 $str .= '<tr><td align="right">' . $value . "<td><td>$type</td></tr>\n";
233 if ($str != '') {
234 echo $str;
235 echo "</table></td></tr></table>\n";
240 function drawMain() {
241 global $passphrase, $folder, $values, $folder_name;
242 global $qty, $type, $location;
243 global $message;
245 $page = 'main';
248 <table border="0" width="99%" cellpadding="3">
249 <tr>
250 <td colspan="2" style="background-color: #c0c0c0; text-align: center;"><span style="font-weight: bold; font-size: 110%;"><a href="javascript:submitPage('refresh');">Refresh</a>
251 &nbsp;
252 <a href="javascript:submitPage('locations');">Locations</a>
253 &nbsp;
254 Types</span></td>
255 </tr>
256 </table>
258 drawValues($folder_name, $values[$folder_name]);
260 <table border="0" width="99%">
261 <form name="mainform" method="post" action="" autocomplete="off">
263 hiddenValue('passphrase'); echo "\n";
264 hiddenValue('folderkv'); echo "\n";
265 hiddenValue('valueskv');
266 hiddenValue('page');
268 <tr>
269 <td align="right">Qty:</td>
270 <td><input style="font-size: 12pt;" type="text" size="25" name="zip" value="<? echo $qty; ?>" style="text-align:right;"></td>
271 </tr><tr>
272 <td></td>
273 <td>
274 <select name="type" style="font-size: 10pt;">
275 <option value="">-- choose asset --</option>
277 foreach ($folder['types'] as $typename => $typearray) {
278 echo '<option value="' . hsc($typename) . '"';
279 if ($type == $typename) echo ' selected="selected"';
280 echo '>' . hsc($typename) . "</option>\n";
283 </select>
284 </td>
285 </tr><tr>
286 <td></td>
287 <td>
288 <select name="location" style="font-size: 10pt;">
289 <option value="">-- choose location --</option>
291 foreach($values as $loc => $value) {
292 if ($loc != $folder_name) {
293 echo '<option value="' . hsc($loc) . '"';
294 if ($loc == $location) echo ' selected="selected"';
295 echo '>' . hsc($loc) . "</option>\n";
299 </select>
300 </td>
301 </tr><tr>
302 <td></td>
303 <td>
304 <input style="font-size: 10pt;" type="submit" name="take" value="Take"/>
305 <input style="font-size: 10pt;" type="submit" name="give" value="Give"/>
306 </td>
307 </tr>
309 if ($message != '') {
310 echo '<tr><td></td><td><span style="color: red; font-weight: bold;">' . hsc($message) . "</span></td></tr>\n";
312 ?></table>
313 </form>
315 foreach ($values as $name => $typevalues) {
316 if ($name != $folder_name) {
317 drawValues($name, $typevalues);
324 function drawLocations() {
325 global $passphrase, $folder, $values, $folder_name;
326 global $qty, $type, $location, $greendot;
327 global $message;
329 $page = 'locations';
331 <table border="0" width="99%" cellpadding="3">
332 <tr>
333 <td colspan="2" style="background-color: #c0c0c0; text-align: center;"><span style="font-weight: bold; font-size: 110%;"><a href="javascript:submitPage('main');">Folder</a>
334 &nbsp;
335 Locations
336 &nbsp;
337 Types</span></td>
338 </tr>
339 </table>
340 <table border="0" width="99%">
341 <form name="mainform" method="post" action="" autocomplete="off">
343 hiddenValue('passphrase'); echo "\n";
344 hiddenValue('folderkv'); echo "\n";
345 hiddenValue('valueskv');
346 echo '<input type="hidden" name="zip" value="' . $qty . '"/>' . "\n";
347 hiddenValue('type');
348 hiddenValue('location');
349 hiddenValue('page');
351 <input type="hidden" name="greendot" value=""/>
352 <table>
353 <tr>
354 <td valign="top"><a class=name_dot href="javascript: greenDot('<? echo $folder_name; ?>')" title="Edit Name">&nbsp;&bull;&nbsp;</a></td>
356 echo "<td>";
357 if ($greendot != $folder_name) echo "<b>$folder_name</b>";
358 else {
359 echo '<input style="font-size: 12pt;" type="text" size="25" name="newname" value="' . $folder_name . '"/><br/>' . "\n";
360 echo '<input type="submit" name="savename" value="Save"/>';
361 echo '<input type="submit" name="cancel" value="Cancel"/>';
363 echo "</td></tr>\n";
364 $locs = $folder['locs'];
365 foreach ($locs as $name => $loc) {
366 if ($name != $folder_name) {
368 <tr>
369 <td valign="top"><a class=name_dot href="javascript: greenDot('<? echo $name; ?>')" title="Edit Name or Delete Folder">&nbsp;&bull;&nbsp;</a></td>
371 echo "<td>";
372 if ($greendot != $name) echo $name;
373 else {
374 echo '<input style="font-size: 12pt;" type="text" size="25" name="newname" value="' . $name . '"/><br/>' . "\n";
375 echo '<input type="submit" name="savename" value="Save"/>';
376 echo '<input type="submit" name="cancel" value="Cancel"/>';
377 echo "&nbsp;&nbsp;";
378 echo '<input type="submit" name="delete" value="Delete..."/><br/>';
379 echo '</td></tr><tr><td colspan="2"><span class="mono">' . "<b>$loc</b></span><br/>\n";
381 echo "</td></tr>\n";
384 ?><tr><td colspan="2"><input type="submit" name="add_location" value="Add Location"/></td></tr>
385 </table>
387 <p>Click the green dot by a folder name to see its hex value, or to rename or delete it.</p>
391 function login() {
392 global $client, $passphrase, $folder, $folderkv, $folder_name, $folder_loc;
394 if ($folder == '') {
395 $loc = $client->hash2location($client->sha256($passphrase));
396 $res = $client->touch_archive($loc, $url);
397 if ($res['status'] != 'success') return FALSE;
398 $folder = $client->parseFolder($loc, $res['content']);
399 $folderkv = $client->array2kv($folder);
401 $folder_name = $folder['name'];
402 $folder_loc = $folder['loc'];
403 return TRUE;
406 function blankToZero($x) {
407 if ($x == '') return 0;
408 return $x;
411 function applyScale($value, $min_precision, $scale) {
412 if ($value < 0) $value++;
413 if ($scale > 0) $value = bcdiv($value, bcpow(10, $scale, 0), $scale);
415 $dotpos = strpos($value, '.');
417 if ($dotpos > 0) {
418 while (substr($value, -1) == '0') {
419 $value = substr($value, 0, strlen($value)-1);
421 if (substr($value, -1) == '.') {
422 $value = substr($value, 0, strlen($value)-1);
423 $dotpos = 0;
427 if ($min_precision > 0) {
428 if ($dotpos == 0) {
429 $value .= ".";
430 $dotpos = strlen($value);
431 } else $dotpos++;
432 $places = strlen($value) - $dotpos;
433 if ($min_precision > $places) {
434 $value .= str_repeat("0", $min_precision - $places);
437 return $value;
440 // This is currently n-squared for pretty big n, since it has to do
441 // a web call for each location/type pair.
442 // Patrick has promised a scan() function in the web API that would
443 // allow it to be done with a single call.
444 function scanFolder($folder) {
445 global $client;
447 $types = $folder['types'];
448 $locs = $folder['locs'];
449 $values = array();
450 foreach ($locs as $locname => $loc) {
451 $loc_values = array();
452 foreach ($types as $typename => $type) {
453 $id = $type['id'];
454 $min_precision = $type['min_precision'];
455 $scale = $type['scale'];
456 $res = $client->touch($id, $loc, $url);
457 if ($res['status'] == 'success') {
458 $value = applyScale($res['value'], $min_precision, $scale);
459 $loc_values[$typename] = $value;
462 $values[$locname] = $loc_values;
464 return $values;