if(file_exists('/www/global/lockdown')) {
if($_COOKIE['4chan_auser'] && $_COOKIE['4chan_apass'] && ($_POST['mode']=='usrdel'||$_GET['mode']=='latest')) {
// ok
}
else {
die('Posting temporarily disabled. Come back later!
—Team 4chan (uptime? what\'s that?)');
}
}
include_once "./yotsuba_config.php";
include("./postfilter.php");
include("./ads.php");
define('SQLLOGBAN', 'banned_users'); //Table (NOT DATABASE) used for holding banned users
define('SQLLOGMOD', 'mod_users'); //Table (NOT DATABASE) used for holding mod users
define('SQLLOGDEL', 'del_log'); //Table (NOT DATABASE) used for holding deletion log
if(BOARD_DIR == 'test') {
ini_set('display_errors', 1);
}
extract($_POST);
extract($_GET);
extract($_COOKIE);
$id = intval($id);
if(array_key_exists('upfile',$_FILES)) {
$upfile_name=$_FILES["upfile"]["name"];
$upfile=$_FILES["upfile"]["tmp_name"];
}
else {
$upfile_name=$upfile='';
}
$path = realpath("./").'/'.IMG_DIR;
ignore_user_abort(TRUE);
if(WORD_FILT&&file_exists("wf.php")){include_once("wf.php");}
if(JANITOR_BOARD == 1)
include_once '/www/global/plugins/broomcloset.php';
//mysql_board_connect();
// truncate $str to $max_lines lines and return $str and $abbr
// where $abbr = whether or not $str was actually truncated
function abbreviate($str,$max_lines) {
if(!defined('MAX_LINES_SHOWN')) {
if(defined('BR_CHECK')) {
define('MAX_LINES_SHOWN', BR_CHECK);
} else {
define('MAX_LINES_SHOWN', 20);
}
$max_lines = MAX_LINES_SHOWN;
}
$lines = explode("
", $str);
if(count($lines) > $max_lines) {
$abbr = 1;
$lines = array_slice($lines, 0, $max_lines);
$str = implode("
", $lines);
} else {
$abbr = 0;
}
//close spans after abbreviating
//XXX will not work with more html - use abbreviate_html from shiichan
$str .= str_repeat("", substr_count($str, "', $lineparts[0]);
$date = $dateparts[1];
$date = "Blotter updated: $date";
}
else {
$date = $lineparts[0];
$date = "Blotter updated: $date";
}
}
$line = trim($line);
$line = str_replace("\\", "\\\\", $line);
$line = str_replace("'", "\'", $line);
$ret .= "'$line'+\n";
}
$ret .= "''";
$cache = array($date,$ret);
return array($date,$ret);
}
// insert into the rapidsearch queue
function rapidsearch_insert($board, $no, $body) {
$board = mysql_real_escape_string($board);
$no = (int)$no;
$body = mysql_real_escape_string($body);
mysql_global_do("INSERT INTO rs.rsqueue (`board`,`no`,`ts`,`com`) VALUES ('$board',$no,NOW(),'$body')");
}
function find_match_and_prefix($regex, $str, $off, &$match)
{
if (!preg_match($regex, $str, $m, PREG_OFFSET_CAPTURE, $off)) return FALSE;
$moff = $m[0][1];
$match = array(substr($str, $off, $moff-$off), $m[0][0]);
return TRUE;
}
function spoiler_parse($com) {
if (!find_match_and_prefix("/\[spoiler\]/", $com, 0, $m)) return $com;
$bl = strlen("[spoiler]"); $el = $bl+1;
$st = '';
$et = '';
$ret = $m[0].$st; $lev = 1;
$off = strlen($m[0]) + $bl;
while (1) {
if (!find_match_and_prefix("@\[/?spoiler\]@", $com, $off, $m)) break;
list($txt, $tag) = $m;
$ret .= $txt;
$off += strlen($txt) + strlen($tag);
if ($tag == "[spoiler]") {
$ret .= $st;
$lev++;
} else if ($lev) {
$ret .= $et;
$lev--;
}
}
$ret .= substr($com, $off, strlen($com)-$off);
$ret .= str_repeat($et, $lev);
return $ret;
}
//rebuild the bans in array $boards
function rebuild_bans($boards) {
$cmd = "nohup /usr/local/bin/suid_run_global bin/rebuildbans $boards >/dev/null 2>&1 &";
exec($cmd);
}
function append_ban($board, $ip) {
$cmd = "nohup /usr/local/bin/suid_run_global bin/appendban $board $ip >/dev/null 2>&1 &";
exec($cmd);
}
// check whether the current user can perform $action (on $no, for some actions)
// board-level access is cached in $valid_cache.
function valid($action='moderator', $no=0){
static $valid_cache; // the access level of the user
$access_level = array('none' => 0, 'janitor' => 1, 'janitor_this_board' => 2, 'moderator' => 5, 'manager' => 10, 'admin' => 20);
if(!isset($valid_cache)) {
$valid_cache = $access_level['none'];
if(isset($_COOKIE['4chan_auser'])&&isset($_COOKIE['4chan_apass'])){
$user = mysql_real_escape_string($_COOKIE['4chan_auser']);
$pass = mysql_real_escape_string($_COOKIE['4chan_apass']);
}
if($user&&$pass) {
$result=mysql_global_call("SELECT allow,deny FROM ".SQLLOGMOD." WHERE username='$user' and password='$pass'");
list($allow,$deny) = mysql_fetch_row($result);
mysql_free_result($result);
if($allow) {
$allows = explode(',', $allow);
$seen_janitor_token = false;
// each token can increase the access level,
// except that we only know that they're a moderator or a janitor for another board
// AFTER we read all the tokens
foreach($allows as $token) {
if($token == 'janitor')
$seen_janitor_token = true;
else if($token == 'manager' && $valid_cache < $access_level['manager'])
$valid_cache = $access_level['manager'];
else if($token == 'admin' && $valid_cache < $access_level['admin'])
$valid_cache = $access_level['admin'];
else if(($token == BOARD_DIR || $token == 'all') && $valid_cache < $access_level['janitor_this_board'])
$valid_cache = $access_level['janitor_this_board']; // or could be moderator, will be increased in next step
}
// now we can set moderator or janitor status
if(!$seen_janitor_token) {
if($valid_cache < $access_level['moderator'])
$valid_cache = $access_level['moderator'];
}
else {
if($valid_cache < $access_level['janitor'])
$valid_cache = $access_level['janitor'];
}
if($deny) {
$denies = explode(',', $deny);
if(in_array(BOARD_DIR,$denies)) {
$valid_cache = $access_level['none'];
}
}
}
}
}
switch($action) {
case 'moderator':
return $valid_cache >= $access_level['moderator'];
case 'textonly':
return $valid_cache >= $access_level['moderator'];
case 'janitor_board':
return $valid_cache >= $access_level['janitor'];
case 'delete':
if($valid_cache >= $access_level['janitor_this_board']) {
return true;
}
// if they're a janitor on another board, check for illegal post unlock
else if($valid_cache >= $access_level['janitor']) {
$query=mysql_global_do("SELECT COUNT(*) from reports WHERE board='".BOARD_DIR."' AND no=$no AND cat=2");
$illegal_count = mysql_result($query, 0, 0);
mysql_free_result($query);
return $illegal_count >= 3;
}
case 'reportflood':
return $valid_cache >= $access_level['janitor'];
case 'floodbypass':
return $valid_cache >= $access_level['moderator'];
default: // unsupported action
return false;
}
}
function sticky_post($no, $position) {
global $log; log_cache();
$post_sticknum="202701010000".sprintf("%02d",$position);
$log[$no]['root'] = $post_sticknum;
$log[$no]['sticky'] = '1';
mysql_board_call('UPDATE '.SQLLOG." SET sticky='1'".
", root='".$post_sticknum."'".
" WHERE no='".mysql_real_escape_string($no)."'");
}
function permasage_post($no) {
global $log; log_cache();
$log[$no]['permasage'] = '1';
mysql_board_call('UPDATE '.SQLLOG." SET permasage='1'".
" WHERE no='".mysql_real_escape_string($no)."'");
}
function rebuildqueue_create_table() {
$sql = << [ data ]
//mysql_board_call("SET read_buffer_size=1048576");
$mysql_unbuffered_reads = 1;
//$query = mysql_board_call("SELECT * FROM ".SQLLOG);
$offset = 0;
$lastno = 0;
//while($row = mysql_fetch_assoc($query)) {
if($row['no'] > $lastno) $lastno = $row['no'];
$ips[$row['host']] = 1;
// initialize log row if necessary
if( !isset($log[$row['no']]) ) {
$log[$row['no']] = $row;
$log[$row['no']]['children'] = array();
} else { // otherwise merge it with $row
foreach($row as $key=>$val)
$log[$row['no']][$key] = $val;
}
// if this is a reply
if($row['resto']) {
// initialize whatever we need to
if( !isset($log[$row['resto']]) )
// $log[$row['resto']] = array();
if( !isset($log[$row['resto']]['children']) )
$log[$row['resto']]['children'] = array();
// add this post to list of children
$log[$row['resto']]['children'][$row['no']] = 1;
if($row['fsize']) {
if(!isset($log[$row['resto']]['imgreplycount']))
$log[$row['resto']]['imgreplycount'] = 0;
else
$log[$row['resto']]['imgreplycount']++;
}
} /*else {
$threads[] = $row['no'];
}*/
}
//$query = mysql_board_call("SELECT no FROM ".SQLLOG." WHERE root>0 order by root desc");
while($row = mysql_fetch_assoc($query)) {
if(isset($log[$row['no']]) && $log[$row['no']]['resto']==0)
$threads[] = $row['no'];
}
$log['THREADS'] = $threads;
$mysql_unbuffered_reads = 0;
// calculate old-status for PAGE_MAX mode
if(EXPIRE_NEGLECTED != 1) {
rsort($threads, SORT_NUMERIC);
$threadcount = count($threads);
if(PAGE_MAX > 0) // the lowest 5% of maximum threads get marked old
for($i = floor(0.95*PAGE_MAX*PAGE_DEF); $i < $threadcount; $i++) {
if(!$log[$threads[$i]]['sticky'] && EXPIRE_NEGLECTED != 1)
$log[$threads[$i]]['old'] = 1;
}
else { // threads w/numbers below 5% of LOG_MAX get marked old
foreach($threads as $thread) {
if($lastno-LOG_MAX*0.95>$thread)
if(!$log[$thread]['sticky'])
$log[$thread]['old'] = 1;
}
}
}
$ipcount = count($ips);
//}
// deletes a post from the database
// imgonly: whether to just delete the file or to delete from the database as well
// automatic: always delete regardless of password/admin (for self-pruning)
// children: whether to delete just the parent post of a thread or also delete the children
// die: whether to die on error
// careful, setting children to 0 could leave orphaned posts.
function delete_post($resno, $pwd, $imgonly=0, $automatic=0, $children=1, $die=1) {
global $log, $path;
log_cache();
$resno = intval($resno);
// get post info
if(!isset($log[$resno])){if ($die) error("Can't find the post $resno.");}
$row=$log[$resno];
// check password- if not ok, check admin status (and set $admindel if allowed)
$delete_ok = ( $automatic || (substr(md5($pwd),2,8) == $row['pwd']) || ($row['host'] == $_SERVER['REMOTE_ADDR']) );
if( ($pwd==ADMIN_PASS || $pwd==ADMIN_PASS2) ) { $delete_ok = $admindel = valid('delete', $resno); }
if(!$delete_ok) error(S_BADDELPASS);
// check ghost bumping
if(!isset($admindel) || !$admindel) {
if(BOARD_DIR == 'a' && (int)$row['time'] > (time() - 25) && $row['email'] != 'sage') {
$ghostdump = var_export(array(
'server'=>$_SERVER,
'post'=>$_POST,
'cookie'=>$_COOKIE,
'row'=>$row),true);
//file_put_contents('ghostbump.'.time(),$ghostdump);
}
}
if(isset($admindel) && $admindel) { // extra actions for admin user
$auser = mysql_escape_string($_COOKIE['4chan_auser']);
$adfsize=($row['fsize']>0)?1:0;
$adname=str_replace(' !','#',$row['name']);
if($imgonly) { $imgonly=1; } else { $imgonly=0; }
$row['sub'] = mysql_escape_string($row['sub']);
$row['com'] = mysql_escape_string($row['com']);
$row['filename'] = mysql_escape_string($row['filename']);
mysql_global_do("INSERT INTO ".SQLLOGDEL." (imgonly,postno,board,name,sub,com,img,filename,admin) values('$imgonly','$resno','".SQLLOG."','$adname','{$row['sub']}','{$row['com']}','$adfsize','{$row['filename']}','$auser')");
}
if($row['resto']==0 && $children && !$imgonly) // select thread and children
$result=mysql_board_call("select no,resto,tim,ext from ".SQLLOG." where no=$resno or resto=$resno");
else // just select the post
$result=mysql_board_call("select no,resto,tim,ext from ".SQLLOG." where no=$resno");
while($delrow=mysql_fetch_array($result)) {
// delete
$delfile = $path.$delrow['tim'].$delrow['ext']; //path to delete
$delthumb = THUMB_DIR.$delrow['tim'].'s.jpg';
if(is_file($delfile)) unlink($delfile); // delete image
if(is_file($delthumb)) unlink($delthumb); // delete thumb
if(OEKAKI_BOARD == 1 && is_file($path.$delrow['tim'].'.pch'))
unlink($path.$delrow['tim'].'.pch'); // delete oe animation
if(!$imgonly){ // delete thread page & log_cache row
if($delrow['resto'])
unset( $log[$delrow['resto']]['children'][$delrow['no']] );
unset( $log[$delrow['no']] );
$log['THREADS'] = array_diff($log['THREADS'], array($delrow['no'])); // remove from THREADS
mysql_global_do("DELETE FROM reports WHERE no=".$delrow['no']); // clear reports
if(USE_GZIP == 1) {
@unlink(RES_DIR.$delrow['no'].PHP_EXT);
@unlink(RES_DIR.$delrow['no'].PHP_EXT.'.gz');
}
else {
@unlink(RES_DIR.$delrow['no'].PHP_EXT);
}
}
}
//delete from DB
if($row['resto']==0 && $children && !$imgonly) // delete thread and children
$result=mysql_board_call("delete from ".SQLLOG." where no=$resno or resto=$resno");
elseif(!$imgonly) // just delete the post
$result=mysql_board_call("delete from ".SQLLOG." where no=$resno");
return $row['resto']; // so the caller can know what pages need to be rebuilt
}
// purge old posts
// should be called whenever a new post is added.
function trim_db() {
if(JANITOR_BOARD == 1) return;
log_cache();
$maxposts = LOG_MAX;
// max threads = max pages times threads-per-page
$maxthreads = (PAGE_MAX > 0)?(PAGE_MAX * PAGE_DEF):0;
// New max-page method
if($maxthreads) {
$exp_order = 'no';
if(EXPIRE_NEGLECTED == 1) $exp_order = 'root';
logtime('trim_db before select threads');
$result = mysql_board_call("SELECT no FROM ".SQLLOG." WHERE sticky=0 AND resto=0 ORDER BY $exp_order ASC");
logtime('trim_db after select threads');
$threadcount = mysql_num_rows($result);
while($row=mysql_fetch_array($result) and $threadcount >= $maxthreads) {
delete_post($row['no'], 'trim', 0, 1); // imgonly=0, automatic=1, children=1
$threadcount--;
}
mysql_free_result($result);
// Original max-posts method (note: cleans orphaned posts later than parent posts)
} else {
// make list of stickies
$stickies = array(); // keys are stickied thread numbers
$result = mysql_board_call("SELECT no from ".SQLLOG." where sticky=1 and resto=0");
while($row=mysql_fetch_array($result)) {
$stickies[ $row['no'] ] = 1;
}
$result = mysql_board_call("SELECT no,resto,sticky FROM ".SQLLOG." ORDER BY no ASC");
$postcount = mysql_num_rows($result);
while($row=mysql_fetch_array($result) and $postcount >= $maxposts) {
// don't delete if this is a sticky thread
if($row['sticky'] == 1) continue;
// don't delete if this is a REPLY to a sticky
if($row['resto'] != 0 && $stickies[ $row['resto'] ] == 1) continue;
delete_post($row['no'], 'trim', 0, 1, 0); // imgonly=0, automatic=1, children=0
$postcount--;
}
mysql_free_result($result);
}
}
//resno - thread page to update (no of thread OP)
//rebuild - don't rebuild page indexes
function updatelog($resno=0,$rebuild=0){
global $log,$path;
set_time_limit(60);
if($_SERVER['REQUEST_METHOD']=='GET' && !valid()) die(''); // anti ddos
log_cache();
$imgdir = ((USE_SRC_CGI==1)?str_replace('src','src.cgi',IMG_DIR2):IMG_DIR2);
if(defined('INTERSTITIAL_LINK')) $imgdir .= INTERSTITIAL_LINK;
$thumbdir = THUMB_DIR2;
$imgurl = DATA_SERVER;
$resno=(int)$resno;
if($resno){
if(!isset($log[$resno])) {
updatelog(0,$rebuild); // the post didn't exist, just rebuild the indexes
return;
}
else if($log[$resno]['resto']) {
updatelog($log[$resno]['resto'],$rebuild); // $resno is a reply, try rebuilding the parent
return;
}
}
if($resno){
$treeline = array($resno); logtime("Formatting thread page");
//if(!$treeline=mysql_board_call("select * from ".SQLLOG." where root>0 and no=".$resno." order by root desc")){echo S_SQLFAIL;}
}else{
$treeline = $log['THREADS']; logtime("Formatting index page");
//if(!$treeline=mysql_board_call("select * from ".SQLLOG." where root>0 order by root desc")){echo S_SQLFAIL;}
}
$counttree=count($treeline);
//$counttree=mysql_num_rows($treeline);
if(!$counttree){
$logfilename=PHP_SELF2;
$dat='';
head($dat,$resno);
form($dat,$resno);
print_page($logfilename, $dat);
}
if(UPDATE_THROTTLING >= 1) {
$update_start = time();
touch("updatelog.stamp", $update_start);
$low_priority = false;
clearstatcache();
if(@filemtime(PHP_SELF) > $update_start-UPDATE_THROTTLING) {
$low_priority = true;
//touch($update_start . ".lowprio");
}
else {
touch(PHP_SELF,$update_start);
}
// $mt = @filemtime(PHP_SELF);
// touch($update_start . ".$mt.highprio");
}
// if we're using CACHE_TTL method
if(CACHE_TTL >= 1) {
if($resno) {
$logfilename = RES_DIR.$resno.PHP_EXT;
}
else {
$logfilename = PHP_SELF2;
}
//if(USE_GZIP == 1) $logfilename .= '.html';
// if the file has been made and it's younger than CACHE_TTL seconds ago
clearstatcache();
if(file_exists($logfilename) && filemtime($logfilename) > (time() - CACHE_TTL)) {
// save the post to be rebuilt later
rebuildqueue_add($resno);
// if it's a thread, try again on the indexes
if($resno && !$rebuild) updatelog();
// and we don't do any more rebuilding on this request
return true;
}
else {
// we're gonna update it now, so take it out of the queue
rebuildqueue_remove($resno);
// and make sure nobody else starts trying to update it because it's too old
touch($logfilename);
}
}
for($page=0;$page<$counttree;$page+=PAGE_DEF){
$dat='';
head($dat,$resno);
form($dat,$resno);
if(!$resno){
$st = $page;
}
$dat.='