-
Notifications
You must be signed in to change notification settings - Fork 12
/
index.php
474 lines (422 loc) · 17.2 KB
/
index.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
<?php
define('IN_MAPBBCODE', 1);
define('VERSION', '1.2-15');
require('config.php');
require('convert.php');
require('db.php');
define('SCRIPT_NAME', 'index.php');
if( !defined('MOD_REWRITE') ) {
if( function_exists('apache_get_modules') )
define('MOD_REWRITE', in_array('mod_rewrite', apache_get_modules()) && file_exists('.htaccess'));
else
define('MOD_REWRITE', true); // true for nginx
}
$doc_path = 'http'.(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 's' : '').'://'.
$_SERVER['HTTP_HOST'].rtrim(dirname($_SERVER['SCRIPT_NAME']), '/\\');
$base_path = MOD_REWRITE ? $doc_path : $doc_path.'/'.SCRIPT_NAME;
ini_set('session.gc_maxlifetime', 7776000);
ini_set('session.cookie_lifetime', 7776000);
session_set_cookie_params(7776000);
session_start();
//if( $_SERVER['HTTP_HOST'] == 'localhost' ) $userid = 'test';
if( isset($_SESSION['user_id']) ) {
$userid = $_SESSION['user_id'];
if( isset($_SESSION['alt_user_id']) ) {
check_alt_user_id($userid, $_SESSION['alt_user_id']);
unset($_SESSION['alt_user_id']);
}
}
$message = '';
$api = isset($_REQUEST['api']);
$params = parse_params();
$action = $params['action'];
$bbcode = $params['bbcode'];
$title = $params['title'];
if( isset($params['id']) )
$scodeid = $params['id'];
if( isset($params['editid']) )
$seditid = $params['editid'];
$data = false;
if( isset($scodeid) ) {
$data = get_data($scodeid);
if( !$data ) {
if( $api )
return_json(array('error' => 'No such code: '.$scodeid));
$message = 'There is no code in the database with given id';
}
}
if( $action == '' && $data && !$params['post'] ) {
// only for GET requests with id (that is, not ?bbcode=)
$bbcode = $data['bbcode'];
$title = $data['title'];
if( $api )
return_json(array('title' => $title, 'bbcode' => $bbcode));
} elseif( $action == '' && isset($_GET['gz']) && strlen($_GET['gz']) > 12 ) {
// action is base64-encoded bbcode+title
$res = decompress_bbcode($_GET['gz']);
if( $res && count($res) == 2 && strlen($res[1]) > 10 ) {
$title = $res[0];
$bbcode = $res[1];
}
}
if( $data && isset($params['key']) && $params['key'] == $data['editid'] ) {
$newcode = false;
$seditid = $params['key'];
$message = '<b><a href="'.$base_path.'/'.$params['id'].'" target="mapbbstatic">Share this link</a></b> for read-only view of this map.<br><a href="'.$base_path.'/'.$params['id'].'?editid='.$params['key'].'">Bookmark this</a> to edit the map later';
$nohide = 1; // do not hide message
if( isset($userid) ) {
update_library($userid, $scodeid, $seditid);
$message .= ' (or check the library)';
} elseif( db_available() )
$message .= ' (sign in to have it stored for you automatically)';
}
$editing = $params['post'] || isset($seditid) || (strlen($bbcode) == 0 && strlen($title) == 0);
$newcode = !isset($seditid);
// see also $actions array in parse_surl
if( $action == 'initdb' && NEED_INIT_DB ) {
initdb();
} elseif( $action == 'fmtlist' ) {
$fmtdesc = get_format_arrays();
return_json($fmtdesc);
} elseif( $action == 'save' ) {
save($params, $data);
} elseif( $action == 'forget' && isset($userid) && isset($scodeid) ) {
if( remove_bookmark($userid, $scodeid) )
$message .= 'This map has been removed from your library.';
} elseif( $action == 'signout' ) {
unset($_SESSION['user_id']);
unset($userid);
} elseif( $action == 'bookmark' && isset($userid) && $data ) {
update_library($userid, $params['id'], '');
$message = 'The code was added to your library';
} elseif( $action == 'import' && isset($_FILES['file']) ) {
if( !is_uploaded_file($_FILES['file']['tmp_name']) || $_FILES['file']['size'] == 0 || $_FILES['file']['error'] > 0 ) {
$errors = array('OK', 'too big', 'bigger than MAX_FILE_SIZE', 'partial upload', 'no file', '', 'nowhere to store', 'failed to write', 'extension error');
$err = $errors[$_FILES['file']['error']];
if( $api )
return_json(array('error' => 'File upload error: '.$err, 'code' => $_FILES['file']['error']));
else
$message = 'File upload error ('.$err.')';
} else {
$titlebb = defined('IMPORT_SINGLE') && !IMPORT_SINGLE ? array($title, $bbcode) : array('', '');
$titlebb = import($_FILES['file']['tmp_name'], $titlebb);
$title = $titlebb[0];
$bbcode = $titlebb[1];
if( $api )
return_json(array('title' => $title, 'bbcode' => $bbcode));
}
}
if( isset($_REQUEST['format']) && preg_match('/^[a-z]+$/', $_REQUEST['format']) ) {
$format = $_REQUEST['format'];
header('Access-Control-Allow-Origin: *');
$result = export($format, $title, $bbcode, isset($scodeid) ? $scodeid : '', !isset($_REQUEST['direct']));
if( $result == CONVERT_OK )
exit;
if( $result == CONVERT_NOT_SUPPORTED ) {
if( $action == 'export' )
$message = 'Unknown or unimplemented export type: '.$format;
else {
header('HTTP/1.1 415 Format Not Suppoprted');
exit;
}
} elseif( $result == CONVERT_EMPTY ) {
if( $action == 'export' )
$message = 'Output file is empty';
else {
header('HTTP/1.1 400 No Data In BBCode');
exit;
}
}
}
$fmtdesc = get_format_arrays();
if( !$editing && strlen($bbcode) < 11 )
$bbcode = '[map][/map]';
if( isset($userid) )
$library = fetch_library($userid);
if( isset($userid) && !isset($scodeid) )
$message .= 'Warning: Google retires its OpenID on April 20th, merge your account with another one to keep your library.';
require('page.php');
// ---------------------------------- FUNCTIONS ----------------------------
// print data as json(p) and exit
function return_json( $data ) {
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json');
$json = json_encode($data);
if( isset($_REQUEST['jsonp']) )
print $_REQUEST['jsonp']."($json);";
else
print $json;
exit;
}
// save bbcode to database (or update)
function save( $params, $data ) {
global $message, $api, $base_path;
$title = $params['title'];
$bbcode = $params['bbcode'];
if( strlen($title) < 3 && strlen($bbcode) < 7 ) {
if( $api )
return_json(array('error' => 'Would not save empty data, sorry'));
else
$message = 'Would not save empty data, sorry';
return;
}
if( strlen($title) > 250 ) {
$spacepos = strrpos($title, ' ');
$title = substr($title, 0, $spacepos !== false ? $spacepos : 250);
}
$db = getdb();
if( $data && isset($params['key']) && $data['editid'] == $params['key'] ) {
// update
$codeid = $params['id'];
$editid = $params['key'];
$sql = !$db ? '' : "update ".DB_TABLE." set updated=now(), title='".$db->escape_string($title)."', bbcode='".$db->escape_string($bbcode)."' where codeid = '$codeid'";
cache_remove($codeid, 'code');
cache_remove(false, 'user'); // yup, now a lot of users can have their libraries updated
} else {
$editid = generate_id(EDIT_HASH_LENGTH);
$tries = 10;
do {
$codeid = generate_id(HASH_LENGTH);
$exists = get_data($codeid) !== false;
} while( $exists );
$sql = !$db ? '' : "insert into ".DB_TABLE." (created, updated, codeid, editid, title, bbcode) values(now(), now(), '$codeid', '$editid', '".$db->escape_string($title)."', '".$db->escape_string($bbcode)."')";
}
if( $db ) {
$res = $db->query($sql);
} else {
// put code to cache
$assoc = array('editid' => $editid, 'title' => $title, 'bbcode' => $bbcode);
cache_put($codeid, 'code', $assoc);
$res = true;
}
if( !$api ) {
if( !$res ) {
$message = 'Failed to insert entry in the database: '.$db->error;
} else {
header("Location: ".$base_path."/$codeid?editid=$editid");
exit;
}
} else {
if( !$res ) {
return_json(array('error' => 'Failed to insert entry in the database: '.$db->error));
} else {
return_json(array('codeid' => $codeid, 'editid' => $editid, 'viewurl' => $base_path."/$codeid", 'editurl' => $base_path."/$codeid/$editid"));
}
exit;
}
}
// Adds code id to the user's library. Edit id is checked to test if user can edit code
function update_library( $userid, $codeid, $editid ) {
$db = getdb();
if( !$db )
return;
$uidesc = $db->escape_string($userid);
$sql = 'select editable from '.DB_TABLE."_users where codeid = '$codeid' and userid = '$uidesc'";
$res = $db->query($sql);
if( !$res )
return;
$cnt = $res->num_rows;
$row = $res->fetch_row();
$editable = $row[0];
$res->free();
if( !$cnt ) {
$sql = 'insert into '.DB_TABLE."_users (userid, codeid, editable) values ('$uidesc', '$codeid', ".(isset($editid) && strlen($editid) > 0 ? 1 : 0).")";
$db->query($sql);
} elseif( !$editable && isset($editid) && strlen($editid) > 0 ) {
$sql = 'update '.DB_TABLE."_users set editable=1 where userid='$uidesc' and codeid='$codeid'";
$db->query($sql);
}
cache_remove($userid, 'user');
}
// Removes a bookmark from user's library
function remove_bookmark( $userid, $codeid ) {
$db = getdb();
if( !$db )
return false;
$sql = 'delete from '.DB_TABLE."_users where codeid = '$codeid' and userid ".get_alt_userid_clause($db, $userid);
$res = $db->query($sql);
if( $res && $db->affected_rows > 0 ) {
cache_remove($userid, 'user');
return true;
}
return false;
}
// Merges two user accounts
function check_alt_user_id( $userid, $alt_userid ) {
$db = getdb();
if( !$db )
return;
$uidesc = $db->escape_string($userid);
$alt_uidesc = $db->escape_string($alt_userid);
$sql = 'select * from '.DB_TABLE."_usermap where (userid = '$uidesc' and puserid = '$alt_uidesc') or (userid = '$alt_uidesc' and puserid = '$uidesc')";
$res = $db->query($sql);
if( !$res )
return;
if( !$res->num_rows ) {
$sql = 'insert into '.DB_TABLE."_usermap (userid, puserid) values ('$alt_uidesc', '$uidesc')";
$db->query($sql);
}
cache_remove($userid, 'user');
cache_remove($alt_userid, 'user');
}
// Updates user clause for merged accounts
function get_alt_userid_clause( $db, $userid ) {
$esc_userid = $db->escape_string($userid);
$sql = 'select userid from '.DB_TABLE."_usermap where puserid = '$esc_userid' union select puserid as userid from ".DB_TABLE."_usermap where userid = '$esc_userid'";
$res = $db->query($sql);
$ids = array("'$esc_userid'");
if( $res )
while( $row = $res->fetch_row() )
$ids[] = "'".$db->escape_string($row[0])."'";
if( count($ids) == 1 )
return '= '.$ids[0];
else
return 'in ('.implode(',', $ids).')';
}
// Returns as array of all library entries sorted by update time
function fetch_library( $userid ) {
$stored = cache_fetch($userid, 'user');
if( $stored !== false )
return $stored;
global $message;
$codes = array();
$db = getdb();
if( !$db )
return $codes;
$user_clause = get_alt_userid_clause($db, $userid);
$sql = 'select now() as now, m.*, u.editable from '.DB_TABLE.' m, '.DB_TABLE.'_users u where u.codeid = m.codeid and u.userid '.$user_clause.' order by m.updated desc limit 100';
$res = $db->query($sql);
if( $res ) {
require_once('mapbbcode.php');
while( $row = $res->fetch_assoc() ) {
$item = array();
$item['codeid'] = $row['codeid'];
$item['editid'] = $row['editable'] ? $row['editid'] : '';
$item['created'] = human_date($row['created'], $row['now']);
$item['updated'] = human_date($row['updated'], $row['now']);
$item['title'] = $row['title'];
$item['stats'] = mapbbcode_stats($row['bbcode']);
$codes[] = $item;
}
$res->free();
cache_put($userid, 'user', $codes);
} else
$message = 'Failed to retrieve user library: '.$db->error;
return $codes;
}
// Generated random id with given length
function generate_id($length) {
$id = '';
for( $i = 0; $i < $length; $i++ )
$id .= chr(mt_rand(97, 122)); // 'a'..'z'
return $id;
}
// Arhives bbcode and title
function compress_bbcode($title, $bbcode) {
$base64 = base64_encode(gzdeflate($bbcode.str_replace('[/map]', '[ /map]', $title), 9));
$firsteq = strpos($base64, '=');
return strtr($firsteq === false ? $base64 : substr($base64, 0, $firsteq), '+/', '-_');
}
// Unarchives bbcode and title
function decompress_bbcode($str) {
$base64 = strtr($str, '-_', '+/');
while( strlen($base64) % 4 )
$base64 .= '=';
$titlebb = gzinflate(base64_decode($base64), 20000);
if( $titlebb === false )
return false;
$bbpos = strpos($titlebb, '[/map]');
if( $bbpos === false ) {
$title = $titlebb;
$bbcode = '';
} else {
$title = substr($titlebb, $bbpos + 6);
$bbcode = substr($titlebb, 0, $bbpos + 6);
}
return array(str_replace('[ /map]', '[/map]', $title), $bbcode);
}
// Screens \ and ' characters (for javascript strings)
function screen_param($str) {
return str_replace(array("\\", "'"), array("\\\\", "\\'"), $str);
}
// Converts mysql date to human-readable string
function human_date($sqldate, $sqlnow = false) {
$now = $sqlnow ? strtotime($sqlnow) : time();
$date = strtotime($sqldate);
$diff = round(($now - $date) / 60); // in minutes
//echo "($sqldate) ".date('Y-m-d H:i:s', $date).' -> '.date('Y-m-d H:i:s', $now)." = $diff<br>";
if( $diff < 0 )
return 'in the future: '.$diff;
if( $diff == 0 ) return 'just now';
if( $diff == 1 ) return 'a minute ago';
if( $diff < 55 ) return $diff.' minutes ago';
$hours = round($diff/60);
$curhour = (int)date('H', $now);
if( $hours == 1 ) return 'an hour ago';
if( $hours <= max(12, $curhour + 1) ) return $hours.' hours ago';
// now test real difference in dates
$curday = floor(strtotime(date('Y-m-d', $now)) / 86400);
$day = floor(strtotime(date('Y-m-d', $date)) / 86400);
if( $curday - $day == 1 ) return 'yesterday';
// if( $curday - $day == 2 ) return 'two days ago';
// now output real dates
//$months = array('', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
$months = array('', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', '');
$month = $months[(int)idate('m', $date)];
if( date('Y', $now) == date('Y', $date) )
return $month.' '.idate('d', $date);
else
return $month.' '.date('Y', $date);
}
// ---------------------------------- URL PARSING ----------------------------
// returns URL for those three parameters
function build_url( $action, $codeid = '', $editid = '' ) {
global $base_path;
$url = $base_path;
if( strlen($codeid) > 0 ) {
$url .= '/'.$codeid;
if( strlen($editid) > 0 )
$url .= '/'.$editid;
}
if( strlen($action) > 0 )
$url .= '?'.$action;
return $url;
}
// returns array(action, id?, key?)
function parse_surl() {
$base_path = rtrim(dirname($_SERVER['SCRIPT_NAME']), '/\\');
if(function_exists('apache_request_headers'))
$url = isset($_SERVER['REDIRECT_URL']) ? $_SERVER['REDIRECT_URL'] : $_SERVER['PHP_SELF'];
else
$url = $_SERVER['REQUEST_URI']; // under nginx, path info is in REQUEST_URI
if( substr($url, 0, strlen($base_path)) == $base_path )
$url = substr($url, strlen($base_path));
if( substr($url, 1, strlen(SCRIPT_NAME)) == SCRIPT_NAME )
$url = substr($url, strlen(SCRIPT_NAME) + 1);
$result = array('action' => '');
$actions = array('initdb', 'fmtlist', 'save', 'signout', 'bookmark', 'import', 'export', 'forget');
if( preg_match('#^/?([a-z]+)(?:/([a-z]+))?/?#', $url, $m) ) {
$result[in_array($m[1], $actions) ? 'action' : 'id'] = $m[1];
$result['key'] = count($m) > 2 ? $m[2] : '';
}
return $result;
}
// returns array(action, id?, key?, codeid?, editid?, title, bbcode)
function parse_params() {
$result = parse_surl();
if( isset($_REQUEST['codeid']) && preg_match('/^[a-z]+$/', $_REQUEST['codeid']) && strlen($_REQUEST['codeid']) == HASH_LENGTH ) {
$result['codeid'] = $_REQUEST['codeid'];
$result['id'] = $result['codeid']; // POST overrides GET
} if( isset($_REQUEST['editid']) && preg_match('/^[a-z]+$/', $_REQUEST['editid']) ) {
$result['editid'] = $_REQUEST['editid'];
$result['key'] = $result['editid'];
}
$result['title'] = trim(isset($_POST['title']) && strlen($_POST['title']) > 0 ? $_POST['title'] : (!isset($result['codeid']) && isset($_GET['title']) ? $_GET['title'] : ''));
$result['post'] = isset($_POST['bbcode']) && strlen($_POST['bbcode']) > 0;
$bbcode = trim($result['post'] ? $_POST['bbcode'] : (!isset($result['codeid']) && isset($_GET['bbcode']) ? $_GET['bbcode'] : ''));
if( strlen($bbcode) > 0 && (strlen($bbcode) < 8 || (substr($bbcode, 0, 4) != '[map' && substr($bbcode, -6) != '[/map]')) )
$bbcode = '[map]'.$bbcode.'[/map]';
$result['bbcode'] = $bbcode;
return $result;
}
?>