bored.php (13722B)
1 <?php 2 3 $HT_DEFAULT_FMTS = [ 4 'year_0' => "An year ago", 5 'years_0' => "%d years ago", 6 'year_1' => "An year from now", 7 'years_1' => "%d years from now", 8 'month_0' => "A month ago", 9 'months_0' => "%d months ago", 10 'month_1' => "A month from now", 11 'months_1' => "%d months from now", 12 'week_0' => "A week ago", 13 'weeks_0' => "%d weeks ago", 14 'week_1' => "A week from now", 15 'weeks_1' => "%d weeks from now", 16 'day_0' => "A day ago", 17 'days_0' => "%d days ago", 18 'day_1' => "A day from now", 19 'days_1' => "%d days from now", 20 'hour_0' => "An hour ago", 21 'hours_0' => "%d hours ago", 22 'hour_1' => "An hour from now", 23 'hours_1' => "%d hours from now", 24 'minute_0' => "A minute ago", 25 'minutes_0' => "%d minutes ago", 26 'minute_1' => "A minute from now", 27 'minutes_1' => "%d minutes from now", 28 'second_0' => "A second ago", 29 'seconds_0' => "%d seconds ago", 30 'second_1' => "A second from now", 31 'seconds_1' => "%d seconds from now", 32 'now_0' => "Just now", 33 'nows_0' => "Few seconds ago", 34 'now_1' => "Just now", 35 'nows_1' => "In a bit", 36 ]; 37 38 $HT_DIVS = [ 39 'year' => 60*60*24*365, 40 'month' => 60*60*24*30, 41 'week' => 60*60*24*7, 42 'day' => 60*60*24, 43 'hour' => 60*60, 44 'minute' => 60, 45 'second' => 30, 46 'now' => 0, 47 ]; 48 49 $dblink = NULL; 50 51 function route($method, $route, $func = NULL) { 52 static $routes = []; 53 if(!$func) { 54 $r = NULL; 55 $n = ''; 56 $argv = []; 57 foreach(explode('/', $route) as $arg) { 58 $n .= ($n == '/' ? $arg : "/$arg"); 59 if($r) 60 $argv[] = $arg; 61 if(isset($routes[$method][$n])) { 62 $r = $routes[$method][$n]; 63 $argv = []; 64 if($route == $n) 65 break; 66 } 67 if(isset($routes[$method]["$n/"])) { 68 $r = $routes[$method]["$n/"]; 69 $argv = []; 70 } 71 } 72 if(!$r || (count($argv) < $r['mandatory']) || (count($argv) > $r['argc'])) 73 exit(http_response_code(404)); 74 return call_user_func_array($r['func'], $argv); 75 } 76 $name = []; 77 $argc = 0; 78 $mandatory = 0; 79 foreach(explode('/', $route) as $arg) { 80 switch(@$arg[0]) { 81 case '!': ++$argc; ++$mandatory; break; 82 case '?': ++$argc; break; 83 default: $name[] = $arg; break; 84 } 85 } 86 $name = implode('/', $name); 87 if($argc) 88 $name .= '/'; 89 $routes[$method][$name] = ['func' => $func, 'argc' => $argc, 'mandatory' => $mandatory]; 90 return 0; 91 } 92 93 function dbopen($host, $user, $pass, $dbname) { 94 global $dblink; 95 96 if(!($r = @mysqli_connect($host, $user, $pass, $dbname))) 97 die('database error'); 98 $dblink = $r; 99 return $r; 100 } 101 102 function dbquery($sql, $limit = 1, $multi = false) { 103 global $dblink; 104 105 if(!is_string($sql) || !($sql = trim($sql))) 106 return false; 107 108 $ck = md5($sql); 109 if(($ret = cache($ck))) 110 return $ret; 111 112 $cn = "$sql-$limit-$multi"; 113 $cmd = strtolower(substr($sql, 0, strpos($sql, ' '))); 114 if($cmd == 'select') { 115 if($limit == -1) 116 $limit = '18446744073709551615'; 117 $sql .= " limit $limit"; 118 } 119 120 $res = $multi ? mysqli_multi_query($dblink, $sql) : mysqli_query($dblink, $sql); 121 if(!$res) 122 return false; 123 if($multi) { 124 $ret = []; 125 for($res = mysqli_use_result($dblink); $res; $res = mysqli_store_result($dblink)) { 126 $r = []; 127 while(($t = mysqli_fetch_assoc($res))) 128 $r[] = $t; 129 $ret[] = $r; 130 mysqli_free_result($res); 131 mysqli_next_result($dblink); 132 } 133 return $ret; 134 } 135 switch($cmd) { 136 case 'select': 137 case 'call': 138 $ret = []; 139 while(($t = mysqli_fetch_assoc($res))) 140 $ret[] = $t; 141 break; 142 case 'insert': 143 $ret = mysqli_insert_id($dblink); 144 if(!$ret) 145 $ret = true; 146 break; 147 case 'delete': 148 $ret = mysqli_affected_rows($dblink); 149 break; 150 default: 151 $ret = $res; 152 break; 153 } 154 return ($limit == 1 && is_array($ret) ? $ret[0] : $ret); 155 } 156 157 function dbping($l = NULL) { 158 global $dblink; 159 160 if(!$l) 161 $l = $dblink; 162 return mysqli_ping($l); 163 } 164 165 function dberr($l = NULL) { 166 global $dblink; 167 168 if(!$l) 169 $l = $dblink; 170 return mysqli_error($l); 171 } 172 173 function dbin($s) { 174 global $dblink; 175 176 if($s === NULL) 177 return "NULL"; 178 if($s == "CURRENT_TIMESTAMP") 179 return $s; 180 return "'".mysqli_real_escape_string($dblink, $s)."'"; 181 } 182 183 function dbids() { 184 /* Assuming transactional DB (InnoDB) */ 185 $sql = "select LAST_INSERT_ID() as s,LAST_INSERT_ID() + ROW_COUNT() - 1 as e"; 186 $t = dbquery($sql)[0]; 187 return range($t["s"], $t["e"]); 188 } 189 190 function dbins($tbl, $items) { 191 $values = []; 192 $fields = array_keys($items[0]); 193 foreach($items as $item) { 194 $vals = []; 195 foreach($item as $k => $v) 196 $vals[] = dbin($v); 197 $values[] = "(".implode(',', $vals).")"; 198 } 199 $sql = "insert into `$tbl` (".implode(',', $fields).") values ".implode(',', $values); 200 return dbquery($sql); 201 } 202 203 function dbupd($tbl, $items, $pk = "id") { 204 $when = []; 205 206 /* collects longest available set of keys */ 207 $keys = []; 208 foreach($items as $item) 209 foreach($item as $k => $v) 210 $keys[$k] = 1; 211 $keys = array_keys($keys); 212 213 foreach($items as $item) { 214 $pv = $item[$pk]; 215 foreach($keys as $k) { 216 if($k == $pk) 217 continue; 218 $v = isset($item[$k]) ? dbin($item[$k]) : "`${k}`"; 219 if(!isset($when[$k])) 220 $when[$k] = []; 221 $when[$k][] = "when '$pv' then $v"; 222 } 223 } 224 $sets = []; 225 foreach($when as $k => $w) 226 $sets[] = "$k = case `$pk` ".implode(' ', $w)." else `$k` end"; 227 $sql = "update `$tbl` set ".implode(',', $sets); 228 return dbquery($sql); 229 } 230 231 function dbdel($tbl, $ids, $pk = "id") { 232 $sql = "delete from `$tbl` where `$pk` IN(".implode(',', $ids).")"; 233 return dbquery($sql); 234 } 235 236 function sizefitbox($src, $dst) { 237 list($ow, $oh) = explode('x', $src); 238 list($tow, $toh) = explode('x', $dst); 239 240 $rw = $tow / $ow; 241 $rh = $toh / $oh; 242 $ratio = min($rw, $rh); 243 244 $w = $ow * $ratio; 245 $h = $oh * $ratio; 246 247 $w = (int)$w; 248 $h = (int)$h; 249 250 return "${w}x${h}"; 251 } 252 253 function imgresize($src, $saveas, $whxy = '64x64-0,0', $opts = NULL) { 254 $in = NULL; 255 $out = NULL; 256 $transparency = false; 257 258 $t = finfo_file(finfo_open(FILEINFO_MIME_TYPE), $src); 259 $ext = explode('/', $t)[1]; 260 //$ext = strtolower((string)@pathinfo($src)['extension']); 261 262 switch($ext) { 263 case 'jpg': 264 case 'jpeg': 265 $in = 'imagecreatefromjpeg'; 266 $out = 'imagejpeg'; 267 break; 268 case 'gif': 269 $in = 'imagecreatefromgif'; 270 $out = 'imagegif'; 271 /* imagegif() doesn't take a third param */ 272 if($opts !== NULL) 273 $opts = NULL; 274 break; 275 case 'bmp': 276 $in = 'imagecreatefromwbmp'; 277 $out = 'imagewbmp'; 278 break; 279 case 'png': 280 $in = 'imagecreatefrompng'; 281 $out = 'imagepng'; 282 $transparency = true; 283 break; 284 default: /* unsupported image */ return -1; 285 } 286 if(!($oi = $in($src))) 287 return 2; 288 289 $t = explode('-', $whxy); 290 $wh = $t[0]; 291 $wh = explode('x', $wh); 292 $xy = isset($t[1]) ? $t[1] : '0,0'; 293 $xy = explode(',', $xy); 294 295 $w = (int)@$wh[0]; 296 $h = isset($wh[1]) ? $wh[1] : $w; 297 $x = (int)@$xy[0]; 298 $y = (int)@$xy[1]; 299 300 list($iw, $ih) = getimagesize($src); 301 $ratio = [$iw / $ih, $w / $h]; 302 if($x != 0 || $y != 0) { 303 $crop = imagecreatetruecolor($w, $h); 304 $cropW = $w; 305 $cropH = $h; 306 307 imagecopy($crop, $oi, 0, 0, (int)$x, (int)$y, $w, $h); 308 if($transparency) { 309 imagealphablending($crop, false); 310 imagesavealpha($crop, true); 311 } 312 } 313 else if($ratio[0] != $ratio[1]) { 314 $scale = min((float)($iw / $w), (float)($ih / $h)); 315 $cropX = (float)($iw - ($scale * $w)); 316 $cropY = (float)($ih - ($scale * $h)); 317 $cropW = (float)($iw - $cropX); 318 $cropH = (float)($ih - $cropY); 319 $crop = imagecreatetruecolor($cropW, $cropH); 320 if($transparency) { 321 imagealphablending($crop, false); 322 imagesavealpha($crop, true); 323 } 324 imagecopy($crop, $oi, 0, 0, (int)($cropX / 2), (int)($cropY / 2), $cropW, $cropH); 325 } 326 $ni = imagecreatetruecolor($w, $h); 327 if($transparency) { 328 imagealphablending($ni, false); 329 imagesavealpha($ni, true); 330 } 331 if(isset($crop)) { 332 imagecopyresampled($ni, $crop, 0, 0, 0, 0, $w, $h, $cropW, $cropH); 333 imagedestroy($crop); 334 } 335 else { 336 imagecopyresampled($ni, $oi, 0, 0, 0, 0, $w, $h, $iw, $ih); 337 } 338 imagedestroy($oi); 339 if($opts !== NULL) 340 $r = $out($ni, $saveas, $opts); 341 else 342 $r = $out($ni, $saveas); 343 imagedestroy($ni); 344 if($r === false) 345 return 1; 346 return 0; 347 } 348 349 function jsonerr($v) { 350 json('ko', $v); 351 } 352 353 function jsonok($v = "") { 354 json('ok', $v); 355 } 356 357 function json($state, $res) { 358 die(json_encode(['state' => $state, 'res' => $res])); 359 } 360 361 function sendmail($from, $to, $subj, $message, $files = NULL) { 362 ini_set('sendmail_from', $from); 363 $headers = "From: $from\n" . 364 "Return-Path: <$from>\r\n" . 365 "MIME-Version: 1.0\n"; 366 if(!(is_array($files) || count($files))) { 367 $headers .= "Content-Type: text/html; charset=\"UTF-8\"\n" . 368 "Content-Transfer-Encoding: 7bit\n\n"; 369 } 370 else { 371 $semi_rand = md5(time()); 372 $mime_boundary = "==Multipart_Boundary_x{$semi_rand}x"; 373 $headers .= "Content-Type: multipart/mixed; boundary=\"{$mime_boundary}\"\n"; 374 $message = "--{$mime_boundary}\n" . 375 "Content-Type: text/plain; charset=\"UTF-8\"\n" . 376 "Content-Transfer-Encoding: 7bit\n\n" . 377 $message . "\n\n"; 378 foreach($files as $fn) { 379 $f = basename($fn); 380 if(!is_file($fn)) 381 continue; 382 $data = chunk_split(base64_encode(file_get_contents($fn))); 383 $message .= "--{$mime_boundary}\n" . 384 "Content-Type: application/octet-stream; name=\"$f\"\n" . 385 "Content-Description: $f\n" . 386 "Content-Disposition: attachment; filename=\"$f\"; size=".filesize($fn).";\n" . 387 "Content-Transfer-Encoding: base64\n\n" . $data . "\n\n"; 388 } 389 $message .= "--{$mime_boundary}--"; 390 } 391 return @mail($to, $subj, $message, $headers); 392 } 393 394 function store(&$to, $k, $v) { 395 $r = isset($to[$k]) ? $to[$k] : NULL; 396 if($v !== NULL) 397 $to[$k] = $v; 398 return $r; 399 } 400 401 function cache($k, $v = NULL) { 402 static $__cache = []; 403 return store($__cache, $k, $v); 404 } 405 406 function sess($k, $v = NULL) { 407 $r = isset($_SESSION[$k]) ? $_SESSION[$k] : NULL; 408 return store($_SESSION, $k, $v); 409 } 410 411 function pre($d) { 412 echo '<pre>'.print_r($d,1).'</pre>'; 413 } 414 415 function humanstime($timestamp, $fmts = NULL) { 416 global $HT_DEFAULT_FMTS, $HT_DIVS; 417 418 $divs = $HT_DIVS; 419 $diff = time() - $timestamp; 420 $isfuture = ($diff < 0); 421 if($isfuture) 422 $diff = -$diff; 423 foreach($divs as $name => $delta) 424 if($diff >= $delta) 425 break; 426 $unit = (int)($delta ? $diff / $delta : $diff); 427 $ht = [ 428 'name' => $name.($unit > 1 ? 's' : ''), 429 'unit' => $unit, 430 'isfuture' => $isfuture 431 ]; 432 if(!$fmts) 433 $fmts = $HT_DEFAULT_FMTS; 434 $k = $ht['name'].'_'.(int)$ht['isfuture']; 435 return sprintf($fmts[$k], $ht['unit']); 436 } 437 438 function curl_post($uri, $curlopts = []) { 439 $c = curl_init(); 440 441 curl_setopt($c, CURLOPT_RETURNTRANSFER, 1); 442 curl_setopt($c, CURLOPT_URL, $uri); 443 curl_setopt($c, CURLOPT_POST, true); 444 if($curlopts) 445 foreach($curlopts as $k => $v) 446 curl_setopt($c, $k, $v); 447 $ret = curl_exec($c); 448 curl_close($c); 449 return $ret; 450 } 451 452 function prepare_form() { 453 $ret = []; 454 $idx = 0; 455 foreach($_FILES as $grp => $fds) { 456 foreach($fds as $k => $vals) { 457 foreach($vals as $i => $txt) { 458 $t = $idx + $i; 459 if(!isset($ret[$t])) { 460 $ret[$t] = []; 461 $ret[$t]["grp"] = $grp; 462 /* related info */ 463 $ret[$t]["info"] = []; 464 foreach((array)@$_POST[$grp] as $ik => $info) { 465 if(!isset($info[$t])) { 466 /* XXX warn the user: possible fields mismatch */ 467 continue; 468 } 469 $ret[$t]["info"][$ik] = $info[$t]; 470 } 471 } 472 $ret[$t][$k] = $txt; 473 } 474 } 475 unset($_POST[$grp]); /* get rid of files-related POST data */ 476 $idx += count($ret); 477 } 478 $_FILES = $ret; 479 } 480 481 function buildhier($items, $pk = 'parent_id', $ck = 'id', $subk = 'children') { 482 $map = []; 483 foreach($items as $k => &$item) { 484 $c = $item[$ck]; 485 $map[$c] = &$item; 486 } 487 unset($item); 488 foreach($items as $item) { 489 $p = $item[$pk]; 490 if(!$p) 491 continue; 492 $c = $item[$ck]; 493 if(!isset($map[$p][$subk])) 494 $map[$p][$subk] = []; 495 $map[$p][$subk][$c] = $map[$c]; 496 $map[$c]['__rm'] = 1; 497 $map[$c] = &$map[$p][$subk][$c]; 498 } 499 foreach($items as $k => $item) 500 if(@$item['__rm']) 501 unset($items[$k]); 502 return $items; 503 } 504 505 /* Note: all variables here are accessible by the view (along with globals) */ 506 function viewinc($incname, $viewdata = []) { 507 if(!defined("VIEWDIR")) 508 die("VIEWDIR not defined"); 509 $viewfile = VIEWDIR.'/'.implode('/', explode('.', $incname)).'.php'; 510 if(!file_exists($viewfile)) 511 return NULL; 512 513 /* only provide specified variable names */ 514 if($viewdata) 515 foreach($viewdata as $k => $v) 516 ${$k} = $v; 517 unset($viewdata); 518 unset($incname); 519 520 ob_start(); 521 require($viewfile); 522 $d = ob_get_contents(); 523 ob_end_clean(); 524 return $d; 525 } 526 527 function lviewinc($name, $data = [], $layout = NULL, $layoutdata = []) { 528 if(!$layout) { 529 if(!defined('DEFAULT_LAYOUT')) 530 die('DEFAULT_LAYOUT not defined'); 531 $layout = DEFAULT_LAYOUT; 532 } 533 $content = viewinc($name, $data); 534 if($content === NULL) 535 return NULL; 536 return viewinc($layout, [ 537 "name" => $name, 538 "content" => $content, 539 "paths" => array_filter(explode('/', $_SERVER["REQUEST_URI"]), function($dir) { 540 return trim($dir); 541 }) 542 ]); 543 } 544 545 function view($name, $data = [], $layout = NULL, $layoutdata = NULL) { 546 if(!$layoutdata) 547 $layoutdata = $data; 548 return lviewinc($name, $data, $layout, $layoutdata); 549 } 550 551 function bored_init() { 552 setlocale(LC_CTYPE, ""); 553 prepare_form(); 554 session_start(); 555 if(defined('DBHOST') && defined('DBUSER') && defined('DBPASS') && defined('DBNAME')) 556 dbopen(DBHOST, DBUSER, DBPASS, DBNAME); 557 register_shutdown_function(function() { 558 global $dblink; 559 560 if($dblink) 561 mysqli_close($dblink); 562 session_write_close(); 563 }); 564 } 565 566 function bored_run($noinit = 0) { 567 if(!$noinit) 568 bored_init(); 569 echo route($_SERVER['REQUEST_METHOD'], (string)@explode('?', $_SERVER['REQUEST_URI'])[0]); 570 } 571 572 ?>