/ 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&amp;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&amp;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( "&amp;", "&", $str ); //remove ampersands
1372  	}
1373  
1374  	return str_replace( ",", "&#44;", $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> &mdash; ";
1621      
1622      echo '<a style="color:red" target="_blank" href="?admin=reportclear&amp;board=' . $report['board'] . '&amp;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\">&nbsp;<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>&nbsp;</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>&nbsp;</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('/\(.*\)/','&nbsp;',$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 ? "&#x2514;" : "" );
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>&nbsp;</td>";
1892  
1893  
1894  		if( $size != 0 ) {
1895  			echo "<td colspan=\"3\"><b>File</b></td>";
1896  			echo "<td colspan=\"5\">&nbsp;</td>";
1897  			echo "</tr>";
1898  			echo "<tr id=\"" . $no . "b\" bgcolor=\"#$bg\" style=\"display: none;\">";
1899  			// echo "<td colspan=2>&nbsp;</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\">&nbsp;</td>";
1920  			echo "</tr>";
1921  			echo "<tr id=\"" . $no . "b\" bgcolor=\"#$bg\" style=\"display: none;\">";
1922  			//echo "<td colspan=3>&nbsp;</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 ) ) . "'\">&nbsp;&nbsp;&nbsp;&nbsp;<input type=\"button\" value=\"Delete all posts by IP\" onClick=\"popup('admin=delall&id=$no');\">";
1927  		/*if ($bantrue) {
1928  	   	echo "&nbsp;&nbsp;&nbsp;&nbsp;<input type=\"button\" value=\"Unban user\" onClick=\"popup('admin=unban&id=$no');\"></td>";
1929    	} else  {*/
1930  		//  if (!$bantrue) {
1931  		echo "&nbsp;&nbsp;&nbsp;&nbsp;<input type=\"button\" value=\"Ban user\" onClick=\"popup('admin=ban&id=$no');\">";
1932  		//  }
1933  		//}
1934  		if( $resto == 0 ) {
1935  			echo "&nbsp;&nbsp;&nbsp;&nbsp;<input type=\"button\" value=\"Thread options\" onClick=\"popup('admin=opt&id=$no');\">";
1936  			if( !$thread ) {
1937  				echo "&nbsp;&nbsp;&nbsp;&nbsp;<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#{&quot;ip&quot;:&quot;$host&quot;}\">Search</a>]" . ($_post_meta['is_mobile'] ? ' <span data-tip="Posted from a mobile device" class="ico-phone">&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 &check;</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 &cross;</li>';
3625  			$vars .= "sticky=0,root=" . $sticktime . ",";
3626  		}
3627  		if( $post_permasage == 1 ) {
3628  			echo "<li>Perma-sage &check;</li>";
3629  			$vars .= "permasage=1,";
3630  		}
3631  		else {
3632  			echo '<li>Perma-sage &cross;</li>';
3633  			$vars .= "permasage=0,";
3634  		}
3635  		if( $post_closed == 1 ) {
3636  			echo "<li>Closed &check;</li>";
3637  			$vars .= "closed=1,";
3638  		}
3639  		else {
3640  			echo '<li>Closed &cross;</li>';
3641  			$vars .= "closed=0,";
3642  		}
3643  		if( $post_permaage ) {
3644  			if( $is_managerplus ) {
3645  			  echo '<li>Perma-age &check;</li>';
3646  			  $vars .= "permaage=1,";
3647  		  }
3648  		}
3649  		else {
3650  			if( $is_managerplus ) {
3651  			  echo '<li>Perma-age &cross;</li>';
3652  			  $vars .= "permaage=0,";
3653  		  }
3654  		}
3655  		if( $post_undead ) {
3656  			//if( $is_managerplus ) {
3657  			  echo '<li>Undead &check;</li>';
3658  			  $vars .= "undead=1,";
3659  		  //}
3660  		}
3661  		else {
3662  			//if( $is_managerplus ) {
3663  			  echo '<li>Undead &cross;</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 &cross;</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 ">&nbsp;&nbsp;&nbsp;&nbsp;<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  }