/ admin-test.php
admin-test.php
1 <? 2 include_once "yotsuba_config.php"; 3 require_once 'lib/util.php'; 4 /* 5 if( isset( $_REQUEST["profile"] ) ) { 6 xhprof_enable( XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY ); 7 register_shutdown_function( "xhprof_save" ); 8 } 9 if ( isset($_REQUEST["sqlprofile"] )) { 10 $mysql_query_log = YES; 11 } 12 */ 13 if( TEST_BOARD ) { 14 ini_set('display_errors', '1'); 15 if( isset( $_REQUEST[ "profile" ] ) ) { 16 xhprof_enable(); 17 register_shutdown_function( "xhprof_save" ); 18 } 19 } 20 21 include_once 'lib/rpc.php'; 22 include_once 'lib/admin-test.php'; 23 include_once 'lib/auth.php'; 24 include_once 'lib/json.php'; 25 include_once 'lib/geoip2.php'; 26 27 //include "strings_e.php"; //String resource file 28 define( SQLLOGBAN, 'banned_users' ); //Table (NOT DATABASE) used for holding banned users 29 define( SQLLOGMOD, 'mod_users' ); //Table (NOT DATABASE) used for holding mod users 30 define( SQLLOGDEL, 'del_log' ); //Table (NOT DATABASE) used for holding deletion log 31 32 extract( $_POST, EXTR_SKIP ); 33 extract( $_GET, EXTR_SKIP ); 34 extract( $_COOKIE, EXTR_SKIP ); 35 36 //if( isset( $_REQUEST['id'] ) ) $id = $_REQUEST['id']; // weird bug? 37 38 if( isset( $_POST[ 'id' ] ) && ctype_digit( $_POST[ 'id' ] ) ) $id = $_POST[ 'id' ]; 39 if( isset( $_GET[ 'id' ] ) && ctype_digit( $_GET[ 'id' ] ) ) $id = $_GET[ 'id' ]; 40 41 if( $argv[1] ) $admin = $argv[1]; 42 43 // FIXME whitelist 44 unset( $dest ); 45 unset( $log ); 46 unset( $update_avg_secs ); 47 48 $access_allow = ''; 49 $access_deny = ''; 50 51 mysql_board_connect( BOARD_DIR ); 52 53 function janitor_votes_left() 54 { 55 $user = $_COOKIE[ '4chan_auser' ]; 56 57 $high = mysql_global_do( "SELECT count(id) FROM janitor_votes WHERE moderator = '%s'", $user ); 58 $high = mysql_result( $high, 0, 0 ); 59 60 $howmany = mysql_global_do( "SELECT COUNT(id) FROM janitor_apps WHERE closed=0 AND age>17", $high ); 61 62 return mysql_result( $howmany, 0, 0 ) - $high; 63 } 64 65 function append_ban( $board, $ip ) 66 { 67 // run in background 68 $cmd = "nohup /usr/local/bin/suid_run_global bin/appendban $board $ip >/dev/null 2>&1 &"; 69 print "User banned from /$board/"; 70 // print $cmd . "<br>"; //disabling this because it's ugly and leaks filepaths 71 exec( $cmd ); 72 } 73 74 function https_self_url() 75 { 76 return "/".BOARD_DIR."/admin"; 77 } 78 79 // for lib/admin.php 80 function delete_uploaded_files() 81 { 82 83 } 84 85 function make_post_json($row) 86 { 87 $nrow = array(); 88 89 foreach( $row as $key => $val ) { 90 if( ctype_digit( $val ) || is_int( $val ) ) { 91 $val = (int)$val; 92 } 93 94 $nrow[ $key ] = $val; 95 } 96 97 return json_encode( $nrow ); 98 } 99 100 function get_board_list() { 101 //mysql_global_call("SET character_set_results = 'utf8'"); 102 103 $query = "SELECT dir, name FROM boardlist ORDER BY dir ASC"; 104 105 $res = mysql_global_call($query); 106 107 if (!$res) { 108 return array(); 109 } 110 111 $boards = array(); 112 113 while ($dir = mysql_fetch_row($res)) { 114 $boards[$dir[0]] = $dir[1]; 115 } 116 117 return $boards; 118 } 119 120 function is_board_valid($board, $allow_hidden = false) { 121 if (!$allow_hidden && ($board === 'test' || $board === 'j')) { 122 return false; 123 } 124 125 $query = "SELECT dir FROM boardlist WHERE dir = '%s' LIMIT 1"; 126 $res = mysql_global_call($query, $board); 127 128 if (!$res) { 129 return false; 130 } 131 132 if (mysql_num_rows($res) === 1) { 133 return true; 134 } 135 136 return false; 137 } 138 139 function admin_clear_reports($board, $post_id) { 140 $query = "UPDATE reports SET cleared = 1 WHERE board = '%s' AND no = %d"; 141 142 mysql_global_call($query, $board, $post_id); 143 144 $query = <<<SQL 145 UPDATE reports_for_posts 146 SET cleared = 1, clearedby = 'Auto-clear' 147 WHERE board = '%s' AND postid = %d 148 SQL; 149 150 mysql_global_call($query, $board, $post_id); 151 } 152 153 function get_bans_summary($value, $by_pass = false) { 154 if (!$value) { 155 return array(); 156 } 157 158 if ($by_pass === false) { 159 $col = 'host'; 160 $col_active = ''; 161 } 162 else { 163 $col = '4pass_id'; 164 $col_active = '(active = 0 OR active = 1) AND '; // pass column doesn't have an index for itself 165 } 166 167 $query = <<<SQL 168 SELECT UNIX_TIMESTAMP(`now`) as created_on, 169 UNIX_TIMESTAMP(`length`) as expires_on, 170 UNIX_TIMESTAMP(`unbannedon`) as unbanned_on 171 FROM banned_users 172 WHERE $col_active$col = '%s' 173 SQL; 174 175 $res = mysql_global_call($query, $value); 176 177 if (!$res) { 178 return array(); 179 } 180 181 $limit = $_SERVER['REQUEST_TIME'] - 31536000; // 1 year 182 183 $total_count = mysql_num_rows($res); 184 $recent_perma_count = 0; 185 $recent_ban_count = 0; 186 $recent_warn_count = 0; 187 $recent_duration = 0; // in days 188 189 while ($ban = mysql_fetch_assoc($res)) { 190 if ($ban['created_on'] < $limit) { 191 continue; 192 } 193 194 if (!$ban['expires_on']) { 195 ++$recent_perma_count; 196 continue; 197 } 198 199 $ban_len = $ban['expires_on'] - $ban['created_on']; 200 201 if ($ban_len <= 10) { 202 ++$recent_warn_count; 203 } 204 else { 205 if ($ban['unbanned_on']) { 206 $spent_len = $ban['unbanned_on'] - $ban['created_on']; 207 208 if ($spent_len > $ban_len) { 209 $spent_len = $ban_len; 210 } 211 } 212 else { 213 $spent_len = $ban_len; 214 } 215 216 $recent_duration += $spent_len; 217 ++$recent_ban_count; 218 } 219 } 220 221 if ($recent_duration) { 222 $recent_duration = round($recent_duration / 86400.0); 223 } 224 225 return array( 226 'total' => $total_count, 227 'recent_bans' => $recent_ban_count, 228 'recent_warns' => $recent_warn_count, 229 'recent_days' => $recent_duration, 230 'recent_permas' => $recent_perma_count 231 ); 232 } 233 234 // Counts recently made threads by IP 235 function admin_get_thread_history($ip) { 236 $long_ip = ip2long($ip); 237 238 if (!$long_ip) { 239 return false; 240 } 241 242 $sql = "SELECT COUNT(*) FROM user_actions WHERE action = 'new_thread' AND ip = $long_ip AND time >= DATE_SUB(NOW(), INTERVAL 60 MINUTE)"; 243 244 $res = mysql_global_call($sql); 245 246 if (!$res) { 247 return false; 248 } 249 250 return (int)mysql_fetch_row($res)[0]; 251 } 252 253 function admin_hash_4chan_pass($pass) { 254 $salt = file_get_contents(SALTFILE); 255 256 if (!$salt || !$pass) { 257 return ''; 258 } 259 260 return sha1($pass . $salt); 261 } 262 263 function get_ban_history_html($ban_summary, $host = false) { 264 $ban_tip = array(); 265 266 if ($ban_summary['recent_bans'] > 0) { 267 $ban_tip[] = $ban_summary['recent_bans'] . ' ban' . ($ban_summary['recent_bans'] > 1 ? 's' : ''); 268 } 269 270 if ($ban_summary['recent_warns'] > 0) { 271 $ban_tip[] = $ban_summary['recent_warns'] . ' warning' . ($ban_summary['recent_warns'] > 1 ? 's' : ''); 272 } 273 274 if ($ban_summary['recent_days'] > 0) { 275 $ban_tip[] = $ban_summary['recent_days'] . ' day' 276 . ($ban_summary['recent_days'] > 1 ? 's' : '') 277 . ' spent banned'; 278 } 279 280 if ($ban_summary['recent_permas'] > 0) { 281 $ban_tip[] = $ban_summary['recent_permas'] . ' permaban' . ($ban_summary['recent_permas'] > 1 ? 's' : ''); 282 } 283 284 $ban_tip = "<strong>Past 12 months history</strong><ul class=\"ban-tip-cnt\"><li>" . implode('</li><li>', $ban_tip) . '</li></ul>'; 285 286 if ($host !== false) { 287 return "<div id=\"ban-tip-ip\" style=\"display:none\">$ban_tip</div><small>[ <a data-tip data-tip-type=\"ip\" data-tip-cb=\"showBanTip\" href=\"https://team.4chan.org/bans?action=search&ip=$host\" target=\"_blank\">{$ban_summary['total']} ban" . 288 (($ban_summary['total'] > 1) ? 's' : '') . " for this IP</a> ]</small>"; 289 } 290 else { 291 return "<div id=\"ban-tip-pass\" style=\"display:none\">$ban_tip</div><small>[ <a data-tip data-tip-type=\"pass\" data-tip-cb=\"showBanTip\" href=\"https://team.4chan.org/bans?action=search&pass_ref=%2F" . BOARD_DIR . "%2F" . (int)$_GET['id'] . "\" target=\"_blank\">{$ban_summary['total']} ban" . 292 (($ban_summary['total'] > 1) ? 's' : '') . " for this Pass</a> ]</small>"; 293 } 294 } 295 296 function ban_post( $no, $globalban, $length, $reason, $is_threadban = 0 ) 297 { 298 $query = mysql_board_call( "SELECT HIGH_PRIORITY * FROM `" . SQLLOG . "` WHERE no=" . intval( $no ) ); //FIXME use assoc 299 $row = mysql_fetch_assoc( $query ); 300 if( !$row ) return ""; 301 extract( $row, EXTR_OVERWRITE ); 302 303 //list( $no, $sticky, $permasage, $closed, $now, $name, $email, $sub, $com, $host, $pwd, $filename, $ext, $w, $h, $tn_w, $tn_h, $tim, $time, $md5, $fsize, $root, $resto ) = $row; 304 $name = str_replace( '</span> <span class="postertrip">!', ' #', $name ); 305 $name = preg_replace( '/<[^>]+>/', '', $name ); // remove all remaining html crap 306 307 if( $host ) $reverse = gethostbyaddr( $host ); 308 $displayhost = ( $reverse && $reverse != $host ) ? "$reverse ($host)" : $host; 309 $xff = ''; 310 311 //$xffresult = mysql_board_call("select host from xff where board='%s' and postno=%d", BOARD_DIR, $no); 312 //$xffresult = mysql_global_call( "SELECT xff from xff where board='%s' AND postno='%d'", BOARD_DIR, $no ); 313 //if( $xffrow = mysql_fetch_row( $xffresult ) ) { 314 // $xff = $xffrow[ 0 ]; 315 // $xff_reverse = gethostbyaddr($xffrow[0]); 316 // $xff = ($xff_reverse && $xff_reverse!=$xffrow[0])?"$xff_reverse ($xff)":$xff; 317 //} 318 $board = BOARD_DIR; 319 $zonly = 0; 320 321 $bannedby = $_COOKIE[ '4chan_auser' ]; 322 $pass_id = $row[ '4pass_id' ]; 323 $post_json = make_post_json($row); 324 325 $result = mysql_global_do( 326 "INSERT INTO " . SQLLOGBAN . " 327 (board,global,zonly,name,host,reverse,xff,reason,length,admin,md5,4pass_id,post_num,post_time,post_json,admin_ip) 328 VALUES 329 ( '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', %d, FROM_UNIXTIME(%d), '%s', '%s')", 330 $board, $globalban, $zonly, $name, $host, $reverse, $xff, $reason, $length, $bannedby, $md5, $pass_id, $no, $time, $post_json, $_SERVER['REMOTE_ADDR'] ); 331 332 //('" . $board . "','" . $globalban . "','" . $zonly . "','" . mysql_escape_string( $name ) . "','" . $host . "','" . mysql_escape_string( $reverse ) . "','" . mysql_escape_string( $xff ) . "','" . mysql_escape_string( $reason ) . "','$length','" . mysql_escape_string( $bannedby ) . "','$md5','$pass_id',$no,FROM_UNIXTIME('$time'), '%s')", $post_json ) ) { 333 if( !$result ) { 334 echo S_SQLFAIL; 335 } 336 337 /*if( $ext != '' ) { 338 $salt = file_get_contents( SALTFILE ); 339 $hash = sha1( BOARD_DIR . $no . $salt ); 340 @copy( THUMB_DIR . "{$tim}s.jpg", BANTHUMB_DIR . "{$hash}s.jpg" ); 341 }*/ 342 /* 343 $afsize = (int)( $fsize > 0 ); 344 validate_admin_cookies(); 345 if( $is_threadban ) mysql_global_do( "INSERT INTO " . SQLLOGDEL . " (imgonly,postno,resto,board,name,sub,com,img,filename,admin,admin_ip) values('0',%d,%d,'%s','%s','%s','%s','%d','%s','%s','%s')", $no, $resto, SQLLOG, $name, $sub, $com, $afsize, $filename.$ext, $bannedby, $_SERVER['REMOTE_ADDR'] ); // FIXME do all this in one insert outside the write lock 346 */ 347 echo "$displayhost banned.<br>\n"; 348 349 return $host; 350 } 351 352 function cpban($no) { 353 $no = (int)$no; 354 355 if (!$no) { 356 die('Invalid thread number.'); 357 } 358 359 $op_reason = htmlspecialchars($_POST['op_reason']); 360 $rep_reason = htmlspecialchars($_POST['rep_reason']); 361 362 if (!$op_reason || !$rep_reason) { 363 die('Ban reason cannot be empty.'); 364 } 365 366 $op_days = (int)$_POST['op_days']; 367 $rep_days = (int)$_POST['rep_days']; 368 369 if ($op_days < 0 || $rep_days < 0 || $op_days > 9999 || $rep_days > 9999) { 370 die('Invalid ban length.'); 371 } 372 373 $op_ban_end = date('YmdHis', time() + $op_days * (24 * 60 * 60)); 374 $rep_ban_end = date('YmdHis', time() + $rep_days * (24 * 60 * 60)); 375 376 $op_host = ban_post($no, 1, $op_ban_end, "$op_reason<>Thread Ban No.$no", 1); 377 378 if (!$op_host) { 379 die("Thread $no doesn't exist."); 380 } 381 382 $query = mysql_board_call("SELECT no, host FROM `" . SQLLOG . "` WHERE resto = $no AND host != '$op_host' GROUP BY host"); 383 384 while ($row = mysql_fetch_assoc($query)) { 385 ban_post($row['no'], 1, $rep_ban_end, "$rep_reason<>Thread Ban No.$no", 1); 386 } 387 388 delete_post($no, false, null, 'threadban'); 389 390 echo 'Done.<script language="JavaScript">setTimeout("self.close()", 3000); postBack("done-ban-' . SQLLOG . '-' . $no . '");</script><br>'; 391 } 392 393 function delete_post($no, $imgonly, $template_id = null, $tool = null) { 394 $url = "/".BOARD_DIR."/post"; 395 396 $post = array( 397 'mode' => 'usrdel', 398 'onlyimgdel' => $imgonly ? 'on' : '', 399 $no => 'delete', 400 'remote_addr' => $_SERVER['REMOTE_ADDR'] 401 ); 402 403 if ($template_id) { 404 $post['template_id'] = $template_id; 405 } 406 407 if ($tool) { 408 $post['tool'] = $tool; 409 } 410 411 rpc_start_request("https://sys.int$url", $post, $_COOKIE, true); 412 413 // don't bother waiting to check for errors 414 415 return true; 416 } 417 418 function archive_thread($thread_id) { 419 $url = "/".BOARD_DIR."/post"; 420 421 $post = array( 422 'mode' => 'forcearchive', 423 'id' => $thread_id 424 ); 425 426 rpc_start_request("https://sys.int$url", $post, $_COOKIE, true); 427 428 // don't bother waiting to check for errors 429 430 return true; 431 } 432 433 function move_thread($thread_id, $board) { 434 $url = "/".BOARD_DIR."/post"; 435 436 $post = array( 437 'mode' => 'movethread', 438 'id' => $thread_id, 439 'board' => $board 440 ); 441 442 rpc_start_request("https://sys.int$url", $post, $_COOKIE, true); 443 444 // don't bother waiting to check for errors 445 446 return true; 447 } 448 449 function rebuild_thread($no, &$error = '', $is_archived = false) { 450 $url = '/' . BOARD_DIR . '/post'; 451 452 if (!$is_archived) { 453 $post = array( 454 'mode' => 'rebuildadmin', 455 'no' => $no 456 ); 457 } 458 else { 459 $post = array(); 460 $post['mode'] = 'rebuild_threads_by_id'; 461 $post['ids'] = array($no); 462 $post = http_build_query($post); 463 } 464 465 rpc_start_request("https://sys.int$url", $post, $_COOKIE, true); 466 467 return true; 468 } 469 470 function rebuild_all(&$error = '') { 471 $url = '/' . BOARD_DIR . '/post'; 472 473 $post = array( 474 'mode' => 'rebuildall' 475 ); 476 477 rpc_start_request("https://sys.int$url", $post, $_COOKIE, true); 478 479 return true; 480 } 481 482 function dir_contents( $dir ) 483 { 484 $d = opendir( $dir ); 485 $a = array(); 486 if( !$d ) return $a; 487 488 while( ( $f = readdir( $d ) ) !== false ) { 489 if( $f == "." || $f == ".." || $f == "" ) continue; 490 $a[ ] = $f; 491 } 492 493 closedir( $d ); 494 495 return $a; 496 } 497 498 function clean() 499 { 500 // Survive oversized boards. 501 set_time_limit(0); 502 ini_set("memory_limit", "-1"); 503 504 $images = array(); 505 $respages = array(); 506 $indexpages = array(); 507 508 if( PAGE_MAX > 0 ) { 509 print "<strong>Running cleanup...</strong><br>Pruning orphaned posts...<br>"; 510 $result = mysql_board_call( "select no from `%s` where resto>0 and resto not in (select no from `%s` where resto=0)", SQLLOG, SQLLOG ); 511 $nos = mysql_column_array( $result ); 512 if( count( $nos ) ) { 513 mysql_board_call( "delete from `" . SQLLOG . "` where no in (%s)", implode( $nos, "," ) ); 514 foreach( $nos as $no ) { 515 print "$no pruned<br>"; 516 } 517 } 518 } 519 520 //clearstatcache(); 521 522 // get list of images that should exist 523 if (MOBILE_IMG_RESIZE) { 524 $cols = ',m_img'; // FIXME, only because not all boards have that column 525 } 526 $result = mysql_board_call( "select tim,filename,ext$cols from `" . SQLLOG . "` where ext != ''" ); 527 while( $row = mysql_fetch_array( $result ) ) { 528 if( $row[ 'ext' ] == '.swf' ) { 529 $images[ "{$row[ 'filename' ]}{$row[ 'ext' ]}" ] = 1; 530 } 531 else { 532 $images[ "{$row[ 'tim' ]}{$row[ 'ext' ]}" ] = 1; // picture 533 $images[ "{$row[ 'tim' ]}s.jpg" ] = 1; // thumb 534 535 if (ENABLE_OEKAKI_REPLAYS) { 536 $images["{$row['tim']}.tgkr"] = 1; // oe animation 537 } 538 539 if (MOBILE_IMG_RESIZE) { 540 $images["{$row['tim']}m.jpg"] = 1; // resized 541 } 542 } 543 } 544 545 // get list of res pages that should exist 546 $result = mysql_board_call( "select no from `" . SQLLOG . "` where resto=0" ); 547 while( $row = mysql_fetch_array( $result ) ) { 548 if( USE_GZIP == 1 ) { 549 $respages[ "{$row[ 'no' ]}.html.gz" ] = 1; 550 551 if (ENABLE_JSON) { 552 $respages[$row['no'] . '.json.gz'] = 1; 553 554 if (JSON_TAIL_SIZE) { 555 $respages[$row['no'] . '-tail.json.gz'] = 1; 556 } 557 } 558 } 559 else { 560 $respages[ "{$row[ 'no' ]}.html" ] = 1; 561 562 if (ENABLE_JSON) { 563 $respages[$row['no'] . '.json'] = 1; 564 565 if (JSON_TAIL_SIZE) { 566 $respages[$row['no'] . '-tail.json'] = 1; 567 } 568 } 569 } 570 571 if( JANITOR_BOARD ) $respages[ $row[ 'no' ] . '.html.php' ] = 1; 572 } 573 574 print "Cleaning src dir...<br>"; 575 foreach( dir_contents( IMG_DIR ) as $filename ) { 576 if( $images[ $filename ] != 1 && !preg_match('/dmca_/', $filename) && $filename !== 'src') { 577 print "Deleted $filename<br>"; 578 //if (file_exists(IMG_DIR . "$filename")) { 579 unlink(IMG_DIR . "$filename") or print "Couldn't delete!<br>"; 580 //} 581 } 582 } 583 584 print "Cleaning thumb dir...<br>"; 585 foreach( dir_contents( THUMB_DIR ) as $filename ) { 586 if( $images[ $filename ] != 1 && !preg_match('/dmca_/', $filename)) { 587 print "Deleted $filename<br>"; 588 //if (file_exists(THUMB_DIR . "$filename")) { 589 unlink(THUMB_DIR . "$filename") or print "Couldn't delete!<br>"; 590 //} 591 } 592 } 593 594 print "Cleaning res dir...<br>"; 595 foreach( dir_contents( RES_DIR ) as $filename ) { 596 if( $respages[ $filename ] != 1 ) { 597 print "Deleted $filename<br>"; 598 unlink( RES_DIR . "$filename" ) or print "Couldn't delete!<br>"; 599 } 600 } 601 602 print "Cleaning index pages...<br>"; 603 $result = mysql_board_call( "SELECT COUNT(*) from `" . SQLLOG . "` WHERE archived = 0 AND resto = 0" ); 604 $lastpage = PAGE_MAX + 1;//(mysql_result( $result, 0, 0 ) / DEF_PAGES) + 1; 605 if( USE_GZIP == 1 ) { 606 $indexpages[ SELF_PATH2_FILE . '.gz' ] = 1; 607 608 if (USE_RSS) { 609 $indexpages[INDEX_DIR . 'index.rss.gz'] = 1; 610 } 611 if (ENABLE_CATALOG) { 612 $indexpages[INDEX_DIR . 'catalog.html.gz'] = 1; 613 } 614 if (ENABLE_JSON_CATALOG) { 615 $indexpages[INDEX_DIR . 'catalog.json.gz'] = 1; 616 } 617 if (ENABLE_JSON_THREADS) { 618 $indexpages[INDEX_DIR . 'threads.json.gz'] = 1; 619 $indexpages[INDEX_DIR . 'archive.json.gz'] = 1; 620 } 621 if (ENABLE_ARCHIVE) { 622 $indexpages[INDEX_DIR . 'archive.html.gz'] = 1; 623 } 624 } 625 626 $indexpages[ SELF_PATH2_FILE ] = 1; 627 628 if (USE_RSS) { 629 $indexpages[INDEX_DIR . 'index.rss'] = 1; 630 } 631 if (ENABLE_CATALOG) { 632 $indexpages[INDEX_DIR . 'catalog.html'] = 1; 633 } 634 if (ENABLE_JSON_CATALOG) { 635 $indexpages[INDEX_DIR . 'catalog.json'] = 1; 636 } 637 if (ENABLE_JSON_THREADS) { 638 $indexpages[INDEX_DIR . 'threads.json'] = 1; 639 $indexpages[INDEX_DIR . 'archive.json'] = 1; 640 } 641 if (ENABLE_ARCHIVE) { 642 $indexpages[INDEX_DIR . 'archive.html'] = 1; 643 } 644 645 for( $page = 1; $page < $lastpage; $page++ ) { 646 if( USE_GZIP == 1 ) { 647 $indexpages[ INDEX_DIR . $page . PHP_EXT . '.gz' ] = 1; 648 if (ENABLE_JSON_INDEXES) { 649 $indexpages[INDEX_DIR . $page . '.json.gz'] = 1; 650 } 651 } 652 $indexpages[ INDEX_DIR . $page . PHP_EXT ] = 1; 653 if (ENABLE_JSON_INDEXES) { 654 $indexpages[INDEX_DIR . $page . '.json'] = 1; 655 } 656 } 657 658 foreach( glob( INDEX_DIR . '*.{html,gz}', GLOB_BRACE ) as $filename ) { 659 $bfilename = basename( $filename ); 660 if( $indexpages[ $filename ] != 1 ) { 661 print "Deleted $bfilename<br>"; 662 unlink( $filename ) or print "Couldn't delete!<br>"; 663 } 664 } 665 666 print "Cleaning tmp uploads...<br>"; 667 $phptmp = ini_get( "upload_tmp_dir" ); 668 exec( "find $phptmp/ -mtime +2h -name php*", $tmpfiles ); 669 exec( "find -E " . INDEX_DIR . " -regex '.*/(gz)?tmp.*$' -mtime +2h", $indextmp ); 670 exec( "find -E " . RES_DIR . " -regex '.*/(gz)?tmp.*$' -mtime +2h", $restmp ); 671 672 $tmpfiles = array_merge( $tmpfiles, $indextmp ); 673 $tmpfiles = array_merge( $tmpfiles, $restmp ); 674 675 foreach( $tmpfiles as $filename ) { 676 $safename = explode( '/' . BOARD_DIR . '/', $filename ); 677 $safename = end( $safename ); 678 679 print "Deleted $safename<br>"; 680 unlink( $filename ) or print "Couldn't delete!<br>"; 681 } 682 /*print "Cleaning /var/tmp<br>"; 683 exec("find /var/tmp/ -mtime +2h -type f", $tmpfiles); 684 foreach($tmpfiles as $filename) { 685 print "Delete $filename<br>"; unlink($filename) or print "Couldn't delete!<br>"; 686 }*/ 687 print "Cleaning up side tables...<br>"; 688 689 mysql_global_call( "DELETE FROM user_actions WHERE time < DATE_SUB(NOW(), INTERVAL 7 DAY)" ); 690 mysql_global_call( "DELETE FROM event_log WHERE created_on < DATE_SUB(NOW(), INTERVAL 7 DAY)" ); 691 mysql_global_call( "DELETE FROM xff WHERE tim < (unix_timestamp(DATE_SUB(NOW(), INTERVAL 7 DAY))*1000)" ); 692 mysql_board_call( "DELETE FROM f_md5 WHERE now < DATE_SUB(NOW(), INTERVAL 2 DAY)" ); 693 mysql_board_call("DELETE FROM r9k_posts WHERE created_on < DATE_SUB(NOW(), INTERVAL 2 YEAR)"); 694 695 print "<strong>Cleanup complete!</strong>"; 696 } 697 698 // Changes relative board urls to absolute //sys.4chan.org admin urls 699 function fix_board_nav($nav) { 700 return preg_replace('/href="\/([a-z0-9]+)\/"/', "href=\"//sys." . L::d(BOARD_DIR) . "/$1/admin\"", $nav); 701 } 702 703 /* head */ 704 function head( &$dat, $is_logged_in = false ) 705 { 706 global $admin, $access_allow, $access_deny; 707 708 $allowed_modes = array('ban', 'delall', 'unban', 'opt', 'banreq', 'editop'); 709 710 if( !is_user() || ( is_user() && ( $admin != "ban" ) && ( $admin != "delall" ) && ( $admin != "unban" ) && ( $admin != "opt" ) && ( $admin != 'banreq' ) && ( $admin != 'editop' ) ) ) { 711 $navinc = fix_board_nav(file_get_contents( NAV_TXT )) . '<br>'; 712 $navinc = str_replace( '[<a href="javascript:void(0);" id="settingsWindowLink">Settings</a>] ', '', $navinc ); 713 } 714 715 if (DEFAULT_BURICHAN) { 716 $style_cookie = 'ws_style'; 717 $ws = 'ws'; 718 } 719 else { 720 $style_cookie = 'nws_style'; 721 $ws = ''; 722 } 723 724 $preferred_style = $_COOKIE[$style_cookie]; 725 726 switch ($preferred_style) { 727 case 'Yotsuba New': 728 $style = 'yotsubanew'; 729 break; 730 case 'Yotsuba B New': 731 $style = 'yotsubluenew'; 732 break; 733 //case 'Futaba New': 734 // $style = 'futabanew'; 735 // break; 736 //case 'Burichan New': 737 // $style = 'burichannew'; 738 // break; 739 case 'Tomorrow': 740 $style = 'tomorrow'; 741 break; 742 case 'Photon': 743 $style = 'photon'; 744 break; 745 default: 746 $style = DEFAULT_BURICHAN ? 'yotsubluenew' : 'yotsubanew'; 747 break; 748 } 749 750 if (!in_array($admin, $allowed_modes)) { 751 $admin = ''; 752 } 753 754 if ($admin == 'ban') { 755 $page_title = 'Ban No.' . (int)$_GET['id'] . ' on /' . BOARD_DIR . '/'; 756 $no_header = true; 757 } 758 else if ($admin == 'banreq') { 759 $page_title = 'Ban request No.' . (int)$_GET['id'] . ' on /' . BOARD_DIR . '/'; 760 $no_header = true; 761 } 762 else { 763 $page_title = TITLE; 764 $no_header = isset($_GET['noheader']); 765 } 766 767 $fb_js = <<<JS 768 Feedback = { 769 showMessage: function(msg) { 770 var el; 771 772 Feedback.hideMessage(); 773 774 el = document.createElement('div'); 775 el.id = 'feedback'; 776 el.innerHTML = '<span class="feedback-error">' + msg + '</span>'; 777 778 document.body.insertBefore(el, document.body.firstElementChild); 779 }, 780 781 hideMessage: function() { 782 var el = document.getElementById('feedback'); 783 784 if (el) { 785 document.body.removeChild(el); 786 } 787 }, 788 789 checkTemplate: function(id) { 790 var tpl; 791 792 Feedback.hideMessage(); 793 794 if (id < 0) { 795 return; 796 } 797 798 tpl = window.templates[id]; 799 800 if (tpl.no == '1') { 801 Feedback.showMessage('<u>Only</u> use this ban template for images depicting apparent child pornography. For links and non-pornographic images, please use the appropriate template(s).'); 802 } 803 else if (tpl.no == '123' || tpl.no == '126') { 804 Feedback.showMessage('Images depicting apparent child pornography should be banned using the "Child Pornography (Explicit Image)" template.'); 805 } 806 } 807 }; 808 JS; 809 810 $tooltip_js = <<<JS 811 var Tip = { 812 node: null, 813 timeout: null, 814 delay: 150, 815 816 init: function() { 817 document.addEventListener('mouseover', this.onMouseOver, false); 818 document.addEventListener('mouseout', this.onMouseOut, false); 819 }, 820 821 onMouseOver: function(e) { 822 var cb, data, t; 823 824 t = e.target; 825 826 if (Tip.timeout) { 827 clearTimeout(Tip.timeout); 828 Tip.timeout = null; 829 } 830 831 if (t.hasAttribute('data-tip')) { 832 data = null; 833 834 if (t.hasAttribute('data-tip-cb')) { 835 cb = t.getAttribute('data-tip-cb'); 836 if (cb.indexOf('.') !== -1) { 837 cb = cb.split('.'); 838 if (window[cb[0]] && (cb = window[cb[0]][cb[1]])) { 839 data = cb(t); 840 } 841 } 842 else if (window[cb]) { 843 data = window[cb](t); 844 } 845 if (data === null) { 846 return; 847 } 848 } 849 Tip.timeout = setTimeout(Tip.show, Tip.delay, e.target, data); 850 } 851 }, 852 853 onMouseOut: function(e) { 854 if (Tip.timeout) { 855 clearTimeout(Tip.timeout); 856 Tip.timeout = null; 857 } 858 859 Tip.hide(); 860 }, 861 862 show: function(t, data, pos) { 863 var el, rect, style, left, top; 864 865 rect = t.getBoundingClientRect(); 866 867 el = document.createElement('div'); 868 el.id = 'tooltip'; 869 870 if (data) { 871 el.innerHTML = data; 872 } 873 else { 874 el.textContent = t.getAttribute('data-tip'); 875 } 876 877 if (!pos) { 878 pos = 'top'; 879 } 880 881 el.className = 'tip-' + pos; 882 883 document.body.appendChild(el); 884 885 left = rect.left - (el.offsetWidth - t.offsetWidth) / 2; 886 887 if (left < 0) { 888 left = rect.left + 2; 889 el.className += '-right'; 890 } 891 else if (left + el.offsetWidth > document.documentElement.clientWidth) { 892 left = rect.left - el.offsetWidth + t.offsetWidth + 2; 893 el.className += '-left'; 894 } 895 896 top = rect.top - el.offsetHeight - 5; 897 898 style = el.style; 899 style.display = 'none'; 900 style.top = (top + window.pageYOffset) + 'px'; 901 style.left = left + window.pageXOffset + 'px'; 902 style.display = ''; 903 904 Tip.node = el; 905 }, 906 907 hide: function() { 908 if (Tip.node) { 909 document.body.removeChild(Tip.node); 910 Tip.node = null; 911 } 912 } 913 } 914 915 Tip.init(); 916 JS; 917 918 $dat .= '<!DOCTYPE html><html><head> 919 <meta http-equiv="content-type" content="text/html; charset=UTF-8"> 920 <meta http-equiv="pragma" content="no-cache"> 921 <meta name="viewport" content="width=device-width,initial-scale=1"> 922 <link rel="icon" type="image/x-icon" href="//s.4cdn.org/image/favicon-team' . $ws . '.ico"> 923 <link rel="stylesheet" style="text/css" href="//s.4cdn.org/css/' . $style . '.387.css"> 924 <script type="text/javascript">' . $fb_js . '</script> 925 <style type="text/css"> 926 table:not([class]) { 927 border: 1px solid #fff; 928 border-collapse: collapse; 929 930 margin: 0 auto; 931 } 932 933 #tooltip { 934 color: #dedede; 935 text-align: left; 936 } 937 938 .ico-phone { 939 opacity: 0.75; 940 font-size: 12px; 941 cursor: default; 942 } 943 944 .ban-tip-cnt { 945 padding: 0 0 0 10px; 946 margin: 2px 0 1px 0; 947 } 948 949 #js-move-board-sel { 950 display: none; 951 width: 100px; 952 } 953 954 #feedback { 955 top: 0px; 956 text-align: center; 957 z-index: 9999; 958 display: block; 959 background-color: #C41E3A; 960 margin: 0px; 961 left: 0px; 962 padding: 5px; 963 box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); 964 } 965 .feedback-error { 966 color: #fff; 967 font-size: 12px; 968 text-shadow: 0 1px rgba(0, 0, 0, 0.2); 969 } 970 971 .bantable { 972 width: 98%; 973 margin: 0; 974 } 975 976 .bantable-extra input[type="text"] { 977 font-size: 11px; 978 white-space: nowrap; 979 width: 260px; 980 } 981 982 .bantable-extra.bantable-tb input[type="text"] { 983 width: 244px; 984 } 985 986 .bantable-extra td { 987 font-size: 11px; 988 white-space: nowrap; 989 } 990 991 .postblock { 992 width: 80px; 993 } 994 995 #ban-days { width: 45px; } 996 997 #submit-ban-btn { 998 position: absolute; 999 right: 10px; 1000 margin-top: -2px; 1001 } 1002 1003 .bantable tr td:first-child { 1004 font-weight: bold; 1005 padding-right: 5px; 1006 } 1007 1008 table:not([class]) td { 1009 padding: 3px; 1010 border: 1px solid #fff; 1011 } 1012 1013 input[type="text"], textarea { 1014 margin: 0px; 1015 margin-right: 2px; 1016 padding: 2px 4px 3px 4px; 1017 border: 1px solid #AAA; 1018 outline: none; 1019 font-family: arial,helvetica,sans-serif; 1020 font-size: 10pt; 1021 } 1022 1023 .inputcenter { text-align: center; } 1024 1025 table { 1026 border-spacing: 1px; 1027 } 1028 1029 .tomorrow textarea { 1030 background-color: #282a2e; 1031 color: #c5c8c6; 1032 } 1033 1034 .tomorrow input[type="text"]:not(:focus), 1035 .tomorrow textarea:not(:focus) { 1036 border-color: #000; 1037 } 1038 1039 .glow-r { box-shadow: 0 0 4px 4px #e04000; } 1040 1041 *[disabled=disabled] { 1042 opacity: 0.5; 1043 } 1044 </style> 1045 <script type="text/javascript"> 1046 ' . ($is_logged_in ? ' 1047 /*localStorage.setItem("extra_path", "' . (has_level('mod') ? ADMIN_JS_PATH : JANITOR_JS_PATH) . '");*/ 1048 ' : '') . ' 1049 function checkBrowser(){ 1050 this.ver=navigator.appVersion 1051 this.dom=document.getElementById?1:0 1052 this.ie5=(this.ver.indexOf("MSIE 5")>-1 && this.dom)?1:0; 1053 this.ie4=(document.all && !this.dom)?1:0; 1054 this.ns5=(this.dom && parseInt(this.ver) >= 5) ?1:0; 1055 this.ns4=(document.layers && !this.dom)?1:0; 1056 this.bw=(this.ie5 || this.ie4 || this.ns4 || this.ns5) 1057 return this 1058 } 1059 bw=new checkBrowser() 1060 1061 function popup(vars) { 1062 //490 - 220 1063 var pheight = /admin=opt/.test(vars) ? 220 : 490; 1064 day = new Date(); 1065 id = day.getTime(); 1066 var newWindow; 1067 var props = \'scrollBars=no,resizable=no,toolbar=no,menubar=no,location=no,directories=no,width=400,height=\' + pheight; 1068 eval(\'popup\'+id+\' = window.open("admin?mode=admin&"+vars, "\'+id+\'", props);\'); 1069 } 1070 1071 function more(div,div2,nest){ 1072 obj=bw.dom?document.getElementById(div).style:bw.ie4?document.all[div].style:bw.ns4?nest?document[nest].document[div]:document[div]:0; 1073 obj2=bw.dom?document.getElementById(div2).style:bw.ie4?document.all[div2].style:bw.ns4?nest?document[nest].document[div2]:document[div2]:0; 1074 if(obj.display==\'\') { 1075 obj.display=\'none\'; 1076 obj2.display=\'none\'; 1077 } else { 1078 obj.display=\'\'; 1079 obj2.display=\'\'; 1080 } 1081 } 1082 1083 function swap(div,div2) { 1084 var el = document.getElementById(div); 1085 var el2 = document.getElementById(div2); 1086 var tmp = el.style.display; 1087 el.style.display = el2.style.display; 1088 el2.style.display = tmp; 1089 } 1090 1091 function postBack(msg) { 1092 if (self !== top) { 1093 window.parent.postMessage(msg, "*"); 1094 } 1095 } 1096 1097 document.addEventListener("DOMContentLoaded", onDOMReady, false); 1098 1099 function onDOMReady() { 1100 var el; 1101 1102 document.removeEventListener("DOMContentLoaded", onDOMReady, false); 1103 1104 el = document.getElementById("move-form"); 1105 1106 if (el) { 1107 el.addEventListener("submit", onMoveThread, false); 1108 }; 1109 1110 el = document.getElementById("autocomplete"); 1111 1112 if (el && !/Android|iPhone|iPad/.test(navigator.userAgent)) { 1113 el.focus(); 1114 }; 1115 1116 el = document.getElementById("js-postban-sel"); 1117 1118 if (el) { 1119 el.addEventListener("change", onPostBanSelChange, false); 1120 }; 1121 1122 el = document.getElementById("js-sticky-cb"); 1123 1124 if (el) { 1125 el.addEventListener("change", onStickyChange, false); 1126 }; 1127 1128 adjustMobileMargin(); 1129 }; 1130 1131 function adjustMobileMargin() { 1132 if (!window.matchMedia("(max-device-width: 480px)").matches) { 1133 return; 1134 } 1135 1136 let oh = document.documentElement.clientHeight; 1137 let ih = document.body.clientHeight; 1138 1139 if (oh - ih > 50) { 1140 document.body.style.paddingTop = (oh - ih - 50) + "px"; 1141 } 1142 } 1143 1144 function onStickyChange(e) { 1145 var el = document.getElementById("js-undead-cb"); 1146 1147 if (!this.checked) { 1148 el.checked = false; 1149 } 1150 1151 if (this.dataset.cur === "0") { 1152 el = document.getElementById("js-set-btn"); 1153 1154 if (this.checked) { 1155 el.classList.add("glow-r"); 1156 } 1157 else { 1158 el.classList.remove("glow-r"); 1159 } 1160 } 1161 } 1162 1163 function onMoveThread(e) { 1164 if (!checkSubmitConfirm(document.getElementById("js-move-btn"))) { 1165 e.preventDefault(); 1166 return; 1167 } 1168 } 1169 1170 function checkSubmitConfirm(el) { 1171 if (el.hasAttribute("data-js-confirming")) { 1172 clearSubmitConfirm(el); 1173 return true; 1174 } 1175 1176 el.setAttribute("data-js-confirming", "1"); 1177 1178 if (el.tagName === "BUTTON") { 1179 el.setAttribute("data-js-label", el.textContent); 1180 el.textContent = "Confirm?"; 1181 } 1182 else { 1183 el.setAttribute("data-js-label", el.value); 1184 el.value = "Confirm?"; 1185 } 1186 1187 let timeout = setTimeout(clearSubmitConfirm, 3000, el); 1188 1189 el.setAttribute("data-js-to", timeout); 1190 1191 return false; 1192 } 1193 1194 function clearSubmitConfirm(el) { 1195 let label = el.getAttribute("data-js-label"); 1196 1197 if (label === null) { 1198 return; 1199 } 1200 1201 let timeout = +el.getAttribute("data-js-to"); 1202 clearTimeout(timeout); 1203 1204 el.removeAttribute("data-js-confirming"); 1205 el.removeAttribute("data-js-to"); 1206 el.removeAttribute("data-js-label"); 1207 1208 if (el.tagName === "BUTTON") { 1209 el.textContent = label; 1210 } 1211 else { 1212 el.value = label; 1213 } 1214 } 1215 1216 admin = "' . $admin . '"; 1217 1218 document.addEventListener("keydown", onKeyDown, false); 1219 1220 function onKeyDown(e) { 1221 var board, postno; 1222 1223 board = "' . BOARD_DIR . '"; 1224 postno = ' . ((int)$_GET['id']) . '; 1225 1226 if (e.keyCode == 27 && !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey) { 1227 postBack("cancel-ban-" + board + "-" + postno); 1228 } 1229 1230 if( admin == "opt" ) { 1231 //sticky 83 1232 //permasage 80 1233 //closed 67 1234 //permaage 69 1235 //undead 85 1236 //spoiler 79 1237 //enter 13 1238 //S, P, C, E, U, O 1239 //Also give the sticky checkbox focus so I can hit tab once to change the number, 1240 //and then hit enter to submit. 1241 1242 if (document.activeElement.nodeName == "INPUT" && document.activeElement.type === "text") { 1243 return; 1244 } 1245 1246 if( e.ctrlkey || e.altkey || e.shiftkey || e.metakey ) return; 1247 1248 var kc = e.keyCode; 1249 var toggle = ""; 1250 1251 switch(kc) { 1252 case 83: 1253 toggle = "sticky"; 1254 break; 1255 case 80: 1256 toggle = "permasage"; 1257 break; 1258 case 67: 1259 toggle = "closed"; 1260 break; 1261 case 69: 1262 toggle = "permaage"; 1263 break; 1264 case 85: 1265 toggle = "undead"; 1266 break; 1267 case 79: 1268 toggle = "spoiler"; 1269 break; 1270 1271 case 13: 1272 document.getElementById("mainform").submit(); 1273 return true; 1274 1275 default: 1276 // hello 1277 break; 1278 } 1279 1280 if(toggle) toggleCheckbox(document.querySelector("input[name=" + toggle + "]")); 1281 1282 } 1283 } 1284 1285 ' . $tooltip_js . ' 1286 1287 function toggleCheckbox(elem) { 1288 elem.checked = !elem.checked; 1289 elem.focus(); 1290 } 1291 1292 function showBanTip(btn) { 1293 return document.getElementById("ban-tip-" + btn.getAttribute("data-tip-type")).innerHTML; 1294 } 1295 '; 1296 1297 if ($admin == 'ban') { 1298 $dat .= <<<JS 1299 function onPostBanSelChange(e) { 1300 var el, opt, to, i, o; 1301 1302 el = document.getElementById('js-move-board-sel'); 1303 1304 if (!el) { 1305 return; 1306 } 1307 1308 opt = this.options[this.selectedIndex]; 1309 1310 if (opt.value === 'move') { 1311 el.style.display = 'inline'; 1312 el.disabled = false; 1313 } 1314 else { 1315 el.style.display = ''; 1316 el.disabled = true; 1317 } 1318 } 1319 JS; 1320 } 1321 1322 $dat .= '</script> 1323 <title>' . $page_title . '</title> 1324 </head> 1325 <body class="' . $style . '"> 1326 '; 1327 1328 if (!$no_header) { 1329 $dat .= ' 1330 ' . str_replace( "12pt", "10pt", $navinc ) . ' 1331 <div class="boardBanner"><div class="boardTitle" style="font-size: 18pt !important;">' . TITLE . '</div></div> 1332 <hr width="90%" size=1>'; 1333 } 1334 } 1335 1336 /* Footer */ 1337 function foot( &$dat ) 1338 { 1339 $dat .= ' 1340 <center> 1341 <small>' . S_FOOT . ' 1342 </small> 1343 </center>wtf? 1344 ' . str_replace( "12pt", "10pt", $navinc2 ) . ' 1345 </body></html>'; 1346 } 1347 1348 function error( $mes, $dest = '' ) 1349 { 1350 global $upfile_name; 1351 if ($dest && file_exists($dest)) { 1352 unlink($dest); 1353 } 1354 head( $dat ); 1355 echo $dat; 1356 echo "<br><br> 1357 <center><font color=\"red\" size=\"5\"><b>$mes</b></font><br><br><font size=\"5\"><b>[<a href=" . SELF_PATH2_ABS . ">" . S_RELOAD . "</a>]</b></font></center>"; 1358 die( "</body></html>" ); 1359 } 1360 1361 /* text plastic surgery */ 1362 function sanitize_text( $str ) 1363 { 1364 global $admin; 1365 $str = trim( $str ); //blankspace removal 1366 if( get_magic_quotes_gpc() ) { //magic quotes is deleted (?) 1367 $str = stripslashes( $str ); 1368 } 1369 if( $admin != $adminpass ) { //admins can use tags 1370 $str = htmlspecialchars( $str ); //remove html special chars 1371 $str = str_replace( "&", "&", $str ); //remove ampersands 1372 } 1373 1374 return str_replace( ",", ",", $str ); //remove commas 1375 } 1376 1377 //check for table existance 1378 function table_exist( $table ) 1379 { 1380 $result = mysql_global_call( "show tables like '$table'" ); 1381 if( !$result ) { 1382 return 0; 1383 } 1384 $a = mysql_fetch_row( $result ); 1385 1386 return $a; 1387 } 1388 1389 function is_local() 1390 { 1391 if (!isset($_SERVER['REMOTE_ADDR'])) { 1392 return true; 1393 } 1394 1395 // local rpc can do anything 1396 $longip = ip2long( $_SERVER['REMOTE_ADDR'] ); 1397 1398 if( 1399 cidrtest( $longip, "10.0.0.0/24" ) || 1400 cidrtest( $longip, "204.152.204.0/24" ) || 1401 cidrtest( $longip, "127.0.0.0/24" ) 1402 ) { 1403 return true; 1404 } 1405 1406 return false; 1407 } 1408 1409 // FIXME hack 1410 function valid( $action = 'moderator', $no = 0 ) 1411 { 1412 return false; 1413 } 1414 1415 /*password validation */ 1416 function adminvalid( $title = 'Manager Mode' ) 1417 { 1418 global $user, $pass, $access_allow, $access_deny, $admin; 1419 1420 $level = 0; 1421 $levelarr = array( 'janitor' => 1, 'mod' => 2, 'manager' => 3, 'admin' => 4 ); 1422 1423 ob_start(); 1424 1425 // 1 = janitor, 2 = mod, 3 = manager, 4 = admin 1426 1427 if( is_local() ) { 1428 echo head( $dat ); 1429 1430 return; 1431 } 1432 1433 $user = $_COOKIE[ '4chan_auser' ]; 1434 $pass = $_COOKIE[ '4chan_apass' ]; 1435 1436 $valid = auth_user(); 1437 1438 if( $valid !== true ) { 1439 error( 'You do not have permission to access this page.' ); 1440 } 1441 1442 //if( !$valid ) admin_login_fail(); 1443 1444 1445 // Do we have permission for this board? 1446 if( $valid && ( $title !== 'Ban Request' && !access_board( BOARD_DIR ) ) ) { 1447 error( 'You do not have permission to access this board.' ); 1448 1449 } 1450 1451 if ($title !== 'Ban Request' && !has_level('mod')) { 1452 die(); 1453 } 1454 1455 if ($title === 'Board Cleanup' && !has_level('manager') && !has_flag('developer')) { 1456 error( 'You do not have permission to access this board.' ); 1457 } 1458 1459 if( $valid && has_level() && $_GET[ 'admin' ] == 'adminext' ) { 1460 return true; 1461 } 1462 1463 1464 head( $dat, $valid ); 1465 echo $dat; 1466 1467 $SELF_PATH2_ABS = SELF_PATH2_ABS; 1468 $S_RETURNS = S_RETURNS; 1469 $SELF_PATH = SELF_PATH; 1470 $S_LOGUPD = S_LOGUPD; 1471 $S_LOGUPDALL = S_LOGUPDALL; 1472 if (!isset($_GET['noheader']) && $title == 'Manager Mode') { 1473 1474 if( $valid && has_level( 'mod' ) ) { 1475 echo '<div style="clear: both;">'; 1476 echo '<span style="float:left;margin-bottom:3px;">[<a href="' . SELF_PATH2_ABS . '">' . S_RETURNS . '</a>] [<a href="' . SELF_PATH . '">' . S_LOGUPD . '</a>] [<a href="' . SELF_PATH . '?mode=rebuildall">' . S_LOGUPDALL . '</a>]'; 1477 1478 if( $_GET[ 'admin' ] == 'cleanup' ) { 1479 echo ' [<a href="admin">Admin</a>]'; 1480 } 1481 else if (has_level('manager') || has_flag('developer')) { 1482 echo ' [<a href="admin?admin=cleanup">Cleanup</a>]'; 1483 } 1484 1485 if( $_GET[ 'admin' ] == 'ban' ) { 1486 echo ' [<a href="//www.' . L::d(BOARD_DIR) . '/rules#' . BOARD_DIR . '" target="_blank" title="Open the rules for this board in a new window">Rules</a>]'; 1487 } 1488 1489 echo '</span>'; 1490 } 1491 elseif( $valid ) { 1492 //echo ' [<a href="//www.4chan.org/rules#' . BOARD_DIR . '" target="_blank" title="Open the rules for this board in a new window">Rules</a>] '; 1493 } 1494 1495 if( ( !isset( $_GET[ 'admin' ] ) || $_GET[ 'admin' ] == 'cleanup' ) && has_level( 'mod' ) ) { 1496 echo "<select style=\"float:right;\" onchange=\"var x=this.options[this.selectedIndex].value;x&&(window.location=x)\">"; 1497 $result = mysql_global_call( "select sql_cache domain,dir from boardlist order by dir" ); 1498 while( $row = mysql_fetch_array( $result ) ) { 1499 $domain = 'https://sys.' . L::d(BOARD_DIR) . '/'; 1500 if( $_GET[ 'admin' ] == 'cleanup' ) { 1501 $querystring = '?admin=cleanup'; 1502 } 1503 else $querystring = ''; 1504 if( $row[ 'dir' ] == SQLLOG ) { 1505 $selected = 'selected'; 1506 } 1507 else $selected = ''; 1508 echo "<option value=\"$domain{$row[ 'dir' ]}/admin$querystring\" $selected>{$row[ 'dir' ]}</option>\n"; 1509 } 1510 echo "</select>"; 1511 } 1512 } 1513 1514 $no_header = isset($_GET['noheader']) || $_GET[ 'admin' ] == 'ban' || $_GET[ 'admin' ] == 'banreq'; 1515 1516 if( $valid && !has_level( 'mod' ) ) { 1517 if ($no_header) { 1518 return; 1519 } 1520 echo "<br><br> 1521 <center><font color=\"red\" size=\"5\"><b>You are logged in as a janitor.</b></font><br><br><font size=\"5\"><b>[<a href=\"//boards." . L::d(BOARD_DIR) . '/' . BOARD_DIR . "/\">Return</a>]</b></font></center>"; 1522 die( '</body></html>' ); 1523 } 1524 1525 //if( $valid && (has_level('manager') || has_flag('developer')) ) { 1526 $GLOBALS[ 'b_sticky' ] = 1; 1527 //} 1528 1529 if( !$valid ) $title = 'Manager Mode'; 1530 if( !$valid ) echo '<div style="clear:both;">[<a href="//boards.' . L::d(BOARD_DIR) . '/' . BOARD_DIR . '" accesskey="a">' . S_RETURN . '</a>]</span></div>'; 1531 if( !$no_header ) echo '<div class="postingMode" style="clear: both;">' . $title . '</div>'; 1532 // Mana login form 1533 if( !$valid ) { 1534 echo "<form action=\"" . https_self_url() . "\" method=\"post\" style=\"margin-top: 5px;\">\n"; 1535 1536 echo "<center>"; 1537 echo "<input type=\"hidden\" name=\"mode\" value=\"admin\">\n"; 1538 echo "<table class=\"postForm\"><tbody>"; 1539 // echo "<tr><td>ID(s):</td><td><input type=text name=res size=8 value=\"".$_GET['res']."\"></td></tr>"; 1540 echo "<tr><td>Username</td><td><input type=\"text\" name=\"userlogin\" style=\"width: 120px; text-align: center;\" tabindex=\"1\"> <input type=\"submit\" value=\"" . S_MANASUB . "\" style=\"margin: 0;\" tabindex=\"3\"></td></tr>"; 1541 echo "<tr><td>Password</td><td><input type=\"password\" name=\"passlogin\" style=\"width: 120px; text-align: center;\" tabindex=\"2\"></td></tr>"; 1542 echo "</tbody></table></form></center>\n"; 1543 //echo file_get_contents( NAV2_TXT ); 1544 echo '</body></html>'; 1545 1546 1547 die(); 1548 } 1549 } 1550 1551 // FIXME 1552 function adminreportclear() { 1553 if (!has_level('mod')) { 1554 die('404'); 1555 } 1556 1557 $pid = (int)$_GET['pid']; 1558 1559 $board = $_GET['board']; 1560 1561 if (!$pid || !$board) { 1562 die('404'); 1563 } 1564 1565 $query = "UPDATE reports SET cleared = 1, cleared_by = '%s' WHERE board = '%s' AND no = $pid"; 1566 1567 $res = mysql_global_call($query, $_COOKIE['4chan_auser'], $board); 1568 1569 if (!$res) { 1570 die("DB error (2-1)"); 1571 } 1572 1573 $query = <<<SQL 1574 UPDATE reports_for_posts 1575 SET cleared = 1, clearedby = '%s' 1576 WHERE board = '%s' AND postid = $pid 1577 SQL; 1578 1579 $res = mysql_global_call($query, $_COOKIE['4chan_auser'], $board); 1580 1581 if (!$res) { 1582 die("DB error (2-2)"); 1583 } 1584 1585 echo "Done"; 1586 } 1587 1588 function adminreportqueue() { 1589 if (!has_level('mod')) { 1590 die('404'); 1591 } 1592 1593 $query = <<<SQL 1594 SELECT *, SUM(weight) as total_weight, COUNT(reports.no) as cnt, 1595 GROUP_CONCAT(DISTINCT report_category) as cats, 1596 UNIX_TIMESTAMP(ts) as `time` 1597 FROM reports 1598 WHERE cleared = 0 1599 GROUP BY reports.no, reports.board 1600 ORDER BY total_weight DESC 1601 LIMIT 200 1602 SQL; 1603 1604 $res = mysql_global_call($query); 1605 1606 if (!$res) { 1607 die('DB error (1)'); 1608 } 1609 1610 while ($report = mysql_fetch_assoc($res)) { 1611 $post = json_decode($report['post_json'], true); 1612 1613 echo '<div style="margin:12px;border:1px solid">'; 1614 if ($post['fsize'] > 0) { 1615 echo '<img src="https://i.4cdn.org/' . $report['board'] . '/' . $post['tim'] . 's.jpg">'; 1616 } 1617 1618 $tid = $post['resto'] ? $post['resto'] : $report['no']; 1619 1620 echo "<a target=\"_blank\" href=\"https://boards." . L::d($report['board']) . "/{$report['board']}/thread/$tid#p{$report['no']}\"><b>/{$report['board']}/{$report['no']} ({$report['total_weight']})</b></a> — "; 1621 1622 echo '<a style="color:red" target="_blank" href="?admin=reportclear&board=' . $report['board'] . '&pid=' . $report['no'] . '">[CLEAR]</a>'; 1623 1624 echo "<p>{$post['com']}</p>"; 1625 1626 echo "</div>"; 1627 } 1628 } 1629 1630 /* Admin deletion */ 1631 // This might not be used anymore 1632 function admin_delete() 1633 { 1634 if( !has_level('mod') ) return true; 1635 1636 global $admin, $onlyimgdel, $res, $thread, $ip, $user, $pass; 1637 1638 if( ( $admin != "ban" ) && ( $admin != "delall" ) && ( $admin != "unban" ) ) { 1639 $navinc = ''; //file_get_contents( NAV_TXT ); 1640 } 1641 if( !isset( $_POST[ 'p' ] ) ) { 1642 $p = 1; 1643 } 1644 else { 1645 $p = $_POST[ 'p' ]; 1646 } 1647 $max_results = 30; 1648 $from = ( ( $p * $max_results ) - $max_results ); 1649 1650 $board = explode( "/", $_SERVER[ 'SCRIPT_NAME' ] ); 1651 $board = $board[ 1 ]; 1652 1653 $threadmode = $_REQUEST[ 'threadmode' ]; 1654 if( !$threadmode ) { // threadmode uses table aliases, so don't bother locking 1655 if( $delflag ) mysql_board_call( "LOCK TABLES `" . SQLLOG . "` WRITE" ); 1656 } 1657 1658 $delno = array(); 1659 $delflag = false; 1660 reset( $_POST ); 1661 while( $item = each( $_POST ) ) { 1662 if( $item[ 1 ] == 'delete' ) { 1663 array_push( $delno, intval( $item[ 0 ] ) ); 1664 $delflag = true; 1665 } 1666 } 1667 if( $delflag ) { 1668 $resultstr = "(" . implode( ",", $delno ) . ")"; 1669 if( $threadmode ) mysql_board_call( "LOCK TABLES `" . SQLLOG . "` WRITE" ); // can finally lock it now 1670 if( !$result = mysql_board_call( "select * from `" . SQLLOG . "` where no in %s or resto in %s", $resultstr, $resultstr ) ) { 1671 echo S_SQLFAIL; 1672 } //FIXME use assoc 1673 $find = false; 1674 while( $row = mysql_fetch_assoc( $result ) ) { 1675 //list( $no, $sticky, $permasage, $closed, $now, $name, $email, $sub, $com, $host, $pwd, $filename, $ext, $w, $h, $tn_w, $tn_h, $tim, $time, $md5, $fsize, $root, $resto ) = $row; 1676 extract( $row, EXTR_OVERWRITE ); 1677 1678 if( $onlyimgdel == 'on' ) { 1679 if( array_search( $no, $delno ) ) { //only a picture is deleted 1680 if( $board == "f" ) { 1681 $delfile = IMG_DIR . $filename . $ext; 1682 } 1683 else { 1684 $delfile = IMG_DIR . $tim . $ext; //only a picture is deleted 1685 } 1686 unlink( $delfile ); //delete 1687 unlink( THUMB_DIR . $tim . 's.jpg' ); //delete 1688 } 1689 } 1690 else { 1691 if( array_search( $no, $delno ) || array_search( $resto, $delno ) ) { //It is empty when deleting 1692 $find = true; 1693 $auser = $_COOKIE[ '4chan_auser' ]; 1694 $apass = $_COOKIE[ '4chan_apass' ]; 1695 if( !mysql_board_call( "delete from `" . SQLLOG . "` where no=" . $no . " or resto=" . $no ) ) { 1696 echo S_SQLFAIL; 1697 } // FIXME can't this be atomic? (one statement) 1698 if( $board == "f" ) { 1699 $delfile = IMG_DIR . $filename . $ext; 1700 } 1701 else { 1702 $delfile = IMG_DIR . $tim . $ext; 1703 } 1704 unlink( $delfile ); //Delete 1705 unlink( THUMB_DIR . $tim . 's.jpg' ); //Delete 1706 if( $fsize > 0 ) $adfsize = 1; 1707 $adname = str_replace( '</span> <span class="postertrip">!', '#', $name ); 1708 if( $onlyimgdel == "on" ) { 1709 $imgonly = 1; 1710 } 1711 else { 1712 $imgonly = 0; 1713 } 1714 validate_admin_cookies(); 1715 mysql_global_do( "INSERT INTO " . SQLLOGDEL . " (imgonly,postno,resto,board,name,sub,com,img,filename,admin,admin_ip) values('$imgonly','$no',$resto,'" . SQLLOG . "','$adname','$sub','$com','$adfsize','$filename$ext','$auser', '" . mysql_real_escape_string($_SERVER['REMOTE_ADDR']) . "')" ); // FIXME do all this in one insert outside the write lock 1716 } 1717 } 1718 } 1719 } 1720 1721 if( $delflag ) mysql_board_call( "UNLOCK TABLES" ); 1722 1723 // Deletion screen display 1724 echo "<center style=\"margin-top: 4px;\"><input type=\"hidden\" name=\"mode\" value=\"admin\">\n"; 1725 echo "<input type=\"hidden\" name=\"admin\" value=\"del\">\n"; 1726 echo "Go to ID(s): <input type=\"text\" name=\"res\" size=\"10\" maxlength=\"10\" class=\"inputcenter\"> <input type=\"submit\" value=\"Go\">"; 1727 if( $threadmode ) { 1728 echo "<input type=\"button\" onclick='location.search=\"\"' value=\"View by order posted\">"; 1729 } 1730 else { 1731 echo "<input type=\"button\" onclick='location.search=\"?threadmode=1\"' value=\"Group by thread\">"; 1732 } 1733 1734 echo "</center></form>"; 1735 1736 echo "<center><p><form action=\"" . SELF_PATH . "\" method=\"POST\"><input type=\"hidden\" name=\"mode\" value=\"admindel\"><input type=\"hidden\" name=\"pwd\" value=\"" . ADMIN_PASS . "\"><input type=\"submit\" value=\"" . S_ITDELETES . "\"> "; 1737 echo "<input type=\"reset\" value=\"" . S_MDRESET . "\">"; 1738 echo " [<input type=\"checkbox\" name=\"onlyimgdel\" value=\"on\"><!--checked-->" . S_MDONLYPIC . "]"; 1739 1740 echo "<table border=\"1\" cellspacing=\"0\">"; 1741 if( $threadmode ) { 1742 echo "<tr bgcolor=\"#408040\" align=\"center\" style=\"color:#eef6ee\">"; 1743 } 1744 else { 1745 echo "<tr bgcolor=\"#6080f6\" align=\"center\">"; 1746 } 1747 echo '<td> </td><td><b>No.</b></td><td><b>Time</b></td><td><b>Name</b></td><td><b>Subject</b></td><td><b>Comment</b></td><td><b>Host</b></td><td> </td>'; 1748 echo "</tr>\n"; 1749 1750 $resq = "`"; 1751 if( $res ) { 1752 $resq = ""; 1753 $splitres = explode( ",", $res ); 1754 foreach( $splitres as $line ) { 1755 $resq .= " no='" . mysql_escape_string( $line ) . "' OR"; 1756 } 1757 $resq = rtrim( $resq, " OR" ); 1758 $resq = "` WHERE" . $resq; 1759 1760 } 1761 elseif( $thread && $ip ) { 1762 $max_results = 5000; 1763 $thread = (int)$thread; 1764 $resq = "` WHERE (no='$thread' OR resto='$thread') AND host='" . sprintf( "%s", long2ip( -( 4294967296 - $ip ) ) ) . "'"; 1765 } 1766 elseif( $ip ) { 1767 $max_results = 5000; 1768 $resq = "` WHERE host='" . sprintf( "%s", long2ip( -( 4294967296 - $ip ) ) ) . "'"; 1769 } 1770 elseif( $thread ) { 1771 $max_results = 5000; 1772 $thread = (int)$thread; 1773 $resq = "` WHERE no='$thread' OR resto='$thread'"; 1774 } 1775 1776 if( $threadmode ) { 1777 if( !$result = mysql_board_call( "(SELECT child.*,parent.root proot from `" . SQLLOG . "` child LEFT OUTER JOIN `" . SQLLOG . "` parent ON child.resto=parent.no) UNION (SELECT *,root proot from `" . SQLLOG . "` parent WHERE resto=0) ORDER BY proot DESC, no ASC LIMIT " . $from . ", " . $max_results ) ) { 1778 echo S_SQLFAIL; 1779 } 1780 } 1781 else { 1782 if( !$result = mysql_board_call( "select * FROM `" . SQLLOG . $resq . " order BY no DESC LIMIT " . $from . ", " . $max_results ) ) { 1783 echo S_SQLFAIL; 1784 } 1785 } 1786 1787 $j = 0; 1788 while( $row = mysql_fetch_assoc( $result ) ) { //FIXME use assoc 1789 $j++; 1790 $img_flag = false; 1791 extract( $row, EXTR_OVERWRITE ); 1792 // Format 1793 //$now=preg_replace('@^(../..)/..@','$1',$now); 1794 //$now=preg_replace('/\(.*\)/',' ',$now); 1795 $fullname = str_replace( '</span> <span class="postertrip">!', ' #', $name ); 1796 $name = strip_tags( $name ); 1797 $fullname = strip_tags( $name ); //for capcode cleaning 1798 1799 if( strpos( $sub, 'SPOILER<>' ) !== false ) $sub = substr( $sub, 9 ); 1800 1801 $fullsub = $sub; 1802 if( strlen( $name ) > 14 ) $name = substr( $name, 0, 15 ) . "..."; 1803 if( strlen( $sub ) > 14 ) $sub = substr( $sub, 0, 15 ) . "..."; 1804 //if( $email ) $name = "<a href=\"mailto:$email\">$name</a>"; 1805 $shortcom = html_entity_decode( preg_replace( "/<[^>]+>/", " ", $com ), ENT_QUOTES, "UTF-8" ); 1806 if( strlen( $shortcom ) > 35 ) $shortcom = substr( $shortcom, 0, 36 ) . "..."; 1807 // Link to the picture 1808 if( $ext ) { 1809 $img_flag = true; 1810 if( !$filedeleted ) { 1811 if( SQLLOG == "f" ) { 1812 $filelink = $filename . $ext; 1813 } 1814 else { 1815 $filelink = $tim . $ext; 1816 } 1817 $clip = "<a href=\"" . IMG_DIR2 . $filelink . "\" target=_blank>" . $filelink . "</a>"; 1818 } 1819 else { 1820 $clip = "<s>" . $filelink . "</s>"; 1821 } 1822 $size = $fsize / 1024; 1823 $size = round( $size, 2 ) . " KB"; 1824 $all = $all + $fsize; //total calculation 1825 $md5 = substr( $md5, 0, 10 ); 1826 } 1827 else { 1828 $clip = ""; 1829 $size = 0; 1830 $md5 = ""; 1831 } 1832 $bg = ( $j % 2 ) ? "d0d0f0" : "f6f6f6"; //BG color 1833 if( $threadmode ) { 1834 $bg = ( !$resto ) ? "d0f0d0" : "eeffee"; 1835 } 1836 1837 $displayhost = $host; 1838 1839 $cboard = explode( "/", $_SERVER[ 'SCRIPT_NAME' ] ); 1840 $cboard = $cboard[ 1 ]; 1841 1842 $bantrue = 0; 1843 if( !$banned = mysql_global_call( "SELECT host,board,global,zonly,DATE_FORMAT(length, 'Until %W, %M %D, %Y.') AS buntil FROM " . SQLLOGBAN . " WHERE host='" . $host . "' AND active=1" ) ) { 1844 echo S_SQLFAIL; 1845 } 1846 $bannedrows = mysql_num_rows( $banned ); 1847 if( $bannedrows > 0 ) { 1848 $row = mysql_fetch_array( $banned ); 1849 $buntil = $row[ 'buntil' ]; 1850 if( $row[ 'board' ] == $cboard ) { 1851 $bg = "f0d0d0"; 1852 if( $buntil == "" ) $buntil = "Indefinitely."; 1853 $bantrue = 1; 1854 } 1855 if( $row[ 'global' ] == 1 ) { 1856 $bg = "f0a0a0"; 1857 if( $buntil == "" ) $buntil = "Indefinitely."; 1858 //$globally = " (Globally)"; 1859 $bantrue = 1; 1860 } 1861 elseif( $row[ 'zonly' ] == 1 ) { 1862 $bg = "a0f0a0"; 1863 if( $buntil == "" ) $buntil = "Indefinitely."; 1864 $bantrue = 0; 1865 //$globally = " (".$board.")"; 1866 } 1867 else { 1868 //$globally = " (".$board.")"; 1869 } 1870 } 1871 1872 echo "<tr bgcolor=\"#$bg\"><td><input type=\"checkbox\" id=\"fake$no\" name=\"$no\" value=\"delete\"></td>"; 1873 if( $resto == 0 ) { 1874 $spec = ""; 1875 if( $sticky == 1 ) $spec = " color: #800080;"; 1876 if( $permasage == 1 ) $spec = " text-decoration: underline;"; 1877 if( $closed == 1 ) $spec = " color: #FF0000;"; 1878 if( $sticky == 1 && $closed == 1 ) $spec = " color: #808080;"; 1879 1880 echo "<td><a href=\"" . SELF_PATH . "?res=$no\" target=\"_blank\" style=\"font-weight: bold;" . $spec . "\">$no</a></td>"; 1881 } 1882 else { 1883 $parentline = ( $threadmode ? "└" : "" ); 1884 echo "<td><a href=\"" . SELF_PATH . "?res=$no#$no\" target=\"_blank\">$parentline$no</a></td>"; 1885 } 1886 echo "<td>$now</td><td title=\"$fullname\" align=\"center\"><b>$name</b></td>"; 1887 echo "<td title=\"$fullsub\">$sub</td><td><span id='short$no' ondblclick='swap(\"full$no\",\"short$no\")' title='Double-click to show full comment'>$shortcom</span><span id='full$no' ondblclick='swap(\"full$no\",\"short$no\")' style='display:none;'>$com</span></td>"; 1888 echo "<td style=\"text-align: center\">$displayhost</td><td><input type=\"button\" value=\"More\" onClick=\"more('" . $no . "a','" . $no . "b');\"></td>\n"; 1889 echo "</tr>\n"; 1890 echo "<tr id=\"" . $no . "a\" bgcolor=\"#a0c0ff\" align=\"center\" style=\"display: none;\">"; 1891 // echo "<td colspan=2> </td>"; 1892 1893 1894 if( $size != 0 ) { 1895 echo "<td colspan=\"3\"><b>File</b></td>"; 1896 echo "<td colspan=\"5\"> </td>"; 1897 echo "</tr>"; 1898 echo "<tr id=\"" . $no . "b\" bgcolor=\"#$bg\" style=\"display: none;\">"; 1899 // echo "<td colspan=2> </td>"; 1900 echo "<td colspan=3 align=\"center\">$clip ($size)</td>"; 1901 } 1902 elseif( $resto == 0 ) { 1903 //echo "<td colspan=3 align=\"left\"><b>Text-only thread</b></td>"; 1904 //echo "<td colspan=2>"; 1905 if( $bannedrows > 0 ) { 1906 echo '<td colspan="6" align="left"><b>Text-only thread</b></td>'; 1907 echo '<td colspan="2"><b>Ban length</b></td>'; 1908 } 1909 else { 1910 echo "<td colspan=8 align=\"left\"><b>Text-only thread</b></td>"; 1911 } 1912 1913 echo "</tr>"; 1914 echo "<tr id=\"" . $no . "b\" bgcolor=\"#$bg\" style=\"display: none;\">"; 1915 $span = 8; 1916 } 1917 else { 1918 echo "<td colspan=\"3\" align=\"center\"><b>Reply to thread</b></td>"; 1919 echo "<td colspan=\"5\"> </td>"; 1920 echo "</tr>"; 1921 echo "<tr id=\"" . $no . "b\" bgcolor=\"#$bg\" style=\"display: none;\">"; 1922 //echo "<td colspan=3> </td>"; 1923 echo "<td colspan=\"3\" align=\"center\"><a href=\"" . $SELF_PATH . "?thread=$resto\">$resto</a></td>"; 1924 $span = 5; 1925 } 1926 echo "<td colspan=\"$span\" align=\"center\"><input type=\"button\" value=\"Display all posts by IP\" onClick=\"location.href='" . $SELF_PATH . "?ip=" . sprintf( "%u", ip2long( $host ) ) . "'\"> <input type=\"button\" value=\"Delete all posts by IP\" onClick=\"popup('admin=delall&id=$no');\">"; 1927 /*if ($bantrue) { 1928 echo " <input type=\"button\" value=\"Unban user\" onClick=\"popup('admin=unban&id=$no');\"></td>"; 1929 } else {*/ 1930 // if (!$bantrue) { 1931 echo " <input type=\"button\" value=\"Ban user\" onClick=\"popup('admin=ban&id=$no');\">"; 1932 // } 1933 //} 1934 if( $resto == 0 ) { 1935 echo " <input type=\"button\" value=\"Thread options\" onClick=\"popup('admin=opt&id=$no');\">"; 1936 if( !$thread ) { 1937 echo " <input type=\"button\" value=\"Replies\" onClick=\"location.href='$SELF_PATH?thread=$no'\"></td>"; 1938 } 1939 } 1940 echo "</tr>"; 1941 } 1942 1943 echo "</table><p style=\"margin: 0px; padding: 0px; text-align: center;\"><input type=\"submit\" value=\"" . S_ITDELETES . "$msg\"> "; 1944 echo "<input type=\"reset\" value=\"" . S_RESET . "\">"; 1945 echo " [<input type=\"checkbox\" name=\"onlyimgdel\" value=\"on\"><!--checked-->" . S_MDONLYPIC . "]</form><br>"; 1946 1947 //$all = (int)($all / 1024); 1948 1949 //page stuff 1950 $total_results = mysql_result( mysql_board_call( "SELECT COUNT(*) as Num FROM `" . SQLLOG . "`" ), 0 ); 1951 $total_pages = ceil( $total_results / $max_results ); 1952 1953 echo "</form><form action=\"" . https_self_url() . "\" method=\"POST\">\n"; 1954 echo "<input type=\"hidden\" name=\"mode\" value=\"admin\"><input type=\"hidden\" name=\"admin\" value=\"del\"><input type=\"hidden\" name=\"user\" value=\"$user\"><input type=\"hidden\" name=\"pass\" value=\"$pass\">\n"; 1955 1956 echo '<center><div class="pagelist" style="float: none!important; display: inline-block;"><div class="prev">'; 1957 if( $p > 1 ) { 1958 $prev = ( $p - 1 ); 1959 echo "<form action=\"" . https_self_url() . "\" method=\"POST\">\n"; 1960 echo "<input type=\"hidden\" name=\"mode\" value=\"admin\"><input type=\"hidden\" name=\"admin\" value=\"del\"><input type=\"hidden\" name=\"user\" value=\"$user\"><input type=\"hidden\" name=\"pass\" value=\"$pass\">\n"; 1961 echo "<input type=\"hidden\" name=\"p\" value=\"$prev\"><input type=\"submit\" value=\"Previous\"></form>"; 1962 } 1963 else { 1964 echo "<span>Previous</span>"; 1965 } 1966 1967 echo "</div><div class=\"next\">"; 1968 if( $p < $total_pages ) { 1969 $next = ( $p + 1 ); 1970 echo "<form action=\"" . https_self_url() . "\" method=\"POST\">\n"; 1971 echo "<input type=\"hidden\" name=\"mode\" value=\"admin\"><input type=\"hidden\" name=\"admin\" value=\"del\"><input type=\"hidden\" name=\"user\" value=\"$user\"><input type=\"hidden\" name=\"pass\" value=\"$pass\">\n"; 1972 echo "<input type=\"hidden\" name=\"p\" value=\"$next\"><input type=\"submit\" value=\"Next\"><input type=\"hidden\" name=\"threadmode\" value=\"$threadmode\"></form>"; 1973 } 1974 else { 1975 echo "<span>Next</span>"; 1976 } 1977 echo "</div></div>"; 1978 echo "</center></center>"; 1979 echo str_replace( "12pt", "10pt", $navinc ); 1980 1981 die( "</body></html>" ); 1982 } 1983 1984 // return the images/thumbnails for a single post 1985 // $row is an assoc. array representation of the post 1986 function image_files_for( $row ) 1987 { 1988 $del_files = array(); 1989 // we always need to delete the image 1990 $del_files[ IMG_DIR . $row[ 'tim' ] . $row[ 'ext' ] ] = 1; 1991 // and the thumbnail 1992 $del_files[ THUMB_DIR . $row[ 'tim' ] . 's.jpg' ] = 1; 1993 // and the oekaki replay 1994 if (ENABLE_OEKAKI_REPLAYS) { 1995 $del_files[IMG_DIR . $row['tim'] . '.tgkr'] = 1; 1996 } 1997 // and the resized mobile images 1998 if (MOBILE_IMG_RESIZE && $row['m_img']) { 1999 $images["{$row['tim']}m.jpg"] = 1; 2000 } 2001 2002 return $del_files; 2003 } 2004 2005 // delete all posts from an IP, maintaining the consistency of the files and db 2006 function delallbyip($ip, $imgonly, $replies_only = false) 2007 { 2008 $ip = mysql_real_escape_string( $ip ); 2009 2010 if ($ip === '') { 2011 error('Invalid IP'); 2012 } 2013 2014 if ($replies_only) { 2015 $_rep_sql = ' AND resto > 0'; 2016 } 2017 else { 2018 $_rep_sql = ''; 2019 } 2020 2021 mysql_board_call( "LOCK TABLES `" . SQLLOG . "` WRITE" ); 2022 $query = mysql_board_call("SELECT * FROM `" . SQLLOG . "` WHERE archived = 0 AND host='$ip'" . $_rep_sql); 2023 $del_files = array(); // keys = delete these files 2024 $update_threads = array(); // keys = update these threads' HTML 2025 $del_threads = array(); // keys = delete replies to these thread numbers from db 2026 $del_all = array(); // keys = these are being deleted from the db (used to clean up reports etc.) 2027 2028 while( $row = mysql_fetch_assoc( $query ) ) { 2029 // we always need to delete the image files 2030 $del_files += image_files_for( $row ); 2031 2032 if( !$imgonly ) // deleting this post from the db 2033 { 2034 $del_all[ $row[ 'no' ] ] = 1; 2035 } 2036 2037 if( $row[ 'resto' ] ) { // it's a reply, need to update parent 2038 $update_threads[ $row[ 'resto' ] ] = 1; 2039 } 2040 elseif( !$imgonly ) { // it's a thread parent and it's getting deleted from db 2041 // need to delete thread html 2042 if( USE_GZIP == 1 ) { 2043 // HTML 2044 $del_files[ RES_DIR . $row[ 'no' ] . PHP_EXT ] = 1; 2045 $del_files[ RES_DIR . $row[ 'no' ] . PHP_EXT . '.gz' ] = 1; 2046 // JSON 2047 $del_files[ RES_DIR . $row[ 'no' ] . '.json' ] = 1; 2048 $del_files[ RES_DIR . $row[ 'no' ] . '.json.gz' ] = 1; 2049 } 2050 else { 2051 // HTML 2052 $del_files [ RES_DIR . $row[ 'no' ] . PHP_EXT ] = 1; 2053 // JSON 2054 $del_files[ RES_DIR . $row[ 'no' ] . '.json' ] = 1; 2055 } 2056 2057 $del_threads[ $row[ 'no' ] ] = 1; 2058 $replyquery = mysql_board_call( "SELECT * FROM `" . SQLLOG . "` WHERE resto='{$row[ 'no' ]}'" ); 2059 while( $replyrow = mysql_fetch_assoc( $replyquery ) ) { 2060 $del_files += image_files_for( $replyrow ); 2061 $del_all[ $replyrow[ 'no' ] ] = 1; 2062 } 2063 mysql_free_result( $replyquery ); 2064 } 2065 2066 { 2067 $auser = $_COOKIE[ '4chan_auser' ]; 2068 $adfsize = ( $row[ 'fsize' ] > 0 ) ? 1 : 0; 2069 $adname = str_replace( '</span> <span class="postertrip">!', '#', $row[ 'name' ] ); 2070 if( $imgonly ) { 2071 $imgonly = 1; 2072 } 2073 else { 2074 $imgonly = 0; 2075 } 2076 //$row['sub'] = mysql_escape_string( $row['sub'] ); 2077 //$row['com'] = mysql_escape_string( $row['com'] ); 2078 //$row['filename'] = mysql_escape_string( $row['filename'] ); 2079 validate_admin_cookies(); 2080 mysql_global_do( "INSERT INTO " . SQLLOGDEL . " (imgonly,postno,resto,board,name,sub,com,img,filename,admin,email,admin_ip,tool) values('%s',%d,%d,'%s','%s','%s','%s','%s','%s','%s','%s','%s', 'del-all-by-ip')", $imgonly, $row[ 'no' ], $row[ 'resto' ], SQLLOG, $adname, $row[ "sub" ], $row[ "com" ], $adfsize, $row[ "filename" ].$row['ext'], $auser, $row[ 'email' ], $_SERVER['REMOTE_ADDR']); 2081 } 2082 } 2083 mysql_free_result( $query ); 2084 2085 // delete IP's posts 2086 if( !$imgonly ) { 2087 mysql_board_call( "DELETE FROM `" . SQLLOG . "` WHERE host='$ip'" . $_rep_sql ); 2088 // delete replies to IP's parent posts 2089 foreach( $del_threads as $parent => $unused ) { 2090 mysql_board_call( "DELETE FROM `" . SQLLOG . "` WHERE resto='$parent'" ); 2091 } 2092 } 2093 else { 2094 mysql_board_call( "UPDATE `" . SQLLOG . "` SET filedeleted=1,root=root WHERE host='$ip'" . $_rep_sql ); 2095 } 2096 mysql_board_call( "UNLOCK TABLES" ); 2097 2098 // delete all necessary files (images and HTML) 2099 foreach( $del_files as $file => $unused ) { 2100 @unlink( $file ); 2101 2102 if( CLOUDFLARE_PURGE_ON_DEL && strpos( $file, IMG_DIR ) !== false ) { 2103 $filename = basename( $file ); 2104 cloudflare_purge_by_basename(BOARD_DIR, $filename); 2105 } 2106 } 2107 2108 // delete reports for deleted posts 2109 foreach( $del_all as $no => $unused ) { 2110 mysql_global_do( "DELETE FROM reports WHERE board='" . SQLLOG . "' AND no='$no'" ); 2111 mysql_global_do( "DELETE FROM reports_for_posts WHERE board='" . SQLLOG . "' AND postid='$no'" ); 2112 } 2113 2114 echo "<br><strong>Deleting posts...</strong><br>"; 2115 if( $imgonly ) { 2116 echo "All images deleted.<br><strong>Deletion successful!</strong><br>"; 2117 } 2118 else { 2119 echo "All posts deleted.<br><strong>Deletion successful!</strong><br>"; 2120 } 2121 2122 // rebuild html 2123 if( count( $update_threads ) > 25 ) { 2124 echo "Rebuilding all pages..."; 2125 echo ( rebuild_all( $error ) ? " OK!" : $error ); // at some number of threads, this must be faster... 2126 } 2127 else { 2128 foreach( $update_threads as $parent => $unused ) { 2129 if( $del_threads[ $parent ] ) continue; // this thread was deleted, forget it 2130 2131 echo "Rebuilding No.$parent..."; 2132 echo ( rebuild_thread( $parent, $error ) ? " OK!" : $error ); 2133 echo "<br>"; 2134 } 2135 } 2136 2137 die( "<br><strong>Rebuild successful!</strong><span style=\"display:none\">Done.</span>" ); 2138 } 2139 2140 function admindelall() 2141 { 2142 global $onlyimgdel, $onlyrepdel, $id, $user, $pass; 2143 $delno = array(); 2144 $delflag = false; 2145 reset( $_POST ); 2146 while( $item = each( $_POST ) ) { 2147 if( $item[ 1 ] == 'delete' ) { 2148 array_push( $delno, intval( $item[ 0 ] ) ); 2149 $delflag = true; 2150 } 2151 } 2152 if( $delflag ) { 2153 if( !$result = mysql_board_call( "SELECT host FROM `" . SQLLOG . "` WHERE archived = 0 AND no=" . mysql_real_escape_string( $id ) ) ) { 2154 echo S_SQLFAIL; 2155 } 2156 $row = mysql_fetch_row( $result ); 2157 list( $host ) = $row; 2158 delallbyip($host, $onlyimgdel, $onlyrepdel === true); 2159 } 2160 echo "<input type=\"hidden\" name=\"mode\" value=\"admin\">\n"; 2161 echo "<input type=\"hidden\" name=\"admin\" value=\"delall\">\n"; 2162 echo "<input type=\"hidden\" name=\"user\" value=\"$user\">\n"; 2163 echo "<input type=\"hidden\" name=\"pass\" value=\"$pass\">\n"; 2164 echo "<input type=\"hidden\" name=\"id\" value=\"$id\">\n"; 2165 echo "<input type=\"hidden\" name=\"$id\" value=\"delete\">\n"; 2166 echo "<input type=\"hidden\" name=\"onlyimgdel\" value=\"on\">\n"; 2167 echo "<input type=\"submit\" value=\"Delete images only\">\n"; 2168 echo "</form><form \"action=\"" . https_self_url() . "\" method=\"POST\">\n"; 2169 echo "<input type=\"hidden\" name=\"mode\" value=\"admin\">\n"; 2170 echo "<input type=\"hidden\" name=\"admin\" value=\"delall\">\n"; 2171 echo "<input type=\"hidden\" name=\"pass value=\"$pass\">\n"; 2172 echo "<input type=\"hidden\" name=\"id\" value=\"$id\">\n"; 2173 echo "<input type=\"hidden\" name=\"$id\" value=\"delete\">\n"; 2174 echo "<input type=\"submit\" value=\"Delete all posts\">\n"; 2175 echo "</form>\n"; 2176 die( "</body></html>" ); 2177 } 2178 2179 function ban_template_js($post_has_file = true, $is_thread = false) { 2180 $templates = array(); 2181 2182 $level_map = get_level_map(); 2183 2184 $query = "SELECT * FROM ban_templates ORDER BY LENGTH(rule), rule ASC"; 2185 $q = mysql_global_call($query); 2186 2187 while ($r = mysql_fetch_assoc($q)) { 2188 if (!preg_match('#^(global|' . BOARD_DIR . ')[0-9]+$#', $r[ 'rule' ])) { 2189 continue; 2190 } 2191 2192 if (($r['no'] == 1 || $r['no'] == 123 || $r['no'] == 213) && !$post_has_file) { 2193 continue; 2194 } 2195 2196 if ($r['no'] == 6 && !DEFAULT_BURICHAN) { 2197 continue; 2198 } 2199 2200 if ($r['no'] == 17 && (BOARD_DIR === 'mlp' || BOARD_DIR === 'trash')) { 2201 continue; 2202 } 2203 2204 if ($r['no'] == 223 && BOARD_DIR === 'pol') { 2205 continue; 2206 } 2207 2208 // Global 3 - Troll posts 2209 if ($r['no'] == 222 && BOARD_DIR === 's4s') { 2210 continue; 2211 } 2212 2213 // Global 3 2214 if ((BOARD_DIR === 'b' || BOARD_DIR === 'bant') && strpos($r['rule'], 'global3') !== false) { 2215 continue; 2216 } 2217 2218 // Skip OP-only templates 2219 if ($r['postban'] === 'move' && !$is_thread) { 2220 continue; 2221 } 2222 2223 if ($level_map[$r['level']] !== true) { 2224 continue; 2225 } 2226 2227 unset($r['special_action']); 2228 2229 $templates[] = $r; 2230 } 2231 2232 return ' 2233 <script type="text/javascript" src="//s.4cdn.org/js/admin_autocomplete.10.js"></script> 2234 <script> 2235 var e_template = document.getElementsByName("template")[0]; 2236 var globalTemplates = ' . json_encode( $templates, JSON_PARTIAL_OUTPUT_ON_ERROR ) . '; 2237 var templates = {}; 2238 var localTemplates = {}; 2239 2240 function $(e) {return document.getElementsByName(e)[0];} 2241 function unhide(e) {$(e).style.visibility="visible";} 2242 2243 function chooseTemplate() { 2244 var i = e_template.selectedIndex - 1; 2245 2246 Feedback.checkTemplate(i); 2247 2248 if (i < 0) { 2249 return; 2250 } 2251 2252 var t = templates[i]; 2253 2254 $("pubreason").value = t.publicreason; 2255 $("pvtreason").value = t.privatereason; 2256 $("days").value = (t.banlen == "" && t.days > 0) ? t.days : ""; 2257 $("warn").checked = (t.banlen == "" && t.days == 0); 2258 $("indefinite").checked = (t.banlen == "indefinite"); 2259 $("banmsg").checked = t.publicban==1; 2260 $("bantype").value = t.bantype; 2261 2262 $("postban").value = t.postban; 2263 2264 if (t.postban === "move") { 2265 document.getElementById("js-move-board-sel").value = t.postban_arg; 2266 } 2267 2268 $("templateno").value = t.no; 2269 2270 onPostBanSelChange.call(document.getElementById("js-postban-sel")); 2271 undisableForm(); 2272 } 2273 2274 function undisableForm() { 2275 var f = document.querySelectorAll("*[disabled=disabled]"); 2276 var len = f.length; 2277 2278 for( var i = 0; i < len; i++ ) { 2279 f[i].removeAttribute("disabled"); 2280 } 2281 } 2282 2283 function updateTemplate() { 2284 var t = {}; 2285 var name = prompt("Enter a name for this template"); 2286 2287 t.banlen = $("indefinite").checked ? "indefinite" : ""; 2288 t.bantype = $("bantype").value; 2289 //t.blacklist; 2290 t.days = ($("warn").checked) ? 0 : $("days").value; 2291 t.name = name; 2292 t.postban = $("postban").value; 2293 t.publicreason = $("pubreason").value; 2294 t.privatereason = $("pvtreason").value; 2295 2296 localTemplates[name] = t; 2297 localStorage.setItem("ban_templates", JSON.stringify(localTemplates)); 2298 2299 initTemplate(); 2300 2301 return false; 2302 } 2303 2304 function deleteTemplate() { 2305 var i = e_template.selectedIndex - 1; 2306 2307 if (i >= globalTemplates.length) 2308 delete localTemplates[templates[i].name]; 2309 2310 localStorage.setItem("ban_templates", JSON.stringify(localTemplates)); 2311 initTemplate(); 2312 2313 return false; 2314 } 2315 2316 function initTemplate() { 2317 templates = globalTemplates; 2318 2319 e_template.innerHTML = "<option value=\"-1\">None Selected (Required)</option>"; 2320 2321 for (var i=0;i<templates.length;i++) { 2322 var t = templates[i]; 2323 //if( !t.name.match(/Global/) && !t.name.match(/\/' . BOARD_DIR . '\//) && i < globalTemplates.length ) continue; 2324 2325 var o = document.createElement("option"); 2326 o.value = i; 2327 o.innerHTML = t.name + ((i < globalTemplates.length) ? "" : " [Local]"); 2328 e_template.appendChild(o); 2329 } 2330 unhide("template_row"); 2331 if (localStorage && false == true) { 2332 unhide("local_template_row"); 2333 unhide("deltemplate"); 2334 } 2335 } 2336 2337 initTemplate(); 2338 </script> 2339 '; 2340 } 2341 2342 function do_post_quarantine( $board, $post ) 2343 { 2344 /* 2345 Gathers - 2346 Current post, current post image 2347 All images of posts in the same thread 2348 */ 2349 2350 mysql_board_lock( true ); 2351 $host = $post[ "host" ]; 2352 2353 $post_json = make_post_json($post); 2354 $postno = $post["no"]; 2355 2356 $xffres = mysql_global_do("select xff from xff where board='%s' and postno=%d", $board, $postno); 2357 if (mysql_num_rows($xffres)) $xff = mysql_fetch_assoc($xffres)["xff"]; 2358 $res = mysql_global_do("insert into ncmec_reports (board,post_num,post_json,xff) value ('%s',%d,'%s','%s')", $board, $postno, $post_json, $xff); 2359 $reportid = mysql_global_insert_id(); 2360 2361 $path = "/www/quarantine/$reportid"; 2362 mkdir( $path ); 2363 2364 $image = $post[ "tim" ] . $post[ "ext" ]; 2365 $dst_path = "$path/$image"; 2366 $tmp_path = $dst_path.".tmp"; 2367 @copy( IMG_DIR . "/$image", $tmp_path ); 2368 @rename($tmp_path, $dst_path); 2369 2370 if (!file_exists($dst_path)) { 2371 // guess we can't quarantine it after all 2372 mysql_global_do("delete from ncmec_reports where id=%d", $reportid); 2373 } else { 2374 $resto = $post["resto"]; 2375 $respred = $resto ? "no=$resto or resto=$resto" : "resto=$postno"; 2376 $q = mysql_board_call( "select * from `%s` where host='%s' and no!=%d and ($respred)", SQLLOG, $host, $postno ); 2377 while( $p = mysql_fetch_assoc( $q ) ) { 2378 $i = $p[ "tim" ] . $p[ "ext" ]; 2379 mkdir( "$path/images" ); 2380 @copy( IMG_DIR . "/$i", "$path/images/$i" ); 2381 } 2382 } 2383 mysql_board_unlock(); 2384 } 2385 2386 function do_template_special_action($template, $board, $row, $is_manager = false) { 2387 if ($template['special_action'] === 'quarantine') { 2388 do_post_quarantine($board, $row); 2389 } 2390 2391 if ($is_manager) { 2392 if( $template['special_action'] === 'quarantine' || $template['special_action'] === 'revokepass_spam' || $template['special_action'] === 'revokepass_illegal') { 2393 $pass = $row['4pass_id']; 2394 $status = $template['special_action'] === 'revokepass_spam' ? 4 : 5; 2395 mysql_global_do("UPDATE pass_users SET status = %d WHERE user_hash = '%s' AND status = 0 LIMIT 1", $status, $pass); 2396 } 2397 } 2398 } 2399 2400 /** 2401 * Auto-rangeban log entries 2402 * $tpl_id: ban or BR template id 2403 * $source: 1 = ban, 0 = ban request 2404 */ 2405 function process_auto_rangeban($ip, $browser_id, $thread_id, $post_id, $tpl_id, $source) { 2406 $thread_id = (int)$thread_id; 2407 2408 if (!$browser_id) { 2409 return false; 2410 } 2411 2412 // Prune stale entries 2413 $sql = "DELETE FROM event_log WHERE type = 'rangeban_hint' AND created_on < DATE_SUB(NOW(), INTERVAL 1 HOUR)"; 2414 2415 $res = mysql_global_call($sql); 2416 2417 if (!$res) { 2418 return false; 2419 } 2420 2421 $need_rangeban = false; 2422 2423 // Check if should apply auto rangeban (2 strikes for BRs, immediate for Bans) 2424 $range_sql = explode('.', $ip); 2425 2426 $range_sql = "{$range_sql[0]}.{$range_sql[1]}.%"; 2427 2428 // Ban 2429 if ($source === 1) { 2430 $need_rangeban = true; 2431 } 2432 // Ban Request 2433 else { 2434 $sql =<<<SQL 2435 SELECT COUNT(DISTINCT ip) FROM event_log WHERE 2436 type = 'rangeban_hint' AND board = '%s' AND thread_id = $thread_id AND ua_sig = '%s' 2437 AND ip LIKE '%s' 2438 SQL; 2439 2440 $res = mysql_global_call($sql, BOARD_DIR, $browser_id, $range_sql); 2441 2442 if (!$res) { 2443 return false; 2444 } 2445 2446 $count = (int)mysql_fetch_row($res)[0]; 2447 2448 if ($count > 0) { 2449 $need_rangeban = true; 2450 } 2451 } 2452 2453 if ($need_rangeban) { 2454 // Skip if a rangeban already exists 2455 $sql =<<<SQL 2456 SELECT COUNT(*) FROM event_log WHERE 2457 type = 'rangeban' AND board = '%s' AND thread_id = $thread_id AND ua_sig = '%s' 2458 AND ip LIKE '%s' AND created_on > DATE_SUB(NOW(), INTERVAL 1 HOUR) 2459 SQL; 2460 2461 $res = mysql_global_call($sql, BOARD_DIR, $browser_id, $range_sql); 2462 2463 if (!$res) { 2464 return false; 2465 } 2466 2467 $count = (int)mysql_fetch_row($res)[0]; 2468 2469 if ($count > 0) { 2470 return true; 2471 } 2472 2473 return add_auto_rangeban_log($ip, $browser_id, $thread_id, $post_id, true, $tpl_id, $source); 2474 } 2475 2476 // Add hint entry 2477 return add_auto_rangeban_log($ip, $browser_id, $thread_id, $post_id, false, $tpl_id, $source); 2478 } 2479 2480 function add_auto_rangeban_log($ip, $browser_id, $thread_id, $post_id, $is_ban = false, $tpl_id = 0, $source = 0) { 2481 if ($is_ban) { 2482 $type = 'rangeban'; 2483 } 2484 else { 2485 $type = 'rangeban_hint'; 2486 } 2487 2488 return write_to_event_log($type, $ip, [ 2489 'board' => BOARD_DIR, 2490 'thread_id' => $thread_id, 2491 'post_id' => $post_id, 2492 'ua_sig' => $browser_id, 2493 'arg_num' => $tpl_id, 2494 'arg_str' => (int)$source 2495 ]); 2496 } 2497 2498 /** 2499 * Collects posts related to the provided Password. 2500 * This is used for banning people who hop between multiple IPs. 2501 * Only posts made from non-mobile devices are collected. 2502 */ 2503 function admin_collect_related($ip, $pwd) { 2504 if (!$pwd || !$ip) { 2505 return null; 2506 } 2507 2508 $range_sql = explode('.', $ip); 2509 2510 $range_sql[0] = (int)$range_sql[0]; 2511 $range_sql[1] = (int)$range_sql[1]; 2512 2513 $range_sql = "{$range_sql[0]}.{$range_sql[1]}."; 2514 2515 $sql = <<<SQL 2516 SELECT SQL_NO_CACHE host, 4pass_id FROM `%s` 2517 WHERE archived = 0 AND pwd = '%s' 2518 AND host NOT LIKE '$range_sql%%' 2519 AND email NOT LIKE '1%%' 2520 GROUP BY host 2521 SQL; 2522 2523 $res = mysql_board_call($sql, BOARD_DIR, $pwd); 2524 2525 if (!$res) { 2526 return null; 2527 } 2528 2529 $data = []; 2530 2531 while ($post = mysql_fetch_assoc($res)) { 2532 $data[] = $post; 2533 } 2534 2535 return $data; 2536 } 2537 2538 function admin_toggle_protected_thread($flag, $board, $thread_id, $duration = 1800) { 2539 if (!$board || !$thread_id || $duration <= 0) { 2540 return false; 2541 } 2542 2543 $m = new Memcached(); 2544 $m->setOption(Memcached::OPT_SERVER_FAILURE_LIMIT, 1); 2545 $m->setOption(Memcached::OPT_SEND_TIMEOUT, 500000); // 500ms 2546 $m->setOption(Memcached::OPT_RECV_TIMEOUT, 500000); // 500ms 2547 $m->addServer(MEMCACHED_HOST, MEMCACHED_PORT); 2548 2549 if (!$m) { 2550 return false; 2551 } 2552 2553 $key = "def.$board.$thread_id"; 2554 2555 if ($flag) { 2556 if ($duration > 43200) { // 12h 2557 $duration = 43200; 2558 } 2559 2560 $now = $_SERVER['REQUEST_TIME']; 2561 2562 return $m->set($key, 1, $now + $duration); 2563 } 2564 else { 2565 return $m->delete($key); 2566 } 2567 } 2568 2569 function admin_get_template_by_id($tpl_id) { 2570 $tpl_id = (int)$tpl_id; 2571 $sql = "SELECT * FROM ban_templates WHERE no = $tpl_id LIMIT 1"; 2572 $res = mysql_global_call($sql); 2573 if (!$res) { 2574 return false; 2575 } 2576 return mysql_fetch_assoc($res); 2577 } 2578 2579 function admin_is_ip_rangebanned($ip) { 2580 require_once 'lib/geoip2.php'; 2581 2582 $_asninfo = GeoIP2::get_asn($ip); 2583 2584 if ($_asninfo) { 2585 $asn = (int)$_asninfo['asn']; 2586 } 2587 else { 2588 $asn = 0; 2589 } 2590 2591 if ($asn > 0) { 2592 $query =<<<SQL 2593 SELECT id FROM iprangebans 2594 WHERE asn = $asn 2595 AND active = 1 AND boards = '' AND expires_on = 0 AND report_only = 0 2596 LIMIT 1 2597 SQL; 2598 2599 $res = mysql_global_call($query); 2600 2601 if (!$res) { 2602 return false; 2603 } 2604 2605 if (mysql_num_rows($res) > 0) { 2606 return true; 2607 } 2608 } 2609 2610 $long_ip = ip2long($ip); 2611 2612 if (!$long_ip) { 2613 $this->error('Invalid IP.'); 2614 } 2615 2616 $query =<<<SQL 2617 SELECT id FROM iprangebans 2618 WHERE range_start <= $long_ip AND range_end >= $long_ip 2619 AND active = 1 AND boards = '' AND expires_on = 0 AND report_only = 0 2620 LIMIT 1 2621 SQL; 2622 2623 $res = mysql_global_call($query); 2624 2625 if (!$res) { 2626 return false; 2627 } 2628 2629 return mysql_num_rows($res) > 0; 2630 } 2631 2632 function admin_protect_thread() { 2633 if (!isset($_GET['thread_id'])) { 2634 error('Bad Request.'); 2635 } 2636 2637 $thread_id = (int)$_GET['thread_id']; 2638 2639 if ($thread_id <= 0) { 2640 error('Bad Request.'); 2641 } 2642 2643 if (isset($_GET['remove']) && $_GET['remove']) { 2644 $flag = false; 2645 } 2646 else { 2647 $flag = true; 2648 } 2649 2650 $ret = admin_toggle_protected_thread($flag, BOARD_DIR, $thread_id); 2651 2652 if (!$ret) { 2653 error('Something went wrong.'); 2654 } 2655 else { 2656 echo "Done."; 2657 } 2658 } 2659 2660 // Signs the ip + timestamp for authenticating reverse dns requests below 2661 // FIXME: This is to avoid delaying ban panels 2662 function admin_get_rev_ip_sig($ip, $t) { 2663 if (!$ip || !$t) { 2664 return false; 2665 } 2666 2667 $secret = 'BusEFdduVhgVKIMAx1ndhvzrgMyA5uCcfRnvIKq4+0X2vL8elzf6wHZCpWS9fsTsNG/XdlwiIBV68hzlGm6sGQ=='; 2668 $secret = base64_decode($secret); 2669 2670 if (!$secret) { 2671 return false; 2672 } 2673 2674 $msg = "$ip $t"; 2675 2676 return hash_hmac('sha256', $msg, $secret); 2677 } 2678 2679 // Prints a JSON response with the hostname of the IP 2680 // FIXME: This is to avoid delaying ban panels 2681 // The IP needs to be in the long int format 2682 function admin_reverse_ip() { 2683 if (!isset($_GET['ip']) || !isset($_GET['t']) || !isset($_GET['s'])) { 2684 die('N/A'); 2685 } 2686 2687 if (!$_GET['t'] || !$_GET['s']) { 2688 die('N/A'); 2689 } 2690 2691 $ip = long2ip($_GET['ip']); 2692 2693 if (!$ip) { 2694 die('N/A'); 2695 } 2696 2697 if ($_SERVER['REQUEST_TIME'] - (int)$_GET['t'] > 3) { 2698 die('N/A'); 2699 } 2700 2701 $sig = admin_get_rev_ip_sig($_GET['ip'], $_GET['t']); 2702 2703 if (!$sig) { 2704 die('N/A'); 2705 } 2706 2707 if (hash_equals($sig, $_GET['s']) !== true) { 2708 die('N/A'); 2709 } 2710 2711 $rev = gethostbyaddr($ip); 2712 2713 if ($rev && $rev == $ip) { 2714 $rev = ''; 2715 } 2716 2717 header('Content-Type: application/json'); 2718 echo json_encode(['rev' => $rev]); 2719 } 2720 2721 /* Admin banning */ 2722 function adminban() 2723 { 2724 if (BOARD_DIR == 'j' && !has_level('manager')) { 2725 die(); 2726 } 2727 2728 global $id, $user, $pass; 2729 2730 $by_tpl_mode = false; 2731 2732 // for async calls from reports.4chan.org 2733 if (isset($_POST['by_tpl']) && $_POST['by_tpl']) { 2734 $template = admin_get_template_by_id($_POST['by_tpl']); 2735 2736 if (!$template) { 2737 die('No such template'); 2738 } 2739 2740 $by_tpl_mode = true; 2741 2742 $_POST['submit'] = 1; 2743 $_POST['pubreason'] = $template['publicreason']; 2744 $_POST['pvtreason'] = $template['privatereason']; 2745 $_POST['days'] = $template['days']; 2746 $_POST['warn'] = (int)($template['days'] == 0 && $template['banlen'] == ''); 2747 $_POST['indefinite'] = (int)($template['banlen'] === 'indefinite'); 2748 $_POST['banmsg'] = 0; 2749 $_POST['bantype'] = $template['bantype']; 2750 2751 // This will be amended later for delall -> delallrep 2752 $_POST['postban'] = $template['postban']; 2753 2754 if ($template['postban'] === 'move' && $template['postban_arg']) { 2755 $_POST['move-board'] = $template['postban_arg']; 2756 } 2757 } 2758 else { 2759 $template = null; 2760 } 2761 2762 $submit = $_POST[ 'submit' ]; 2763 $start_time = microtime( true ); 2764 $xff = htmlspecialchars($_POST[ 'xff' ], ENT_QUOTES); 2765 $pubreason = nl2br( htmlspecialchars( $_POST[ 'pubreason' ] ), false ); 2766 $pvtreason = nl2br( htmlspecialchars( $_POST[ 'pvtreason' ] ), false ); 2767 $reason = "$pubreason<>$pvtreason"; 2768 $bannedby = $_COOKIE['4chan_auser']; 2769 $days = $_POST[ 'days' ]; 2770 $warn = $_POST[ 'warn' ]; 2771 $indefinite = $_POST[ 'indefinite' ]; 2772 $banmsg = $_POST[ 'banmsg' ] == 1; 2773 $globalban = $_POST[ 'bantype' ] == 'global'; 2774 $zonly = isset($_POST['zonly']) && $_POST['zonly'] === '1'; 2775 //$pass_id = htmlspecialchars($_POST[ 'pass_id' ], ENT_QUOTES); 2776 $board = BOARD_DIR; 2777 $postid = (int)$id; 2778 2779 if ($by_tpl_mode) { 2780 $template_used = (int)$_POST['by_tpl']; 2781 } 2782 else { 2783 $template_used = (int)$_POST['templateno']; 2784 } 2785 2786 if( !$result = mysql_board_call( "SELECT HIGH_PRIORITY * FROM `" . SQLLOG . "` WHERE no=" . $postid ) ) { 2787 die( 'Post no longer exists.<script language="JavaScript">setTimeout("self.close()", 3000); postBack("error-ban-' . $board . '-' . $postid . '");</script>' ); 2788 } 2789 $row = mysql_fetch_assoc( $result ); 2790 if( $row === false ) die( 'Post no longer exists.<script language="JavaScript">setTimeout("self.close()", 3000); postBack("error-ban-' . $board . '-' . $postid . '");</script>' ); 2791 2792 if ($row['archived']) { 2793 die('This post is archived.<script language="JavaScript">setTimeout("self.close()", 3000); postBack("error-ban-' . $board . '-' . $postid . '");</script>'); 2794 } 2795 2796 $post_has_file = $row['ext'] && !$row['file_deleted']; 2797 2798 //list( $no, $sticky, $permasage, $closed, $now, $name, $email, $sub, $com, $host, $pwd, $filename, $ext, $w, $h, $tn_w, $tn_h, $tim, $time, $md5, $fsize, $root, $resto ) = $row; 2799 extract( $row, EXTR_OVERWRITE ); 2800 2801 $password = $pwd; 2802 2803 // insert tripcode (trip or !sectrip) if not warning 2804 $tripcode = ''; 2805 2806 if ($warn != 1) { 2807 $name_bits = explode('</span> <span class="postertrip">!', $name); 2808 2809 if ($name_bits[1]) { 2810 $tripcode = preg_replace('/<[^>]+>/', '', $name_bits[1]); // fixme: why do we do that? 2811 } 2812 } 2813 2814 $name = str_replace( '</span> <span class="postertrip">!', ' #', $name ); 2815 $name = preg_replace( '/<[^>]+>/', '', $name ); // remove all remaining html crap 2816 2817 if( !$result = mysql_board_call( "select COUNT(*) FROM `" . SQLLOG . "` WHERE host='$host' AND no=$resto" ) ) { 2818 echo S_SQLFAIL; 2819 } 2820 2821 if (mysql_result($result, 0, 0) || $resto == 0) { 2822 $poster_is_op = true; 2823 } 2824 else { 2825 $poster_is_op = false; 2826 } 2827 2828 if( $submit != "" ) { // pressed submit 2829 if (!$host) { 2830 error('You cannot ban this post'); 2831 } 2832 2833 if ($host) { 2834 $reverse = gethostbyaddr($host); 2835 } 2836 else { 2837 $reverse = ''; 2838 } 2839 2840 $displayhost = ( $reverse && $reverse != $host ) ? "$reverse ($host)" : $host; 2841 2842 if ($template_used > -1) { 2843 if (!$template) { 2844 $template = mysql_global_row("ban_templates", "no", $template_used); 2845 } 2846 2847 if (!$template) { 2848 error('Invalid template'); 2849 } 2850 2851 if (!has_level($template['level'])) { 2852 error('You cannot use this template'); 2853 } 2854 2855 if (($template['no'] == 1 || $template['no'] == 123 || $template['no'] == 213) && !$post_has_file) { 2856 error('This template requires a post with a file'); 2857 } 2858 } 2859 2860 if( !$template_used ) { 2861 $rule = ''; 2862 } 2863 else { 2864 $rule = $template[ 'rule' ]; 2865 } 2866 2867 if( !$row ) { 2868 echo "This post doesn't exist anymore.<br>"; 2869 die( "[<a href=\"javascript:void(0)\" onclick=\"history.back()\">Back</a>]</body></html>" ); 2870 } 2871 if( $pubreason == "" ) { 2872 echo "Public reason not specified.<br>"; 2873 die( "[<a href=\"javascript:void(0)\" onclick=\"history.back()\">Back</a>]</body></html>" ); 2874 } 2875 elseif( $bannedby == "" ) { 2876 echo "Admin name not specified.<br>"; 2877 die( "[<a href=\"javascript:void(0)\" onclick=\"history.back()\">Back</a>]</body></html>" ); 2878 } 2879 elseif( !is_numeric( $days ) && ( $indefinite != 1 ) && ( $warn != 1 ) ) { 2880 echo "Length of ban not specified.<br>"; 2881 die( "[<a href=\"javascript:void(0)\" onclick=\"history.back()\">Back</a>]</body></html>" ); 2882 } 2883 else { 2884 if( $warn != 1 ) { 2885 $ubd_ts = date( "Y-m-d H:i:s", time() + $days * ( 24 * 60 * 60 ) ); 2886 } 2887 else { 2888 $ubd_ts = date( "Y-m-d H:i:s", time() ); 2889 } 2890 if( $indefinite == 1 ) { 2891 $length = "00000000000000"; 2892 } 2893 else { 2894 $length = $ubd_ts; 2895 } 2896 } 2897 2898 $is_manager = has_level('manager'); 2899 2900 if (!$is_manager) { 2901 $zonly = 0; 2902 } 2903 2904 $nrow = array(); 2905 2906 foreach( $row as $key => $val ) { 2907 if( ctype_digit( $val ) || is_int( $val ) ) { 2908 $val = (int)$val; 2909 } 2910 $nrow[ $key ] = $val; 2911 } 2912 2913 if ($row['resto']) { 2914 $sub_query = mysql_board_call("SELECT sub FROM `%s` WHERE no = %d", $board, $row['resto']); 2915 $sub_res = mysql_fetch_assoc($sub_query); 2916 if ($sub_res) { 2917 $rel_sub = $sub_res['sub']; 2918 2919 if (strpos($rel_sub, 'SPOILER<>') === 0) { 2920 $rel_sub = substr($rel_sub, 9); 2921 } 2922 2923 if ($rel_sub !== '') { 2924 $nrow['rel_sub'] = $rel_sub; 2925 } 2926 } 2927 } 2928 2929 // FIXME: email field 2930 if (isset($row['email'])) { 2931 $nrow['ua'] = $row['email']; 2932 unset($nrow['email']); 2933 } 2934 2935 $post_json = json_encode($nrow); 2936 $no_thumb = false; 2937 2938 if ($template && $template['save_post'] !== 'everything') { 2939 $no_thumb = true; 2940 } 2941 2942 $result = mysql_global_do( "INSERT INTO " . SQLLOGBAN . " (board,global,zonly,name,host,reverse,xff,reason,length,admin,md5,4pass_id,post_num,rule,post_time,post_json,template_id,admin_ip,tripcode,password) VALUES ('%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, '%s', FROM_UNIXTIME(%d), '%s', %d, '%s', '%s', '%s')", $board, $globalban, $zonly, $name, $host, $reverse, $xff, $reason, $length, $bannedby, $md5, $row[ '4pass_id' ], $no, $rule, $time, $post_json, $template_used, $_SERVER['REMOTE_ADDR'], $tripcode, $password ); 2943 2944 if( !$result ) { 2945 echo S_SQLFAIL; 2946 } 2947 2948 if( $ext != '' && $template_used && !$no_thumb ) { 2949 $salt = file_get_contents( SALTFILE ); 2950 $hash = sha1( BOARD_DIR . $no . $salt ); 2951 @copy( THUMB_DIR . "{$tim}s.jpg", BANTHUMB_DIR . "{$hash}s.jpg" ); 2952 } 2953 2954 if( $banmsg ) { 2955 if( $warn ) { 2956 $samessage = S_USERWARNEDFORPOST; 2957 } 2958 else { 2959 $samessage = S_USERBANNEDFORPOST; 2960 } 2961 2962 //if( isset( $_GET[ 'santa' ] ) && $_GET[ 'santa' ] == 'hohoho' ) $samessage = 'USER WAS GIVEN COAL FOR THIS POST'; 2963 2964 if (($is_manager || has_flag('banmsg')) && isset($_POST['custombanmsg']) && $_POST['custombanmsg'] != '') { 2965 $samessage = mysql_real_escape_string(htmlspecialchars($_POST['custombanmsg'], ENT_QUOTES)); 2966 } 2967 2968 if( !$result = mysql_board_call( "UPDATE `" . SQLLOG . "` SET root=root,com=CONCAT(com,' <br><br><strong style=\"color: red;\">($samessage)</strong>') WHERE no=%d", $postid ) ) { 2969 echo S_SQLFAIL; 2970 } 2971 mysql_board_call("UPDATE `%s` SET root=root,last_modified=%d where no=%d", SQLLOG, (int)$_SERVER['REQUEST_TIME'], ($resto ? $resto : $postid)); 2972 2973 if ($_POST['postban'] !== 'delpost' && $_POST['postban'] !== 'move' && $_POST['postban'] !== 'archive') { 2974 admin_clear_reports(BOARD_DIR, $postid); 2975 } 2976 } 2977 2978 //print "\n<br>insert: " . (time() - $start_time)."\n<br>"; //disabling this because again nobody needs to see it/leaks filepaths 2979 echo "<strong>Banning " . $displayhost . " from "; 2980 2981 if( $globalban == 1 ) { 2982 echo "the entirety of 4chan...</strong><br>"; 2983 //append_ban( "global", $host ); 2984 } 2985 else { 2986 echo "/" . $board . "/...</strong><br>"; 2987 //append_ban( $board, $host ); 2988 } 2989 2990 if( $length == "00000000000000" ) { 2991 echo " for an indefinite amount of time."; 2992 } 2993 else { 2994 echo " until " . date( 'l, F jS, Y', time() + $days * ( $warn ? 0 : 24 * 60 * 60 ) ) . ".<br><strong>Ban successful!</strong>"; 2995 } 2996 //print "\n<br>rebuild : " . (microtime(1) - $start_time); //disabling because no point in showing it/leaks file paths 2997 if( $template ) { 2998 global $gcon; 2999 $inserted_ban_id = mysql_insert_id($gcon); 3000 do_template_special_action( $template, $board, $row, $is_manager ); 3001 if( ( $template[ "blacklist" ] == "image" || $template[ 'blacklist' ] == 'rejectimage' ) && $md5 ) { 3002 $blban = (int)( $template[ 'blacklist' ] === 'image' ); 3003 $len = $blban ? $template['days'] : '0'; 3004 mysql_global_do( "insert into blacklist (field,contents,description,addedby,ban,banlength,banreason)" . 3005 "values ('md5','%s','%s','%s','$blban','$len','%s')", 3006 $md5, $template[ "name" ] . " (via ban template, ban ID: $inserted_ban_id)", $bannedby, $template[ "publicreason" ] ); 3007 } 3008 } 3009 3010 // Auto-rangebans processing (bans) 3011 // FIXME: email field 3012 $_post_meta = decode_user_meta($row['email']); 3013 3014 if (!$warn && $_post_meta && $_post_meta['is_mobile']) { // mobile devices only 3015 // global rules only 3016 if ($template && strpos($template['rule'], 'global') !== false) { 3017 process_auto_rangeban($host, $_post_meta['browser_id'], $row['resto'], $row['no'], $template['no'], 1); 3018 } 3019 3020 // Collect and ban other IPs based on the password 3021 /* 3022 $related_posts = admin_collect_related($host, $password); 3023 3024 if ($related_posts) { 3025 write_to_event_log('rel_posts', $host, [ 3026 'board' => BOARD_DIR, 3027 'thread_id' => $row['resto'], 3028 'post_id' => $no, 3029 'pwd' => $password, 3030 'arg_str' => $rule, 3031 'meta' => json_encode($related_posts) 3032 ]); 3033 } 3034 */ 3035 } 3036 3037 $should_delete = $_POST[ 'postban' ] == 'delpost' || $_POST[ 'postban' ] == 'delfile'; 3038 3039 $skip_rebuild = false; 3040 3041 if( $should_delete ) { 3042 echo "<br>"; 3043 if (delete_post($no, $_POST[ 'postban' ] == 'delfile' ? 1 : 0, $template ? $template['no'] : false, 'ban')) { 3044 echo ( ( $_POST[ 'postban' ] == 'delfile' ) ? "<strong>File deleted.</strong>" : "<strong>Post deleted.</strong>" ); 3045 } 3046 // Fixme, this is for the temporary is2/is3 cache purging api 3047 if ($ext != '' && $template_used && $template['rule'] == 'global1' && !UPLOAD_BOARD) { 3048 purge_cache_internal_temp(BOARD_DIR, "$tim$ext"); 3049 } 3050 //print "\n<br>delete post: " . (microtime(true) - $start_time); //disabling, no point and leaks dirs 3051 } 3052 else if ($resto == 0) { 3053 if ($_POST['postban'] == 'move') { 3054 if (!isset($_POST['move-board']) || !is_board_valid($_POST['move-board'])) { 3055 echo ('<strong>Invalid destination board. The thread was not moved.</strong>'); 3056 } 3057 else { 3058 move_thread($no, $_POST['move-board']); 3059 3060 echo ('<br><strong>Thread moved to /' . htmlspecialchars($_POST['move-board']) . '/.</strong>'); 3061 3062 $skip_rebuild = true; 3063 } 3064 } 3065 else if ($_POST['postban'] === 'archive') { 3066 archive_thread($no); 3067 3068 echo ('<br><strong>Thread archived.</strong>'); 3069 3070 $skip_rebuild = true; 3071 } 3072 else if ($_POST['postban'] === 'close') { 3073 if (mysql_board_call('UPDATE `%s` SET closed = 1 WHERE no = %d LIMIT 1', BOARD_DIR, $no)) { 3074 log_thread_opts_action($row, $row['sticky'], $row['permasage'], 1, $row['permaage'], $row['undead']); 3075 echo ('<br><strong>Thread closed.</strong>'); 3076 } 3077 else { 3078 echo ('<br><strong>Could not close thread.</strong>'); 3079 } 3080 } 3081 else if ($_POST['postban'] === 'permasage') { 3082 if (mysql_board_call('UPDATE `%s` SET permasage = 1 WHERE no = %d LIMIT 1', BOARD_DIR, $no)) { 3083 log_thread_opts_action($row, $row['sticky'], 1, $row['closed'], $row['permaage'], $row['undead']); 3084 echo ('<br><strong>Thread perma-saged.</strong>'); 3085 } 3086 else { 3087 echo ('<br><strong>Could not perma-sage thread.</strong>'); 3088 } 3089 } 3090 } 3091 3092 echo '<script language="JavaScript">setTimeout("self.close()", 3000); postBack("done-ban-' . $board . '-' . $no . '");</script>'; 3093 if( $banmsg && !$should_delete && !$skip_rebuild) { //need to update log because of the ban message 3094 rebuild_thread( ( $resto ) ? $resto : $no ); 3095 } 3096 3097 // Delete all posts by IP, including threads 3098 if ($_POST['postban'] === 'delall') { 3099 delallbyip($host, false); 3100 } 3101 // Delete only replies by IP 3102 else if ($_POST['postban'] === 'delallrep') { 3103 // Delete the thread if the target post is an OP 3104 if (!$resto) { 3105 delete_post($no, 0, $template ? $template['no'] : false, 'ban'); 3106 } 3107 3108 delallbyip($host, false, true); 3109 } 3110 3111 //print "\n<br>total time: " . (microtime(1) - $start_time); //dont need to display this 3112 } else { 3113 // Banning screen display 3114 $adminuser = mysql_real_escape_string( $_COOKIE[ '4chan_auser' ] ); 3115 // see if user is banned 3116 $ban_summary = get_bans_summary($host); 3117 3118 if( $ban_summary['total'] > 0 ) { // don't bother checking the active ban if there weren't ever any bans on this IP... 3119 if( !$banned = mysql_global_call( "SELECT host,board,global,zonly, DATE_FORMAT(length, 'Until %W, %M %D, %Y.') AS buntil FROM " . SQLLOGBAN . " WHERE host='" . $host . "' AND active=1" ) ) { 3120 echo S_SQLFAIL; 3121 } 3122 $bannedrows = mysql_num_rows( $banned ); 3123 if( $bannedrows > 0 ) { 3124 while ($ban_row = mysql_fetch_array($banned)) { 3125 $buntil = $ban_row[ 'buntil' ]; 3126 $gban = $ban_row[ 'global' ]; 3127 $bannedboard = $ban_row[ 'board' ]; 3128 $bannedzonly = $ban_row[ 'zonly' ]; 3129 if( $bannedboard == BOARD_DIR ) { 3130 $bg = "f0d0d0"; 3131 if( $buntil == "" ) $buntil = "Indefinitely."; 3132 $bantrue = 1; 3133 } 3134 if( $gban == 1 ) { 3135 $bg = "f0a0a0"; 3136 if( $buntil == "" ) $buntil = "Indefinitely."; 3137 $globally = " (Globally)"; 3138 $bantrue = 1; 3139 break; 3140 } 3141 else { 3142 $globally = " (" . $board . ")"; 3143 } 3144 } 3145 } 3146 if( $bantrue ) { 3147 echo "<style>body { background: #$bg; }</style>"; 3148 } 3149 } 3150 3151 $note = array(); 3152 3153 if ($poster_is_op) { 3154 $note[] = 'This poster is the OP'; 3155 3156 if ($resto == 0) { 3157 $_count = admin_get_thread_history($host); 3158 3159 if ($_count > 1) { 3160 $note[0] .= ' <sup data-tip="Other threads made in the past hour">' . ($_count - 1) . '</sup>'; 3161 } 3162 } 3163 } 3164 3165 if ($row['4pass_id'] != '') { 3166 $has_4chan_pass = $row['4pass_id']; 3167 3168 $note[] = 'This user is using a 4chan Pass'; 3169 3170 $ban_summary_pass = get_bans_summary($has_4chan_pass, true); 3171 } 3172 else { 3173 $has_4chan_pass = false; 3174 $ban_summary_pass = null; 3175 } 3176 3177 if (!preg_match('/Android|iPhone|iPad/', $_SERVER['HTTP_USER_AGENT'])) { 3178 $autofocus_html = ' autofocus="autofocus"'; 3179 } 3180 else { 3181 $autofocus_html = ''; 3182 } 3183 3184 if ($host) { 3185 $geoinfo = GeoIP2::get_country($host); 3186 $asninfo = GeoIP2::get_asn($host); 3187 } 3188 else { 3189 $geoinfo = $asninfo = false; 3190 } 3191 3192 if ($asninfo && isset($asninfo['aso'])) { 3193 $aso_formatted = ' (' . htmlspecialchars($asninfo['aso'], ENT_QUOTES) . ')'; 3194 } 3195 else { 3196 $aso_formatted = ''; 3197 } 3198 3199 echo '<form action="" method="post">'; 3200 echo csrf_tag(); 3201 echo "<input type=\"hidden\" name=\"mode\" value=\"admin\">\n"; 3202 echo "<input type=\"hidden\" name=\"admin\" value=\"ban\">\n"; 3203 echo "<input type=\"hidden\" name=\"user\" value=\"$user\">\n"; 3204 echo "<input type=\"hidden\" name=\"pass\" value=\"$pass\">\n"; 3205 echo "<input type=\"hidden\" name=\"id\" value=\"$postid\">\n"; 3206 echo '<input type="hidden" name="templateno" value="-1">' . "\n"; 3207 echo "<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\" class=\"bantable\">\n"; 3208 echo "<tr><td class=\"postblock\">Autocomplete</td><td><input type=\"text\" name=\"autocomplete\" placeholder=\"Start typing...\" id=\"autocomplete\" size=\"40\"$autofocus_html autocomplete=\"off\" style=\"width: 100%;\"></td></tr>" . "\n"; 3209 echo "<tr name=\"template_row\" style=\"visibility: hidden;\"><td style=\"height: 20px;\" class=\"postblock\">Template</td><td><select name=\"template\" style=\"width: 100%;\" onchange=\"chooseTemplate();\"></select></td></tr>\n"; 3210 echo "<tr><td class=\"postblock\">Name</td><td><input type=\"text\" name=\"name\" value=\"$name\" size=\"40\" style=\"width: 100%;\" readonly=\"readonly\"></td></tr>\n"; 3211 echo "<tr><td class=\"postblock\">IP</td><td><input type=\"text\" name=\"ip\" value=\"$host$aso_formatted\" size=\"40\" style=\"width: 100%;\" readonly=\"readonly\"></td></tr>\n"; 3212 echo "<tr><td class=\"postblock\">Host</td><td><input type=\"text\" id=\"js-ip-rev\" name=\"reverse\" value=\"...\" size=\"40\" style=\"width: 100%;\" readonly=\"readonly\"></td></tr>\n"; 3213 3214 if ($geoinfo && isset($geoinfo['country_code'])) { 3215 $geo_loc = array(); 3216 3217 if (isset($geoinfo['city_name'])) { 3218 $geo_loc[] = $geoinfo['city_name']; 3219 } 3220 3221 if (isset($geoinfo['state_code'])) { 3222 $geo_loc[] = $geoinfo['state_code']; 3223 } 3224 3225 $geo_loc[] = $geoinfo['country_name']; 3226 3227 $loc = htmlspecialchars(implode(', ', $geo_loc), ENT_QUOTES); 3228 3229 echo ' 3230 <tr> 3231 <td class="postblock">Location</td> 3232 <td><input type="text" value="' . $loc . '" readonly="readonly" style="width: 100%;"></td> 3233 </tr> 3234 '; 3235 } 3236 3237 $ban_history_row = array(); 3238 3239 if ($ban_summary['total'] > 0) { 3240 $ban_history_row[] = get_ban_history_html($ban_summary, $host); 3241 3242 } 3243 3244 if ($ban_summary_pass['total'] > 0) { 3245 $ban_history_row[] = get_ban_history_html($ban_summary_pass); 3246 } 3247 3248 if (!empty($ban_history_row)) { 3249 echo "<tr><td class=\"postblock\" style=\"height: 20px;\">Ban History</td><td style=\"padding-top: 4px; padding-bottom: 4px;\">" . 3250 implode(' ', $ban_history_row) . "</td></tr>\n"; 3251 } 3252 3253 // Browser ID 3254 $_post_meta = decode_user_meta($row['email']); 3255 3256 $query = "SELECT warn_req, ban_templates.name FROM ban_requests LEFT JOIN ban_templates ON ban_template = ban_templates.no WHERE host='%s'"; 3257 $result = mysql_global_call($query, $host); 3258 $brpending = array(); 3259 while ($row = mysql_fetch_assoc($result)) { 3260 $brpending[] = $row['name'] . ($row['warn_req'] ? ' [Warn]' : ''); 3261 } 3262 $brtooltip = join("\n", $brpending); 3263 $pending = ''; 3264 $brcount = count($brpending); 3265 if ($brcount > 0) { 3266 $plural = ($brcount > 1) ? 's' : ''; 3267 $pending = <<<HTML 3268 <tr> 3269 <td style="height: 20px;" class="postblock">Ban Requests</td> 3270 <td style="cursor:help;" title="$brtooltip"> 3271 [$brcount pending ban request$plural] 3272 </td> 3273 </tr> 3274 HTML; 3275 } 3276 echo $pending; 3277 3278 echo "<tr><td class=\"postblock\">Public Ban Reason</td><td><textarea disabled=\"disabled\" name=\"pubreason\" value=\"\" cols=\"30\" rows=\"3\" title=\"The banned user will see this message.\" style=\"width: 100%; margin-bottom: 0px !important;\"></textarea></td></tr>\n"; 3279 3280 echo "<tr><td style=\"height: 20px;\" class=\"postblock\">Private Info</td><td><input disabled=\"disabled\" type=\"text\" style=\"width: 100%;\" name=\"pvtreason\" value=\"\" size=\"40\" title=\"Optional extra information for 4chan moderators. This will show up on the ban list.\"></td></tr>\n"; 3281 3282 echo "<tr><td class=\"postblock\">Unban In</td><td><input id=\"ban-days\" disabled=\"disabled\" name=\"days\" type=\"number\" size=\"4\" min=\"0\" maxlength=\"4\" class=\"inputcenter\" /> days [<input type=\"checkbox\" name=\"warn\" value=\"1\">Warn] [<input type=\"checkbox\" name=\"indefinite\" value=\"1\">Perma]</tr>\n"; 3283 3284 echo "<tr id=\"more_file\"><td style=\"height: 20px;\" class=\"postblock\">More Info</td><td style=\"padding-top: 4px; padding-bottom: 4px;\">[<a href='javascript:more(\"more_info\",\"more_info\")'>View Info</a>] [<a target=\"_blank\" data-tip=\"Search posts by IP\" href=\"https://team.4chan.org/search#{"ip":"$host"}\">Search</a>]" . ($_post_meta['is_mobile'] ? ' <span data-tip="Posted from a mobile device" class="ico-phone">☎</span>' : '') . "</td></tr>"; 3285 3286 if (!empty($note)) { 3287 $note = implode('<br>', $note); 3288 3289 echo "<tr><td style=\"height: 20px;\" class=\"postblock\">Note</td><td><strong>$note</strong></td></tr>"; 3290 } 3291 3292 if (has_level('manager')/* || has_flag('developer')*/) { 3293 $toz = ' [<input type="checkbox" name="zonly" value="1">Unappealable]'; 3294 } 3295 else { 3296 $toz = ''; 3297 } 3298 3299 if (has_level('manager') || has_flag('banmsg')) { 3300 $ban_msg_row = "<tr style=\"display:none\" id=\"pub-ban-msg\"><td style=\"height: 20px;\" class=\"postblock\">Message</td><td><input name=\"custombanmsg\" placeholder=\"USER WAS BANNED FOR THIS POST\" type=\"text\" style=\"width: 100%;\" title=\"Custom public ban message\">"; 3301 3302 $ban_msg_row .= <<<JS 3303 <script type="text/javascript"> 3304 function toggleBanMsg(cb) { 3305 var el = document.getElementById('pub-ban-msg'); 3306 if (!el) { return; } 3307 if (cb.checked) { 3308 el.style.display = ''; 3309 } 3310 else { 3311 el.style.display = 'none'; 3312 } 3313 } 3314 </script></td></tr> 3315 JS; 3316 3317 $pub_ban_evt = ' onchange="toggleBanMsg(this)"'; 3318 } 3319 else { 3320 $ban_msg_row = $pub_ban_evt = ''; 3321 } 3322 3323 if ($resto == 0 && ENABLE_ARCHIVE) { 3324 $_opt_archive = '<option value="archive">Archive</option>'; 3325 } 3326 else { 3327 $_opt_archive = ''; 3328 } 3329 3330 if (!$host) { 3331 $btn_disabled = ' disabled'; 3332 } 3333 else { 3334 $btn_disabled = ''; 3335 } 3336 3337 echo "<tr><td style=\"height: 20px;\" class=\"postblock\">Ban Scope</td><td><input$btn_disabled id=\"submit-ban-btn\" type=\"submit\" name=\"submit\" value=\"Submit\"><select name=\"bantype\"><option value=\"local\">Ban from /$board/</option><option value=\"global\">Global ban</option></select><div>[<span title=\"Display (USER WAS BANNED FOR THIS POST) message.\"><input type=\"checkbox\"$pub_ban_evt name=\"banmsg\" value=\"1\">Public Ban</span>]$toz</div></td></tr>$ban_msg_row"; 3338 3339 echo "<tr><td style=\"height: 20px;\" class=\"postblock\">Post-Ban</td><td><select id=\"js-postban-sel\" name=\"postban\"><option value=\"\">Nothing</option><option value=\"delpost\">Delete post</option><option value=\"delfile\">Delete file only</option>$_opt_archive<option value=\"delallrep\" style=\"color:red\">Delete all replies by IP</option><option value=\"delall\" style=\"font-weight:bold;color:red\">Delete all posts by IP</option>"; 3340 3341 if ($resto == 0) { 3342 echo "<option value=\"close\">Close</option><option value=\"permasage\">Perma-sage</option>"; 3343 3344 $board_sel = get_board_options_html(); 3345 echo "<option value=\"move\">Move</option>"; 3346 echo "</select><select id=\"js-move-board-sel\" name=\"move-board\">$board_sel</select></td></tr>"; 3347 } 3348 else { 3349 echo "</select></td></tr>"; 3350 } 3351 3352 $can_thread_ban = false; 3353 3354 if ($resto == 0 && (has_level('manager') || has_flag('threadban'))) { 3355 echo "<tr><td style=\"height: 20px;\" class=\"postblock\">Ban Thread</td><td><input$btn_disabled type=\"button\" value=\"Ban Entire Thread\" style=\"margin-left: 0px;\" id=\"js-tb-btn\"></td></tr>"; 3356 $can_thread_ban = true; 3357 } 3358 3359 echo "</table>\n"; 3360 echo "</form>"; 3361 3362 // Async reverse IP request 3363 if ($host) { 3364 $_rev_long_ip = ip2long($host); 3365 $_rev_ts = $_SERVER['REQUEST_TIME']; 3366 $_rev_sig = admin_get_rev_ip_sig($_rev_long_ip, $_rev_ts); 3367 ?> 3368 <script type="text/javascript"> 3369 async function admin_rev_ip() { 3370 let el = document.getElementById('js-ip-rev'); 3371 if (!el) { return; } 3372 let ip = <?php echo $_rev_long_ip ?>; 3373 let t = <?php echo $_rev_ts ?>; 3374 let s = '<?php echo $_rev_sig ?>'; 3375 const resp = await fetch(`?admin=rev&ip=${ip}&t=${t}&s=${s}`); 3376 if (resp.ok) { 3377 const json = await resp.json(); 3378 el.value = json.rev; 3379 } 3380 } 3381 admin_rev_ip(); 3382 </script> 3383 <?php } 3384 3385 if ($can_thread_ban) { ?> 3386 <div id="thread-ban-layer" style="position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.6); display: none;"> 3387 <div style="position:absolute; left: 50%; top: 50%; padding: 6px; margin-left: -185px; margin-top: -52px;" class="post reply preview"> 3388 <form action="" method="POST"> 3389 <?php echo csrf_tag(); ?> 3390 <input type="hidden" name="mode" value="admin"> 3391 <input type="hidden" name="admin" value="cpban"> 3392 <input type="hidden" name="no" value="<?php echo $no ?>"> 3393 <table class="bantable bantable-extra bantable-tb"> 3394 <tr><td data-tip="Public reason for the OP" class="postblock">OP Reason</td><td><input type="text" name="op_reason" value="Posting off-topic threads."></td></tr> 3395 <tr><td data-tip="Ban length in days for the OP" class="postblock">OP Ban Length</td><td><input type="text" autocomplete="off" name="op_days" value="3"></td></tr> 3396 <tr><td data-tip="Public reason for replies" class="postblock">Rep. Reason</td><td><input type="text" name="rep_reason" value="Replying to off-topic threads."></td></tr> 3397 <tr><td data-tip="Ban length in days for replies" class="postblock">Rep. Ban Length</td><td><input type="text" autocomplete="off" name="rep_days" value="0"></td></tr> 3398 <tr><td></td><td style="text-align: right; padding-top: 8px"><button type="submit">Ban</button><button type="button" style="margin-left: 20px" id="js-tb-cancel">Cancel</button></td></tr> 3399 </table> 3400 </form> 3401 </div> 3402 </div> 3403 <script type="text/javascript"> 3404 document.getElementById('js-tb-btn').addEventListener('click', toggleThreadBanPanel, false); 3405 document.getElementById('js-tb-cancel').addEventListener('click', toggleThreadBanPanel, false); 3406 3407 function toggleThreadBanPanel(e) { 3408 let el = document.getElementById('thread-ban-layer'); 3409 3410 if (el.style.display === 'none') { 3411 el.style.display = 'block'; 3412 } 3413 else { 3414 el.style.display = 'none'; 3415 } 3416 } 3417 </script> 3418 <?php 3419 } 3420 3421 $html = <<<HTML 3422 <script type="text/javascript"> 3423 var el; 3424 3425 function submitRequest(e) { 3426 var select, index; 3427 3428 select = document.forms[0].template; 3429 index = select.selectedIndex; 3430 3431 if (index === 0) { 3432 e.preventDefault(); 3433 e.stopPropagation(); 3434 alert("You forgot to select a template."); 3435 } 3436 else { 3437 if (/ Child |\[Perm\]/.test(select.options[index].textContent)) { 3438 if (!checkSubmitConfirm(this)) { 3439 e.preventDefault(); 3440 e.stopPropagation(); 3441 return; 3442 } 3443 } 3444 postBack("start-ban-$board-$no"); 3445 } 3446 } 3447 3448 if (el = document.getElementById("submit-ban-btn")) { 3449 el.addEventListener("click", submitRequest, false); 3450 } 3451 </script> 3452 HTML; 3453 3454 echo $html; 3455 3456 $is_manager = has_level('manager') || has_flag('developer'); 3457 3458 echo ban_template_js($post_has_file, $resto == 0); 3459 3460 echo "<div id=\"more_info\" style=\"position: absolute; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.6); display: none;\"><div style=\"position:absolute; left: 50%; top: 50%; padding: 6px; margin-left: -185px; margin-top: -52px;\" class=\"post reply preview\"><table class=\"bantable bantable-extra\">"; 3461 3462 if ($has_4chan_pass && has_level('mod')) { 3463 if ($is_manager) { 3464 echo "<tr class=\"more_pass\"><td class=\"postblock\">4chan Pass</td><td><input type=\"text\" value=\"$has_4chan_pass\" readonly=\"readonly\"></td></tr>"; 3465 } 3466 else { 3467 $hashed_4chan_pass = admin_hash_4chan_pass($has_4chan_pass); 3468 echo "<tr class=\"more_pass\"><td class=\"postblock\"><span data-tip=\"Hashed 4chan Pass\">Hashed Pass</span></td><td><input type=\"text\" value=\"$hashed_4chan_pass\" readonly=\"readonly\"></td></tr>"; 3469 } 3470 } 3471 3472 if ($md5) { 3473 echo "<tr class=\"more_file\"><td class=\"postblock\">MD5</td><td><input type=\"text\" name=\"md5_disp\" value=\"$md5\" size=\"34\" readonly=\"readonly\"></td></tr>"; 3474 echo "<tr class=\"more_file\"><td class=\"postblock\">Filename</td><td><input type=\"text\" name=\"md5_disp\" value=\"$filename\" size=\"34\" readonly=\"readonly\"></td></tr>"; 3475 3476 echo "<tr><td data-tip=\"Perceptual hash\" class=\"postblock\">PHash</td><td><input type=\"text\" value=\"{$tmd5}\" size=\"34\" readonly=\"readonly\"></td></tr>"; 3477 } 3478 3479 echo "<tr id=\"more_pwd\"><td class=\"postblock\">Password</td><td><input type=\"text\" name=\"pwd_disp\" value=\"$pwd\" size=\"34\" readonly=\"readonly\"></td></tr>"; 3480 3481 if ($host && admin_is_ip_rangebanned($host)) { 3482 echo "<tr><td class=\"postblock\">Rangeban</td><td><input type=\"text\" name=\"rangeban_info\" value=\"Yes\" size=\"34\" readonly=\"readonly\"></td></tr>"; 3483 } 3484 3485 if ($_post_meta['req_sig']) { 3486 echo "<tr><td data-tip=\"Signature of the HTTP request\" class=\"postblock\">Req. Sig.</td><td><input type=\"text\" value=\"{$_post_meta['req_sig']}\" size=\"34\" readonly=\"readonly\"></td></tr>"; 3487 } 3488 3489 if ($_post_meta['browser_id']) { 3490 echo "<tr><td class=\"postblock\">Browser ID</td><td><input type=\"text\" value=\"{$_post_meta['browser_id']}\" size=\"34\" readonly=\"readonly\"></td></tr>"; 3491 } 3492 3493 $_user_status = user_known_status_to_str($_post_meta['known_status']); 3494 3495 if ($_post_meta['verified_level']) { 3496 $_user_status .= ', Verified'; 3497 } 3498 3499 if ($_user_status) { 3500 echo "<tr><td class=\"postblock\">User Status</td><td><input type=\"text\" value=\"$_user_status\" size=\"34\" readonly=\"readonly\"></td></tr>"; 3501 } 3502 3503 echo "<tr><td></td><td>[<a href='javascript:more(\"more_info\",\"more_info\")'>Close</a>]</td></tr>"; 3504 echo "</table></div></div>"; 3505 3506 die( "</body></html>" ); 3507 } 3508 } 3509 3510 function adminToggleSpoiler($post, $new_spoiler) { 3511 if (strpos($post['sub'], 'SPOILER<>') === 0) { 3512 $old_subject = substr($post['sub'], 9); 3513 $old_spoiler = true; 3514 } 3515 else { 3516 $old_subject = $post['sub']; 3517 $old_spoiler = false; 3518 } 3519 3520 if ($old_spoiler == $new_spoiler) { 3521 return false; 3522 } 3523 3524 if ($new_spoiler) { 3525 $subject = 'SPOILER<>' . $old_subject; 3526 $actionType = 1; 3527 } 3528 else { 3529 $subject = $old_subject; 3530 $actionType = 2; 3531 } 3532 3533 $query = "UPDATE " . BOARD_DIR . " SET sub = '%s' WHERE no = %d LIMIT 1"; 3534 $res = mysql_board_call($query, $subject, $post['no']); 3535 3536 if (!$res) { 3537 die('Database error (ats).'); 3538 } 3539 3540 $maskShift = 128; 3541 $actionId = $maskShift + $actionType; 3542 3543 $query =<<<SQL 3544 INSERT INTO actions_log (oldmask, newmask, postno, board, name, sub, com, filename, admin) 3545 VALUES (0, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s') 3546 SQL; 3547 3548 mysql_global_call($query, 3549 $actionId, 3550 $post['no'], 3551 BOARD_DIR, 3552 $post['name'], 3553 $post['sub'], 3554 $post['com'], 3555 $post['filename'] . $post['ext'], 3556 $_COOKIE['4chan_auser'] 3557 ); 3558 3559 return true; 3560 } 3561 3562 function adminopt() 3563 { 3564 global $id, $user, $pass; 3565 $submit = $_POST[ 'submit' ]; 3566 $post_sticky = intval( $_POST[ 'sticky' ] ); 3567 $post_sticknum = intval( $_POST[ 'sticknum' ] ); 3568 $post_permaage = intval( $_POST[ 'permaage' ] ); 3569 $post_undead = intval( $_POST[ 'undead' ] ); 3570 $post_permasage = intval( $_POST[ 'permasage' ] ); 3571 $post_closed = intval( $_POST[ 'closed' ] ); 3572 $post_id = (int)$id; 3573 3574 $adminuser = mysql_real_escape_string( $_COOKIE[ '4chan_auser' ] ); 3575 3576 $is_managerplus = has_level( 'manager' ) || has_flag('developer'); 3577 3578 if( !$result = mysql_board_call( "SELECT * FROM `" . SQLLOG . "` WHERE archived = 0 AND no=" . intval( $id ) ) ) { 3579 echo S_SQLFAIL; 3580 } 3581 3582 if (!mysql_num_rows($result)) { 3583 die("Thread not found."); 3584 } 3585 3586 $row = mysql_fetch_assoc( $result ); 3587 3588 if (!$row) { 3589 die('Datbase error.'); 3590 } 3591 3592 extract( $row, EXTR_OVERWRITE ); 3593 3594 if( !$is_managerplus ) $post_permaage = $permaage; // force setting to old value to stop forgery 3595 //if( !$is_managerplus ) $post_undead = $undead; // force setting to old value to stop forgery 3596 3597 if( $resto != 0 ) die(); 3598 3599 if( $submit != "" ) { 3600 if( $post_sticky == 1 && ( $post_sticknum < 0 || $post_sticknum > 60 ) ) { 3601 echo "Sticky number must be between 0 and 59. Higher numbers appear above lower numbers.<br>"; 3602 die( "[<a href=\"javascript:void(0)\" onclick=\"history.back()\">Back</a>]</body></html>" ); 3603 } 3604 else { 3605 if( strlen( $post_sticknum ) == 1 ) $post_sticknum = "0" . $post_sticknum; 3606 $post_sticknum = "202701010000" . $post_sticknum; 3607 } 3608 3609 echo "<script language=\"JavaScript\">setTimeout(\"self.close()\", 3000); postBack('done-threadopt');</script>"; 3610 $vars = ""; 3611 echo "Thread flag status:<ul>"; 3612 if( $post_sticky == 1 ) { 3613 echo "<li>Sticky ✓</li>"; 3614 $vars .= "sticky=1,root=" . $post_sticknum . ","; 3615 } 3616 else { 3617 if( $sticky == 1 ) { 3618 $sticktime = "now()"; 3619 } 3620 else { 3621 $sticktime = "root"; 3622 } 3623 3624 echo '<li>Sticky ✗</li>'; 3625 $vars .= "sticky=0,root=" . $sticktime . ","; 3626 } 3627 if( $post_permasage == 1 ) { 3628 echo "<li>Perma-sage ✓</li>"; 3629 $vars .= "permasage=1,"; 3630 } 3631 else { 3632 echo '<li>Perma-sage ✗</li>'; 3633 $vars .= "permasage=0,"; 3634 } 3635 if( $post_closed == 1 ) { 3636 echo "<li>Closed ✓</li>"; 3637 $vars .= "closed=1,"; 3638 } 3639 else { 3640 echo '<li>Closed ✗</li>'; 3641 $vars .= "closed=0,"; 3642 } 3643 if( $post_permaage ) { 3644 if( $is_managerplus ) { 3645 echo '<li>Perma-age ✓</li>'; 3646 $vars .= "permaage=1,"; 3647 } 3648 } 3649 else { 3650 if( $is_managerplus ) { 3651 echo '<li>Perma-age ✗</li>'; 3652 $vars .= "permaage=0,"; 3653 } 3654 } 3655 if( $post_undead ) { 3656 //if( $is_managerplus ) { 3657 echo '<li>Undead ✓</li>'; 3658 $vars .= "undead=1,"; 3659 //} 3660 } 3661 else { 3662 //if( $is_managerplus ) { 3663 echo '<li>Undead ✗</li>'; 3664 $vars .= "undead=0,"; 3665 //} 3666 } 3667 3668 // Clear the undead flag when a moderator modifies the sticky flag 3669 // so the thread doesn't turn into a rolling sticky or get stuck as undead 3670 /* 3671 if ($undead && !$is_managerplus && $post_sticky != $sticky) { 3672 echo '<li>Undead ✗</li>'; 3673 $vars .= "undead=0,"; 3674 } 3675 */ 3676 $vars .= "last_modified=".$_SERVER['REQUEST_TIME']; // FIXME consider checking if we only change hidden vars and don't update this 3677 3678 echo '</ul><script language="JavaScript">setTimeout("self.close()", 3000); postBack("done-threadopt");</script>'; 3679 3680 if( !$result = mysql_board_call( "UPDATE `" . SQLLOG . "` SET %s WHERE no=%d", $vars, $post_id ) ) { 3681 echo S_SQLFAIL; 3682 } 3683 3684 log_thread_opts_action($row, $post_sticky, $post_permasage, $post_closed, $post_permaage, $post_undead); 3685 3686 if( $post_sticky != $sticky || $post_closed != $closed) rebuild_thread( $post_id ); 3687 } 3688 else { 3689 echo '<form action="" method="post">'; 3690 echo csrf_tag(); 3691 echo "<input type=\"hidden\" name=\"mode\" value=\"admin\"><input type=\"hidden\" name=\"admin\" value=\"opt\"><input type=\"hidden\" name=\"user\" value=\"$user\"><input type=\"hidden\" name=\"pass\" value=\"$pass\"><input type=\"hidden\" name=\"id\" value=\"$post_id\">\n"; 3692 echo "<table class=\"goawayborder\" style=\"width: 100%\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n"; 3693 3694 if( BOARD_DIR != 'b' || $GLOBALS[ 'b_sticky' ] ) { 3695 echo "<tr><td class=\"postblock\" style=\"height: 20px; width: 80px;\"><u>S</u>ticky</td><td><input type=checkbox name=\"sticky\" id=\"js-sticky-cb\" value=\"1\""; 3696 if( $sticky == 1 ) { 3697 echo ' checked data-cur="1"'; 3698 $sticknum = substr( $root, -2 ); 3699 if( $sticknum{0} == "0" ) $sticknum = substr( $sticknum, -1 ); 3700 } 3701 else { 3702 echo ' data-cur="0"'; 3703 $sticknum = "0"; 3704 } 3705 echo "> <input type=\"text\" name=\"sticknum\" value=\"$sticknum\" size=\"2\" maxlength=\"2\" class=\"inputcenter\" style=\"height: 20px; width: 40px;\"> (Order: 0-59)</td></tr>\n"; 3706 } 3707 echo "<tr><td class=\"postblock\" style=\"height: 20px; width: 80px;\"><u>P</u>erma-sage</td><td><input type=\"checkbox\" name=\"permasage\" value=\"1\""; 3708 if( $permasage == 1 ) echo " CHECKED"; 3709 echo "></td></tr>\n"; 3710 echo "<tr><td class=\"postblock\" style=\"height: 20px; width: 80px;\"><u>C</u>losed</td><td><input type=\"checkbox\" name=\"closed\" value=\"1\""; 3711 if( $closed == 1 ) echo " CHECKED"; 3712 echo "></td></tr>\n"; 3713 3714 if ($is_managerplus) { 3715 echo "<tr><td class=\"postblock\" style=\"height: 20px; width: 80px;\">P<u>e</u>rma-age</td><td><input type=\"checkbox\" name=\"permaage\" value=\"1\""; 3716 if( $permaage == 1 ) echo 'checked="checked"'; 3717 echo "></td></tr>\n"; 3718 } 3719 3720 //if ($is_managerplus) { 3721 echo "<tr><td class=\"postblock\" style=\"height: 20px; width: 80px;\"><u>U</u>ndead</td><td><input type=\"checkbox\" name=\"undead\" id=\"js-undead-cb\" value=\"1\""; 3722 if( $undead == 1 ) echo 'checked="checked"'; 3723 echo "></td></tr>\n"; 3724 //} 3725 3726 echo "<tr><td></td><td><input style=\"width:100px;margin-top: -25px;position: absolute;right: 5px;\" id=\"js-set-btn\" type=\"submit\" name=\"submit\" value=\"Set Options\"></td></tr>\n"; 3727 3728 echo "</table>\n"; 3729 echo "</form>"; 3730 3731 3732 /** 3733 * Thread moving form 3734 */ 3735 if (BOARD_DIR !== 'b' && !UPLOAD_BOARD && !JANITOR_BOARD) { 3736 echo move_thread_form($post_id); 3737 } 3738 3739 if (BOARD_DIR === 'test') { 3740 echo admin_protect_thread_form($post_id); 3741 } 3742 3743 die( "</body></html>" ); 3744 } 3745 } 3746 3747 function log_thread_opts_action($post_data, $sticky, $permasage, $closed, $permaage, $undead) { 3748 if (!isset($post_data['no']) || !$post_data['no']) { 3749 die('Internal Server Error (ltoa)'); 3750 } 3751 3752 $new_mask = 0 + (($sticky) ? 1 : 0) 3753 + (($permasage) ? 2 : 0) 3754 + (($closed) ? 4 : 0) 3755 + (($permaage) ? 8 : 0) 3756 + ($undead ? 16 : 0); 3757 3758 $old_mask = 0 + (($post_data['sticky']) ? 1 : 0) 3759 + (($post_data['permasage']) ? 2 : 0) 3760 + (($post_data['closed']) ? 4 : 0) 3761 + (($post_data['permaage']) ? 8 : 0) 3762 + ($post_data['undead'] ? 16 : 0); 3763 3764 if ($new_mask == $old_mask) { 3765 return false; 3766 } 3767 3768 $query = <<<SQL 3769 INSERT INTO actions_log (oldmask,newmask,postno,board,name,sub,com,filename,admin) 3770 VALUES (%d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s') 3771 SQL; 3772 3773 return !!mysql_global_call($query, 3774 $old_mask, 3775 $new_mask, 3776 $post_data['no'], 3777 BOARD_DIR, 3778 $post_data['name'], 3779 $post_data['sub'], 3780 $post_data['com'], 3781 $post_data['filename'] . $post_data['ext'], 3782 $_COOKIE['4chan_auser'] 3783 ); 3784 } 3785 3786 function get_board_options_html() { 3787 $boardlist = get_board_list(); 3788 3789 $board_sel = array('<option value=""> Board</option>'); 3790 3791 foreach ($boardlist as $b_dir => $b_title) { 3792 if ($b_dir === BOARD_DIR || $b_dir === 'f') { 3793 continue; 3794 } 3795 $board_sel[] = '<option value="' . $b_dir . '">' . $b_dir . ' - ' 3796 . $b_title . '</option>'; 3797 } 3798 3799 return implode("\n", $board_sel); 3800 } 3801 3802 function move_thread_form($post_id) { 3803 $csrf_tag = csrf_tag(); 3804 3805 $board_sel = get_board_options_html(); 3806 3807 if (!ENABLE_ARCHIVE) { 3808 $del_attrs = ' checked'; 3809 } 3810 else { 3811 $del_attrs = ''; 3812 } 3813 3814 return <<<HTML 3815 <hr> 3816 <form id="move-form" action="post" method="POST"> 3817 $csrf_tag 3818 <input type="hidden" name="id" value="$post_id"> 3819 <table class="goawayborder" style="width: 100%" border="0" cellspacing="0" cellpadding="0"> 3820 <tr> 3821 <td class="postblock" style="height: 20px; width: 80px;">Move to</td> 3822 <td><select name="board" required>$board_sel</select></td> 3823 </tr> 3824 <tr> 3825 <td class="postblock" style="height: 20px; width: 80px;"><label for="move_del">and delete</label></td> 3826 <td><input$del_attrs id="move_del" name="move_del" type="checkbox"></td> 3827 </tr> 3828 <tr> 3829 <td></td> 3830 <td> 3831 <button id="js-move-btn" style="margin-top: -25px;position: absolute;right: 5px;" type="submit" name="mode" value="movethread">Move</button> 3832 </td> 3833 </tr> 3834 </table> 3835 </form> 3836 HTML; 3837 } 3838 3839 function admin_protect_thread_form($thread_id) { 3840 return <<<HTML 3841 <hr> 3842 <form id="protect-form" action="?" method="GET"> 3843 <input type="hidden" name="thread_id" value="$thread_id"> 3844 <button type="submit" name="admin" value="protectthread">Set Protected</button> 3845 </form> 3846 HTML; 3847 } 3848 3849 function adminExt() 3850 { 3851 global $thread; 3852 $where = ''; 3853 3854 if( isset( $_GET[ 'from' ] ) && ctype_digit( $_GET[ 'from' ] ) ) { 3855 $from = intval( $_GET[ 'from' ] ); 3856 $where = " AND no >= $from"; 3857 } 3858 3859 if( !$thread ) return false; 3860 3861 $thread = (int)$thread; 3862 if( !$result = mysql_board_call( "SELECT `host`, `no` FROM `%s` WHERE (no=%d OR resto=%d)$where", SQLLOG, $thread, $thread ) ) { 3863 echo S_SQLFAIL; 3864 3865 return false; 3866 } 3867 $json = array(); 3868 3869 $salt = file_get_contents('/www/keys/2014_admin.salt'); 3870 3871 if (!$salt) { 3872 die('Internal Server Error'); 3873 } 3874 3875 while ($row = mysql_fetch_assoc($result)) { 3876 $hash = substr(base64_encode(pack( "H*", sha1($row['host'] . $salt))), 0, 8); 3877 3878 $json[$row['no']] = $hash; 3879 } 3880 3881 echo json_encode( $json, JSON_NUMERIC_CHECK ); 3882 3883 die(); 3884 } 3885 3886 function adminBanReq() 3887 { 3888 $no = (int)$_GET['id']; 3889 $board = BOARD_DIR; 3890 $janitor = $_COOKIE['4chan_auser']; 3891 3892 if ($board === 'j') { 3893 die(); 3894 } 3895 3896 $result = mysql_board_call( "SELECT * FROM `%s` WHERE no=%d", $board, $no ); 3897 3898 if (!mysql_num_rows($result)) { 3899 echo '<script language="JavaScript">postBack("error-ban-' . $board . '-' . $no . '");</script>'; 3900 error("This post doesn't exist anymore"); 3901 } 3902 3903 $post = mysql_fetch_assoc($result); 3904 3905 if ($post['archived']) { 3906 echo '<script language="JavaScript">postBack("error-ban-' . $board . '-' . $no . '");</script>'; 3907 error("This post is archived"); 3908 } 3909 3910 if ($post['host'] === '') { 3911 error('You cannot request a ban for this post.'); 3912 } 3913 3914 $post_has_file = $post['ext'] && !$post['file_deleted']; 3915 3916 if (!access_board(BOARD_DIR)) { 3917 // Check if the report is unlocked, weight threshold is 1500 3918 $query = <<<SQL 3919 SELECT CEIL(SUM(IF(resto > 0, weight, weight * 1.25))) as total_weight 3920 FROM reports 3921 WHERE board = '%s' AND no = %d 3922 SQL; 3923 3924 $result = mysql_global_call($query, $board, $no); 3925 3926 if (!$result) { 3927 error('Database Error (abru1'); 3928 } 3929 3930 $total_weight = (int)mysql_fetch_row($result)[0]; 3931 3932 if (!$total_weight || $total_weight < 1500) { 3933 error('You do not have permission to access this board.'); 3934 } 3935 } 3936 3937 // for async calls from reports.4chan.org 3938 if (isset($_POST['by_tpl']) && $_POST['by_tpl']) { 3939 $_POST['template'] = $_POST['by_tpl']; 3940 unset($_POST['warn_req']); 3941 } 3942 3943 if( $_POST[ 'template' ] ) { 3944 $template = (int)$_POST['template']; 3945 3946 if ($template < 1) { 3947 error('You forgot to select a template.'); 3948 } 3949 3950 $bquery = mysql_global_call( "SELECT * FROM ban_templates WHERE no=%d", $template ); 3951 $bres = mysql_fetch_assoc( $bquery ); 3952 3953 if (!has_level($bres['level'])) { 3954 error('You cannot use this template'); 3955 } 3956 3957 if (($bres['no'] == 1 || $bres['no'] == 123 || $bres['no'] == 213) && !$post_has_file) { 3958 error('This template requires a post with a file'); 3959 } 3960 3961 $reason = $bres['publicreason']; 3962 3963 $xffquery = mysql_global_call( "SELECT xff FROM xff WHERE board = '%s' AND postno = %d", $board, $no ); 3964 $reverse = gethostbyaddr( $post['host'] ); 3965 3966 if( $xffresult = mysql_fetch_row( $xffquery ) ) { 3967 if( !( $xff = gethostbyaddr( $xffresult[0] ) ) ) $xff = $xffresult[0]; 3968 } 3969 3970 if (isset($_POST['warn_req']) && $_POST['warn_req']) { 3971 if (!$bres['can_warn']) { 3972 error('You cannot issue warn requests using this template.'); 3973 } 3974 $warn_req = 1; 3975 } 3976 else if ($bres['days'] === '0') { 3977 $warn_req = 1; 3978 } 3979 else { 3980 $warn_req = 0; 3981 } 3982 3983 // Fixme: for the cache purger below 3984 if ($post['ext'] != '') { 3985 $post_filename = "{$post['tim']}{$post['ext']}"; 3986 } 3987 else { 3988 $post_filename = null; 3989 } 3990 3991 // Make sure we don't have any illegal reports (stop illegal images being stored) 3992 $illegal = mysql_global_call( "SELECT COUNT(*) FROM reports WHERE board='%s' AND no=%d AND cat=2", $board, $_POST['no'] ); 3993 if ((mysql_result($illegal, 0, 0) == 0) && $bres['save_post'] === 'everything') { 3994 $salt = file_get_contents( SALTFILE ); 3995 $hash = sha1($board . $post['no'] . $salt); 3996 3997 @copy( 3998 IMG_DIR . "{$post['tim']}{$post['ext']}", 3999 BANIMG_ROOT . "$board/$hash{$post['ext']}" 4000 ); 4001 4002 @copy( 4003 THUMB_DIR . "{$post['tim']}s.jpg", 4004 BANTHUMB_DIR . "{$hash}s.jpg" 4005 ); 4006 } 4007 else { 4008 //unset($post['ext']); 4009 $post['raw_md5'] = $post['md5']; 4010 } 4011 4012 if ($post['resto']) { 4013 $sub_query = mysql_board_call("SELECT sub FROM `%s` WHERE no = %d", $board, $post['resto']); 4014 $sub_res = mysql_fetch_assoc($sub_query); 4015 if ($sub_res) { 4016 $rel_sub = $sub_res['sub']; 4017 4018 if (strpos($rel_sub, 'SPOILER<>') === 0) { 4019 $rel_sub = substr($rel_sub, 9); 4020 } 4021 4022 if ($rel_sub !== '') { 4023 $post['rel_sub'] = $rel_sub; 4024 } 4025 } 4026 } 4027 4028 $tpl_name = $bres['name']; 4029 $tpl_global = $bres['bantype'] !== 'local' ? 1 : 0; 4030 4031 delete_post($no, false, $template, 'ban-req'); 4032 4033 $res = mysql_global_call("INSERT INTO ban_requests SET host='%s', reverse='%s', pwd='%s', xff='%s', reason='', global = $tpl_global, tpl_name = '%s', ban_template='%s', board='%s', janitor='%s', spost='%s', post_json='%s', warn_req = %d", $post['host'], $reverse, $post['pwd'], $xff, $tpl_name, $template, $board, $janitor, serialize( $post ), json_for_post($board, $post), $warn_req); 4034 4035 if (!$res) { 4036 error('Database error.'); 4037 } 4038 4039 // Auto-rangebans processing (ban requests) 4040 // FIXME: email field 4041 $_post_meta = decode_user_meta($row['email']); 4042 4043 if (!$warn_req && $_post_meta && $_post_meta['is_mobile']) { // mobile devices only 4044 // global rules only 4045 if ($bres && strpos($bres['rule'], 'global') !== false) { 4046 process_auto_rangeban($post['host'], $_post_meta['browser_id'], $post['resto'], $post['no'], $bres['no'], 0); 4047 } 4048 } 4049 4050 // Fixme, this is for the temporary is2/is3 cache purging api 4051 if ($post_filename && $bres && $bres['rule'] == 'global1') { 4052 purge_cache_internal_temp(BOARD_DIR, $post_filename); 4053 } 4054 echo '<script language="JavaScript">setTimeout("self.close()", 3000); postBack("done-ban-' . $board . '-' . $no . '");</script>'; 4055 die( ($warn_req ? 'Warn' : 'Ban') . ' request submitted! Window will now close...' ); 4056 } 4057 4058 $name = str_replace( '</span> <span class="postertrip">!', ' !', $post[ 'name' ] ); 4059 4060 $query = "SELECT warn_req, ban_templates.name FROM ban_requests LEFT JOIN ban_templates ON ban_template = ban_templates.no WHERE host='%s'"; 4061 $result = mysql_global_call($query, $post['host']); 4062 $brpending = array(); 4063 while ($row = mysql_fetch_assoc($result)) { 4064 $brpending[] = $row['name'] . ($row['warn_req'] ? ' [Warn]' : ''); 4065 } 4066 $brtooltip = join("\n", $brpending); 4067 $pending = ''; 4068 $brcount = count($brpending); 4069 if ($brcount > 0) { 4070 $plural = ($brcount > 1) ? 's' : ''; 4071 $pending = <<<HTML 4072 <tr> 4073 <td style="height: 20px;" class="postblock">Note</td> 4074 <td colspan="2" style="cursor:help;" title="$brtooltip"> 4075 [$brcount pending ban request$plural] 4076 </td> 4077 </tr> 4078 HTML; 4079 } 4080 4081 $csrf_tag = csrf_tag(); 4082 4083 if (!preg_match('/Android|iPhone|iPad/', $_SERVER['HTTP_USER_AGENT'])) { 4084 $autofocus_html = ' autofocus="autofocus"'; 4085 } 4086 else { 4087 $autofocus_html = ''; 4088 } 4089 4090 $html = <<<HTML 4091 <form action="" method="post">$csrf_tag 4092 <table border="0" cellspacing="0" cellpadding="0" class="bantable"> 4093 <tr> 4094 <td class="postblock">Autocomplete</td> 4095 <td colspan="2"> 4096 <input type="text" name="autocomplete" placeholder="Start typing..." id="autocomplete" size="40"$autofocus_html autocomplete="off" style="width: 100%;"> 4097 </td> 4098 </tr> 4099 4100 <tr name="template_row" style="visibility: hidden;"> 4101 <td style="height: 20px;" class="postblock">Template</td> 4102 <td colspan="2"> 4103 <select name="template" style="width: 100%;" onchange="chooseTemplate();"></select> 4104 </td> 4105 </tr> 4106 4107 $pending 4108 4109 <tr> 4110 <td class="postblock">Name</td> 4111 <td colspan="2"> 4112 <input type="text" name="name" value="$name" size="40" style="width: 100%;" readonly="readonly"> 4113 </td> 4114 </tr> 4115 4116 <tr> 4117 <td class="postblock">Reason</td> 4118 <td colspan="2"> 4119 <textarea id="reason" name="reason" value="" cols="30" rows="4" title="The banned user will see this message." style="width: 100%; margin-bottom: 0px !important;" readonly="readonly"></textarea> 4120 </td> 4121 </tr> 4122 4123 <tr> 4124 <td class="postblock">Warn?</td> 4125 <td colspan="2"> 4126 <input id="warn-req" type="checkbox" name="warn_req" value="1" title="Request the user be warned instead of banned."> 4127 </td> 4128 </tr> 4129 4130 <tr> 4131 <td class="postblock">Requested By</td> 4132 <td> 4133 <input style=" width: 100%;" type="text" name="bannedby" value="$janitor" readonly="readonly"> 4134 </td> 4135 <td align="right"> 4136 <input id="submit-br-btn" type="submit" value="Submit Request" style="margin: -1px"> 4137 <script type="text/javascript"> 4138 var el; 4139 4140 function submitRequest(e) { 4141 var select, index; 4142 4143 select = document.forms[0].template; 4144 index = select.selectedIndex; 4145 4146 if (index === 0) { 4147 e.preventDefault(); 4148 e.stopPropagation(); 4149 alert("You forgot to select a template."); 4150 } 4151 else { 4152 if (/ Child |\[Perm\]/.test(select.options[index].textContent)) { 4153 if (!checkSubmitConfirm(this)) { 4154 e.preventDefault(); 4155 e.stopPropagation(); 4156 return; 4157 } 4158 } 4159 postBack("start-ban-$board-$no"); 4160 } 4161 } 4162 4163 if (el = document.getElementById("submit-br-btn")) { 4164 el.addEventListener("click", submitRequest, false); 4165 } 4166 </script> 4167 </td> 4168 </tr> 4169 </table> 4170 </form> 4171 HTML; 4172 4173 echo $html; 4174 4175 $templates = array(); 4176 $level_map = get_level_map(); 4177 4178 $q = mysql_global_do("SELECT * FROM ban_templates ORDER BY length(rule), rule asc"); 4179 4180 while( $r = mysql_fetch_assoc( $q ) ) { 4181 if (!preg_match('#^(global|' . BOARD_DIR . ')[0-9]+$#', $r['rule'])) { 4182 continue; 4183 } 4184 4185 if (($r['no'] == 1 || $r['no'] == 123 || $r['no'] == 213) && !$post_has_file) { 4186 continue; 4187 } 4188 4189 if ($r['no'] == 6 && !DEFAULT_BURICHAN) { // NWS on Worksafe Board 4190 continue; 4191 } 4192 4193 if ($r['no'] == 17 && (BOARD_DIR === 'mlp' || BOARD_DIR === 'trash')) { // Pony/Ponies Outside of /mlp/ 4194 continue; 4195 } 4196 4197 if ($r['no'] == 222 && (BOARD_DIR === 's4s' || BOARD_DIR === 'bant')) { // Global 3 - Troll posts 4198 continue; 4199 } 4200 4201 if ($r['no'] == 223 && BOARD_DIR === 'pol') { // Global 3 - Racism 4202 continue; 4203 } 4204 4205 // Global 3 4206 if ((BOARD_DIR === 'b' || BOARD_DIR === 'bant') && strpos($r['rule'], 'global3') !== false) { 4207 continue; 4208 } 4209 4210 if ($r['no'] == 59 && $post['resto']) { // Request Thread Outside of /r/ 4211 continue; 4212 } 4213 4214 if ($level_map[$r['level']] !== true) { 4215 continue; 4216 } 4217 4218 unset($r[ 'special_action' ], $r[ 'blacklist' ], $r[ 'bantype' ], $r[ 'postban' ], $r[ 'privatereason' ]); 4219 4220 $templates[] = $r; 4221 } 4222 4223 $encTemp = json_encode( $templates ); 4224 4225 $v = <<<HTML 4226 <script type="text/javascript" src="//s.4cdn.org/js/admin_autocomplete.9.js"></script> 4227 <script type="text/javascript"> 4228 var e_template = document.getElementsByName("template")[0]; 4229 var globalTemplates = $encTemp; 4230 var templates = {}; 4231 var localTemplates = {}; 4232 4233 function $(e) {return document.getElementsByName(e)[0];} 4234 function unhide(e) { 4235 $(e).style.visibility="visible"; 4236 }; 4237 4238 function chooseTemplate() { 4239 var i = e_template.selectedIndex - 1; 4240 4241 Feedback.checkTemplate(i); 4242 4243 if (i < 0) { 4244 return; 4245 } 4246 4247 var t = templates[i]; 4248 document.getElementById('reason').innerHTML = t.publicreason; 4249 4250 document.getElementById('warn-req').disabled = t.can_warn == '0'; 4251 document.getElementById('warn-req').checked = (t.banlen == '' && t.days == 0); 4252 4253 //undisableForm(t); 4254 } 4255 4256 function undisableForm(t) { 4257 if( t.name != 'Other...' ) return; 4258 document.getElementById('reason').removeAttribute('disabled'); 4259 } 4260 4261 function initTemplate() { 4262 templates = globalTemplates; 4263 4264 if (localStorage) { 4265 var lt = JSON.parse(localStorage.getItem("ban_templates")); 4266 if (lt) { 4267 localTemplates = lt; 4268 for (var t in localTemplates) 4269 templates = templates.concat(localTemplates[t]); 4270 } 4271 } 4272 4273 e_template.innerHTML = '<option value="-1">None Selected (Required)</option>'; 4274 4275 for (var i=0;i<templates.length;i++) { 4276 var t = templates[i]; 4277 //if( !t.name.match(/Global/) && !t.name.match(/\/' . BOARD_DIR . '\//) && i < globalTemplates.length ) continue; 4278 4279 var o = document.createElement("option"); 4280 o.value = t.no; 4281 o.innerHTML = t.name + ((i < globalTemplates.length) ? "" : " [Local]"); 4282 e_template.appendChild(o); 4283 } 4284 unhide("template_row"); 4285 if (localStorage) { 4286 // unhide("local_template_row"); 4287 // unhide("deltemplate"); 4288 } 4289 } 4290 4291 initTemplate(); 4292 </script> 4293 HTML; 4294 4295 echo $v; 4296 } 4297 4298 /* FIXME: this is for the temporary is2/is3 cache purge api */ 4299 function purge_cache_internal_temp($board, $file) { 4300 $url = "http://g0ch4.brazil.jp:24502"; 4301 4302 $post = array(); 4303 $post['rmpath'] = "/$board/$file"; 4304 $post['key'] = '6a310437e13935b64beefcf10da8dba3'; 4305 $post = http_build_query($post); 4306 4307 rpc_start_request($url, $post, null, false); 4308 } 4309 4310 /** 4311 * Sets or usnets the spoiler flag for images 4312 * Does its own access validation. 4313 * Accessible to janitors 4314 */ 4315 function admin_toggle_spoiler() { 4316 header('Content-Type: text/plain'); 4317 4318 if (!SPOILERS) { 4319 echo '0'; die(); 4320 } 4321 4322 auth_user(); 4323 4324 if (!has_level() && (!has_level('janitor') || !access_board(BOARD_DIR))) { 4325 echo '-1'; die(); 4326 } 4327 4328 if (!isset($_GET['pid']) || !isset($_GET['flag'])) { 4329 echo '0'; die(); 4330 } 4331 4332 $query = "SELECT * FROM `" . SQLLOG . "` WHERE no = %d"; 4333 4334 $res = mysql_board_call($query, $_GET['pid']); 4335 4336 if (!$res) { 4337 echo '0'; die(); 4338 } 4339 4340 $post = mysql_fetch_assoc($res); 4341 4342 if (!$post) { 4343 echo '0'; die(); 4344 } 4345 4346 $spoiler_updated = adminToggleSpoiler($post, (bool)$_GET['flag']); 4347 4348 if ($spoiler_updated) { 4349 if ($post['resto']) { 4350 $thread_id = (int)$post['resto']; 4351 } 4352 else { 4353 $thread_id = (int)$post['no']; 4354 } 4355 4356 rebuild_thread($thread_id, $error, (bool)$post['archived']); 4357 } 4358 4359 echo '1'; die(); 4360 } 4361 4362 function validate_csrf($ref_only = false) { 4363 if ($_SERVER['REQUEST_METHOD'] == 'POST' && !$ref_only) { 4364 if (!isset($_COOKIE['_tkn']) || !isset($_POST['_tkn']) 4365 || $_COOKIE['_tkn'] == '' || $_POST['_tkn'] == '' 4366 || $_COOKIE['_tkn'] !== $_POST['_tkn']) { 4367 4368 if (!is_local()) { 4369 error('Bad Request.'); 4370 } 4371 } 4372 } 4373 else { 4374 if (isset($_SERVER['HTTP_REFERER']) && $_SERVER['HTTP_REFERER'] != '' 4375 && !preg_match('/^https?:\/\/([_a-z0-9]+)\.(4chan|4channel)\.org(\/|$)/', $_SERVER['HTTP_REFERER'])) { 4376 error('Bad Request.'); 4377 } 4378 } 4379 } 4380 4381 /*-----------Main-------------*/ 4382 4383 // Can't check for csrf token for this. Only check the referer. 4384 validate_csrf($admin === 'delall'); 4385 4386 switch($admin) { 4387 case 'adminext': 4388 adminvalid(); 4389 adminExt(); 4390 break; 4391 4392 case 'banreq': 4393 adminvalid( 'Ban Request' ); 4394 adminBanReq(); 4395 break; 4396 case 'del': 4397 adminvalid(); 4398 //admin_delete(); 4399 break; 4400 case 'delall': 4401 adminvalid(); 4402 admindelall(); 4403 break; 4404 case 'delallbyip': 4405 adminvalid(); 4406 delallbyip( $_POST[ 'ip' ], $_POST[ 'imgonly' ] ); 4407 break; 4408 case 'ban': 4409 adminvalid( 'Ban User' ); 4410 adminban(); 4411 break; 4412 case 'opt': 4413 adminvalid( 'Thread Options' ); 4414 adminopt(); 4415 break; 4416 case 'spoiler': 4417 admin_toggle_spoiler(); 4418 break; 4419 case 'cleanup': 4420 adminvalid( 'Board Cleanup' ); 4421 clean(); 4422 break; 4423 case 'cpban': 4424 adminvalid(); 4425 cpban((int)$_POST['no']); 4426 break; 4427 case 'protectthread': 4428 adminvalid(); 4429 admin_protect_thread(); 4430 break; 4431 case 'rev': 4432 admin_reverse_ip(); 4433 break; 4434 /* 4435 case 'reportqueue': 4436 adminvalid(); 4437 adminreportqueue(); 4438 break; 4439 case 'reportclear': 4440 adminvalid(); 4441 adminreportclear(); 4442 break; 4443 */ 4444 default: 4445 adminvalid(); 4446 //admin_delete(); 4447 }