' . LOCKDOWN_MSG . ''); } } if (TEST_BOARD && (!has_level() || !has_flag('developer'))) { die(''); } // test if( TEST_BOARD || has_flag('developer') ) { ini_set( 'display_errors', 1 ); //error_reporting(E_ALL & ~E_NOTICE); } extract( $_POST, EXTR_SKIP ); extract( $_GET, EXTR_SKIP ); extract( $_COOKIE, EXTR_SKIP ); if (isset( $_COOKIE['4chan_pass']) && $_COOKIE['4chan_pass']) { $pwdc = $_COOKIE['4chan_pass']; } else { $pwdc = null; } // FIXME whitelist unset( $dest ); unset( $log ); unset( $update_avg_secs ); if( $argv[1] ) $mode = $argv[1]; $id = intval( $id ); if( $_SERVER["REQUEST_METHOD"] == "POST" ) { // bust cache header( 'Cache-Control: private, no-cache, must-revalidate' ); header( 'Expires: -1' ); header( 'Vary: *' ); } if( array_key_exists( 'upfile', $_FILES ) ) { $upfile_name = $_FILES["upfile"]["name"]; $upfile = $_FILES["upfile"]["tmp_name"]; } else { $upfile_name = $upfile = ''; } $fwritetimer = 0.0; ignore_user_abort( true ); $word_filters_enabled = false; if (WORD_FILT) { $word_filt_root = '/www/global/yotsuba/wordfilters/'; if (file_exists($word_filt_root . BOARD_DIR . '.php')) { include_once($word_filt_root . BOARD_DIR . '.php'); $word_filters_enabled = true; } else if (file_exists($word_filt_root . 'global.php')) { include_once($word_filt_root . 'global.php'); $word_filters_enabled = true; } } if( JANITOR_BOARD == 1 && !has_level( 'janitor' ) ) { die( '' ); } if( JANITOR_BOARD == 1 ) include_once 'plugins/broomcloset.php'; // QENHANCE if( META_BOARD ) { include_once 'plugins/enhance_q.php'; } $mysql_connect_opts = 0; mysql_board_connect(BOARD_DIR); $board_flags_array = null; if (ENABLE_BOARD_FLAGS) { $_flags_type = (defined('BOARD_FLAGS_TYPE') && BOARD_FLAGS_TYPE) ? BOARD_FLAGS_TYPE : BOARD_DIR; $_board_flags_path = '/www/global/yotsuba/lib/board_flags_' . $_flags_type . '.php'; if (file_exists($_board_flags_path)) { include_once($_board_flags_path); $board_flags_array = get_board_flags_array(); } } $thread_unique_ips = 0; $index_rbl = PAGE_MAX; $index_last_thread = 0; $index_last_post = 0; if (JANITOR_BOARD && PAGE_MAX == 0) { $index_rbl = ceil(LOG_MAX / DEF_PAGES); } $valid_boards = "3|aco|adv|an|biz|diy|fa|fit|gd|gif|int|lit|hc|hr|a|b|ck|co|cm|c|d|e|f|g|h|i|k|lgbt|m|n|o|out|p|r|s|t|u|vp|vg|vr|v|w|x|y|wg|ic|cgl|hm|mlp|mu|pol|po|r9k|s4s|sci|soc|tg|tv|toy|trv|jp|sp|wsg|qa|qst|his|trash|news|wsr|vip|bant|vrpg|vmg|vst|vt|vm|pw|xs"; $boards_matching_arr = array(); $captcha_bypass = null; $rangeban_bypass = false; $passid = ''; // FIXME, this should be put somewhere else. function is_local() { $longip = ip2long( $_SERVER[ 'REMOTE_ADDR' ] ); return !$longip || cidrtest( $longip, "10.0.0.0/24" ) || cidrtest( $longip, "204.152.204.0/24" ) || cidrtest( $longip, "127.0.0.0/24" ); } /** * Abbreviates posts on index pages. * Truncate $str to $max_lines lines and return $str and $abbr * where $abbr = whether or not $str was actually truncated. * Expects well-formed HTML. */ function abbreviate($str, $max_lines = 20) { $lines = explode('
', $str); if (count($lines) > $max_lines) { $abbr = 1; $lines = array_slice($lines, 0, $max_lines); $str = implode('
', $lines ); $unpaired_tags = array( 'img' => true, 'br' => true, 'input' => true, 'hr' => true, 'param' => true ); preg_match_all('/<\/([^>]+)>/', $str, $closed_tags, PREG_SET_ORDER); $closed_count = count($closed_tags); $closed_map = array(); foreach ($closed_tags as $m) { if (!isset($closed_map[$m[1]])) { $closed_map[$m[1]] = 1; } else { $closed_map[$m[1]] += 1; } } preg_match_all('/<([a-z0-9]+)(?: |>)/', $str, $open_tags, PREG_SET_ORDER); $open_count = count($open_tags); for ($i = 0; $i < $open_count; ++$i) { $tag = $open_tags[$i][1]; if (isset($unpaired_tags[$tag])) { continue; } if (!isset($closed_map[$tag])) { $str .= ""; } else if ($closed_map[$tag] > 0) { $closed_map[$tag] -= 1; } else if ($closed_map[$tag] <= 0) { $str .= ""; } } } else { $abbr = 0; } return array($str, $abbr); } /** * Currently only used on /archive * strips html tags and replaces sjis art with [SJIS] placeholders */ function truncate_comment($str, $length, $keep_spoilers = false) { // remove sjis if (SJIS_TAGS && strpos($str, '/', '[SJIS]', $str); } $len = mb_strlen($str); if ($len <= $length) { return $str; } if (!$keep_spoilers) { $str = strip_tags($str); } else { $str = strip_tags($str, ''); } if ($len <= $length) { return $str; } $str = mb_substr($str, 0, $length); // remove truncated html entities $str = preg_replace('/&[^;]*$/', '', $str); if ($keep_spoilers) { $str = preg_replace('/<[^>]*$/', '', $str); $oc = substr_count($str, ''); if ($oc) { $cc = substr_count($str, ''); $dc = $oc - $cc; if ($dc > 0) { $str .= str_repeat('', $dc); } } } $str .= '…'; return $str; } function paranoid_rename( $src, $dest ) { $across_devices = false; //keep around for future use $u = false; if( $across_devices ) { // rename to dest dir, then over dest $dsrc = dirname( $dest ) . "/" . basename( $src ); if( !@rename( $src, $dsrc ) ) $u = $src; else if( !@rename( $dsrc, $dest ) ) $u = $dsrc; } else { if( !@rename( $src, $dest ) ) $u = $src; } if( $u ) unlink( $u ); } function rename_across_device( $src, $dest ) { // FIXME: copy() does a chmod but we don't need that copy($src, $dest); unlink($src); } function getmypid_cached() { static $pid = -1; if ($pid === -1) $pid = getmypid(); return $pid; } // print $contents to $filename by using a temporary file and renaming it // may destroy $contents in the process // (makes *.html and *.gz if USE_GZIP is on) function print_page( $filename, &$contents, $force_nogzip = 0, $trim_whitespace = 1 ) { global $fwritetimer; $timestarted = microtime( true ); if( NEW_HTML == 1 && $trim_whitespace ) { $contents = str_replace( array("\r\n", "\n", "\t"), array('', '', ''), $contents ); } $gzip = ( USE_GZIP == 1 && !$force_nogzip ); if( $gzip ) { $tempname = dirname( $filename )."/gztmp".getmypid_cached(); // FIXME: number of syscalls done by gzwrite is not optimal (it does a small one then 4KB writes after) // for small files (how small?) do gzencode() and file_put_contents() instead. $fp = gzopen($tempname, "wb9"); if( $fp === false ) return; gzwrite($fp, $contents); gzclose($fp); // chmod( $tempname, 0664 ); //it was created 0600 paranoid_rename( $tempname, $filename . ".gz" ); } else { $tempname = dirname( $filename )."/tmp".getmypid_cached(); if( file_put_contents( $tempname, $contents ) === false ) return; // chmod( $tempname, 0664 ); //it was created 0600 paranoid_rename( $tempname, $filename ); } $fwritetimer += ( microtime( true ) - $timestarted ); } function file_get_contents_cached( $filename ) { static $cache = array(); if( isset( $cache[$filename] ) ) return $cache[$filename]; $cache[$filename] = @file_get_contents( $filename ); return $cache[$filename]; } function file_array_cached( $filename ) { static $cache = array(); if( isset( $cache[$filename] ) ) return $cache[$filename]; $cache[$filename] = @file( $filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES ); return $cache[$filename]; } function get_blotter() { if (!SHOW_BLOTTER) { return ''; } $msg_limit = 3; $blotter = <<
HTML; $query = << 0) { while ($row = mysql_fetch_assoc($res)) { if ($mtime === 0) { $mtime = $row['date']; } $blotter .= '' . date('m/d/y', $row['date']) . '' . $row['content'] . ''; } } else { return ''; } $blotter .= '[Hide] [Show All]'; return $blotter; } function blotter_contents() { static $cache; if( isset( $cache ) ) return $cache; $ret = ""; $topN = 4; //how many lines to print $bl_lines = file( BLOTTER_PATH ); $bl_top = array_slice( $bl_lines, 0, $topN ); $date = ""; foreach( $bl_top as $line ) { if( !$date ) { $lineparts = explode( ' - ', $line ); if( strpos( $lineparts[0], '', $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); } 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; } // skip_on_spoilers will stop parsing and return the unmodified comment // if spoiler tags are found inside the string to wrap. // This is to avoid sjis.spoiler tags mixing mostly. function parse_bbcode_one( $com, $tn, $st, $et, $nest_limit = 2, $skip_on_spoilers = false ) { if( !find_match_and_prefix( "/\[$tn\]/", $com, 0, $m ) ) return $com; $bracket_tn = "[$tn]"; $bl = strlen( $bracket_tn ); $el = $bl + 1; $ret = $m[0] . $st; $lev = 1; $off = strlen( $m[0] ) + $bl; while( 1 ) { if (!find_match_and_prefix( "@\[/?$tn\]@", $com, $off, $m)) break; list( $txt, $tag ) = $m; if (!$skip_on_spoilers || $tag === $bracket_tn) { $ret .= $txt; } else if (preg_match('/\[\/?spoiler\]/', $txt)) { return $com; } $off += strlen( $txt ) + strlen( $tag ); if( $tag == $bracket_tn ) { if( $lev < $nest_limit ) $ret .= $st; $lev++; } else if( $lev ) { if( $lev <= $nest_limit ) $ret .= $et; $lev--; } } $tail = substr($com, $off, strlen($com) - $off); $ret .= $tail; $lev = min( $lev, $nest_limit ); if ($lev > 0) { if ($skip_on_spoilers && preg_match('/\[\/?spoiler\]/', $tail)) { return $com; } $ret .= str_repeat( $et, $lev ); } return $ret; } function spoiler_parse( $com ) { return parse_bbcode_one( $com, 'spoiler', '', '' ); } function jsmath_parse( $com ) { $com = parse_bbcode_one( $com, "math", '', '' ); $com = parse_bbcode_one( $com, "eqn", '
    ', '
    ' ); return $com; } /* BBCode for bold, italic, and r/g/b color tags */ function parse_op_markup($com) { $com = parse_bbcode_one($com, 'b', '', '', 1); $com = parse_bbcode_one($com, 'i', '', '', 1); $com = parse_bbcode_one($com, 'red', '', '', 1); $com = parse_bbcode_one($com, 'green', '', '', 1); $com = parse_bbcode_one($com, 'blue', '', '', 1); return $com; } function code_parse( $com ) { return parse_bbcode_one( $com, 'code', '
    ', '
    ' ); } function sjis_parse($com) { $skip_on_spoilers = strpos($com, '[spoiler]') !== false; return parse_bbcode_one($com, 'sjis', '', '', 1, $skip_on_spoilers); } // convenience function for wordfilters. // text must be html escaped function random_color( $str, $background = 1, $foreground = 1 ) { $style = ""; if( $background ) { $r = rand( 0, 255 ); $g = rand( 0, 255 ); $b = rand( 0, 255 ); $style = $style . "background: #" . sprintf( "%02x%02x%02x", $r, $g, $b ) . "; "; } if( $foreground ) { $r = rand( 0, 255 ); $g = rand( 0, 255 ); $b = rand( 0, 255 ); $style = $style . "color: #" . sprintf( "%02x%02x%02x", $r, $g, $b ) . "; "; } if( $style ) { return "$str"; } return $str; } 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. // FIXME move to lib/admin.php function valid( $action = 'moderator', $no = 0 ) { static $valid_cache, $can_post_html; // 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['apass'] ) ) { $user = mysql_real_escape_string( $_COOKIE['4chan_auser'] ); $pass = $_COOKIE['apass']; } if( $user && $pass ) { $result = mysql_global_call( "SELECT allow,deny,password_expired,username,password FROM " . SQLLOGMOD . " WHERE username='$user' LIMIT 1" ); list( $allow, $deny, $expired, $username, $password ) = mysql_fetch_row( $result ); mysql_free_result( $result ); $admin_salt = file_get_contents('/www/keys/2014_admin.salt'); if (!$admin_salt) { die('Internal Server Error (s0)'); } $hashed_admin_password = hash('sha256', $username . $password . $admin_salt); if ($hashed_admin_password !== $pass) { return false; } if( $expired ) { error( 'Your password has expired; check IRC for instructions on changing it.' ); } 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 $cphtml = false; 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 } elseif( $token == 'html' ) { $cphtml = true; } } $can_post_html = $cphtml; // 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']; } } } } } { // local rpc can do anything $longip = ip2long( $_SERVER['REMOTE_ADDR'] ); if( !$longip || cidrtest( $longip, "10.0.0.0/24" ) || cidrtest( $longip, "204.152.204.0/24" ) || cidrtest( $longip, "127.0.0.0/24" ) ) return YES; } switch( $action ) { case 'moderator': return $valid_cache >= $access_level['moderator']; case 'textonly': return $valid_cache >= $access_level['moderator']; case 'htmlnopw': return $valid_cache >= $access_level['manager']; case 'htmlpost': return $can_post_html; 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['janitor']; case 'rebuild': return $valid_cache >= $access_level['janitor']; case 'admin': return $valid_cache >= $access_level['admin']; default: // unsupported action return false; } } function iplog_add( $board, $no, $ip, $time, $is_thread, $tim, $had_image ) { mysql_global_call( "INSERT INTO user_actions (board,postno,ip,time,uploaded,action,had_image) VALUES ('%s',%d,%d,from_unixtime(%d),%d,'%s',%d)",$board, $no, ip2long($ip), $time, $tim, $is_thread ? "new_thread" : "new_reply", $had_image); } function clean_log_bool( &$row ) { static $bool_cols = array('sticky', 'permasage', 'closed', 'filedeleted', 'permaage', 'undead', 'archived'); foreach ($bool_cols as $col) { if (!isset($row[$col])) continue; // FIXME split this function up to avoid this test $c = &$row[$col]; settype($c, "bool"); // NOTES: $c = $c ? TRUE : FALSE allocates new bools each time // $c = $c ? &$itrue : &$ifalse causes them to be converted to strings(!) // Put this back to TRUE : FALSE instead of a settype call sometime so we can look at the php bytecodes } } function clean_log_int( &$row ) { static $int_cols = array('no', 'w', 'h', 'tn_w', 'tn_h', 'last_modified', 'time', 'fsize', 'resto'); // turn columns into int (does this help?) foreach( $int_cols as $col ) { if (!isset($row[$col])) continue; $c = &$row[$col]; settype($c, "int"); } } function clean_log_delreply( &$row ) { if (!$row['resto']) return; static $del_cols = array('sticky', 'permasage', 'closed', 'last_modified', 'root', 'undead', 'permaage'); // delete fields not used for replies foreach( $del_cols as $col ) { unset($row[$col]); } } function clean_log_intern( &$row ) { static $intern_cols = array('name', 'ext', 'capcode', 'country'); // Intern repeated strings that are usually the same value. // In PHP 6 this... doesn't seem to do anything? Let's try again in 7. static $log_intern; if( !isset( $log_intern ) ) { $log_intern = array(); foreach( $intern_cols as $col ) $log_intern[$col] = array(); } foreach( $intern_cols as $col ) { $intern_array = &$log_intern[$col]; $c = &$row[$col]; $v = $c; if( !isset($intern_array[$v]) ) $intern_array[$v] = $v; $c = &$intern_array[$c]; } } function clean_log_row( &$row ) { //static $rn = 0; clean_log_delreply( $row ); clean_log_bool( $row ); clean_log_int( $row ); //clean_log_intern( $row ); //if (++$rn == 100) { // debug_zval_dump($row); //} } function log_bad_cache_entry($no) { global $log_cache_level; global $log; internal_error_log("logcache", "missing children for OP no $no, cache level $log_cache_level, cache contents ".count($log)); } function invalidate_log($thread) { global $log_cache_level; global $log; if (isset($log[$thread])) { if ($log[$thread]['resto']) { die(S_ASSERT); } unset($log[$thread]); } if ($log_cache_level==2) $log_cache_level = 1; } // build a structure out of all the posts in the database. // this lets us replace a LOT of queries with a simple array access. // it only builds the first time it was called. // rather than calling log_cache(1) to rebuild everything, // you should just manipulate the structure directly. // $thread may be any postno in a thread // without a thread, $archive_mode fetches all live threads if 0 and all archived threads if 1 function log_cache($invalidate = 0, $thread = 0, $archive_mode = 0) { global $log_cache_level; global $log, $ipcount, $mysql_unbuffered_reads; if (!isset($log) || $invalidate) { $log = array(); $log_cache_level = 0; } // Optimisation for index rebuilding when REPLIES_SHOWN is 0. // No need to fetch the entire board in this case. $optimised_indexes = !$thread && !$archive_mode && !REPLIES_SHOWN && IS_REBUILDD; // Handle cache // 1 = Live OPs are cached, 2 = Some threads are cached, 3 = Whole live board is cached if ($optimised_indexes) { $nlog_cache_level = 1; } else { $nlog_cache_level = $thread ? 2 : 3; } // Whole board is cached, nothing to do. if ($log_cache_level == 3 && $archive_mode === 0) { return; } // Thread is cached, nothing to do. if ($log_cache_level == 2 && isset($log[$thread])) { return; } // Live OPs are cached, nothing to do. if ($log_cache_level == 1 && $optimised_indexes) { return; } if ($nlog_cache_level > $log_cache_level) { $log_cache_level = $nlog_cache_level; } $ips = array(); mysql_board_call( "SET read_buffer_size=1048576" ); $mysql_unbuffered_reads = 1; $query_archived = false; if ($thread) { if ($archive_mode === 0) { $where = " WHERE archived = 0 AND (resto = $thread OR no = $thread)"; } else if ($archive_mode === 1) { $where = " WHERE archived = 1 AND (resto = $thread OR no = $thread)"; } else { $query_archived = true; $where = " WHERE (archived = 0 AND resto = $thread) OR (archived = 1 AND resto = $thread) OR no = $thread"; } } else { if ($archive_mode === 1) { $query_archived = true; $where = ' WHERE archived = 1'; } else if ($optimised_indexes) { $where = ' WHERE archived = 0 AND resto = 0'; $_thread_ids = array(); } else { $where = ' WHERE archived = 0'; } } $fields = "no,sticky,permasage,closed,now,name,sub,com,host,pwd,filename,ext,w,h,tn_w,tn_h,tim,time,md5,fsize,last_modified,root,resto,filedeleted,id,capcode,country,undead,permaage,since4pass"; if ($query_archived) { $fields .= ",archived"; } if (MOBILE_IMG_RESIZE) { $fields .= ",m_img"; } if (ENABLE_BOARD_FLAGS) { $fields .= ",board_flag"; } $sql_cache = "sql_no_cache"; $query = mysql_board_call( "SELECT $sql_cache $fields FROM `" . SQLLOG . "`" . $where ); $offset = 0; while( $row = mysql_fetch_assoc( $query ) ) { if (!$query_archived) $row['archived'] = $archive_mode; clean_log_row( $row ); $row_no = $row['no']; $row_resto = $row['resto']; // IF mysql returns rows in order by default then replies come after OP // so if OP doesn't exist, this post is orphaned and should be skipped // TODO: let's not skip for now, it seems more likely to cause bugs than anything //if ($fetching_whole_threads && $row_resto && !isset($log[$row_resto])) continue; if ($optimised_indexes) { $_thread_ids[] = (int)$row_no; } $ips[$row['host']] = TRUE; if (!$row_resto) { $row['children'] = array(); $row['imgreplycount'] = 0; $row['replycount'] = 0; } else { if (isset($log[$row_resto])) { $log[$row_resto]['children'][$row_no] = TRUE; if ($row['fsize'] && !$row['filedeleted']) { $log[$row_resto]['imgreplycount']++; } $log[$row_resto]['replycount']++; } } $log[$row_no] = $row; } $query = null; $nrows = count($log); //if (BOARD_DIR=="b" && !$thread) quick_log_to("/www/perhost/logcaches.log", "inefficient all-board log_cache run t=$thread r=$nrows inv=$invalidate lev=$log_cache_level", true); // if (!STATIC_REBUILD && $thread) quick_log_to("/www/perhost/logcaches.log", "inefficient? one-thread log_cache run t=$thread r=$nrows inv=$invalidate lev=$log_cache_level", true); $is_single_thread = $thread && isset($log[$thread]['children']); $ipcount = count( $ips ); unset($ips); $mysql_unbuffered_reads = 0; mysql_board_call( "SET read_buffer_size=131072" ); if (!$thread) { if ($optimised_indexes) { if (empty($_thread_ids)) { return; } $_thread_ids = implode(',', $_thread_ids); $query = mysql_board_call("SELECT resto, COUNT(*) AS r_count, SUM(IF(fsize > 0 AND filedeleted = 0, 1, 0)) AS i_count FROM `" . SQLLOG . "` WHERE resto IN($_thread_ids) GROUP BY resto"); while ($row = mysql_fetch_assoc($query)) { $log[$row['resto']]['imgreplycount'] = (int)$row['i_count']; $log[$row['resto']]['replycount'] = (int)$row['r_count']; } $query = mysql_board_call("SELECT no FROM `" . SQLLOG . "` WHERE no IN($_thread_ids) ORDER BY root DESC"); } else { if ($archive_mode === 1) { $archived = "archived = 1"; } else { $archived = "archived = 0"; } $query = mysql_board_call("SELECT no FROM `" . SQLLOG . "` WHERE $archived AND resto = 0 AND root > 0 ORDER BY root DESC"); } $threads = array(); // IDs while ($row = mysql_fetch_row($query)) { $no = (int)$row[0]; if (isset($log[$no])) { $threads[] = $no; } } $log['THREADS'] = $threads; foreach( $threads as $thread ) { $this_thread = $log[$thread]; if (!$this_thread['permaage'] && !$this_thread['sticky'] && $this_thread['replycount'] >= MAX_RES) { $log[$thread]['bumplimit'] = TRUE; } else { $log[$thread]['bumplimit'] = FALSE; } if ($this_thread['archived']) { $log[$thread]['archived_on'] = strtotime($this_thread['root']); } if (!$this_thread['permaage'] && !$this_thread['sticky'] && !$log[$thread]['undead'] && $this_thread['imgreplycount'] >= MAX_IMGRES) { $log[$thread]['imagelimit'] = TRUE; } else { $log[$thread]['imagelimit'] = FALSE; } $log[$thread]['semantic_url'] = generate_href_context($this_thread['sub'], $this_thread['com']); } } else if ($is_single_thread) { if (!$log[$thread]['permaage'] && !$log[$thread]['sticky'] && $log[$thread]['replycount'] >= MAX_RES) { $log[$thread]['bumplimit'] = TRUE; } else { $log[$thread]['bumplimit'] = FALSE; } if ($log[$thread]['archived']) { $log[$thread]['archived_on'] = strtotime($log[$thread]['root']); } if (!$log[$thread]['permaage'] && !$log[$thread]['sticky'] && !$log[$thread]['undead'] && $log[$thread]['imgreplycount'] >= MAX_IMGRES) { $log[$thread]['imagelimit'] = TRUE; } else { $log[$thread]['imagelimit'] = FALSE; } $log[$thread]['semantic_url'] = generate_href_context($log[$thread]['sub'], $log[$thread]['com']); } // calculate old-status for PAGE_MAX mode //$threadcount = count( $threads ); /*if(EXPIRE_NEGLECTED != 1) { rsort($threads, SORT_NUMERIC); if(PAGE_MAX > 0) // the lowest 5% of maximum threads get marked old for($i = floor(0.95*PAGE_MAX*DEF_PAGES); $i < $threadcount; $i++) { if(!$log[$threads[$i]]['sticky']) $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; } } } else { $rthreads = array(); foreach ($threads as $t) { $root = $log[$t]['root']; $rthreads[$t] = $root; } arsort($rthreads); $rthreads = array_keys($rthreads); if (PAGE_MAX > 0) { $floor = (int)floor(0.95*PAGE_MAX*DEF_PAGES); for($i = $floor; $i < $threadcount; $i++) { if(!$log[$rthreads[$i]]['sticky']) $log[$rthreads[$i]]['old'] = 1; } } }*/ } function rebuildallthumb($archiveonly = false) { global $log; if( !has_level() ) return; $starttime = microtime( true ); set_time_limit( 0 ); if (!$archiveonly) { log_cache(); $nposts = count($log); echo "Rebuilding $nposts live posts
    \n"; foreach( $log as $post ) { if( !$post["ext"] ) continue; $ext = $post["ext"]; $tim = $post["tim"]; $resto = $post["resto"]; $fname = IMG_DIR . $tim . $ext; make_thumb( $fname, $tim, $ext, $resto, $TN_W, $TN_H, $tmd5 ); } $totaltime = microtime( true ) - $starttime; echo "Took $totaltime seconds for live posts
    \n"; } // Run again for archived thumbs $starttime = microtime( true ); mysql_check_connections(); log_cache(1, 0, 1); // fetch archives instead $nposts = count($log); echo "Rebuilding $nposts archived posts
    \n"; foreach( $log as $post ) { if( !$post["ext"] ) continue; $ext = $post["ext"]; $tim = $post["tim"]; $resto = $post["resto"]; $fname = IMG_DIR . $tim . $ext; make_thumb( $fname, $tim, $ext, $resto, $TN_W, $TN_H, $tmd5 ); } $totaltime = microtime( true ) - $starttime; echo "Took $totaltime seconds for archived posts
    \n"; } /** * Displays some text on a lightweight page styled using the default stylesheet. * $message should be html-escaped */ function headless_message($message, $autoclose = false) { if (DEFAULT_BURICHAN) { $css = 'yotsubluenew'; $ws = 'ws'; } else { $css = 'yotsubanew'; $ws = ''; } $css_ver = TEST_BOARD ? CSS_VERSION_TEST : CSS_VERSION; if ($error) { $err_css = 'color: red;'; } else { $err_css = ''; } if ($autoclose) { $js = <<setTimeout(function() { self.close(); }, 3000); JS; } else { $js = ''; } $html = <<
    $message
    $js HTML; return $html; } function do_move_thread() { if (!isset($_POST['id']) || !isset($_POST['board'])) { updating_index(); } if (!has_level()) { updating_index(); } $del = isset($_POST['move_del']) && $_POST['move_del']; $ret = move_thread($_POST['id'], $_POST['board'], $del); if (is_array($ret)) { $message = 'Thread moved to /' . $ret[0] . '/' . $ret[1] . ''; echo headless_message($message, true); } else { echo headless_message("$ret"); } } function do_copy_threads() { if (!has_level()) { updating_index(); return; } global $argv; $to_board = $_REQUEST["board"]; if (!$to_board) $to_board = $argv[2]; if (!$to_board) { updating_index(); return; } set_time_limit( 0 ); header( "Pragma: no-cache" ); _print(fancystyle()); if (UPLOAD_BOARD || JANITOR_BOARD) { $ret = "The current board doesn't support this feature."; } else if ($to_board === 'f' || $to_board === 'j') { $ret = "The destination board doesn't support this feature."; } else { $threads = mysql_column_array(mysql_board_call("SELECT no FROM `%s` WHERE resto = 0 AND archived = 0", BOARD_DIR)); foreach ($threads as $thread) {$ret = copy_thread($thread, $to_board); _print("$thread
    \n");} } if (is_array($ret)) { $message = 'Thread copied to /' . $ret[0] . '/' . $ret[1] . ''; echo headless_message($message, true); } else { echo headless_message("$ret"); } } function copy_thread($thread_id, $to_board, $delete = false) { $thread_id = (int)$thread_id; if (!$thread_id) { return "Invalid thread ID."; } // Validate destination board if (!preg_match('/^[a-z0-9]+$/', $to_board)) { return 'Invalid destination board.'; } $query = "SELECT COUNT(*) FROM boardlist WHERE dir = '%s' LIMIT 1"; $res = mysql_global_call($query, $to_board); if (!$res) { return "Database Error (1)"; } if (mysql_num_rows($res) < 1) { return "Destination board doesn't exist."; } // --- // Fetch the whole thread $posts = array(); $res = mysql_board_call("SELECT * FROM `%s` WHERE no = %d AND resto = 0", BOARD_DIR, $thread_id); if (!$res) { return "Database Error (3)"; } $row = mysql_fetch_assoc($res); if (!$row) { return "Thread not found."; } $posts[] = $row; $res = mysql_board_call("SELECT * FROM `%s` WHERE resto = %d", BOARD_DIR, $thread_id); if (!$res) { return "Database Error (4)"; } while ($row = mysql_fetch_assoc($res)) { if (!$row) { continue; } $posts[] = $row; } // Copy posts to the other board mysql_board_call('START TRANSACTION'); $new_resto = 0; $from_pids = array(); $to_pids = array(); foreach ($posts as &$post) { $comment = str_replace($from_pids, $to_pids, $post['com']); if ($new_resto === 0) { $root_time = $post['root']; } else { $root_time = 0; } $query = "INSERT INTO `$to_board`(now,name,sub,com,host,pwd,filename,ext,w, h,tn_w,tn_h, tim,time,last_modified,md5,fsize,root,resto,capcode, 4pass_id,since4pass,filedeleted,tmd5,id,sticky,closed,country) VALUE (" . "'" . $post['now'] . "'," . "'" . mysql_real_escape_string($post['name']) . "'," . "'" . mysql_real_escape_string($post['sub']) . "'," . "'" . mysql_real_escape_string($comment) . "'," . "'" . mysql_real_escape_string($post['host']) . "'," . "'" . mysql_real_escape_string($post['pwd']) . "'," . "'" . mysql_real_escape_string($post['filename']) . "'," . "'" . $post['ext'] . "'," . (int)$post['w'] . "," . (int)$post['h'] . "," . (int)$post['tn_w'] . "," . (int)$post['tn_h'] . "," . "'" . $post['tim'] . "'," . (int)$post['time'] . "," . (int)$post['time'] . "," . "'" . $post['md5'] . "'," . (int)$post['fsize'] . "," . "'" . $root_time . "'," . $new_resto . "," . "'" . $post['capcode'] . "'," . "'" . $post['4pass_id'] . "'," . (int)$post['since4pass'] . "," . (int)$post['filedeleted'] . "," . "'" . $post['tmd5'] . "'," . "'" . mysql_real_escape_string($post['id']) . "'," . (int)$post['sticky'] . "," . (int)$post['closed'] . "," . "'XX')"; $res = mysql_board_call($query); if (!$res) { if ($new_resto === 0) { mysql_board_call('ROLLBACK'); return 'Database Error (5)'; } $post['ext'] = null; continue; } $new_pid = mysql_board_insert_id(); if ($new_resto === 0) { $new_resto = $new_pid; } $from_pids[] = ">>{$post['no']}"; $to_pids[] = ">>$new_pid"; $post['new_id'] = $new_pid; } unset($post); mysql_board_call('COMMIT'); // Copy files // If the file already exists, update the database and set it as "deleted" $to_img_dir = preg_replace('/' . BOARD_DIR . '\/$/', '', IMG_ROOT) . $to_board . '/'; $to_thumb_dir = preg_replace('/' . BOARD_DIR . '\/$/', '', THUMB_ROOT) . $to_board . '/'; $dup_pids = array(); foreach ($posts as $post) { if (!$post['ext'] || $post['filedeleted']) { continue; } $src_thumb = THUMB_DIR . $post['tim'] . 's.jpg'; $dest_thumb = $to_thumb_dir . $post['tim'] . 's.jpg'; if (file_exists($dest_thumb)) { //$dup_pids[] = $post['new_id']; continue; } $src_img = IMG_DIR . $post['tim'] . $post['ext']; $dest_img = $to_img_dir . $post['tim'] . $post['ext']; @copy($src_thumb, $dest_thumb); @copy($src_img, $dest_img); } if (!empty($dup_pids)) { $dup_clause = implode(',', $dup_pids); $query = "UPDATE `$to_board` SET filedeleted = 1, ext = '' WHERE no IN($dup_clause)"; $res = mysql_board_call($query); } // Ask the destination board to build the new thread remote_rebuild_live_thread($to_board, $new_resto); // Rebuild indexes if (!STATIC_REBUILD) { updatelog(0, 0); } return array($to_board, $new_resto); } function move_thread($thread_id, $to_board, $delete = false) { if (UPLOAD_BOARD || BOARD_DIR === 'b' || JANITOR_BOARD) { return "The current board doesn't support this feature."; } if ($to_board === 'f' || $to_board === 'j') { return "The destination board doesn't support this feature."; } if ($to_board === BOARD_DIR) { return "Invalid destination board."; } $thread_id = (int)$thread_id; if (!$thread_id) { return "Invalid thread ID."; } // Validate destination board if (!preg_match('/^[a-z0-9]+$/', $to_board)) { return 'Invalid destination board.'; } $query = "SELECT COUNT(*) FROM boardlist WHERE dir = '%s' LIMIT 1"; $res = mysql_global_call($query, $to_board); if (!$res) { return "Database Error (1)"; } if (mysql_num_rows($res) < 1) { return "Destination board doesn't exist."; } // --- $board = mysql_real_escape_string(BOARD_DIR); // Lock the thread immediately $query = "UPDATE `$board` SET closed = 1 WHERE no = $thread_id"; $res = mysql_board_call($query); if (!$res) { return "Database Error (2)"; } if (mysql_affected_rows() !== 1) { return "This thread is locked."; } // Fetch the whole thread $posts = array(); $query = "SELECT * FROM `$board` WHERE no = $thread_id AND resto = 0"; $res = mysql_board_call($query); if (!$res) { return "Database Error (3)"; } $row = mysql_fetch_assoc($res); if (!$row) { return "Thread not found."; } if ($row['archived'] !== '0') { return "You cannot move archived threads."; } $posts[] = $row; $query = "SELECT * FROM `$board` WHERE resto = $thread_id"; $res = mysql_board_call($query); if (!$res) { return "Database Error (4)"; } while ($row = mysql_fetch_assoc($res)) { if (!$row) { continue; } $posts[] = $row; } // Copy posts to the other board mysql_board_call('START TRANSACTION'); $new_resto = 0; $from_pids = array(); $to_pids = array(); foreach ($posts as &$post) { $comment = str_replace($from_pids, $to_pids, $post['com']); if ($new_resto === 0) { $root_time = 'NOW()'; } else { $root_time = 0; } if (SHOW_COUNTRY_FLAGS && $post['board_flag'] == '') { $flag_val = $post['country']; } else { $flag_val = 'XX'; } $query = "INSERT INTO `$to_board`(now,name,sub,com,host,pwd,email,filename,ext,w, h,tn_w,tn_h, tim,time,last_modified,md5,fsize,root,resto,capcode, 4pass_id,since4pass,filedeleted,tmd5,id,country) VALUE (" . "'" . $post['now'] . "'," . "'" . mysql_real_escape_string($post['name']) . "'," . "'" . mysql_real_escape_string($post['sub']) . "'," . "'" . mysql_real_escape_string($comment) . "'," . "'" . mysql_real_escape_string($post['host']) . "'," . "'" . mysql_real_escape_string($post['pwd']) . "'," . "'" . mysql_real_escape_string($post['email']) . "'," . "'" . mysql_real_escape_string($post['filename']) . "'," . "'" . $post['ext'] . "'," . (int)$post['w'] . "," . (int)$post['h'] . "," . (int)$post['tn_w'] . "," . (int)$post['tn_h'] . "," . "'" . $post['tim'] . "'," . (int)$post['time'] . "," . (int)$post['time'] . "," . "'" . $post['md5'] . "'," . (int)$post['fsize'] . "," . $root_time . "," . $new_resto . "," . "'" . $post['capcode'] . "'," . "'" . $post['4pass_id'] . "'," . (int)$post['since4pass'] . "," . (int)$post['filedeleted'] . "," . "'" . $post['tmd5'] . "'," . "''," . "'" . $flag_val . "')"; $res = mysql_board_call($query); if (!$res) { if ($new_resto === 0) { mysql_board_call('ROLLBACK'); return 'Database Error (5)'; } $post['ext'] = null; continue; } $new_pid = mysql_board_insert_id(); if ($new_resto === 0) { $new_resto = $new_pid; } $from_pids[] = ">>{$post['no']}"; $to_pids[] = ">>$new_pid"; $post['new_id'] = $new_pid; } unset($post); mysql_board_call('COMMIT'); // Log the action $thread = $posts[0]; $log_com = "From /$board/$thread_id to /$to_board/$new_resto"; if ($thread['com'] !== '') { $log_com = $thread['com'] . '

    ' . $log_com; } $action_log_post = array( 'no' => $thread['no'], 'name' => $thread['name'], 'sub' => $thread['sub'], 'com' => $log_com, 'filename' => $thread['filename'], 'ext' => $thread['ext'] ); log_mod_action(6, $action_log_post); // Copy files // If the file already exists, update the database and set it as "deleted" $to_img_dir = preg_replace('/' . BOARD_DIR . '\/$/', '', IMG_ROOT) . $to_board . '/'; $to_thumb_dir = preg_replace('/' . BOARD_DIR . '\/$/', '', THUMB_ROOT) . $to_board . '/'; $dup_pids = array(); foreach ($posts as $post) { if (!$post['ext'] || $post['filedeleted']) { continue; } $src_thumb = THUMB_DIR . $post['tim'] . 's.jpg'; $dest_thumb = $to_thumb_dir . $post['tim'] . 's.jpg'; if (file_exists($dest_thumb)) { $dup_pids[] = $post['new_id']; continue; } $src_img = IMG_DIR . $post['tim'] . $post['ext']; $dest_img = $to_img_dir . $post['tim'] . $post['ext']; copy($src_thumb, $dest_thumb); copy($src_img, $dest_img); } if (!empty($dup_pids)) { $dup_clause = implode(',', $dup_pids); $query = "UPDATE `$to_board` SET filedeleted = 1, ext = '' WHERE no IN($dup_clause)"; $res = mysql_board_call($query); } // Insert notification post if deletion is not requested if (!$delete) { $msg = sprintf(S_THREAD_MOVED, ">>>/$to_board/$new_resto"); $post_time = $_SERVER['REQUEST_TIME']; $tim = generate_tim(); $query = "INSERT INTO `$board`(now,name,sub,com,host,pwd,filename,ext,w, h,tn_w,tn_h, tim,time,last_modified,md5,fsize,resto,capcode, 4pass_id,tmd5,id) VALUE (" . "'" . date('m/d/y(D)H:i:s', $post_time) . "'," . "'" . S_ANONAME . "'," . "''," . "'" . mysql_real_escape_string($msg) . "'," . "'', '', '', '', 0, 0, 0, 0, '" . $tim . "'," . $post_time . "," . $post_time . ", '',0," . $thread_id . ", 'mod', '', '', '')"; $res = mysql_board_call($query); } // Ask the destination board to build the new thread remote_rebuild_live_thread($to_board, $new_resto); // Delete source thread if deletion is requested if ($delete) { // id, pwd, imgonly, auto, die delete_post($thread_id, 'trim', 0, 2, 1, 0); } // Archive source thread if archiving is enabled else if (ENABLE_ARCHIVE) { archive_thread($thread_id); if (!STATIC_REBUILD && ENABLE_JSON_THREADS) { generate_board_archived_json(); } } // Rebuild source thread and indexes if archiving is disabled else { updatelog($thread_id, 1); } // Rebuild indexes if (!STATIC_REBUILD) { updatelog(0, 0); } return array($to_board, $new_resto); } function remote_rebuild_live_thread($board, $pid) { $post = array( 'mode' => 'rebuildadmin', 'no' => $pid ); rpc_start_request("https://sys.int/$board/imgboard.php", $post, $_COOKIE, true); return true; } function archive_thread($thread_id) { global $log; $thread_id = (int)$thread_id; $board = mysql_real_escape_string(BOARD_DIR); if (!$thread_id) { return; } // Regenerate the user ID before clearing the IP $uid = ''; if (DISP_ID && DISP_ID_PER_THREAD && !DISP_ID_RANDOM) { $th = null; if (!IS_REBUILDD && isset($log[$thread_id])) { $th = $log[$thread_id]; } else { $query = 'SELECT id, host FROM `' . BOARD_DIR . "` WHERE no = $thread_id"; $res = mysql_board_call($query); if ($res) { $th = mysql_fetch_assoc($res); } } if ($th && $th['id'] !== '' && $th['host']) { $uid = generate_uid($thread_id, $_SERVER['REQUEST_TIME'], $th['host']); $uid = ", id = '" . mysql_real_escape_string($uid) . "'"; } } // Update the OP. "root" is used for archive pruning. $query = <<(ID: $id)"; } function emailencode( $str ) { return str_replace( "%40", "@", rawurlencode( $str ) ); } function renderPostHtml($no, $in_thread, $sorted_replies = null, $reply_count = null, $shown_replies = null, $is_archived = false) { global $log, $board_flags_array; extract($log[$no]); $namestyle = ''; if( JANITOR_BOARD == 1 ) { $namestyle = broomcloset_style( $name ); $name = broomcloset_name( $name ); } $mname = $name; $mname_truncated = ''; if( $capcode == 'none' && mb_strlen( $name ) > 30 ) { $mname = explode( '', $name, 2 ); if( mb_strlen( $mname[0] ) > 30 ) { $mname[0] = htmlspecialchars_decode($mname[0], ENT_QUOTES); $mname[0] = mb_substr( $mname[0], 0, 30 ) . '(...)'; $mname[0] = htmlspecialchars($mname[0], ENT_QUOTES); $mname_truncated = ' data-tip data-tip-cb="mShowFull"'; } $mname = implode( '', $mname ); } $hasCapcode = $capcode === 'none' ? '' : ' capcode'; // NEW CAPCODE STUFF switch( $capcode ) { case 'admin': $capcodeStart = ' ## Admin'; $capcode_class = ' capcodeAdmin'; $capcode = ' Admin Icon'; $highlight = ''; break; case 'founder': $capcodeStart = ' ## Founder'; $capcode_class = ' capcodeFounder'; $capcode = ' Founder Icon'; $highlight = ''; break; case 'admin_highlight': $capcodeStart = ' ## Admin'; $capcode_class = ' capcodeAdmin'; $capcode = ' Admin Icon'; $highlight = ' highlightPost'; break; case 'mod': $capcodeStart = ' ## Mod'; $capcode_class = ' capcodeMod'; $capcode = ' Mod Icon'; $highlight = ''; break; case 'developer': $capcodeStart = ' ## Developer'; $capcode_class = ' capcodeDeveloper'; $capcode = ' Developer Icon'; $highlight = ''; break; case 'manager': $capcodeStart = ' ## Manager'; $capcode_class = ' capcodeManager'; $capcode = ' Manager Icon'; $highlight = ''; break; case 'verified': $capcodeStart = ' ## Verified'; $capcode_class = ' capcodeVerified'; $capcode = ''; $highlight = ''; break; default: $capcode = $capcodeStart = $highlight = $capcode_class = ''; break; } $spoiler = 0; if( strpos( $sub, 'SPOILER<>' ) === 0 ) { $sub = substr( $sub, strlen( 'SPOILER<>' ) ); $spoiler = 1; } // Only OPs have subjects if ($sorted_replies !== null) { $subshortm = $sub; if( mb_strlen( $sub ) > 30 ) { $sub = str_replace( array(','), ',', $sub ); $cutsub = htmlspecialchars_decode( $sub, ENT_QUOTES ); $cutsub = mb_substr( $cutsub, 0, 30 ); $cutsub = htmlspecialchars( $cutsub, ENT_QUOTES ); $subshortm = '' . $cutsub . '(...)'; } $subshortm = '' . $subshortm . ' '; $sub = '' . $sub . ' '; } else { $sub = $subshortm = ''; } $com = auto_link( $com, $in_thread ); if( !$in_thread ) { list( $com, $abbreviated ) = abbreviate( $com, MAX_LINES_SHOWN ); } if( isset( $abbreviated ) && $abbreviated ) { $com .= '

    Comment too long. Click here to view the full text.'; } // Image tag creation $file = ''; if( $ext ) { $img = IMG_DIR . $tim . $ext; $displaysrc = IMG_DIR2 . $tim . $ext; //if ($ext !== ".swf" && BOARD_DIR !== 'j') { //if ($no % 100 >= 74) { //$displaysrc = "//is2.4chan.org/" . BOARD_DIR . "/" . $tim . $ext; //} //} $linksrc = ( ( USE_SRC_CGI == 1 ) ? ( str_replace( '.cgi', '', IMG_DIR2 ) . $tim . $ext ) : $displaysrc ); if( defined( 'INTERSTITIAL_LINK' ) ) { $linksrc = str_replace( INTERSTITIAL_LINK, '', $linksrc ); } // Original filename truncation $unescaped_filename = htmlspecialchars_decode($filename, ENT_QUOTES); if( mb_strlen( $unescaped_filename, 'UTF-8' ) > 30 ) { $shortname = mb_substr($unescaped_filename, 0, 25, 'UTF-8'); $shortname = htmlspecialchars($shortname, ENT_QUOTES). '(...)' . $ext; $longname = $filename . $ext; $need_file_tooltip = true; } else { $shortname = $longname = $filename . $ext; $need_file_tooltip = false; } if( THREAD_AD == 1 ) { if( defined( 'THREAD_AD_TXT' ) && THREAD_AD_TXT ) { $ad = text_link_ad( THREAD_AD_TXT ); if( $ad ) $dat .= "" . S_ADNAME . " : $ad
    "; } } $s_src = IMG_DIR . $tim . $ext; // 32>24 byte ascii>base64 conversion $shortmd5 = base64_encode( pack( 'H*', $md5 ) ); if( $fsize >= 1048576 ) { $size = round( ( $fsize / 1048576 ), 2 ) . ' M'; } elseif( $fsize >= 1024 ) { $size = round( $fsize / 1024 ) . ' K'; } else { $size = $fsize . ' '; } $ftype = strtoupper( substr( $ext, 1 ) ); $mFileInfo = '
    ' . $size . 'B ' . $ftype . '
    '; if( !$tn_w && !$tn_h && $ext == '.gif' ) { $tn_w = $w; $tn_h = $h; } $class = ''; if( $spoiler ) { $class = ' imgspoiler'; // Replace 3 image tags with one, makes it easier to change in the future $imgthumb_src = SPOILER_THUMB; $tn_w = '100'; $tn_h = '100'; } else { //$imgthumb_src = thumb_url() . $tim . 's.jpg'; $imgthumb_src = '//' . THUMB_DIR2_PART . $tim . 's.jpg'; } if (MOBILE_IMG_RESIZE && $m_img) { $m_img_attr = ' data-m'; } else { $m_img_attr = ''; } $imgsrc = '' . $size . 'B' . $mFileInfo . ''; if( $filedeleted ) { $fileinfo = 'File deleted.'; $imgsrc = ''; } else { $dimensions = ( $ext == '.pdf' ) ? 'PDF' : $w . 'x' . $h; if( !$spoiler ) { $fileinfo = '
    ' . S_PICNAME . ': ' . $shortname . ' (' . $size . 'B, ' . $dimensions . ')
    '; } else { $fileinfo = '
    ' . S_PICNAME . ': Spoiler Image (' . $size . 'B, ' . $dimensions . ')
    '; } } $file = << $fileinfo $imgsrc HTML; } /** * OP specific html */ if ($sorted_replies !== null) { if ($in_thread) { $href = ''; $postinfo_extra = ''; } else { $href = RES_DIR2 . $no . PHP_EXT2; if ($semantic_url !== '') { $semantic_url = "/$semantic_url"; } $postinfo_extra = '   [' . S_REPLY . ']'; } $oldtext = ''; $extra = ''; // Marked for deletion (old) if (isset($log[$no]['old'])) { $oldtext .= '' . S_OLD . '
    '; } $postInfo = ''; // Count omitted replies and images if (!$in_thread) { $s = $reply_count - $shown_replies; if ($shown_replies) { $t = 0; $total_t = 0; $cur = 1; while ($s >= $cur) { list($row) = each($sorted_replies); if ($log[$row]['fsize'] && !$log[$row]['filedeleted']) { $t++; } $cur++; } $total_t = $t; while ($reply_count >= $cur) { list($row) = each($sorted_replies); if ($log[$row]['fsize'] && !$log[$row]['filedeleted']) { $total_t++; } $cur++; } if ($reply_count != 0) { reset($sorted_replies); } } else { $total_t = $t = $imgreplycount; } // desktop $posts = ( $s < 2 ) ? ' reply' : ' replies'; $replies = ( $t < 2 ) ? ' image' : ' images'; if (( $s > 0 ) && ( $t == 0 )) { $extra .= '' . $s . $posts . ' omitted. Click here to view.'; } elseif (( $s > 0 ) && ( $t > 0 )) { $extra .= '' . $s . $posts . ' and ' . $t . $replies . ' omitted. Click here to view.'; } // mobile $posts = ( $reply_count < 2 ) ? ' Reply' : ' Replies'; $replies = ( $total_t < 2 ) ? ' Image' : ' Images'; if (( $reply_count > 0 ) && ( $total_t == 0 )) { // Text replies only $info = '' . $reply_count . $posts . ''; } elseif (( $reply_count > 0 ) && ( $total_t > 0 )) { // Image replies $info = '' . $reply_count . $posts . ' / ' . $total_t . $replies . ''; } else { // nothing $info = ''; } $postInfo = << $info View Thread HTML; } else { $s = 0; } // Sticky - Closed $threadmodes = ''; if ($sticky == 1) { $threadmodes .= ' Sticky'; } if ($closed == 1) { if ($archived) { $threadmodes .= ' Archived'; } else { $threadmodes .= ' Closed'; } } // Staff replies indicator if (META_BOARD) { $posts = meta_is_thread_flagged($sorted_replies); // admin = 0 // dev = 1 // mod = 2 // manager = 3 // array (css_class, text_name) $larr = array( 0 => array('Admin', 'Administrator'), 1 => array('Developer', 'Developer'), 2 => array('Mod', 'Moderator'), 3 => array('Manager', 'Manager') ); if ($posts[0] || $posts[1] || $posts[2] || $posts[3]) { $com .= '

    '; foreach ($posts as $key => $postlist) { if (!$postlist) { continue; } $postlist = explode(',', $postlist); $replies = count($postlist) > 1 ? 'Replies' : 'Reply'; $com .= '' . $larr[$key][1] . ' ' . $replies . ': '; foreach ($postlist as $postnum) { $com .= '>>' . $postnum . ' '; } $com .= '
    '; } $com .= '
    '; } } if (DISP_ID && DISP_ID_PER_THREAD && !$is_archived && !DISP_ID_RANDOM && $id !== '') { $id = generate_uid($no, $time, $host); } $reply_file = ''; $op_file = $file; $post_class = 'op'; $sidearrows = ''; } /** * Reply */ else { $href = $in_thread ? '' : RES_DIR2 . $resto . PHP_EXT2; $threadmodes = $postinfo_extra = $oldtext = $postInfo = $extra = $op_file = ''; $reply_file = $file; $post_class = 'reply'; $sidearrows = '
    >>
    '; $sub = ''; } $dispuid = ''; $dispuid = display_uid( $id, $capcode ); if( $dispuid == '' || $capcode != '' ) { $dispuid = $capcode; } else { $capcodeStart = ''; if (FORCED_ANON) { $capcode_class = ''; } } $countryFlag = ''; if ($capcode == '') { if (ENABLE_BOARD_FLAGS && $board_flag != '' && isset($board_flags_array[$board_flag])) { $cname = board_flag_code_to_name($board_flag); $countryFlag = ' '; } else if (SHOW_COUNTRY_FLAGS) { $cname = country_code_to_name($country); $countryFlag = ' '; } } $quote = $in_thread ? 'javascript:quote(\'' . $no . '\');' : $href . '#q' . $no; $postM = ''; // Forced anon on meta boards if (META_BOARD && $capcode_class != ' capcodeAdmin') { $name = $mname = S_ANONAME; } if (FORCE_COM && !$capcode) { $com = FORCE_COM_TEXT; } $display_no = display_no($no); if ($since4pass && $capcode == '' && $since4pass < 10000) { $since4passTag = " "; } else { $since4passTag = ''; } // April 2024 if ($since4pass && $capcode == '' && $since4pass >= 10000) { $since4passTag = april_2024_get_name_badge($since4pass); $_xa24_post_cls = april_2024_get_post_cls($since4pass); } else { $_xa24_post_cls = ''; } return <<$sidearrows
    $op_file $reply_file
    $com
    $postInfo $oldtext $extra HTML; } // 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, $lazy_rebuild = false, $archived_deletion = false, $tool = null, $user_is_known = true) { global $log; $resno = intval( $resno ); log_cache( 0, $resno, $archived_deletion ? 1 : 0 ); $post_exists = true; if (!isset( $log[$resno])) { if ($die) { //error( "Can't find the post $resno." ); updating_index(); die(); } else { if ($automatic) { return 0; } $post_exists = false; } } $row = $log[$resno]; if (!$automatic && !has_level('janitor')) { $cant_del = $cant_del_old = false; if ($row['resto']) { $cant_del = NO_DELETE_REPLY; } else { $cant_del = NO_DELETE_OP; } if ($_SERVER['REQUEST_TIME'] - $log[$resno]['time'] >= RENZOKU_DEL_CANT_AFTER) { $cant_del = true; $cant_del_old = true; } if ($cant_del) { error($cant_del_old ? S_RENZOKU_DEL_CANT_AFTER : S_MAYNOTDEL); } } // if (!$row['pwd'] && BOARD_DIR=='c') { // echo ""; // } if ($archived_deletion) { // Only authed users can delete archived posts $pass_ok = false; $host_ok = false; } else { $pass_ok = $pwd && $pwd === $row['pwd']; $host_ok = $row['host'] == $_SERVER['REMOTE_ADDR']; } $admin_ok = has_level() || can_delete( $resno ); $can_flood_delete = has_level( 'janitor' ); $can_delete = $automatic || $pass_ok || $host_ok || $admin_ok; // quick_log_to( "/www/perhost/del.log", date( "r" ) . " deletion of #$resno by " . $_SERVER['REMOTE_ADDR'] . " pwd \"$pwd\" auto " . (int)$automatic . " adminok " . (int)$admin_ok . " hostok " . (int)$host_ok . " passok " . (int)$pass_ok . " orig ip " . $row['host'] . " pwd " . $row['pwd'] . " com " . substr( $row["com"], 0, 50 ) . " " . ( $can_delete ? "succeeded" : "failed" )."\n" ); if( !$can_delete ) error( S_BADDELPASS ); if( $row['sticky'] ) { if( has_level() && !has_level( 'admin' ) && !$automatic && !$archived_deletion) error( S_MAYNOTDELSTICKY ); if( !has_level() ) error( S_MAYNOTDEL ); } if( BOARD_DIR == 'vg' && !$row['resto'] && !$admin_ok && !$archived_deletion ) error( S_MAYNOTDEL ); if( !$row['resto'] && !$automatic && !$admin_ok ) { foreach( $row['children'] as $child => $unused ) { if( $log[$child]['capcode'] != 'none' ) error( S_MAYNOTDEL ); } } if( !$automatic && !$admin_ok && !$can_flood_delete ) { if ($user_is_known) { $_renzoku_del = RENZOKU_DEL; } else { $_renzoku_del = 600; // FIXME: 10 minutes } if( ( time() - (int)$row['time'] ) < $_renzoku_del ) { error(S_RENZOKU_DEL); } } // User is authed staff if ($admin_ok && !IS_REBUILDD) { $auser = $_COOKIE['4chan_auser']; // Use POSTed IP instead of the local one if the deletion was triggered via RPC if (isset($_POST['remote_addr']) && is_local()) { $remote_addr = $_POST['remote_addr']; } else { $remote_addr = $_SERVER['REMOTE_ADDR']; } // Authed user is deleting a post that isn't his if (!$pass_ok && !$host_ok) { $adfsize = ( $row['fsize'] > 0 ) ? 1 : 0; $adname = str_replace( ' !', '#', $row['name'] ); if( $imgonly ) { $imgonly = 1; } else { $imgonly = 0; } if (isset($_POST['template_id'])) { $template_id = (int)$_POST['template_id']; } else { $template_id = 0; } if ($post_exists && (!$automatic || $automatic === 2)) { validate_admin_cookies(); if (!$tool || !in_array($tool, array('search', 'ban', 'ban-req', 'autopurge', 'threadban'))) { $tool = ''; } mysql_global_do( "INSERT INTO " . SQLLOGDEL . " (imgonly,postno,resto,board,name,sub,com,img,filename,admin,admin_ip,template_id,tool) values('%s',%d, %d,'%s','%s','%s','%s','%s','%s','%s', '%s', %d, '%s')", $imgonly, $resno, $row['resto'], SQLLOG, $adname, $row["sub"], $row["com"], $adfsize, $row["filename"].$row["ext"], $auser, $remote_addr, $template_id, $tool ); } // Clear the report queue if only the file is deleted if ($imgonly) { $query = "DELETE FROM reports WHERE board = '" . SQLLOG . "' AND no = " . (int)$resno; $res = mysql_global_call($query); $query = "DELETE FROM reports_for_posts WHERE board = '" . SQLLOG . "' AND postid = " . (int)$resno; $res = mysql_global_call($query); } } // Staff member is deleting a post that is his, or other type of deletions else if (!$automatic || $automatic === 2) { if ($auser) { log_staff_event('staff_self_del', $auser, $remote_addr, $pwd, BOARD_DIR, $row); } else { log_staff_event('staff_auto_del', 'Auto-ban', $remote_addr, $pwd, BOARD_DIR, $row); } } } $delete_children = $row['resto'] == 0 && $children && !$imgonly; $restoq = $delete_children ? "OR (archived = 0 and resto=$resno) OR (archived = 1 and resto=$resno)" : ''; if (UPLOAD_BOARD) { $up_col = ',filename'; } else { $up_col = ''; } if (MOBILE_IMG_RESIZE) { $up_col .= ',m_img'; } $result = mysql_board_call( "select no,resto,tim,ext$up_col from `" . SQLLOG . "` where no=$resno $restoq" ); // Array of threads to update after one or more replies were deleted. $updated_threads = array(); // Array of post number for report and xff clearing $deleted_threads = array(); $deleted_replies = array(); $purge_files = array(); $img_webroot = 'http://i.4cdn.org/' . BOARD_DIR . '/'; while( $delrow = mysql_fetch_array( $result ) ) { // delete if( $delrow['ext'] ) { if (UPLOAD_BOARD) { $delfile = IMG_DIR . $delrow['filename'] . $delrow['ext']; //path to delete @unlink($delfile); // delete image if( CLOUDFLARE_PURGE_ON_DEL ) { cloudflare_purge_url($img_webroot . rawurlencode($delrow['filename']) . $delrow['ext'], true); } } else { $delfile = IMG_DIR . $delrow['tim'] . $delrow['ext']; //path to delete $delthumb = THUMB_DIR . $delrow['tim'] . 's.jpg'; @unlink( $delfile ); // delete image @unlink( $delthumb ); // delete thumb if (ENABLE_OEKAKI_REPLAYS && file_exists(IMG_DIR . $delrow['tim'] . '.tgkr')) { unlink(IMG_DIR . $delrow['tim'] . '.tgkr'); if (CLOUDFLARE_PURGE_ON_DEL) { $purge_files[] = $img_webroot . $delrow['tim'] . '.tgkr'; } } if (MOBILE_IMG_RESIZE && $delrow['m_img']) { @unlink(IMG_DIR . $delrow['tim'] . 'm.jpg'); // delete mobile } if (CLOUDFLARE_PURGE_ON_DEL) { $purge_files[] = $img_webroot . $delrow['tim'] . $delrow['ext']; $purge_files[] = $img_webroot . $delrow['tim'] . 's.jpg'; if (MOBILE_IMG_RESIZE && $delrow['m_img']) { $purge_files[] = $img_webroot . $delrow['tim'] . 'm.jpg'; } } } } if( $imgonly ) { mysql_board_call( "UPDATE `" . SQLLOG . "` SET filedeleted=1,root=root,last_modified=%d WHERE no=%d", $_SERVER['REQUEST_TIME'], $delrow['no'] ); $log[$delrow['no']]['filedeleted'] = TRUE; if ($delrow['resto']) { mysql_board_call( "UPDATE `" . SQLLOG . "` SET root=root,last_modified=%d WHERE no=%d", $_SERVER['REQUEST_TIME'], $delrow['resto'] ); if (isset($log[$delrow['resto']])) $log[$delrow['resto']]['last_modified'] = (int)$_SERVER['REQUEST_TIME']; } //cloudflare_purge_by_basename(BOARD_DIR, $delrow['tim'] . $delrow['ext']); } else { // Thread if (!$delrow['resto']) { $thread_key = @array_search( $delrow['no'], $log['THREADS'] ); if( $thread_key !== false ) { unset( $log['THREADS'][$thread_key] ); } if( USE_GZIP == 1 ) { @unlink( RES_DIR . $delrow['no'] . PHP_EXT . '.gz' ); @unlink( RES_DIR . $delrow['no'] . '.json.gz' ); } else { @unlink( RES_DIR . $delrow['no'] . PHP_EXT ); @unlink( RES_DIR . $delrow['no'] . '.json' ); } update_json_tail_deletion($delrow['no'], true); $deleted_threads[] = (int)$delrow['no']; } // Reply. Thread's last_modified field will need to be updated else if (!isset($updated_threads[$delrow['resto']])) { $updated_threads[$delrow['resto']] = true; $deleted_replies[] = (int)$delrow['no']; } unset( $log[$delrow['no']] ); } } if (!empty($purge_files)) { cloudflare_purge_url($purge_files, true); } // Updating last_modified field (threads) foreach ($updated_threads as $thread_id => $true) { mysql_board_call("UPDATE `".SQLLOG."` set root=root,last_modified=%d where no=%d", $_SERVER['REQUEST_TIME'], $thread_id); if (isset($log[$thread_id])) $log[$thread_id]['last_modified'] = (int)$_SERVER['REQUEST_TIME']; unset( $log[$thread_id]['children'][$delrow['no']] ); } // Clearing reports and xff if ($deleted_replies) { $in_clause = 'IN(' . implode(',', $deleted_replies) . ')'; mysql_global_do("DELETE FROM reports WHERE board='" . BOARD_DIR . "' AND no " . $in_clause); mysql_global_do("DELETE FROM reports_for_posts WHERE board='" . BOARD_DIR . "' AND postid " . $in_clause); if (SAVE_XFF) { mysql_global_do("UPDATE xff SET is_live = 0 WHERE board='" . BOARD_DIR . "' AND postno " . $in_clause); } } if ($deleted_threads) { $in_clause = 'IN(' . implode(',', $deleted_threads) . ')'; mysql_global_do("DELETE FROM reports WHERE board='" . BOARD_DIR . "' AND (no $in_clause OR resto $in_clause)"); mysql_global_do("DELETE FROM reports_for_posts WHERE board='" . BOARD_DIR . "' AND (postid $in_clause OR threadid $in_clause)"); if (SAVE_XFF) { mysql_global_do("UPDATE xff SET is_live = 0 WHERE board='" . BOARD_DIR . "' AND postno " . $in_clause); } } // Halloween 2017 /* if ($tool && defined('CSS_EVENT_NAME') && CSS_EVENT_NAME === 'spooky2017') { if ($tool === 'ban') { decrease_halloween_score($resno); } else if ($tool === 'ban-req') { decrease_halloween_score($resno, 0.90); } } */ //delete from DB if( $delete_children ) // 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" ); rpc_task(); if( $imgonly && $row['resto'] == 0 ) { return $resno; // return thread number to stop deletion silliness } return $row['resto']; // so the caller can know what pages need to be rebuilt } function rebuild_deletions($rebuild, $lazy_rebuild = false) { global $log; foreach( $rebuild as $key => $val ) { log_cache( 0, $key ); if (!isset($log[$key]['children'])) { internal_error_log("rebuild_deletions", "missing children for OP /" . BOARD_DIR . "/$key"); continue; } updatelog( $key, 1 ); // leaving the second parameter as 0 rebuilds the index each time! update_json_tail_deletion($key); } if( STATIC_REBUILD ) return; updatelog(0, 0, $lazy_rebuild); // update the index page last } /** * Removes old archived posts */ function trim_archive() { if (STATIC_REBUILD && !IS_REBUILDD) { return; } if (!ARCHIVE_MAX_AGE) { return; } $interval = (int)ARCHIVE_MAX_AGE; $query = << 0 ) ? ( PAGE_MAX * DEF_PAGES ) : 0; $threads = array(); $rebuild_archive_list = false; $rebuild_archive_json = false; if (ENABLE_ARCHIVE) { if (IS_REBUILDD) { clearstatcache(true, INDEX_DIR . 'archive' . PHP_EXT . '.gz'); } if (filemtime(INDEX_DIR . 'archive' . PHP_EXT . '.gz') < time() - (int)ARCHIVE_REBUILD_DELAY) { $rebuild_archive_list = true; } } // 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 archived=0 AND sticky=0 AND undead=0 AND resto=0 ORDER BY $exp_order ASC" ); //logtime( 'trim_db after select threads' ); $threadcount = mysql_num_rows( $result ); if (!$threadcount && $rebuild_archive_list) { $rebuild_archive_list = false; } while( $row = mysql_fetch_array( $result ) and $threadcount > $maxthreads ) { if (ENABLE_ARCHIVE) { $rebuild_archive_json = true; archive_thread($row['no']); } else { delete_post( $row['no'], 'trim', 0, 1 ); // imgonly=0, automatic=1, children=1 } $threads[$row['no']] = 1; $threadcount--; } mysql_free_result( $result ); if (ENABLE_ARCHIVE) { if ($rebuild_archive_list) { rebuild_archive_list(); } if ($rebuild_archive_json && ENABLE_JSON_THREADS) { generate_board_archived_json(); } } // Original max-posts method (note: cleans orphaned posts later than parent posts) } else { // make list of stickies $stickies = array(); // keys are stickied thread numbers $undead = array(); // COMBINE FOR MAXIMUM EFFICIENCY! $result = mysql_board_call( "SELECT no from `" . SQLLOG . "` where (sticky=1 OR undead=1) and resto=0" ); while( $row = mysql_fetch_array( $result ) ) { if( $row['sticky'] ) $stickies[$row['no']] = 1; if( $row['undead'] ) $undead[$row['no']] = 1; } // FIXME these if ... continue checks need to be SQL conditions! $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 or is undeletable if( $row['sticky'] == 1 || $row['undead'] == 1 ) continue; // don't delete if this is a REPLY to a sticky or is in an undeletable thread if( $row['resto'] != 0 && ( $stickies[$row['resto']] == 1 || $undead[$row['resto']] == 1 ) ) continue; delete_post( $row['no'], 'trim', 0, 1, 0 ); // imgonly=0, automatic=1, children=0 $threads[$row['no']] = 1; $postcount--; } mysql_free_result( $result ); } } // FIXME archives // debug function, deletes all archived threads function purge_archive() { $query = "SELECT no FROM `test` WHERE archived = 1 AND resto = 0"; $res = mysql_board_call($query); if (!$res) { return; } while ($thread = mysql_fetch_assoc($res)) { echo "Deleting {$thread['no']}
    "; delete_post((int)$thread['no'], '', 0, 0, 1, true, false, true); } } function rebuild_archived_thread($thread_id) { global $log; log_cache(0, $thread_id, 1); if (!isset($log[$thread_id])) { return false; } // Build the JSON if (ENABLE_JSON) { $tailSize = get_json_tail_size($thread_id); if ($tailSize) { generate_thread_json($thread_id, false, false, false, $tailSize); } else { update_json_tail_deletion($thread_id); } generate_thread_json($thread_id); } // Build the HTML $dat = ''; head($dat, $thread_id); form($dat, $thread_id); $dat .= '
    '; $reply_count = $log[$thread_id]['replycount']; // Open thread tag and render OP $sorted_replies = $log[$thread_id]['children']; ksort($sorted_replies); $dat .= '
    ' . renderPostHtml($thread_id, $thread_id, $sorted_replies, $reply_count, null, true); // Render replies $repCount = 0; while (list($resrow) = each($sorted_replies)) { if (!$log[$resrow]['no']) { break; } $dat .= renderPostHtml($resrow, $thread_id, null, null, null, true); $repCount++; } // Close thread tag $dat .= '

    '; $dat .= '
    '; // Close board tag $lang = S_FORM_REPLY; $dat .= '
    '; $dat .= '
    '; /** * ADS */ if (defined('AD_ADGLARE_BOTTOM') && AD_ADGLARE_BOTTOM) { $dat .= '

    '; } if (defined('AD_ADGLARE_BOTTOM_MOBILE') && AD_ADGLARE_BOTTOM_MOBILE) { $dat .= '

    '; } if (defined('AD_RC_BOTTOM') && AD_RC_BOTTOM) { $dat .= '

    '; } if (defined('AD_BSA_BOTTOM') && AD_BSA_BOTTOM) { $dat .= AD_BSA_BOTTOM; } if (defined('AD_RC_BOTTOM_MOBILE') && AD_RC_BOTTOM_MOBILE) { $dat .= '

    '; } if (defined('AD_ADNIUM_BOTTOM_MOBILE') && AD_ADNIUM_BOTTOM_MOBILE) { $dat .= '

    '; } if (defined('AD_ABC_BOTTOM_MOBILE') && AD_ABC_BOTTOM_MOBILE) { $dat .= '

    '; } /* if (defined('AD_BIDGEAR_BOTTOM') && AD_BIDGEAR_BOTTOM) { $dat .= '
    '; } else if (defined('AD_TERRA_BOTTOM_DESKTOP') && AD_TERRA_BOTTOM_DESKTOP) { $_ad_info = explode(',', AD_TERRA_BOTTOM_DESKTOP); $dat .= '
    '; } */ if (defined('ADS_DANBO') && ADS_DANBO) { $dat .= '

    '; } if (defined('AD_CUSTOM_BOTTOM') && AD_CUSTOM_BOTTOM) { $dat .= '
    ' . AD_CUSTOM_BOTTOM . '
    '; } $resredir = ''; // deletion mode is "arcdel" instead of "usrdel" $dat .= '
    ' . S_REPDEL . $resredir . ' [' . S_DELPICONLY . '] '; if (!defined('CSS_FORCE')) { $dat .= 'Style: '; } $dat .= '
    '; foot($dat); // Write the page print_page(RES_DIR . $thread_id . PHP_EXT, $dat); } function calculate_indexes_to_rebuild( $updated_thread ) { global $index_rbl; $query = mysql_board_call( "SELECT COUNT(no) FROM `%s` WHERE archived = 0 AND root > (SELECT root FROM `%s` WHERE no=%d)", SQLLOG, SQLLOG, $updated_thread ); $index_rbl = floor( mysql_result( $query, 0, 0 ) / DEF_PAGES ); } function rebuild_indexes_daemon() { global $index_rbl, $index_last_thread, $index_last_post, $log; static $index_arr = array(); $index_rbl = PAGE_MAX; // Get latest thread $query = mysql_board_call( "SELECT max(no) last_post, max(resto) last_thread FROM `%s` WHERE archived = 0", SQLLOG ); $q = mysql_fetch_assoc( $query ); $latest_thread = $q['last_thread']; $latest_post = $q['last_post']; if( $index_last_thread != $latest_thread ) { // cry :( $index_last_thread = $latest_thread; $index_last_post = $latest_post; updatelog(); if (ENABLE_JSON_THREADS && ENABLE_ARCHIVE) { generate_board_archived_json(); } return; } if( $index_last_post != $latest_post ) { $index_last_thread = $latest_thread; $index_last_post = $latest_post; $post_arr = $log['THREADS']; // Now we know we're not going to have identical arrays. $count = count( $post_arr ); $last_seen_thread = 0; for( $i = 0; $i < $count; $i++ ) { if( $post_arr[$i] != $index_arr[$i] ) $last_seen_thread = $i; } $index_rbl = floor( $last_seen_thread / DEF_PAGES ); $index_arr = $post_arr; updatelog(); return; } // YAY NOTHING TO UPDATE! return; } function style_group() { return ( DEFAULT_BURICHAN == 1 ) ? "ws_style" : "nws_style"; } function rebuildd_stats() { if (!IS_REBUILDD) return ""; global $update_avg_secs; global $rpc_chs; $avgtime = $update_avg_secs; $memuse = (int)(memory_get_usage(true) / 1024); $peakmemuse = (int)(memory_get_peak_usage(true) / 1024); $rpccount = count($rpc_chs); return ""; } // Changes relative board urls to absolute //board.4chan.org urls // mostly for /j/ and error pages on sys.4chan function fix_board_nav($nav, $fix_protocol = false) { if ($fix_protocol) { $protocol = (stripos($_SERVER["HTTP_REFERER"], "https") === 0) ? 'https:' : 'http:'; } else { $protocol = ''; } return preg_replace('/href="\/([a-z0-9]+)\/"/', "href=\"$protocol//boards." . L::d(BOARD_DIR) . "/$1/\"", $nav); } // Same but for /archive lmao function fix_board_nav_archive($nav) { $nav = preg_replace('/href="\/([a-z0-9]+)\/"/', 'href="/$1/archive"', $nav); $nav = preg_replace('/href="\/f\/archive"/', 'href="/f/"', $nav); $nav = preg_replace('/href="\/b\/archive"/', 'href="/b/"', $nav); return $nav; } function head( &$dat, $res, $error = 0, $page = 0, $npages = 0, $is_arclist = false ) { //( $dat, 0, 0, 0, 0, true ) global $log, $thread_unique_ips; $titlepart = $rta = $favicon = $css = $rss = $subtitle = $extra = ''; $includenav = file_get_contents_cached(NAV_TXT); if( JANITOR_BOARD == 1 ) { $dat .= broomcloset_head( $dat ); $includenav = fix_board_nav($includenav); } else if ($error) { $includenav = fix_board_nav($includenav, true); } else if ($is_arclist) { $includenav = fix_board_nav_archive($includenav); } if( TITLE_IMAGE_TYPE == 1 ) { $titleimg = rand_from_flatfile( YOTSUBA_DIR, 'title_banners.txt' ); //$titleimg = STATIC_SERVER . 'image/title/' . $titleimg; $titlepart .= '
    '; } elseif( TITLE_IMAGE_TYPE == 2 ) { $titlepart .= ''; } if( defined( 'SUBTITLE' ) ) { $subtitle = '
    ' . SUBTITLE . '
    '; } // CSS Workings $cssVersion = TEST_BOARD ? CSS_VERSION_TEST : CSS_VERSION; $defaultcss = ( DEFAULT_BURICHAN == 1 ) ? 'yotsubluenew' : 'yotsubanew'; $mobilecss = ( ( DEFAULT_BURICHAN == 1 ) ? 'yotsublue' : 'yotsuba' ) . 'mobile.' . $cssVersion . '.css'; $styles = array( 'Yotsuba New' => "yotsubanew.$cssVersion.css", //'Yotsuba' => "yotsuba.$cssVersion.css", 'Yotsuba B New' => "yotsubluenew.$cssVersion.css", 'Futaba New' => "futabanew.$cssVersion.css", 'Burichan New' => "burichannew.$cssVersion.css", 'Photon' => "photon.$cssVersion.css", 'Tomorrow' => "tomorrow.$cssVersion.css" ); // /j/ versioning fix if( BOARD_DIR == 'j' ) { $css = ''; $extra = << document.addEventListener('mousedown', function(e) { var t = e.target; if (t === document) { return; } if (/^>>>\//.test(t.textContent) && /sys\.4chan/.test(t.href)) { t.href = t.href.replace('sys.4chan', 'boards.4chan'); } }, false); JJS; } else { if( defined( 'CSS_FORCE' ) ) { foreach( $styles as $style => $stylecss ) { $rel = ( $style == 'Yotsuba New' ) ? 'stylesheet' : 'alternate stylesheet'; $css .= ''; } } else { $dcssl = $defaultcss . '.' . $cssVersion . '.css'; $css .= ''; foreach( $styles as $style => $stylecss ) { $css .= ''; } if (defined('CSS_EVENT_NAME') && CSS_EVENT_NAME) { $css .= ''; } } // Christmas 2021 if (defined('CSS_EVENT_NAME') && CSS_EVENT_NAME === 'tomorrow') { $extra = << JJS; } // Halloween spooky.css if (defined('CSS_EVENT_NAME') && CSS_EVENT_NAME === 'spooky') { $extra = << function fc_skelrot(e) { var el, idx, thres, max; if (e && e.detail && e.detail.count) { thres = 0.33; } else { thres = 0.0; } max = 23; if (Math.random() < thres) { return; } if (el = document.getElementById('skellington')) { el.parentNode.removeChild(el); } idx = 1 + Math.floor(Math.random() * max); el = document.createElement('img'); el.id = 'skellington'; el.className = 'desktop' + (Math.random() < 0.25 ? ' topskel' : ''); el.alt = ''; if (Math.random() < 0.01) { el.src = '//s.4cdn.org/image/temp/dinosaur.gif'; } else { el.src = '//s.4cdn.org/image/skeletons/' + idx + '.gif'; } document.body.insertBefore(el, document.body.firstElementChild); } function fc_spooky_init() { if (window.matchMedia && window.matchMedia('(min-width: 481px)').matches) { document.addEventListener('4chanThreadUpdated', fc_skelrot, false); window.dark_captcha = true; fc_skelrot(); } } function fc_spooky_cleanup() { var el = document.getElementById('skellington'); window.dark_captcha = false; document.removeEventListener('4chanThreadUpdated', fc_skelrot, false); el && el.parentNode.removeChild(el); } JJS; } } $css .= ''; // April 2024 $css .= ''; if (SHOW_COUNTRY_FLAGS) { $css .= ''; } if (ENABLE_BOARD_FLAGS) { $_flags_type = (defined('BOARD_FLAGS_TYPE') && BOARD_FLAGS_TYPE) ? BOARD_FLAGS_TYPE : BOARD_DIR; $css .= ''; } if( CODE_TAGS ) { $css .= ''; } // Various optional tags if( USE_RSS == 1 ) { $rss = ''; } if( RTA == 1 ) { $rta = ''; } if( defined( 'FAVICON' ) ) { $favicon = ''; } $thread_unique_ips = 0; $jsUniqueIps = ''; if (SHOW_THREAD_UNIQUES) { if ($res) { $thread_unique_ips = get_unique_ip_count($res); } if ($thread_unique_ips) { $jsUniqueIps = 'var unique_ips = ' . $thread_unique_ips . ';'; } } // js tags $jsVersion = TEST_BOARD ? JS_VERSION_TEST : JS_VERSION; $comLen = MAX_COM_CHARS; $styleGroup = style_group(); $maxFilesize = MAX_KB * 1024; $maxLines = MAX_LINES; $jsCooldowns = json_encode(array( 'thread' => RENZOKU3, 'reply' => RENZOKU, 'image' => RENZOKU2 )); $tailSizeJs = ''; if ($res) { $tailSize = get_json_tail_size($res); if ($tailSize) { $tailSizeJs = ",tailSize = $tailSize"; } } $title = TITLE; $scriptjs = << var style_group = "$styleGroup", cssVersion = $cssVersion, jsVersion = $jsVersion, comlen = $comLen, maxFilesize = $maxFilesize, maxLines = $maxLines, clickable_ids = 1, cooldowns = $jsCooldowns $tailSizeJs; $jsUniqueIps JS; if (defined('CSS_EVENT_NAME') && CSS_EVENT_NAME) { $scriptjs .= 'var css_event = "' . CSS_EVENT_NAME . '";'; if (defined('CSS_EVENT_VERSION')) { $_css_event_version = (int)CSS_EVENT_VERSION; } else { $_css_event_version = 1; } $scriptjs .= 'var css_event_v = ' . $_css_event_version . ';'; } if ((int)MAX_WEBM_FILESIZE < (int)MAX_KB) { $scriptjs .= 'var maxWebmFilesize = ' . (MAX_WEBM_FILESIZE * 1024) . ';'; } $_is_archived = false; if (ENABLE_ARCHIVE) { $scriptjs .= 'var board_archived = true;'; if ($res && $log[$res]['archived']) { $scriptjs .= 'var thread_archived = true;'; $_is_archived = true; } } if (DISP_ID) { $scriptjs .= 'var user_ids = true;'; } if (JSMATH) { $scriptjs .= 'var math_tags = true;'; } if (SJIS_TAGS) { $scriptjs .= 'var sjis_tags = true;'; } if (SPOILERS) { $scriptjs .= 'var spoilers = true;'; } if (CAPTCHA_TWISTER) { $scriptjs .= 'var t_captcha = true;'; } if( $res && $log[$res]['bumplimit'] ) { $scriptjs .= 'var bumplimit = 1;'; } if( $res && $log[$res]['imagelimit'] ) { $scriptjs .= 'var imagelimit = 1;'; } if( AD_PLEA ) $scriptjs .= 'var check_for_block = ' . (int)AD_PLEA . ';'; if( $error ) $scriptjs .= 'is_error = "true";'; // Danbo ads if (defined('ADS_DANBO') && ADS_DANBO) { if (DEFAULT_BURICHAN) { $scriptjs .= "var danbo_rating = '__SFW__';"; } else { $scriptjs .= "var danbo_rating = '__NSFW__';"; } // Set up fallbacks $_danbo_fallbacks = []; if (defined('AD_BIDGEAR_TOP') && AD_BIDGEAR_TOP) { $_danbo_fallbacks['t_bg'] = AD_BIDGEAR_TOP; } else { if (defined('AD_ABC_TOP_DESKTOP') && AD_ABC_TOP_DESKTOP) { $_danbo_fallbacks['t_abc_d'] = AD_ABC_TOP_DESKTOP; } if (defined('AD_ABC_TOP_MOBILE') && AD_ABC_TOP_MOBILE) { $_danbo_fallbacks['t_abc_m'] = AD_ABC_TOP_MOBILE; } } if (defined('AD_BIDGEAR_BOTTOM') && AD_BIDGEAR_BOTTOM) { $_danbo_fallbacks['b_bg'] = AD_BIDGEAR_BOTTOM; } else if (defined('AD_ABC_BOTTOM_MOBILE') && AD_ABC_BOTTOM_MOBILE) { $_danbo_fallbacks['b_abc_m'] = AD_ABC_BOTTOM_MOBILE; } if (!$_danbo_fallbacks) { $_danbo_fallbacks = 'null'; } else { $_danbo_fallbacks = json_encode($_danbo_fallbacks); } $scriptjs .= 'var danbo_fb = ' . $_danbo_fallbacks . ';'; // Tag closed further below $scriptjs .= ''; // PubFuture if (DEFAULT_BURICHAN) { $scriptjs .= ''; } $testjs = ( TEST_BOARD ) ? 'test/core-8psvqAqszI.' . JS_VERSION_TEST . '.js' : 'core.min.' . JS_VERSION_CORE . '.js'; $testextra = ( TEST_BOARD ) ? 'test/extension-8psvqAqszI.' . JS_VERSION_TEST . '.js' : 'extension.min.' . JS_VERSION_EXT . '.js'; $scriptjs .= ''; if( !$error ) $scriptjs .= ''; // April 2022 //$scriptjs .= ''; if (TEST_BOARD) { $stylejs = ''; } else { $stylejs = ''; } if (ENABLE_PAINTERJS && $_GET['mode'] != 'oe_finish') { if (TEST_BOARD) { $scriptjs .= ''; $css .= ''; } else { $scriptjs .= ''; $css .= ''; } } /* if (!$is_arclist && defined('CSS_MATERIAL') && CSS_MATERIAL) { $css .= ''; } */ if( !$res ) { $prev = ( $page - DEF_PAGES ) / DEF_PAGES; $next = ( $page + DEF_PAGES ) / DEF_PAGES; if( $prev == 0 ) { $prev_link = SELF_PATH2; } else if( $prev > 0 ) { $prev_link = $prev . PHP_EXT2; } // maybe >= ? if( ( $npages - $page ) > DEF_PAGES ) { $next_link = $next . PHP_EXT2; } } if ($is_arclist) { $canonical = ''; } else if (!$res) { if ($page > 0) { $canonical = ''; } else { $canonical = ''; } } elseif ($res) { $href_context = $log[$res]['semantic_url']; if ($href_context !== '') { $href_context = "/$href_context"; } $canonical = ''; } else { $canonical = ''; } $clean_title = strip_tags(TITLE); if ($res) { $page_metatags = generate_page_metatags($log[$res]['sub'], $log[$res]['com']); if ($page_metatags) { $page_description = $page_metatags[0] . ' - ' . META_DESCRIPTION; $page_keywords = META_KEYWORDS . $page_metatags[1]; } else { $page_description = META_DESCRIPTION; $page_keywords = META_KEYWORDS; } $page_title = generate_page_title($res, $log[$res]['sub'], $log[$res]['com']) . ' - ' . preg_replace('/^[\[\/][a-z0-9]+[\]\/] - /i', '', $clean_title); } else { $page_description = META_DESCRIPTION; $page_keywords = META_KEYWORDS; $page_title = $clean_title; if ($is_arclist) { $page_title .= ' - Archive'; } else if ($page > 0) { $page_title .= ' - Page ' . (($page / DEF_PAGES) + 1); } } $page_title .= ' - 4chan'; if (!$_is_archived) { $_delegate_ch = ''; } else { $_delegate_ch = ''; } $dat .= ' ' . $rta . ' ' . $favicon . ' ' . $css . ' ' . $canonical . ' ' . $rss . $_delegate_ch . ' ' . $page_title . '' . $scriptjs . $extra; $embedearly = EMBEDEARLY; $adembedearly = AD_EMBEDEARLY; if (AD_ADBLOCK_TEXT && (DEFAULT_BURICHAN || BOARD_DIR === 'pol' || BOARD_DIR === 'bant')) { $adembedearly .= file_get_contents_cached(AD_ADBLOCK_TEXT); } $board_class = 'board_' . BOARD_DIR; if (!$res) { if ($is_arclist) { $board_class = 'is_arclist ' . $board_class; } else { $board_class = 'is_index ' . $board_class; } } else { $board_class = 'is_thread ' . $board_class; } if (TEXT_ONLY) { $board_class = 'text_only ' . $board_class; } if (!$is_arclist) { $abovePostForm = '
    '; } else { $abovePostForm = ''; } $dat .= << $stylejs $includenav
    $titlepart
    $title
    $subtitle
    $abovePostForm HTML; if (!$error && !$is_arclist) { /* if (defined('ADS_BIDGLASS_TOP_MOBILE') && ADS_BIDGLASS_TOP_MOBILE) { $dat .= ''; } else if (defined('AD_ABC_TOP_MOBILE') && AD_ABC_TOP_MOBILE) { $dat .= '

    '; }*/ } } function delete_uploaded_files() { global $upfile_name, $upfile, $dest, $pchfile; if( $dest || $upfile ) { @unlink( $dest ); @unlink( $upfile ); } } /* Footer */ function foot( &$dat, $error = false, $is_arclist = false ) { global $update_avg_secs; $includenav = file_get_contents_cached(NAV2_TXT); $dat .= $includenav; $dat .= rebuildd_stats(); if( CODE_TAGS ) { $dat .= ''; } $dat .= EMBEDLATE . ''; } function error($mes, $unused = '') { global $mode; if (isset($_SERVER['HTTP_ACCEPT']) && $_SERVER['HTTP_ACCEPT'] === 'application/json') { error_json($mes); } if( $mode == "report" ) fancydie( $mes ); delete_uploaded_files(); head( $dat, 0, 1 ); $protocol = (stripos($_SERVER["HTTP_REFERER"], "https") === 0) ? 'https:' : 'http:'; $dat .= '
    ' . $mes . '

    [" . S_RELOAD . "]



    "; foot( $dat, true ); if (TEST_BOARD==1) { internal_error_log("post error", $mes); } die( $dat ); } function error_json($msg) { delete_uploaded_files(); header('Content-Type: application/json'); echo json_encode(['error' => $msg]); die(); } function error_redirect($mes, $redirect, $timeout = 3000) { delete_uploaded_files(); head( $dat, 0, 1 ); $dat .= << setTimeout(function() { window.location = "$redirect"; }, $timeout); HTML; $dat .= '
    ' . $mes . '

    [" . S_RELOAD . "]



    "; foot( $dat ); if (TEST_BOARD==1) { internal_error_log("post error", $mes); } die( $dat ); } /* Auto Linker */ function normalize_link_cb( $m ) { $subdomain = $m[1]; $original = $m[0]; $board = strtolower( $m[2] ); $m[0] = $m[1] = $m[2] = ''; $count = count( $m ) - 1; for( $i = $count; $i > 2; $i-- ) { if( $m[$i] ) { $no = $m[$i]; break; } } if( $subdomain != 'boards') { return $original; } if( stripos( $no, 'catalog' ) === 0 ) { if( ( $pos = stripos( $no, '#s=' ) ) !== false ) { $term = substr( $no, $pos + 3 ); } else { $term = 'catalog'; } return ">>>/$board/$term"; } if( $board == BOARD_DIR && $no && $no != 'catalog' ) { return ">>$no"; } else { return ">>>/$board/$no"; } } function normalize_links( $proto ) { // change http://xxx.4chan.org/board/res/no links into plaintext >># or >>>/board/# $proto = preg_replace_callback( '@https?://([a-z]*)[.](?:4chan|4channel)[.]org/(\w+)/(?:(res|thread)/(\d+)(?:\/[-a-z0-9]+)?(?:#[qp]?(\d*))?|(catalog(?:#s=[a-z0-9+]+)?)|\w+.php[?]res=(\d+)(?:#[qp]?(\d*))?|)(?=[\s. false); return false; } $r = (int)mysql_fetch_row($q)[0]; $log[$no] = array('resto' => $r); return $r; return false; } // FIXME // This might not be used anywhere anymore function intraboard_link_cb( $m ) { global $intraboard_cb_resno, $log; $no = $m[1]; $resno = $intraboard_cb_resno; $resto = post_resto( $no ); // doesn't like assignment in condition if( $resto !== false ) { $resdir = ( $resno ? '' : RES_DIR2 ); $ext = PHP_EXT2; $id = NEW_HTML ? "p$no" : "$no"; if( $resno && $resno == $resto ) // linking to a reply in the same thread return ">>$no"; elseif( $resto == 0 ) // linking to a thread return ">>$no"; else // linking to a reply in another thread return ">>$no"; } return '' . $m[0] . ''; } // FIXME // This might not be used anywhere anymore, see parse_intraboard_link() function intraboard_links( $proto, $resno ) { global $intraboard_cb_resno; $intraboard_cb_resno = $resno; $proto = preg_replace_callback( '/>>([0-9]+)/', 'intraboard_link_cb', $proto ); return $proto; } function other_board_resto( $board, $resno ) { // this function is a little slow // board requirements - either is the current board (for /test/) or is public (in boardlist) // returns - // FALSE if resno does not exist // 0 if resno is a thread // an id if resno is a reply to a thread static $boardlist = array(); if( !$boardlist ) $boardlist = array_flip( mysql_column_array( mysql_global_call( "select sql_cache dir from boardlist" ) ) ); if( $board != BOARD_DIR && !isset( $boardlist[$board] ) ) return false; $q = mysql_board_call( "select resto from `%s` where no=%d", $board, $resno ); if( !mysql_num_rows( $q ) ) return false; $r = mysql_result( $q, 0 ); return $r; } function interboard_link_cb( $m ) { // on one hand, we can link to imgboard.php, using any old subdomain, // and let apache & imgboard.php handle it when they click on the link // on the other hand, we can use the database to fetch the proper subdomain // and even the resto to construct a proper link to the html file (and whether it exists or not) // for now, we'll assume there's more interboard links posted than interboard links visited. $board = '/'; $otherboard = mb_strtolower( $m[1] ); if( $m[2] ) { $resto = other_board_resto( $otherboard, $m[2] ); $id = "#p"; if( $resto === false ) $url = ""; else if( $resto ) $url = $board . $otherboard . '/thread/' . $resto . $id . $m[2]; else $url = $board . $otherboard . '/thread/' . $m[2] . $id . $m[2]; } else $url = $board . $otherboard . '/'; $b = $m[1]; $mlp_hack = BOARD_DIR == 'mlp' && ( $b == 'b' || $b == 'co' ); $original = mb_strtolower( $m[0] ); if( !$url || $mlp_hack ) return '' . $original . ''; else return "{$original}"; } function interboard_catalog_link_cb( $m ) { $board = $m[1]; if( $board == 'f' ) return $m[0]; $lsearchquery = strtolower( urlencode( urldecode( $m[2] ) ) ); $original = mb_strtolower( str_replace( ">", "> ", $m[0] ) ); if( $lsearchquery == "catalog" ) { return "$original"; } elseif( $lsearchquery == 'rules' ) { return '' . $original . ''; } else { return "$original"; } } function boards_matching_arr() { static $boards_matching_arr; global $valid_boards; if( empty( $boards_matching_arr ) ) $boards_matching_arr = explode( '|', $valid_boards ); return $boards_matching_arr; } // Normalize and linkify internal and non-quote links // before inserting the post into the database. function normalize_and_linkify($proto) { if (strpos($proto, "4chan") !== false || strpos($proto, "4cdn.org") !== false) { // normalize long links $proto = normalize_links($proto); // linkify other internal links if ((strpos($proto, "4chan") !== false && strpos($proto, "/derefer") === false) || strpos($proto, "4cdn.org") !== false) { $proto = preg_replace_callback( '/(https?:\/\/(?:[A-Za-z]*\.)?)(4chan|4channel|4cdn)(\.org)(\/[\w\-\.,@?^=%&;:\/~\+#\(\)]*[\w\-\@?^=%&;\/~\+#])?/i', 'clean_internal_link', $proto ); $proto = preg_replace( '/([<][^>]*?)\\2<\/a>([^<]*?[>])/i', '\\1\\3\\4\\5\\6\\7\\8', $proto ); } } if (strpos($proto, '>>>') !== false) { $proto = preg_replace_callback('#>>>/([a-z0-9]+)/([a-z0-9+/,l\-]*)#', 'auto_link_static_cb', $proto); } return $proto; } // Removes >> links from internal 4chan.org links function clean_internal_link($matches) { $link = preg_replace('/>>>|>>/', '', $matches[0]); return "$link"; } function auto_link_static_cb($matches) { $post = $matches[0]; $inter_board = $matches[1]; $no = $matches[2]; $boards_matching_arr = boards_matching_arr(); $full_link = $post; $inter_board = strtolower( $inter_board ); $is_board_link = ($no == ''); $resno = $no; // Text boards // Catalog, rules, and /rs/ links if (!is_numeric( $resno ) || $is_board_link) { $url = urlencode( urldecode( $resno ) ); $target = ''; if( strpos( $resno, 'rules' ) === 0 ) { $ruleno = ''; $ruleloc = strpos( $resno, '/' ); if( $ruleloc !== false ) { $ruleno = substr( $resno, $ruleloc + 1 ); } $parsed_link = '//www.' . L::d($inter_board) . "/rules#$inter_board$ruleno"; $target = ' target="_blank"'; } else if (in_array($inter_board, $boards_matching_arr)) { $parsed_link = '//boards.' . L::d($inter_board) . "/$inter_board/"; if( $inter_board == 'f' && $url == 'catalog' ) return $full_link; if( !$is_board_link ) { $parsed_link .= ($url == 'catalog' ? 'catalog' : "catalog#s=$url"); } } else { return $full_link; } return '' . $full_link . ''; } return $full_link; } function auto_link( $proto, $resno ) { global $current_resno; static $has_gen = 0; if( !$has_gen ) { boards_matching_arr(); $has_gen = 1; } // The majority of posts don't contain links, so don't go there and waste time on preg junk if( strpos( $proto, '>>' ) !== false ) { $current_resno = $resno; $proto = preg_replace_callback( '#(>>[0-9]+|>>>/[a-z0-9]+/[a-z0-9+/-]*)#', 'auto_link_cb', $proto ); } return $proto; } function auto_link_cb( $post ) { global $current_resno; //var_dump($post); $is_inter = ( strpos( $post[0], '>>>/' ) === 0 ); $post = $post[0]; if( $is_inter ) { preg_match( '#>>>/([a-z0-9]+)/(.*)#', $post, $match ); return parse_interboard_link( $match[0], $match[1], $match[2] ); } else { $no = explode( '>>', $post ); return parse_intraboard_link( $post, $no[1], $current_resno ); } } function parse_intraboard_link( $post, $no, $resno ) { $full_link = $post; //$no = substr( $post, $i - $in_link_char, $in_link_char ); $resto = post_resto( $no ); if( $resto === false ) { $parsed_link = '' . $full_link . ''; return $parsed_link; } $ext = PHP_EXT2; $id = NEW_HTML ? "p$no" : "$no"; // linking to a reply or the OP in the same thread if ($resno && ($resno == $resto || $resno == $no)) { $parsed_link = ">>$no"; } // linking to an OP in another thread or from indexes elseif ($resto == 0) { $parsed_link = ">>$no"; } // linking to a reply in another thread or from indexes else { $parsed_link = ">>$no"; } return $parsed_link; } function parse_interboard_link( $post, $inter_board, $no ) { global $valid_boards; // make sure to account for > not > $full_link = $post; $inter_board = strtolower( $inter_board ); // Are we a board link? $is_board_link = ( $no == '' ); // ... to get resno! $resno = $no; $valid_rule = $inter_board == 'global' && strpos( $resno, 'rules' ) !== false; // Skip static links (boards, catalog) if ($is_board_link || $valid_rule || !is_numeric($resno) || !in_array($inter_board, boards_matching_arr()) ) { return $full_link; } // Valid board, now check post number $resto = other_board_resto( $inter_board, $resno ); if( $resto === false ) { // dead link $url = ''; } elseif( $resto ) { // different thread $url = '//boards.' . L::d($inter_board) . "/{$inter_board}/thread/$resto#p$resno"; } else { // same thread $url = '//boards.' . L::d($inter_board) . "/{$inter_board}/thread/$resno#p$resno"; } $disable = BOARD_DIR == 'mlp' && ( $inter_board == 'b' || $inter_board == 'co' ); if( !$url || $disable ) { $parsed_link = '' . $full_link . ''; } else { $parsed_link = "$full_link"; } return $parsed_link; } function trans_same_board_links( &$com ) { $match = '>>>/' . BOARD_DIR . '/'; $len = strlen( $match ); $i = stripos( $com, $match ); $boardlen = strlen( BOARD_DIR ) - 1; $dir = BOARD_DIR; $com .= '~'; while( isset( $com{$i} ) ) { if( is_numeric( $com{$i + $len} ) ) { // Match, replace out $com = substr_replace( $com, '>>', $i, $len ); } $i = stripos( $com, $match, $i + $len ); if( $i === false ) { $com = substr( $com, 0, strlen( $com ) - 1 ); return; } } } function auto_link_parser( $post, $resno ) { $post .= '<'; $i = 0; $in_link = false; $in_link_char = 0; $is_inter = false; $inter_found_board = false; $inter_board = ''; $inter_is_catalog = false; $is_intra = false; $gt_count = 0; $mbcl = 10; // forgot about dis links :( $dbg = ""; while( isset( $post{$i} ) ) { $seen_gt_this = false; $c = $post{$i}; if( !$in_link ) { // Not in a link, find > if( $c == '&' ) { if( $post{$i + 1} == 'g' && $post{$i + 2} == 't' && $post{$i + 3} == ';' ) { if( $gt_count < 3 ) $gt_count++; $i = $i + 4; continue; } } if( ( $c == '/' && $gt_count == 3 ) || ( $gt_count > 1 && is_numeric( $c ) ) ) { $in_link_char = 0; $in_link = true; $i--; // shift us back a char to get the right match... } $gt_count = 0; } else { // We can be sure we have a valid character for our link if( $in_link_char == 0 ) { if( $c == '/' ) { $is_inter = true; $is_intra = false; } else { $is_intra = true; $is_inter = false; } } if( $is_inter ) { if( $in_link_char == 0 ) { $in_link_char++; $i++; continue; } if( $in_link_char > $mbcl && $c != '/' && !$inter_found_board ) { // yup :( $in_link = false; $is_inter = false; $i++; continue; } if( !$inter_found_board && $c == '/' ) { $inter_board = substr( $post, $i - ( $in_link_char - 1 ), $in_link_char - 1 ); $inter_found_board = true; $in_link_char++; $i++; continue; } if( $inter_found_board ) { $match = ( ctype_alnum( $c ) || $c == '+' || $c == '/' || $c == '-' ); if( $match ) { $in_link_char++; $i++; continue; } } if( !$inter_found_board ) { $in_link_char++; $i++; continue; } parse_interboard_link( $post, $i, $in_link_char, $inter_board ); $is_inter = false; $in_link = false; $inter_found_board = false; } if( $is_intra ) { if( is_numeric( $c ) ) { $in_link_char++; } else { // reached the end parse_intraboard_link( $post, $i, $in_link_char, $resno ); $in_link = false; $is_intra = false; } } } $i++; } return substr( $post, 0, strlen( $post ) - 1 ) . $dbg; } /** * New version of check_blacklist() * $post must be an array with the following fields: * resto, filename, name, tripcode, password, 4pass_id */ function check_md5_blacklist($md5, $original_md5, $post, $dest) { if (!$md5) { return false; } $board = BOARD_DIR; if (DEFAULT_BURICHAN) { $ws_clause = " OR boardrestrict = '_ws_'"; } else { $ws_clause = ''; } $sql =<< 0) { $private_reason = "DMCA complaint from {$row['description']} (blacklist ID: {$row['id']})"; auto_ban_poster($ban_name, 3, 1, $private_reason, S_DMCABANREASON, true, $pwd, $pass_id); error_redirect(S_BANNED, 'https://www.' . L::d(BOARD_DIR) . '/banned'); } else { $query = "INSERT INTO user_actions (board,postno,ip,time,uploaded,action) VALUES ('%s', %d, %d, NOW(), 0, 'fail_dmca')"; mysql_global_call($query, $board, 0, $ip); } error(S_DMCAFAIL, $dest); } if ($row['quiet']) { show_post_successful_fake($resto); die(); } error(S_FAILEDUPLOAD, $dest); } function check_blacklist($post, $dest, $file_ext = '', $resto = 0, $pwd = null, $pass_id = null) { //if( has_level() ) return; $board = BOARD_DIR; if (DEFAULT_BURICHAN) { $ws_clause = " OR boardrestrict = '_ws_'"; } else { $ws_clause = ''; } $querystr = "SELECT SQL_NO_CACHE * FROM blacklist WHERE active=1 AND (boardrestrict='' or boardrestrict='$board'$ws_clause) AND (0 "; foreach( $post as $field => $contents ) { if( $contents ) { $contents = mysql_real_escape_string( html_entity_decode( $contents ) ); $querystr .= "OR (field='$field' AND contents='$contents') "; } } $querystr .= ") LIMIT 1"; $query = mysql_global_call( $querystr ); if( mysql_num_rows( $query ) == 0 ) return false; $row = mysql_fetch_assoc( $query ); $prvreason = "Blacklisted ${row['field']} - " . htmlspecialchars( $row['contents'] ); if ($row['field'] == 'md5') { $prvreason .= ' - Filename: ' . htmlspecialchars($post['filename']) . $file_ext; } if (!$row['ban']) { if (TEST_BOARD) { error( "Blacklisted: " . $prvreason, $dest ); } } // Auto-ban else if ($row['ban'] == '1') { auto_ban_poster($post['trip'] ? $post['nametrip'] : $post['name'], $row['banlength'], 1, $prvreason, $row['banreason'], false, $pwd, $pass_id); } // Show error (DMCA requests) else if ($row['ban'] == '2') { $ip = ip2long($_SERVER['REMOTE_ADDR']); $query = "SELECT ip FROM user_actions WHERE action = 'fail_dmca' AND ip = %d AND time >= DATE_SUB(NOW(), INTERVAL 1 DAY)"; $res = mysql_global_call($query, $ip); if ($res && mysql_num_rows($res) > 0) { $prvreason = "DMCA complaint from {$row['description']} (blacklist ID: {$row['id']})"; auto_ban_poster($post['trip'] ? $post['nametrip'] : $post['name'], 3, 1, $prvreason, S_DMCABANREASON, true, $pwd, $pass_id); error_redirect(S_BANNED, 'https://www.' . L::d(BOARD_DIR) . '/banned'); } else { $query = "INSERT INTO user_actions (board,postno,ip,time,uploaded,action) VALUES ('%s',%d,%d,NOW(),0,'fail_dmca')"; mysql_global_call($query, $board, 0, $ip); } error(S_DMCAFAIL, $dest); } /* { $ip = $_SERVER['REMOTE_ADDR']; quick_log_to("/www/perhost/blacklist.log", "IP $ip board /$board/: $prvreason"); } */ if ($row['quiet']) { show_post_successful_fake($resto); die(); } error(S_FAILEDUPLOAD, $dest); } // we've already failed the floodcheck, check if they're a repeat offender and ban them function check_fail_floodcheck($info) { $ip = ip2long($_SERVER['REMOTE_ADDR']); mysql_global_call("insert into user_actions (ip,board,action,time) values (%d,'%s','fail_floodcheck',now())", $ip, ''); $query = mysql_global_call("select count(*)>%d from user_actions where ip=%d and action='fail_floodcheck' and time >= subdate(now(), interval 1 hour)", LOGIN_FAIL_HOURLY, $ip); quick_log_to("/www/perhost/floodchecks.log", $info); if(mysql_result($query,0,0)) { auto_ban_poster("Anonymous", 1, 1, "got a flood check warning 5 times in an hour", "Sending an excessive number of server requests"); } } // word-wrap without touching things inside of tags function wordwrap2( $str, $cols, $cut ) { // if there's no runs of $cols non-space characters, wordwrap is a no-op if( mb_strlen( $str ) < $cols || !preg_match( '/[^ <>]{' . $cols . '}/', $str ) ) { return $str; } $sections = preg_split( '/[<>]/', $str ); $str = ''; for( $i = 0; $i < count( $sections ); $i++ ) { if( $i % 2 ) { // inside a tag $str .= '<' . $sections[$i] . '>'; } else { // outside a tag $words = explode( ' ', $sections[$i] );/* $exclude = array( 'http://', 'https://', 'www.' ); */ foreach( $words as &$word ) {/* foreach( $exclude as $match ) { if( stripos( $word, $match ) === 0 && stripos( $word, '4chan.org' ) !== false ) continue 2; }*/ $word = htmlspecialchars_decode( $word, ENT_QUOTES ); $word = utf8_wordwrap( $word, $cols, $cut, true ); $word = htmlspecialchars( $word, ENT_QUOTES ); } $str .= implode( ' ', $words ); } } return $str; } function logtime( $desc ) { static $run = -1; if( !PROFILING ) return; if( $run == -1 ) { $run = getmypid_cached(); } $board = BOARD_DIR; $time = microtime( true ); mysql_global_call( "INSERT INTO profiling_times VALUES ('$board',$run,$time,'$desc')" ); } function time_log($r) { if (TEST_BOARD && $_SERVER['HTTP_ACCEPT'] !== 'application/json') { echo "\n"; } } function is_bad_xff( $xff ) { if ($xff === '8.8.8.8' || $xff === '62.210.138.29' || $xff === '212.129.0.228') { return true; } list( $xffs ) = post_filter_get( "xffwhitelist" ); $ipnum = ip2long( $xff ); if( !$ipnum ) return true; // text in xff field return find_ipxff_in( 0, $ipnum, $xffs ); } function has_doubles( $id ) { if( $id % 1000 == 0 ) return false; $ones = $id % 10; $tens = ( $id / 10 ) % 10; return $ones == $tens; } function generate_uid($resto, $time, $ip = false) { if (DISP_ID_RANDOM) { $str = mt_rand(); } else { $str = !$ip ? $_SERVER["REMOTE_ADDR"] : $ip; if (DISP_ID_PER_THREAD) { $str .= $resto ? $resto : date( 'Ymd', $time ); } else { $str .= 'hats'; // we will put a hat on it to confuse people :) } } $salt = file_get_contents_cached( SALTFILE ); $hash = base64_encode( pack( "H*", sha1( $str . $salt ) ) ); return substr( $hash, 0, 8 ); } function parse_vip_capcode($capcode) { // Flood check $longip = ip2long($_SERVER['REMOTE_ADDR']); $query = <<= SUBDATE(NOW(), INTERVAL 1 HOUR) SQL; $res = mysql_global_call($query, $longip); if (!$res) { return false; } $count = mysql_fetch_row($res)[0]; if ($count >= 3) { return false; } // Now check the capcode list($_, $user_id, $user_key) = explode('!', $capcode, 3); if (!$user_id || !$user_key) { return false; } $query = "SELECT name, user_key FROM vip_capcodes WHERE active = 1 AND user_id = '%s' LIMIT 1"; $res = mysql_global_call($query, $user_id); if (!$res) { return false; } $user = mysql_fetch_assoc($res); if ($user && password_verify($user_key, $user['user_key'])) { $query = "UPDATE vip_capcodes SET last_used = %d, last_ip = '%s' WHERE user_id = '%s' LIMIT 1"; mysql_global_call($query, $_SERVER['REQUEST_TIME'], $_SERVER['REMOTE_ADDR'], $user_id); return $user['name']; } // Log the failure $query = <<getPwd(); if (!$pass) { error(S_GENERICERROR, $dest); } $resto = (int)$resto; // time $time = $_SERVER['REQUEST_TIME']; $tim = generate_tim(); $captcha_bypass_allow_credits = false; $memcached = null; if (isset($_POST['recaptcha_challenge_field'])) { error('Legacy captcha is no longer supported.'); } else if (isset($_POST['g-recaptcha-response'])) { if (CAPTCHA_TWISTER) { error('reCAPTCHA v2 is no longer supported.'); } // Recaptcha v2 start_auth_captcha(); if (!$captcha_bypass) { $_c_ret = end_recaptcha_verify(); } } else { if (valid_captcha_bypass() !== true) { $memcached = create_memcached_instance(); $_unsolved_count = 0; // Captcha bypass credits if ($resto && isset($_POST['t-challenge']) && $_POST['t-challenge'] === 'noop') { if (use_twister_captcha_credit($memcached, $host, $userpwd) === false) { error(S_CAPTCHATIMEOUT); } } // Captcha verification failed else if (is_twister_captcha_valid($memcached, $host, $userpwd, BOARD_DIR, $resto, $_unsolved_count) === false) { // Silent captcha failure for new suspicious users //$_bad_actor = spam_filter_is_bad_actor(); //$_threat_score = spam_filter_get_threat_score($_SERVER['HTTP_X_GEO_COUNTRY'], !$resto, true); if (isset($_SERVER['HTTP_X_BOT_SCORE'])) { $_bot_score = (int)$_SERVER['HTTP_X_BOT_SCORE']; } else { $_bot_score = 100; } if (!$userpwd || $userpwd->isUserKnownOrVerified(1440, 1) === false) { if ($_bot_score > 1 && $_bot_score < 80) { $_meta = spam_filter_format_http_headers(htmlspecialchars($com), $_SERVER['HTTP_X_GEO_COUNTRY'], $upfile_name, $_threat_score); if (isset($_POST['t-challenge']) && $_POST['t-response'] && isset($_COOKIE['_tcs'])) { log_failed_captcha($host, $userpwd, BOARD_DIR, $resto, true, $_meta); } show_post_successful_fake($resto, false); die(); } } error(S_BADCAPTCHA); } // Captcha verification succeeded else if ($_unsolved_count < 2 && CAPTCHA_ALLOW_BYPASS && spam_filter_is_bad_actor() === false) { $captcha_bypass_allow_credits = true; } } } /* if (spam_filter_is_bad_actor()) { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('log_bad_actor', BOARD_DIR, $resto, $host, 1, $_bot_headers); } */ if (PASS_POST_ONLY && !$captcha_bypass) { error(S_PASS_POST_ONLY); } // Validate 2FA if posting html if ((isset($_POST['html']) && $_POST['html']) && (has_level('manager') || has_flag('html') || has_flag('developer'))) { validate_otp(); $log_html_post = $log_mod_action = true; } $locked_time = $time; // check closed if( $resto ) { if( !$cchk = mysql_board_call( "select closed,sticky,undead,archived,sub,com from `" . SQLLOG . "` where no=" . $resto ) ) { echo S_SQLFAIL; } list( $closed, $sticky, $undead, $is_archived, $_thread_sub, $_thread_com ) = mysql_fetch_row( $cchk ); if ($is_archived) { error(S_MAYNOTREPLY, $upfile); } $is_undead_sticky = $sticky == 1 && $undead == 1; if( $closed == 1 && !has_level() ) error( S_MAYNOTREPLY, $upfile ); mysql_free_result( $cchk ); $sub = ''; } else { $is_undead_sticky = false; } $has_image = $upfile && file_exists( $upfile ); $md5 = null; $original_md5 = null; // MD5 before exif and other metadata stripping if( $has_image ) { if (UPLOAD_BOARD) { if( file_exists( IMG_DIR . $upfile_name ) ) error( "Filename already exists.", $upfile ); $dest = $upfile; $upfile_name = sanitize_text( $upfile_name ); if( !is_file( $dest ) ) error( S_FAILEDUPLOAD, $dest ); if ($upfile_name[0] === '.') { error('Error: Invalid filename (first character cannot be a period).', $dest); } $size = getimagesize( $dest ); if( !is_array( $size ) ) error( S_NOREC, $dest ); $W = $size[0]; $H = $size[1]; $fsize = filesize( $dest ); if( $fsize > MAX_KB * 1024 ) error( S_TOOLARGE, $dest ); if( $size[2] == 6 || $size[2] == 5 ) { error( S_FAILEDUPLOAD, $dest ); } switch( $size[2] ) { case 4 : case 13 : $ext = ".swf"; break; default : $ext = ".xxx"; error( S_FAILEDUPLOAD, $dest ); break; } time_log( "sfpi" ); rpc_task(); $len = strlen( $ext ); } else { // NOT upload board // check image limit if( $resto && !$sticky && !$undead && !has_level() ) { if( !$result = mysql_board_call( "SELECT COUNT(*) FROM `" . SQLLOG . "` WHERE archived = 0 AND resto=$resto AND fsize!=0 AND filedeleted=0" ) ) { echo S_SQLFAIL; } $countimgres = mysql_result( $result, 0, 0 ); if( $countimgres >= MAX_IMGRES && !has_level() ) error(S_MAXIMAGESREACHED, $upfile ); mysql_free_result( $result ); } //upload processing $dest = $upfile; // TODO: what does that preg_replace do? those are probably utf8 codes $upfile_name = sanitize_text( preg_replace('/\xe2\x80(\xae|\xad|\x8f|\x8e)/', '', $upfile_name) ); if (!is_file($dest)) { error(S_FAILEDUPLOAD, $dest); } // Use filesize() later as it's possible to trick jpegtrans to generate a much bigger file $fsize = $_FILES['upfile']['size']; if (!$fsize || $fsize > MAX_KB * 1024) { error( S_TOOLARGE, $dest ); } $webm_sar = null; // PDF processing if( ENABLE_PDF == 1 && strcasecmp( '.pdf', substr( $upfile_name, -4 ) ) == 0 ) { $ext = '.pdf'; $W = $H = 1; // run through ghostscript to check for validity if( pclose( popen( "/usr/local/bin/gs -q -dSAFER -dNOPAUSE -dBATCH -sDEVICE=nullpage $dest", 'w' ) ) ) { error( S_FAILEDUPLOAD, $dest ); } } // Webm / MP4 else if (ENABLE_WEBM && preg_match('/\.(webm|mp4)$/i', $upfile_name)) { if ($fsize > MAX_WEBM_FILESIZE * 1024) { error(S_TOOLARGE, $dest); } $original_md5 = md5_file($dest); $ext = '.' . strtolower(pathinfo($upfile_name, PATHINFO_EXTENSION)); //if ($ext == '.mp4' && BOARD_DIR != 'test') { // error(S_NOREC, $dest); //} $size = validate_webm($dest, $ext); $W = $size[0]; $H = $size[1]; $webm_sar = $size[2]; } // PNG, GIF, JPEG else { $size = getimagesize( $dest ); if( !is_array( $size ) ) { quick_log_to( "/www/perhost/bad-upload.log", "unrecognized file $upfile_name"); error( S_NOREC, $dest ); } $W = $size[0]; $H = $size[1]; switch( $size[2] ) { case 1 : $ext = ".gif"; break; case 2 : $ext = ".jpg"; break; case 3 : $ext = ".png"; break; case 4 : $ext = ".swf"; error( S_FAILEDUPLOAD, $dest ); break; case 5 : $ext = ".psd"; error( S_FAILEDUPLOAD, $dest ); break; case 6 : $ext = ".bmp"; error( S_FAILEDUPLOAD, $dest ); break; case 7 : $ext = ".tiff"; error( S_FAILEDUPLOAD, $dest ); break; case 8 : $ext = ".tiff"; error( S_FAILEDUPLOAD, $dest ); break; case 9 : $ext = ".jpc"; error( S_FAILEDUPLOAD, $dest ); break; case 10 : $ext = ".jp2"; error( S_FAILEDUPLOAD, $dest ); break; case 11 : $ext = ".jpx"; error( S_FAILEDUPLOAD, $dest ); break; case 13 : $ext = ".swf"; error( S_FAILEDUPLOAD, $dest ); break; default : $ext = ".xxx"; error( S_FAILEDUPLOAD, $dest ); break; } if (GIF_ONLY == 1 && $size[2] != 1 && $ext != '.webm') error(S_FAILEDUPLOAD, $dest); } // end PDF processing -else // This doesn't seem to use the $md5 arg if ($upfile_name === '') { error('Blank file names are not supported.', $dest); } time_log( "sfpi" ); rpc_task(); // Picture reduction if( !$resto ) { $maxw = MAX_W; $maxh = MAX_H; } else { $maxw = MAXR_W; $maxh = MAXR_H; } if( defined( 'MIN_W' ) && MIN_W > $W ) error( S_TOOSMALL, $dest ); if( defined( 'MIN_H' ) && MIN_H > $H ) error( S_TOOSMALL, $dest ); if( defined( 'MAX_DIMENSION' ) ) $maxdimension = MAX_DIMENSION; else $maxdimension = 5000; if( $W > $maxdimension || $H > $maxdimension ) { error( S_TOOLARGERES, $dest ); } elseif( $W > $maxw || $H > $maxh ) { $W2 = $maxw / $W; $H2 = $maxh / $H; ( $W2 < $H2 ) ? $key = $W2 : $key = $H2; $TN_W = ceil( $W * $key ) + 1; $TN_H = ceil( $H * $key ) + 1; } // Strip metadata, exif, comments and other embeddded extra data if ($ext === '.jpg') { // Embed detection is done later below if STRIP_EXIF is disabled if (STRIP_EXIF) { $original_md5 = md5_file($dest); if (strip_jpeg_exif($dest) === false) { error(S_FAILEDUPLOAD, $dest); } clearstatcache(true, $dest); } } else if ($ext === '.png') { $original_md5 = md5_file($dest); $_ret = strip_png_chunks($dest, MAX_KB * 1024); if ($_ret < 0) { if ($_ret === -2) { error('APNG format not supported.', $dest); } else { error(S_FAILEDUPLOAD, $dest); } } else if ($_ret > 0) { clearstatcache(true, $dest); } } else if ($ext === '.gif') { $original_md5 = md5_file($dest); $_ret = strip_gif_extra_data($dest, $fsize); if ($_ret < 0) { error(S_FAILEDUPLOAD, $dest); } clearstatcache(true, $dest); } // It should be safe to check the filesize now // clearstatcache() must be called if the file was modified $fsize = filesize($dest); if (!$fsize) { error(S_TOOLARGE, $dest); } else if ($ext === '.webm' && $fsize > MAX_WEBM_FILESIZE * 1024) { error(S_TOOLARGE, $dest); } else if ($fsize > MAX_KB * 1024) { error(S_TOOLARGE, $dest); } // Check for JPEG embedded data. jpegtran seems to remove unknown data // so this only needs to be done if STRIP_EXIF is disabled if (!STRIP_EXIF && $ext === '.jpg' && $fsize > 204800) { validate_jpeg_size($dest, $fsize); } } $insfile = preg_replace('/\.[a-z0-9]+$/i', '', $upfile_name); $md5 = md5_file( $dest ); $mes = $upfile_name . ' ' . S_UPGOOD; } if( $_FILES["upfile"]["error"] > 0 ) { if( $_FILES["upfile"]["error"] == UPLOAD_ERR_INI_SIZE ) error( S_TOOLARGE, $dest ); if( $_FILES["upfile"]["error"] == UPLOAD_ERR_FORM_SIZE ) error( S_TOOLARGE, $dest ); if( $_FILES["upfile"]["error"] == UPLOAD_ERR_PARTIAL ) error( S_FAILEDUPLOAD, $dest ); if( $_FILES["upfile"]["error"] == UPLOAD_ERR_CANT_WRITE ) error( S_FAILEDUPLOAD, $dest ); } if( $upfile_name && $_FILES["upfile"]["size"] == 0 ) { error( S_TOOLARGEORNONE, $dest ); } if( ENABLE_EXIF == 1 ) { $exif = htmlspecialchars( shell_exec( "/usr/local/bin/exiftags $dest" ) ); } $resto = (int)$resto; if( $resto ) { if( !mysql_result( mysql_board_call( "select count(no) from `" . SQLLOG . "` where root>0 and no=$resto" ), 0, 0 ) ) error( S_NOTHREADERR, $dest ); } $pass_is_bannable = !$userpwd->isNew(); // Most common errors checked, now check for post-block from ban requests if (BLOCK_ON_BR && !has_level()) { check_for_ban_request($host, $pass_is_bannable ? $pass : null); } // Standardize new character lines $com = str_replace( "\r\n", "\n", $com ); $com = str_replace( "\r", "\n", $com ); $comlim = has_level() ? MAX_COM_CHARS_AUTHED : MAX_COM_CHARS; $longlim = has_level() ? 255 : 100; if( mb_strlen( $com ) > $comlim ) error( S_TOOLONG, $dest ); if( strlen( $name ) > $longlim ) error( S_TOOLONG, $dest ); if( strlen( $email ) > $longlim ) error( S_TOOLONG, $dest ); if( strlen( $sub ) > $longlim ) error( S_TOOLONG, $dest ); if( strlen( $resto ) > 10 ) error( S_GENERICERROR, $dest ); if( strlen( $url ) > 10 ) error( S_GENERICERROR, $dest ); $sub = normalize_content( $sub ); // start of some attempt to get rid of *all* zero width bollocks if( BOARD_DIR != 'jp' && BOARD_DIR != 'a' && !SJIS_TAGS) { $com = normalize_content( $com ); $com = strip_zerowidth( $com ); } // strip no break spaces and soft hyphens $com = str_replace(array("\xC2\xAD", "\xC2\xA0"), '', $com); // name/subject too! $sub = strip_zerowidth( $sub ); $name = strip_zerowidth( $name ); // Strip unicode emoticons $name = strip_emoticons($name, SJIS_TAGS); if ($sub !== '') { $sub = strip_emoticons($sub, SJIS_TAGS); } if ($com !== '') { $com = strip_emoticons($com, SJIS_TAGS); } // strip out ltr junk from name $name = preg_replace( '#([\x{2000}-\x{200F}]|[\x{2028}-\x{202F}])#u', '', $name ); if ($sub !== '') { $sub = strip_fake_capcodes($sub); } if( !strlen( $name ) || preg_match( "/^[ | |]*$/", $name ) ) $name = ""; if( !strlen( $com ) || preg_match( "/^[ | |\t]*$/", $com ) ) $com = ""; if( !strlen( $sub ) || preg_match( "/^[ | |]*$/", $sub ) ) $sub = ""; //$name = str_replace( S_MANAGEMENT, "\"" . S_MANAGEMENT . "\"", $name ); //$name = str_replace( S_DELETION, "\"" . S_DELETION . "\"", $name ); // Remove intra spoilers if (SPOILERS && stripos($com, '[spoiler]') !== false) { //$com = preg_replace( '/\[spoiler\]\s+\[\/spoiler\]/', '', $com ); $com = preg_replace('/(\S)\[spoiler\](.*?)\[\/spoiler\](\S)/', '\\1\\2\\3', $com); } //lol /b/ $xff = get_request_xff(); //if( is_bad_xff( $xff ) ) $xff = ""; $youbi = array(S_SUN, S_MON, S_TUE, S_WED, S_THU, S_FRI, S_SAT); $yd = $youbi[date( "w", $time )]; if( SHOW_SECONDS == 1 ) { $now = date( "m/d/y", $time ) . "(" . (string)$yd . ")" . date( "H:i:s", $time ); } else { $now = date( "m/d/y", $time ) . "(" . (string)$yd . ")" . date( "H:i", $time ); } $c_name = $name; $c_email = $email; if (JANITOR_BOARD == 1) { $name = get_hashed_mod_name($_COOKIE['4chan_auser']); $email = ''; } // April 2023 //$_has_xa23_content = $com && preg_match('/^[^>]{8,}/m', $com) > 0; $com = preg_replace( '#>>>/' . BOARD_DIR . '/([0-9]+)#', '>>$1', $com ); $sub = sanitize_text( $sub ); $sub = preg_replace( "/[\r\n]/", "", $sub ); $sub = strip_private_unicode($sub); $url = sanitize_text( $url ); $url = preg_replace( "/[\r\n]/", "", $url ); $resto = sanitize_text( $resto ); $resto = preg_replace( "/[\r\n]/", "", $resto ); $com = sanitize_text( $com, 1, true ); $com = strip_private_unicode($com); if( FORCED_ANON == 1 ) { if( !has_level('admin') ) $name = S_ANONAME; $sub = ''; } if (UPLOAD_BOARD) { if( NO_TEXTONLY == 1 ) { if( !$resto && !$has_image ) error( S_NOPIC, $dest ); } else { if( !$resto && !$textonly && !$has_image ) error( S_NOPIC, $dest ); } } else { if (!TEXT_ONLY) { if( NO_TEXTONLY == 1 && (!has_level() || $email === '') ) { if( !$resto && !$has_image ) error( S_NOPIC, $dest ); } else { if( !$resto && !$textonly && !$has_image ) error( S_NOPIC, $dest ); } } if( REQUIRE_SUBJECT && !$resto && !strlen( $sub ) ) error( S_NOSUB, $dest ); } // Check for sage, nonoko and nonokosage $is_sage = false; if (stripos($email, 'sage') !== false) { $is_sage = true; $email = str_ireplace('sage', '', $email); } $email_lower = strtolower($email); if ($email_lower === 'nonoko') { $is_nonoko = true; } if( SPOILERS == 1 && $spoiler ) { $sub = "SPOILER<>$sub"; } if( !has_level() ) { $match = array(); if( substr_count( $com, "\n" ) > 6 ) preg_match_all( '#([^\n]+\n+)\1{5,}#', $com, $match ); if( !empty( $match[0] ) ) { foreach( $match[1] as $key => $var ) { //auto_ban_poster( $name, 0, 1, 'Matched the same string 5 times or more in 1 post seperated by newlines (Matched: ' . $var . ')', 'Please do not spam.' ); error( S_REJECTTEXTBAN ); } } } // FIXME sanitize_text() replaces repeated \n too, is this duplicate? // disable on code tag boards (we replace multiple brs instead) if (!CODE_TAGS && !SJIS_TAGS) { $com = preg_replace( "/\n(( | )*\n){3,}/", "\n", $com ); } if( !has_level() && substr_count( $com, "\n" ) > MAX_LINES ) error(S_TOOMANYLINES, $dest ); if( ENABLE_EXIF == 1 && $exif ) { //turn exif into a table $exiflines = explode( "\n", $exif ); $exif = ""; foreach( $exiflines as $exifline ) { list( $exiftag, $exifvalue ) = explode( ': ', $exifline ); if( $exifvalue != '' ) $exif .= ""; else $exif .= ""; } $exif .= '
    $exiftag$exifvalue
    $exiftag
    '; $exiftext .= '

    ' . sprintf(S_EXIF_TOGGLE, $tim) . '
    '; $exiftext .= "$exif"; } $name = preg_replace( "/[\r\n]/", "", $name ); $names = mb_convert_encoding($name, 'CP932', 'UTF-8'); // convert to Windows Japanese #kami //start new tripcode crap list ( $name ) = explode( "#", $name ); // Strip unicode point-of-interest and # characters if (preg_match('/[\x{2318}\x{ff03}\x{FE5F}]/u', $name)) { $name = preg_replace('/[\x{2318}\x{ff03}\x{FE5F}]/u', '', $name); } $name = normalize_content( $name ); $name = sanitize_text( $name ); $name = strip_private_unicode($name); if (preg_match('/^\s+$/', $name)) { $name = ''; } $name = str_replace('!', '', $name); if( preg_match( "/\#+$/", $names ) ) { $names = preg_replace( "/\#+$/", "", $names ); } if( preg_match( "/\#/", $names ) ) { $names = str_replace( "&#", "&&", htmlspecialchars( $names, ENT_COMPAT | ENT_HTML401, 'Shift_JIS' ) ); # otherwise HTML numeric entities screw up explode()! list ( $nametemp, $trip, $sectrip ) = str_replace( "&&", "&#", explode( "#", $names, 3 ) ); if ($sectrip != '') { $trip = ''; } $names = $nametemp; if( STRIP_TRIPCODE == 0 ) $name .= "
    "; if ($trip != "" && STRIP_TRIPCODE == 0) { $salt = strtr( preg_replace( "/[^\.-z]/", ".", substr( $trip . "H.", 1, 2 ) ), ":;<=>?@[\\]^_`", "ABCDEFGabcdef" ); $trip = substr( crypt( $trip, $salt ), -10 ); $name .= " !" . $trip; } if( $sectrip != "" && STRIP_TRIPCODE == 0 ) { $salt = file_get_contents_cached( SALTFILE ); $sha = base64_encode( pack( "H*", sha1( $sectrip . $salt ) ) ); $sha = substr( $sha, 0, 11 ); $name .= " !!" . $sha; } } //end new tripcode crap // Check the length of the name field again to prevent truncation in the middle of tripcode HTML if (strlen($name) > 255) { error(S_TOOLONG, $dest); } //Cookies $cookie_domain = '.' . L::d(BOARD_DIR); setrawcookie( "4chan_name", rawurlencode( $c_name ), $time + ( $c_name ? ( 7 * 24 * 3600 ) : -3600 ), '/', $cookie_domain ); if( !strlen( $name ) ) $name = S_ANONAME; if( !strlen( $com ) ) $com = S_ANOTEXT; if( !strlen( $sub ) ) $sub = S_ANOTITLE; /* since4pass */ if ($captcha_bypass && $passid && $email && strpos(" $email ", ' since4pass ') !== false) { $since4pass = get_since_4chan($passid); } else { $since4pass = 0; } // April 2024 /* if ($email) { $_xa24_since4pass = april_2024_parse_email($email); $since4pass = $_xa24_since4pass; } else { $_xa24_since4pass = false; } */ if (FORTUNE_TRIP == 1 && $email == 'fortune') { $fortunes = array("Bad Luck", "Average Luck", "Good Luck", "Excellent Luck", "Reply hazy, try again", "Godly Luck", "Very Bad Luck", "Outlook good", "Better not tell you now", "You will meet a dark handsome stranger", "キタ━━━━━━(゚∀゚)━━━━━━ !!!!", "( ´_ゝ`)フーン ", "Good news will come to you by mail"); // Christmas 2021 /* $fortunes = array("You're on the nice list!", "You're on the naughty list!", "Krampus is coming to your house!", "It's going to be a white Christmas!", "Merry Christmas!", "Happy Hanukkah!", "Happy Kwanzaa!", "Feliz Navidad!", "Happy Festivus!", "You're getting a lump of coal in your stocking!", "Blessed Yule!", "You're standing under the mistletoe!", "The poster above you has been very very naughty!", "You got an extra thick slice of fruitcake.", "You're on the Elf Watchlist.", "Your heart is two sizes too small.", "Your heart grew three sizes!", "Bah! Humbug."); */ $fortunenum = rand( 0, sizeof( $fortunes ) - 1 ); $fortcol = "#" . sprintf( "%02x%02x%02x", 127 + 127 * sin( 2 * M_PI * $fortunenum / sizeof( $fortunes ) ), 127 + 127 * sin( 2 * M_PI * $fortunenum / sizeof( $fortunes ) + 2 / 3 * M_PI ), 127 + 127 * sin( 2 * M_PI * $fortunenum / sizeof( $fortunes ) + 4 / 3 * M_PI ) ); $com .= "

    Your fortune: " . $fortunes[$fortunenum] . "
    "; } if (DICE_ROLL == 1) { if( $email ) { if( preg_match( "/dice[ +](\\d+)[ d+](\\d+)(([ +-]+?)(-?\\d+))?/", $email, $match ) ) { $dicetxt = S_DICE_PFX . ' '; $dicenum = min( 25, $match[1] ); $diceside = $match[2]; $diceaddexpr = $match[3]; $dicesign = $match[4]; $diceadd = intval( $match[5] ); for( $i = 0; $i < $dicenum; $i++ ) { $dicerand = mt_rand( 1, $diceside ); if( $i ) $dicetxt .= ", "; $dicetxt .= $dicerand; $dicesum += $dicerand; } if( $diceaddexpr ) { if( strpos( $dicesign, "-" ) > 0 ) $diceadd *= -1; $diceadd_formatted = ( $diceadd >= 0 ? " + " : " - " ) . abs( $diceadd ); $dicetxt .= $diceadd_formatted; $dicesum += $diceadd; } if ($dicenum > 1) { $dicetxt .= " = $dicesum"; } $dicetxt .= " ({$dicenum}d{$diceside}" . ($diceaddexpr ? $diceadd_formatted : "") . ")

    "; $com = "$dicetxt" . $com; } } } // fixme: this is needed for bypassing the r9k filter. $email gets reset below. $options_field = $email; $emails = $email; $admin_highlight = false; $uid = null; $capcode = 'none'; $delay_refresh = false; if (strpos($email, 'capcode_') === 0) { // are we trying to capcode? if (!has_level('admin') && !has_flag('capcodename')) { $name = S_ANONAME; } // Only pass and authed users can use VIP capcodes if ($captcha_bypass) { $capcode = parse_capcode($email, $name); } else { $capcode = parse_capcode($email); } $ma = ( $capcode == 'admin_highlight') ? 'admin' : $capcode; $ma = ucfirst( $ma ); if( DISP_ID == 1 && $ma != 'None' ) { $uid = $ma; } if( $ma != 'None' && STRIP_EXIF_ON_CAPCODE && $ext == ".jpg" ) { system( "/usr/local/bin/jpegtran -copy none -outfile '$dest' '$dest'" ); $md5 = md5_file( $dest ); } if ($capcode !== 'none') { $log_mod_action = $log_capcode_post = true; } setcookie('options', $email, $time + (7 * 24 * 3600), '/', $cookie_domain); } else if (isset($_COOKIE['options'])) { setcookie('options', null, $time -3600, '/', $cookie_domain); } $email = ''; $nameparts = explode( '
    !', $name ); time_log( "trip" ); //logtime( "starting autoban checks" ); /** * Ban check step */ $ban_fields = array(); if ($pass_is_bannable) { $ban_fields['password'] = $pass; } /* if ($nameparts[1]) { $ban_fields['tripcode'] = $nameparts[1]; } */ if ($passid) { $ban_fields['4pass_id'] = $passid; } $user_is_banned = check_for_ban($host, $ban_fields, $resto, $userpwd && $userpwd->verifiedLevel()); if ($user_is_banned) { if (!$captcha_bypass) { mysql_global_call("INSERT INTO user_actions (board,ip,time,action) VALUES ('%s',%d,from_unixtime(%d),'%s')", BOARD_DIR, ip2long($host), $time, 'is_banned'); } $redirect = 'https://www.' . L::d(BOARD_DIR) . '/banned'; if ($user_is_banned == 1) { // Banned error_redirect(S_BANNED, $redirect); } else if ($user_is_banned == 2) { // Warned error_redirect(S_WARNED, $redirect); } else { // Ban evasion error("Error: $user_is_banned"); } } /** * Validate the maximum number of allowed threads per user */ if (!$resto) { validate_user_thread_limit( $_SERVER['REMOTE_ADDR'], isset($ban_fields['password']) ? $pass : null, $passid ); } /* * Embedded data detection and banned re-post block */ if ($has_image) { // Check if the file contains embedded data. if (false && CLEANUP_UPLOADS) { // Update the size if the file was modified if (cleanup_uploaded_file($dest, $ext)) { clearstatcache(true, $dest); $fsize = filesize($dest); if (!$fsize) { error(S_TOOLARGE, $dest); } $md5 = md5_file($dest); } } // Check if the uploaded file should be blocked because of previous bans if (check_for_banned_upload($md5)) { //log_spam_filter_trigger('block_banned_reupload', BOARD_DIR, $resto, $host, 1, $md5); error(S_FAILEDUPLOAD, $dest); } } // --- $autosage = false; // See bellow wordwrap2() if (strpos($com, 'rep?~') !== false) { $com = str_replace(array('~?rep?~', '~?erep?~'), '', $com); } if (!has_level() || $capcode === 'none' || BOARD_DIR == 'test') { $autosage = spam_filter_post_content_new(BOARD_DIR, $resto, $com, $sub, $name, $upfile_name, $pass, ($captcha_bypass && $passid) ? $passid : null); spam_filter_post_ip($userpwd, $resto, $has_image); } if (!$capcode || $capcode == 'none') { check_md5_blacklist($md5, $original_md5, [ 'resto' => $resto, 'name' => $nameparts[0], 'tripcode' => $nameparts[1], 'filename' => "$insfile$ext", 'password' => $pass, '4pass_id' => ($captcha_bypass && $passid) ? $passid : null ], $dest); } spam_filter_post_trip( $name, $trip, $dest ); time_log( "ab" ); // Only process linebreaks for non-html posts if (!$log_html_post) { $com = nl2br($com, false); $com = str_replace("\n", "", $com); //\n is erased } $com .= $exiftext; // must be done after spam filter, since it has a javascript: link if (SJIS_TAGS) { $com = sjis_parse($com); } if( SPOILERS == 1 ) { $com = spoiler_parse( $com ); if (stripos( $com, '') !== false) { $com = preg_replace('/(\s|
    |(?R))*<\/s>/', '', $com); //$com = preg_replace('/(\S)(.*?)<\/s>(\S)/', '\\1\\2\\3', $com); } } /* if( JSMATH == 1 ) { $com = jsmath_parse( $com ); } */ if( CODE_TAGS ) { $com = preg_replace( '#(\[code\](.{0,6})\[\/code\])#', '\\2', $com ); $com = code_parse( $com ); $com = str_replace( '

    ', '
    ', $com );
    		$com = preg_replace( '#(
    ){4,}#', '


    ', $com ); } if (OP_MARKUP && (!$resto || is_poster_op($host, $pass, $resto))) { $com = parse_op_markup($com); } // pull this down to here to get rid of any shenanigans if (!$resto) { // new threads require subject or comment if ($sub === '' && ($com === '' || preg_match('/^(?:
    |\s)+$/', $com))) { if ($options_field === '' || !$has_image || !has_level()) { error(S_NOTEXT_OP, $dest); } } else if (TEXT_ONLY && $sub === '') { error(S_NOSUB, $dest); } } else if (!$has_image && ($com === '' || preg_match('/^(?:
    |\s)+$/', $com))) { // replies without image error(S_NOTEXT, $dest); } if( WORD_FILT && $word_filters_enabled ) { $com = word_filter( $com, "com" ); if( $sub ) $sub = word_filter( $sub, "sub" ); $namearr = explode( ' ', $name ); if( strstr( $name, ' ' ) ) { $nametrip = ' ' . $namearr[1]; } else { $nametrip = ""; } if( $namearr[0] != S_ANONAME ) $name = word_filter( $namearr[0], "name" ) . $nametrip; } /*if( $html != 1 || ( !has_level('manager') ) ) { $com = wordwrap2( $com, 35, "{{w_br}}" ); }*/ // April 2022 /* if (strpos($com, ':') !== false) { $com = april_process_post_emotes($com, $_COOKIE['xa_sid']); } */ $com = normalize_and_linkify($com); //$html = isset( $_POST['html'] ) && $_POST['html'] == 1; $html = 0; if( (!has_level('manager') && !has_flag('html')) || $html != 1 ) { $com = wordwrap2( $com, 35, "{{w_br}}" ); } $com = preg_replace( '#(>>>/[a-z0-9]+/[^ <$]*|>>[0-9]+)#', '~?rep?~\\1~?erep?~', $com ); $com = preg_replace( "!(^|r>|r> )(>[^<]*)!", "\\1\\2", $com ); $com = preg_replace( '#~?rep?~(.+?)~?erep?~#', '\\1', $com ); $com = str_replace( array( '~?rep?~', '~?erep?~' ), '', $com ); $com = str_replace( '{{w_br}}', '', $com ); $admin_style = "padding: 5px;margin-left: .5em;border-color: #faa;border: 2px dashed rgba(255,0,0,.1);border-radius: 2px"; //FIXME make it easier to edit css so this can go in it if( $admin_highlight ) { $com = "
    $com
    "; } if( DISP_ID == 1 && !$uid ) { $uid = $is_sage && !META_BOARD && !DISP_ID_NO_HEAVEN ? "Heaven" : generate_uid( $resto, $time ); } // Validate comment for OPs by regex if (!$resto && COM_REGEX) { if (preg_match(COM_REGEX, $com) === 0) { error(S_INVALID_COM); } } //post text is now completely created, thumbnail not if( !$silent_reject ) { //logtime( "Before flood check" ); $may_flood = has_level( 'janitor' ); if (!$may_flood || (!has_level() && (META_BOARD || $_POST['name'] != ''))) { if( $com ) { // Check for duplicate comments $query = "select sql_no_cache max(time) from `%s` where com='%s' " . "and host='%s' " . "and time>%d"; $result = mysql_board_call( $query, SQLLOG, $com, $host, $time - RENZOKU_DUPE ); if( $ltime = mysql_result( $result, 0, 0 ) ) { //check_fail_floodcheck($com); $str = sprintf(S_RENZOKU_DUP, sec2hms( ( $ltime + RENZOKU_DUPE ) - $time, false, true ) ); error( $str, $dest ); } mysql_free_result( $result ); } /** * Posting cooldowns */ if (!$resto) { /** * New threads */ $query = "select max(time) from `%s` where time>%d " . "and host='%s' and root>0"; //root>0 == non-sticky $result = mysql_board_call( $query, SQLLOG, ( $time - RENZOKU3 ), $host ); if( $ltime = mysql_result( $result, 0, 0 ) ) { $str = sprintf(S_RENZOKU3, sec2hms( ( $ltime + RENZOKU3 ) - $time, false, true ) ); error( $str, $dest ); } mysql_free_result( $result ); // Cross-board cooldown $query = "SELECT 1 FROM user_actions WHERE ip = %d AND action = 'new_thread' AND board != '%s' AND time >= DATE_SUB(NOW(), INTERVAL 5 MINUTE)"; $result = mysql_global_call($query, ip2long($host), BOARD_DIR); if (mysql_num_rows($result) > 0) { error( S_RENZOKU3, $dest ); // You must wait longer before posting another thread } } else { /** * Replies * Pass users have lower cooldowns */ // Check for same image flood first if ($has_image && $resto) { $query = "SELECT time FROM `%s` WHERE host = '%s' AND md5 = '%s' AND resto != %d ORDER BY no DESC LIMIT 1"; $result = mysql_board_call($query, SQLLOG, $host, $md5, $resto); if ($flood_row = mysql_fetch_assoc($result)) { $last_time = (int)$flood_row['time']; $cooldown = RENZOKU_DUPE; $cooldown_error = S_RENZOKU2_DUP; if ($captcha_bypass) { $cooldown = ceil((int)$cooldown / 2); } else { $cooldown_error .= S_RENZOKU_PASS; } if ($last_time > $time - $cooldown) { error(sprintf($cooldown_error, sec2hms($last_time + $cooldown - $time, false, true)), $dest); } } } // Now the standard cooldown $query = "SELECT time, resto, fsize FROM `%s` WHERE host = '%s' AND resto > 0 ORDER BY no DESC LIMIT 1"; $result = mysql_board_call($query, SQLLOG, $host); if ($flood_row = mysql_fetch_assoc($result)) { $last_time = (int)$flood_row['time']; if ($has_image) { $cooldown = RENZOKU2; $cooldown_error = S_RENZOKU2; } else { $cooldown = RENZOKU; $cooldown_error = S_RENZOKU; } if ($captcha_bypass) { $cooldown = ceil((int)$cooldown / 2); } else { $cooldown_error .= S_RENZOKU_PASS; } if ($last_time > $time - $cooldown) { error(sprintf($cooldown_error, sec2hms($last_time + $cooldown - $time, false, true)), $dest); } } } /* if (SAVE_XFF == 1 && $xff) { // Check for multiple ips with same xff $result = mysql_global_call( "select count(distinct ip)>2 from xff where xff='%s' and is_live=1", $xff ); if( mysql_result( $result, 0, 0 ) ) { auto_ban_poster( $name, 14, 1, "Detected 3 proxies for same IP", "Proxy/Tor exit node." ); error( S_GENERICERROR, $dest ); } // Check for multiple xffs with same ip? } */ // Check for OP bump limiting if ($resto && RENZOKU_OP) { $query = 'SELECT host, time FROM `%s` WHERE no = %d'; $query = mysql_board_call($query, SQLLOG, $resto); if ($query) { $result = mysql_fetch_assoc($query); // Poster is OP if ($result && $result['host'] === $host) { // OP can only bump his thread RENZOKU_OP_TIME seconds after its creation if ($result['time'] > ($time - RENZOKU_OP_TIME)) { $is_sage = 1; } // OP can only bump his thread every RENZOKU_OP_TIME2 seconds else { $query2 = "SELECT time FROM `%s` WHERE host = '%s' AND resto = %d ORDER BY no DESC LIMIT 1"; $query2 = mysql_board_call($query2, SQLLOG, $host, $resto); if ($query2) { $result = mysql_fetch_assoc($query2); if ($result && $result['time'] > ($time - RENZOKU_OP_TIME2)) { $is_sage = 1; } } mysql_free_result($query2); } } } mysql_free_result($query); } } // Minimal cooldowns for authed users (3s) if ($may_flood) { $query = "SELECT time FROM `%s` WHERE host = '%s' ORDER BY no DESC LIMIT 1"; $result = mysql_board_call($query, SQLLOG, $host); if ($flood_row = mysql_fetch_assoc($result)) { $last_time = (int)$flood_row['time']; $cooldown = 5; if ($last_time > $time - $cooldown) { error(sprintf(S_RENZOKU, sec2hms($last_time + $cooldown - $time, false, true)), $dest); } } } time_log( "fc" ); $tensorchan_score = 0; // thumbnail $image_path = ""; $m_img = false; if ($has_image) { if( USE_THUMB && !UPLOAD_BOARD ) { // Detect and block NSFW content $_need_inference = tensorchan_is_needed($userpwd, $resto, $W, $H, $ext); if ($_need_inference) { $_tensor_png = false; } else { $_tensor_png = null; } $ret = make_thumb( $dest, $tim, $ext, $resto, $TN_W, $TN_H, $tmd5, $webm_sar, $_tensor_png); if (!$ret && $ext != ".pdf") { error(S_IMGFAIL, $dest); } if ($_need_inference && $_tensor_png) { $tensorchan_score = tensorchan_check_nsfw($_tensor_png); unset($_tensor_png); } } $name_part = UPLOAD_BOARD ? $insfile : $tim; $image_path = IMG_DIR . $name_part . $ext; if( move_uploaded_file( $dest, $image_path ) === false ) { error( S_FAILEDUPLOAD, $dest ); } chmod( $image_path, 0664 ); if (MOBILE_IMG_RESIZE) { $m_img = resize_mobile_image($image_path, $W, $H, $fsize, $tim, $ext); } // Oekaki if (ENABLE_PAINTERJS) { // Replays if (ENABLE_OEKAKI_REPLAYS) { $oe_replay_path = null; if (isset($_FILES['oe_replay']) && $_FILES['oe_replay']['name'] === 'tegaki.tgkr' && $insfile === 'tegaki') { if (oekaki_validate_replay($_FILES['oe_replay']) === true) { $oe_replay_path = IMG_DIR . $tim . '.tgkr'; if (move_uploaded_file($_FILES['oe_replay']['tmp_name'], $oe_replay_path) === false) { error(S_FAILEDUPLOAD); } chmod($oe_replay_path, 0664); } } // Oekaki meta if (isset($_POST['oe_time'])) { if (isset($_POST['oe_src']) && $resto) { $oe_src_pid = oekaki_get_valid_src_pid($_POST['oe_src'], BOARD_DIR, $resto); } else { $oe_src_pid = null; } $com .= oekaki_format_info( $_POST['oe_time'], $oe_replay_path ? $tim : null, $oe_src_pid ); } } } } //logtime( "Thumbnail created" ); time_log( "t" ); // Infrequent flood check (dupe image) if( $has_image && (!$capcode || $capcode === 'none')) { if ($resto) { $result = mysql_board_call("SELECT sql_no_cache `no`,`resto` FROM `" . SQLLOG . "` WHERE archived = 0 and (resto = %d OR no = %d) AND `md5`='%s' AND filedeleted=0 limit 1", $resto, $resto, $md5); } else { $result = mysql_board_call("SELECT sql_no_cache `no`,`resto` FROM `" . SQLLOG . "` WHERE archived = 0 AND resto = 0 AND `md5`='%s' AND filedeleted=0 limit 1", $md5); } if( mysql_num_rows( $result ) ) { list( $dupeno, $duperesto ) = mysql_fetch_row( $result ); if( !$duperesto ) $duperesto = $dupeno; error( '' . S_DUPE . ' here.', $dest ); } if ($resto && MAX_IMG_REPOST_COUNT > 0) { $_query = 'SELECT COUNT(*) FROM `' . SQLLOG . "` WHERE archived = 0 AND resto != 0 AND `md5` = '%s' AND filedeleted = 0"; $result = mysql_board_call($_query, $md5); if ($result) { $_count = (int)mysql_fetch_row($result)[0]; if ($_count >= MAX_IMG_REPOST_COUNT) { error(S_DUPE); } } } if ( defined('SQLLOGMD5') ) { // TODO: There's a race here. This should just be INSERT and check for failure! $result = mysql_board_call("SELECT sql_no_cache * FROM `%s` WHERE md5='%s' AND now > DATE_SUB(NOW(), INTERVAL 1 DAY) limit 1", SQLLOGMD5, $md5); if ( mysql_num_rows( $result ) ) { list( $dc_now, $dc_filename, $dc_md5p ) = mysql_fetch_row( $result ); if( $dc_now ) { error('Error: You must wait longer before reposting this file.', $dest ); } } } } $rootpredicate = $resto ? "0" : "now()"; // ROBOT9000 if (defined('ROBOT9000') && ROBOT9000) { // Logged in uses can bypass r9k by using the "bypass_r9k" command in the Options field. // Capcoded posts always bypass r9k. if (($options_field !== 'bypass_r9k' || !has_level('janitor')) && $capcode === 'none') { require_once 'plugins/robot9000.php'; $r9k_status = r9k_process($com, $md5, ip2long($host)); if ($r9k_status !== R9K_OK) { error($r9k_status, $dest ); } } } // FIX ME, comments with html get truncated and break the layout, but only mods and janitors can bypass the regular limits if (strlen($com) > 65536) { error(S_TOOLONG, $dest); } //logtime( "Before insertion" ); //find sticky & autosage // auto-sticky //$sticky = false; // autosagin is now done in spam_filter_post_content //$autosage = spam_filter_should_autosage( $com, $sub, $name, $fsize, $resto, $W, $H, $dest, $insertid ); //old auto-sticky code -- disabled // if(defined('AUTOSTICKY') && AUTOSTICKY) { // $autosticky = preg_split("/,\s*/", AUTOSTICKY); // if($resto == 0) { // if($insertid % 1000000 == 0 || in_array($insertid,$autosticky)) // $sticky = true; // } // } $flag_cols = ""; $flag_vals = ""; if( $captcha_bypass ) { $flag_cols .= ',4pass_id'; $flag_vals .= ",'" . $passid . "'"; } if ($since4pass) { $flag_cols .= ',since4pass'; $flag_vals .= ",$since4pass"; } /* if( $sticky ) { $flag_cols .= ",sticky"; $flag_vals .= ",1"; } */ //permasage just means "is sage" for replies if( $resto ? $is_sage : $autosage ) { $flag_cols .= ",permasage"; $flag_vals .= ",1"; } if( $capcode ) { $flag_cols .= ',capcode'; $flag_vals .= ",'$capcode'"; } if ($m_img) { $flag_cols .= ',m_img'; $flag_vals .= ",1"; } //$country = geoip_country_code_by_addr( $_SERVER['REMOTE_ADDR'] ); //if( !$country ) $country = 'XX'; $geo_data = GeoIP2::get_country($_SERVER['REMOTE_ADDR']); if ($geo_data && isset($geo_data['country_code'])) { $country = $geo_data['country_code']; // FIXME: football cups /* if (BOARD_DIR === 'sp' && $country === 'GB' && isset($geo_data['sub_code'])) { if ($geo_data['sub_code'] === 'WLS') { $country = 'XW'; } else if ($geo_data['sub_code'] === 'SCT') { $country = 'XS'; } else if ($geo_data['sub_code'] !== 'NIR') { $country = 'XE'; } } */ } else { $country = 'XX'; } // User Agent ID $browser_id = spam_filter_get_browser_id(); // Request Signature $_req_sig = spam_filter_get_req_sig(); /** * Flood checks */ $_threat_score = 0; if (!$captcha_bypass) { $_pwd_known = $userpwd && $userpwd->isUserKnownOrVerified(360); $_pwd_trusted = $userpwd && ($userpwd->postCount() > 10 || $userpwd->verifiedLevel()); $_pwd_verified = $userpwd && $userpwd->verifiedLevel(); if (!$_pwd_known) { $_threat_score = spam_filter_get_threat_score($country, !$resto, true); } // Sample the user agent of known users if (false && $userpwd && $userpwd->isUserKnownOrVerified(4320) && $userpwd->postCount() > 10) { if (mt_rand(0, 9999) < 2000) { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('log_safe_req', BOARD_DIR, $resto, $host, 1, $_bot_headers); } } if ($userpwd && !$userpwd->verifiedLevel() && $userpwd->postCount() > 0 && $userpwd->pwdLifetime() < 86400 && $userpwd->maskChanged()) { log_spam_filter_trigger('log_mask_changed', BOARD_DIR, $resto, $host, 1); } if (!$_pwd_known && ($_threat_score > 0.31)) { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('block_txt_threat', BOARD_DIR, $resto, $host, 1, $_bot_headers); show_post_successful_fake($resto); return; } if (false && !$_pwd_known && $resto == 18361689 && BOARD_DIR === 'fa' && mt_rand(0, 9) >= 1 && $country != 'US') { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('block_other', BOARD_DIR, $resto, $host, 1, $_bot_headers); error(S_IPRANGE_BLOCKED . ' ' . S_IPRANGE_BLOCKED_TEMP . S_IPRANGE_BLOCKED_L1); } if (!$_pwd_trusted && $resto && $has_image && BOARD_DIR === 'g' && strpos($_thread_sub, '/aicg/') !== false) { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('block_aicg', BOARD_DIR, $resto, $host, 1, $_bot_headers); error(S_IPRANGE_BLOCKED_IMG . ' ' . S_IPRANGE_BLOCKED_TEMP . S_IPRANGE_BLOCKED_L1); } if (!$_pwd_trusted && $resto && $has_image && BOARD_DIR === 'vg' && strpos($_thread_com, '/lolg/') !== false) { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('block_lolg', BOARD_DIR, $resto, $host, 1, $_bot_headers); error(S_IPRANGE_BLOCKED_IMG . ' ' . S_IPRANGE_BLOCKED_TEMP . S_IPRANGE_BLOCKED_L1); //show_post_successful_fake($resto); //return; } if (!$_pwd_trusted && $resto && $has_image && BOARD_DIR === 'vg' && strpos($_thread_com, '/overwatch') !== false) { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('block_owg', BOARD_DIR, $resto, $host, 1, $_bot_headers); error(S_IPRANGE_BLOCKED_IMG . ' ' . S_IPRANGE_BLOCKED_TEMP . S_IPRANGE_BLOCKED_L1); //show_post_successful_fake($resto); //return; } if (false && !$_pwd_trusted && $resto && $has_image && BOARD_DIR === 'fa' && strpos($_thread_sub, 'Workwear General') !== false) { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('block_denim', BOARD_DIR, $resto, $host, 1, $_bot_headers); error(S_IPRANGE_BLOCKED_IMG . ' ' . S_IPRANGE_BLOCKED_TEMP . S_IPRANGE_BLOCKED_L1); //show_post_successful_fake($resto); //return; } if (!$_pwd_known && $resto && $has_image && BOARD_DIR === 'vg' && strpos($_thread_sub, '/bag/') !== false && $browser_id === '04d2237a2') { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('block_bag', BOARD_DIR, $resto, $host, 1, $_bot_headers); error(S_IPRANGE_BLOCKED_IMG . ' ' . S_IPRANGE_BLOCKED_TEMP . S_IPRANGE_BLOCKED_L1); } if (false && !$_pwd_known && !$resto && (BOARD_DIR === 'co' || BOARD_DIR === 'a') && $country !== 'XX' && $browser_id === '02b99990d' && ($country == 'GB' || $country == 'DE' || $country == 'AU' || strpos($_COOKIE['_tcs'], $_SERVER['HTTP_X_TIMEZONE']) === false)) { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('block_peridot', BOARD_DIR, $resto, $host, 1, $_bot_headers); error(S_IPRANGE_BLOCKED_IMG . ' ' . S_IPRANGE_BLOCKED_TEMP . S_IPRANGE_BLOCKED_L1); } if (!$_pwd_trusted && $resto && $has_image && BOARD_DIR === 'vg' && strpos($_thread_sub, 'granblue') !== false) { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('block_gbfg', BOARD_DIR, $resto, $host, 1, $_bot_headers); error(S_IPRANGE_BLOCKED_IMG . ' ' . S_IPRANGE_BLOCKED_TEMP . S_IPRANGE_BLOCKED_L1); //show_post_successful_fake($resto); //return; } if (!$_pwd_known && $resto && $has_image && BOARD_DIR === 'v' && strpos($_thread_sub, 'gamesdonequick') !== false && $_threat_score >= 0.09 && mt_rand(0, 9) >= 1) { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('block_adgq', BOARD_DIR, $resto, $host, 1, $_bot_headers); show_post_successful_fake($resto); return; } if (!$_pwd_known && $resto && $has_image && BOARD_DIR === 'vg' && strpos($_thread_sub, '/zzz/') !== false && $_threat_score >= 0.09 && mt_rand(0, 9) >= 1) { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('block_zzz', BOARD_DIR, $resto, $host, 1, $_bot_headers); show_post_successful_fake($resto); return; } if (!$_pwd_verified && $resto && $has_image && BOARD_DIR === 'vg' && strpos($_thread_sub, '/funkg/') !== false && $_threat_score >= 0.09) { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('block_funkg', BOARD_DIR, $resto, $host, 1, $_bot_headers); show_post_successful_fake($resto); return; } if (!$has_image) { if (!$_pwd_verified && $_threat_score >= 0.09 && mt_rand(0, 9) >= 5 && BOARD_DIR !== 'f') { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('block_pub_prox', BOARD_DIR, $resto, $host, 1, $_bot_headers); show_post_successful_fake($resto); return; } } else { if (!$resto) { $_thres = 3; } else { $_thres = 4; } if (!$_pwd_verified && $_threat_score >= 0.09 && mt_rand(0, 9) >= $_thres && BOARD_DIR !== 'f') { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('block_pub_prox', BOARD_DIR, $resto, $host, 1, $_bot_headers); if (mt_rand(0, 1)) { error(S_IPRANGE_BLOCKED_IMG . ' ' . S_IPRANGE_BLOCKED_TEMP . S_IPRANGE_BLOCKED_L1); } else { show_post_successful_fake($resto); return; } } } if (!$_pwd_known && $resto && $has_image && BOARD_DIR === 'vg' && $country === 'US' && isset($_COOKIE['_tcs']) && strpos($_COOKIE['_tcs'], '.America/Bogota.') !== false) { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('block_bag_scat', BOARD_DIR, $resto, $host, 1, $_bot_headers); show_post_successful_fake($resto); return; } // FLOOD CHECK $flood_status = 0;//spam_filter_is_post_flood($host, BOARD_DIR, $resto, null); if ($flood_status === 3) { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('block_flood_check', BOARD_DIR, $resto, $host, $flood_status, $_bot_headers); // Raw thread flood if ($flood_status === 3) { error(S_FAILEDUPLOAD); } else { show_post_successful_fake($resto); return; } } // Check Cloudflare's bot score and block using the lenient rangeban message if ($userpwd && !$userpwd->isUserKnownOrVerified(1440) && isset($_SERVER['HTTP_X_BOT_SCORE'])) { if (spam_filter_is_pwd_blocked($userpwd->getPwd(), 'block_bm_bot', 24)) { log_spam_filter_trigger('block_bm_bot_pwd', BOARD_DIR, $resto, $host, $_SERVER['HTTP_X_BOT_SCORE']); error(S_IPRANGE_BLOCKED . ' ' . S_IPRANGE_BLOCKED_TEMP . S_IPRANGE_BLOCKED_L1); } if (spam_filter_is_likely_automated($memcached)) { $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); write_to_event_log('block_bm_bot', $host, [ 'pwd' => $userpwd->getPwd(), 'arg_num' => $_SERVER['HTTP_X_BOT_SCORE'], 'board' => BOARD_DIR, 'thread_id' => $resto, 'meta' => $_bot_headers ]); error(S_IPRANGE_BLOCKED . ' ' . S_IPRANGE_BLOCKED_TEMP . S_IPRANGE_BLOCKED_L1); } } // If the country has changed, log the pwd and then block it for the next 24h if ($userpwd && !$userpwd->verifiedLevel() && $userpwd->postCount() < 20) { if (spam_filter_has_country_changed($userpwd->getPwd())) { log_spam_filter_trigger('block_country_changed', BOARD_DIR, $resto, $host, 1); error(S_IPRANGE_BLOCKED . ' ' . S_IPRANGE_BLOCKED_TEMP . S_IPRANGE_BLOCKED_L1); } if ($userpwd->envChanged()) { write_to_event_log('country_changed', $host, [ 'pwd' => $userpwd->getPwd(), 'arg_str' => $country, 'board' => BOARD_DIR, 'thread_id' => $resto ]); log_spam_filter_trigger('block_country_changed', BOARD_DIR, $resto, $host, 1); error(S_IPRANGE_BLOCKED . ' ' . S_IPRANGE_BLOCKED_TEMP . S_IPRANGE_BLOCKED_L1); } } } /** * Custom flag selection */ if (ENABLE_BOARD_FLAGS) { if ($_POST['flag'] === '0' || !isset($board_flags_array[$_POST['flag']])) { $board_flag_code = ''; // FIXME: remove this eventually as we use localStorage now if (isset($_COOKIE['4chan_flag'])) { setcookie('4chan_flag', '', $time - 3600, '/', $cookie_domain); } } else { $board_flag_code = mysql_real_escape_string($_POST['flag']); } } else { $board_flag_code = ''; } if ($board_flag_code) { $board_flag_col = ',board_flag'; $board_flag_val = ",'$board_flag_code'"; } else { $board_flag_col = ''; $board_flag_val = ''; } if( $resto ) calculate_indexes_to_rebuild( $resto ); // Remove old replies if the thread is sticky+undead if ($is_undead_sticky && STICKY_CAP > 1) { $query = "SELECT MIN(no) FROM (SELECT no FROM `" . BOARD_DIR . "` WHERE resto = $resto ORDER BY no DESC LIMIT " . (STICKY_CAP - 1) . ") as subsel"; $result = mysql_board_call($query); if ($result) { $prune_row = mysql_fetch_row($result); mysql_free_result($result); $min_no = (int)$prune_row[0]; if ($min_no > $resto) { $query = "SELECT no FROM `" . BOARD_DIR . "` WHERE resto = $resto AND no < $min_no"; $result = mysql_board_call($query); if ($result) { while ($prune_row = mysql_fetch_assoc($result)) { delete_post((int)$prune_row['no'], '', 0, 1, 1, 0); } mysql_free_result($result); } } } } // April 2024 /* if ($_xa24_since4pass && !UPLOAD_BOARD && !JANITOR_BOARD) { $name = april_2024_get_name(); if ($emails == '$DESU' && strlen($com) < 10000) { $com .= '
    '; } } */ $user_meta = encode_user_meta($browser_id, substr($_req_sig, 0, 8), $userpwd); $insert_tries = 2; do { if( SKIP_DOUBLES == 1 ) mysql_board_call( "START TRANSACTION" ); $query = "insert into `" . SQLLOG . "` (now,name,sub,com,host,pwd,email,filename,ext,w,h,tn_w,tn_h,tim,time,last_modified,md5,fsize,root,resto$flag_cols,tmd5,id,country$board_flag_col) values (" . "'" . $now . "'," . "'" . mysql_real_escape_string( $name ) . "'," . mysql_nullify( mysql_real_escape_string( $sub ) ) . "," . "'" . mysql_real_escape_string( $com ) . "'," . "'" . mysql_real_escape_string( $host ) . "'," . "'" . mysql_real_escape_string( $pass ) . "'," . "'" . mysql_real_escape_string($user_meta) . "'," . "'" . mysql_real_escape_string( $insfile ) . "'," . mysql_nullify( $ext ) . "," . (int)$W . "," . (int)$H . "," . (int)$TN_W . "," . (int)$TN_H . "," . "'" . $tim . "'," . (int)$time . "," . (int)$time . "," . mysql_nullify( $md5 ) . "," . (int)$fsize . "," . $rootpredicate . "," . (int)$resto . $flag_vals . "," . mysql_nullify( $tmd5 ) . "," . mysql_nullify( $uid ) . "," . "'$country'$board_flag_val)"; if( !$result = mysql_board_call( $query ) ) { echo S_SQLFAIL; } //post registration time_log( "i" ); $insertid = mysql_board_insert_id(); if( SKIP_DOUBLES == 1 ) { if( has_doubles( $insertid ) ) { mysql_board_call( "ROLLBACK" ); // retry } else { mysql_board_call( "COMMIT" ); $insert_tries = 0; } } else { $insert_tries = 0; } } while( $insert_tries-- ); // Captcha bypass token if ($captcha_bypass_allow_credits && $_threat_score < 0.15) { set_twister_captcha_credits($memcached, $host, $userpwd, $time); } /* if (!$captcha_bypass) { if (mt_rand(0, 99) === 0) { write_to_event_log('known_sample', $host, [ 'board' => BOARD_DIR, 'thread_id' => $resto, 'arg_num' => $userpwd->isUserKnown(), ]); } } */ $userpwd->updatePostActivity(!$resto, $has_image); $userpwd->setCookie($cookie_domain); // April 2019 /* if (defined('LIKE_MAX_LIKES') && LIKE_MAX_LIKES > 0) { like_update_post_score(); } */ // Halloween 2017 /* if ($resto && defined('CSS_EVENT_NAME') && CSS_EVENT_NAME === 'spooky2017') { process_halloween_score($com, $resto, $passid, $pass, $pass_is_bannable); } */ // April 2018 //update_april_team_scores(); // Log mod action if posted with html if ($log_mod_action) { $action_log_post = array( 'no' => $insertid, 'name' => $name, 'sub' => $sub, 'com' => $com, 'filename' => $insfile, 'ext' => $ext ); if ($log_html_post) { $action_log_post['com'] = htmlspecialchars($com, ENT_QUOTES); log_mod_action(4, $action_log_post); } // capcode posting if ($log_capcode_post) { $action_log_post['name'] .= ' ## ' . ucfirst($capcode); log_mod_action(5, $action_log_post, $capcode === 'verified'); } } if( $resto ) { //sage or age action $resline = mysql_board_call( "select count(no) from `" . SQLLOG . "` where archived=0 and resto=" . $resto ); $countres = mysql_result( $resline, 0, 0 ); $permasage_hours = (int)PERMASAGE_HOURS; if ($permasage_hours > 0) { $time_col = 'time,'; } else { $time_col = ''; } // FIXME: a similar query is done at line ~4723 $resline = mysql_board_call( "select {$time_col}sticky,permasage,permaage,root from `" . SQLLOG . "` where no=" . $resto ); $resline = mysql_fetch_assoc($resline); if ($resline['sticky'] || $resline['permasage']) { $root_col = ''; } else if ($resline['permaage']) { $root_col = 'root=now(),'; } else if ($is_sage || $countres >= MAX_RES) { $root_col = ''; } else if ($permasage_hours && ($time - ($permasage_hours * 3600) >= $resline['time'])) { $root_col = ''; } else { $root_col = 'root=now(),'; if (!$captcha_bypass && BOARD_DIR === 'jp') { if (!spam_filter_can_bump_thread($resline['root'])) { $root_col = ''; $_bot_headers = spam_filter_format_http_headers($com, $country, "$insfile$ext", $_threat_score, $_req_sig); log_spam_filter_trigger('necrobump', BOARD_DIR, $resto, $host, 1, $_bot_headers); } } } mysql_board_call("update `" . SQLLOG . "` set {$root_col}last_modified=%d where no=%d", $_SERVER['REQUEST_TIME'], $resto); } if( defined( 'AUTOSTICKY' ) && AUTOSTICKY ) { $autosticky = preg_split( "/,\s*/", AUTOSTICKY ); if( $resto == 0 ) { if( $insertid % 1000000 == 0 || in_array( $insertid, $autosticky ) ) { $sticky = true; mysql_board_call( "update " . SQLLOG . " set sticky=1,root=root where no=$insertid" ); } } } if( SAVE_XFF == 1 && $xff ) { mysql_global_do( "INSERT INTO xff (tim,board,xff,ip,postno,is_live) VALUES ('%s','%s','%s',%d,%d,1)", $tim, BOARD_DIR, $xff, ip2long( $host ), $insertid ); } if (UPLOAD_BOARD && $md5 ) { $result = mysql_board_call( "insert ignore into `%s` (filename,md5) values('%s','%s')", SQLLOGMD5, $insfile, $md5 ); } // determine url to redirect to $proto = ( stripos( $_SERVER["HTTP_REFERER"], "https" ) !== false ) ? "https:" : "http:"; if( !$is_nonoko && !$resto ) { $redirect = $proto . '//boards.' . L::d(BOARD_DIR) . '/' . BOARD_DIR . "/thread/" . $insertid . PHP_EXT2; } else if( !$is_nonoko ) { $redirect = $proto . '//boards.' . L::d(BOARD_DIR) . '/' . BOARD_DIR . "/thread/" . $resto . PHP_EXT2 . '#p' . $insertid; } else { $redirect = $proto . '//boards.' . L::d(BOARD_DIR) . '/' . BOARD_DIR . '/'; } // To let the JavaScript thread watcher know the newly created thread ID if (!$resto && isset($_POST['awt'])) { setcookie('4chan_awt', $insertid, 0, '/' . BOARD_DIR . '/', $cookie_domain); } show_post_successful( $mes, $com, $insertid, $resto, $redirect, $delay_refresh ); $static_rebuild = ( STATIC_REBUILD == 1 ); //logtime( "Before trim_db" ); // trim database if (!$resto) { if (!$static_rebuild) { trim_db(); if (ENABLE_ARCHIVE && ARCHIVE_MAX_AGE) { trim_archive(); } } } //logtime( "After trim_db" ); //time_log( "tr" ); $_need_updatelog = true; if (AUTOARCHIVE_CAP && $resto && !$sticky && !$undead && ENABLE_ARCHIVE) { if (count_thread_replies(BOARD_DIR, $resto) >= AUTOARCHIVE_CAP) { $_need_updatelog = false; archive_thread($resto); } } // update html if ($_need_updatelog) { updatelog( $resto ? $resto : $insertid ); } //logtime( "Pages rebuilt" ); //time_log( "r" ); // late tasks happen below here iplog_add( BOARD_DIR, $insertid, $host, $time, $resto == 0, $tim, $has_image ); // Auto-report possibly nsfw post if ($tensorchan_score && $tensorchan_score > 0.5) { tensorchan_log(BOARD_DIR, $insertid, $resto, $tim, $ext, $tensorchan_score); } } else { // silent reject $insertid = 0; $noko = 0; } /* if( STATS_USER_JS ) { mysql_global_do( "UPDATE `user_stats` SET `count` = `count`+1 WHERE name='%s'", $stats_ok ); } */ } // Redirects to the most rcently created thread // This is to confuse spambots function show_post_successful_fake($resto = 0, $captcha_passed = true) { $thread_id = (int)$resto; $insert_id = 0; if (!$resto) { $query = 'SELECT resto FROM `' . BOARD_DIR . '` WHERE resto != 0 ORDER BY resto DESC LIMIT 1'; $res = mysql_board_call($query); if ($res) { $row = mysql_fetch_row($res); $insert_id = (int)$row[0]; } } else { $query = 'SELECT no FROM `' . BOARD_DIR . '` ORDER BY no DESC LIMIT 1'; $res = mysql_board_call($query); if ($res) { $row = mysql_fetch_row($res); $insert_id = (int)$row[0] + 1; } } if (!$thread_id) { $redirect = 'https://boards.' . L::d(BOARD_DIR) . '/' . BOARD_DIR . "/thread/" . $insert_id . PHP_EXT2; } else { $redirect = 'https://boards.' . L::d(BOARD_DIR) . '/' . BOARD_DIR . "/thread/" . $thread_id . PHP_EXT2 . '#p' . $insert_id; } $cookie_domain = '.' . L::d(BOARD_DIR); $now = $_SERVER['REQUEST_TIME']; // Name cookie $c_name = $_POST['name']; setrawcookie('4chan_name', rawurlencode($c_name), $now + ($c_name ? (7 * 24 * 3600) : -3600), '/', $cookie_domain); // Password cookie $userpwd = UserPwd::getSession(); if ($userpwd) { if ($captcha_passed) { $userpwd->setCookie($cookie_domain); } else if (!$userpwd->isFake() && !$userpwd->isNew()) { $userpwd->setCookie($cookie_domain); } else { UserPwd::setFakeCookie($now, $cookie_domain); } } show_post_successful(null, null, $insert_id, $thread_id, $redirect); } function show_post_successful( $mes, $com, $insertid, $resto, $redirect, $delay_refresh = false ) { if (isset($_SERVER['HTTP_ACCEPT']) && $_SERVER['HTTP_ACCEPT'] === 'application/json') { return show_post_successful_json($insertid, $resto); } if( !$mes ) $mes = S_POSTING_DONE; $time_to_refresh = ( $delay_refresh ) ? 10 : 1; $script = ""; if( defined( 'POST_SUCCESSFUL_FILE' ) ) { $file = file_get_contents( POST_SUCCESSFUL_FILE ); $success = str_replace( "@REDIRECT@", $redirect, $file ); } else { // FIXME templating $icon = DEFAULT_BURICHAN ? 'favicon-ws.ico' : 'favicon.ico'; $defaultcss = DEFAULT_BURICHAN ? 'yotsubluenew' : 'yotsubanew'; $cssVersion = TEST_BOARD ? CSS_VERSION_TEST : CSS_VERSION; $sg = style_group(); $styles = array( 'Yotsuba New' => "yotsubanew.$cssVersion.css", 'Yotsuba B New' => "yotsubluenew.$cssVersion.css", 'Futaba New' => "futabanew.$cssVersion.css", 'Burichan New' => "burichannew.$cssVersion.css", 'Photon' => "photon.$cssVersion.css", 'Tomorrow' => "tomorrow.$cssVersion.css" ); $css = ''; if( isset( $_COOKIE[$sg] ) ) { if( isset( $styles[$_COOKIE[$sg]] ) ) { $css = ''; } } else { $dcssl = $defaultcss . '.' . $cssVersion . '.css'; $css = ''; } if (defined('CSS_EVENT_NAME') && CSS_EVENT_NAME) { $css = ''; } if( BOARD_DIR == 'j' ) { $css = ''; } $script .= ''; $success = "$script" . S_POSTING_DONE . "$css

    $mes

    "; } echo $success; if ($resto) { fastcgi_finish_request(); } } function show_post_successful_json($post_id, $thread_id) { header('Content-Type: application/json'); echo '{"tid":' . $thread_id . ',"pid":' . $post_id . '}'; if ($thread_id) { fastcgi_finish_request(); } } function resredir( $res, $delete = 0, $no_exit = false ) { if (!$_SERVER["HTTP_REFERER"]) { $proto = 'https:'; } else { $proto = ( stripos( $_SERVER["HTTP_REFERER"], "https" ) !== false ) ? "https:" : "http:"; } $res = (int)$res; //mysql_board_lock( true ); if( !$redir = mysql_board_call( "select no,resto from `" . SQLLOG . "` where no=" . $res ) ) { echo S_SQLFAIL; } list( $no, $resto ) = mysql_fetch_row( $redir ); // if we're deleting and no post/resto (thread gone) if( !$no && $delete ) { // send us back to the board updating_index(); //mysql_board_unlock(); if (!$no_exit) { die; } else { return; } } if( !JANITOR_BOARD ) { header("Cache-Control: public, max-age=2"); } if( !$no ) { // If no < max(no) then this could be 410 Gone. http_response_code(404); error( S_NOTHREADERR, $dest ); } if( $resto == "0" ) { // thread $redirect = $proto . '//boards.' . L::d(BOARD_DIR) . '/' . BOARD_DIR . "/thread/" . $no . PHP_EXT2 . '#p' . $no; } else { $redirect = $proto . '//boards.' . L::d(BOARD_DIR) . '/' . BOARD_DIR . "/thread/" . $resto . PHP_EXT2 . '#p' . $no; } $redirect = JANITOR_BOARD ? str_replace( 'boards.', 'sys.', $redirect ) : $redirect; header("Location: $redirect", true, 301); echo ""; //mysql_board_unlock(); } function tensorchan_is_needed($userpwd, $resto, $W, $H, $ext) { // Inference is disabled if (!defined('TENSORCHAN_MODE') || !TENSORCHAN_MODE) { return false; } // Can't check if (!$userpwd) { return false; } // User is known or verified if ($userpwd->isUserKnownOrVerified(240)) { // 4 hours return false; } // OPs only but the post is a reply if (TENSORCHAN_MODE == 1 && $resto) { return false; } if ($W < 150 || $H < 150 || $ext == '.pdf') { return false; } return true; } function tensorchan_check_nsfw($tensor_png) { if (!$tensor_png) { return false; } $tensor_res = tensorchan_predict($tensor_png); if (!$tensor_res) { return false; } if (isset($tensor_res['error'])) { write_to_event_log('tensor_err', $_SERVER['REMOTE_ADDR'], [ 'board' => BOARD_DIR, 'meta' => htmlspecialchars($tensor_res['error']) ]); return false; } else { if (!isset($tensor_res['nsfw'])) { return false; } return (float)$tensor_res['nsfw']; } } function tensorchan_log($board, $post_id, $thread_id, $file_id, $file_ext, $score) { $post_id = (int)$post_id; $thread_id = (int)$thread_id; $score = (float)$score; $sql =<< $_err]; } $resp_status = curl_getinfo($curl, CURLINFO_HTTP_CODE); if ($resp_status >= 300) { return ["error" => "HTTP $resp_status: $resp"]; } curl_close($curl); if ($resp[0] == '{') { $resp = json_decode($resp, true); } else { return ["error" => "Not a JSON response"]; } return $resp; } function background_color( $im, $is_thread ) { if( DEFAULT_BURICHAN == 0 ) { if( $is_thread ) { list( $r, $g, $b ) = array(0xFF, 0xFF, 0xEE); } else { list( $r, $g, $b ) = array(0xF0, 0xE0, 0xD6); } } else { if( $is_thread ) { list( $r, $g, $b ) = array(0xEE, 0xF2, 0xFF); } else { list( $r, $g, $b ) = array(0xD6, 0xDA, 0xF0); } } return imagecolorallocate( $im, $r, $g, $b ); } function optimize_thumb($tmppath) { system("/usr/local/bin/jpegoptim -q --strip-all '$tmppath' >/dev/null 2>&1"); } // Calculates perceptual hash for a thumbnail // $img is a reference to a GD resource function get_thumb_dhash(&$img, $width, $height) { if (!$img) { return false; } $data = imagecreatetruecolor(9, 8); imagecopyresampled($data, $img, 0, 0, 0, 0, 9, 8, $width, $height); imagefilter($data, IMG_FILTER_GRAYSCALE); $hash = 0; $bit = 1; for ($y = 0; $y < 8; $y++) { $previous = imagecolorat($data, 0, $y) & 0xFF; for ($x = 1; $x < 9; $x++) { $current = imagecolorat($data, $x, $y) & 0xFF; if ($previous > $current) { $hash |= $bit; } $bit = $bit << 1; $previous = $current; } } imagedestroy($data); return sprintf("%016x", $hash); } //thumbnails function make_thumb( $fname, $tim, $ext, $resto, &$TN_W, &$TN_H, &$tmd5, $webm_sar = null, &$tensor_png = null ) { $thumb_dir = THUMB_DIR; //thumbnail directory $outpath = $thumb_dir . $tim . 's.jpg'; if( !$resto ) { $width = MAX_W; //output width $height = MAX_H; //output height $jpeg_quality = 50; } else { $width = MAXR_W; //output width (imgreply) $height = MAXR_H; //output height (imgreply) $jpeg_quality = 40; } if( ENABLE_PDF == 1 && $ext == '.pdf' ) { // create jpeg for the thumbnailer $pdfjpeg = IMG_DIR . $tim . '.pdf.tmp'; @exec( "/usr/local/bin/gs -q -dSAFER -dNOPAUSE -dBATCH -sDEVICE=jpeg -sOutputFile=$pdfjpeg $fname" ); if( !file_exists( $pdfjpeg ) ) unlink( $fname ); $fname = $pdfjpeg; } else if (ENABLE_WEBM && ($ext == '.webm' || $ext == '.mp4')) { $webm_thumb = thumb_webm($fname, $ext); if (!$webm_thumb) { unlink($fname); return false; } $fname = $webm_thumb; } $size = @GetImageSize($fname); if ($size === false) { return; } // File size needs to be checked again because of cleanup_uploaded_file if (defined('MAX_DIMENSION') && $ext == '.gif') { if ($size[0] > MAX_DIMENSION || $size[1] > MAX_DIMENSION) { error(S_TOOLARGERES); } } $memory_limit_increased = false; $maybe_transparent = true; // Don't increase memory limit on CLI so that the user can do it with a CLI parameter instead if( $size[0] * $size[1] > 3000000 && isset($_SERVER['REMOTE_ADDR']) ) { $memory_limit_increased = true; ini_set( 'memory_limit', memory_get_usage() + $size[0] * $size[1] * 15 ); // for huge images } switch( $size[2] ) { case 1 : $im_in = ImageCreateFromGIF( $fname ); if (!$im_in) { return; } break; case 2 : $im_in = ImageCreateFromJPEG( $fname ); if( !$im_in ) { return; } $maybe_transparent = false; break; case 3 : $im_in = ImageCreateFromPNG( $fname ); if( !$im_in ) { return; } break; default : return; } $source_w = $size[0]; $source_h = $size[1]; if ($webm_sar) { if ($webm_sar > 1) { $size[1] = round($size[1] / $webm_sar); if ($size[1] == 0) { $size[1] = 1; } } else { $size[0] = round($size[0] * $webm_sar); if ($size[0] == 0) { $size[0] = 1; } } } // Resizing if( $size[0] > $width || $size[1] > $height ) { $key_w = $width / $size[0]; $key_h = $height / $size[1]; ( $key_w < $key_h ) ? $keys = $key_w : $keys = $key_h; $out_w = floor( $size[0] * $keys ); $out_h = floor( $size[1] * $keys ); } else { $out_w = $size[0]; $out_h = $size[1]; } // the thumbnail is created $im_out = ImageCreateTrueColor( $out_w, $out_h ); if( !$im_out ) return; if( $maybe_transparent ) { $background = background_color( $im_out, $resto == 0 ); ImageFill( $im_out, 0, 0, $background ); } // copy resized original ImageCopyResampled( $im_out, $im_in, 0, 0, 0, 0, $out_w, $out_h, $source_w, $source_h ); $tmppath = tempnam( ini_get( "upload_tmp_dir" ), "thumb" ); // thumbnail saved ImageJPEG( $im_out, $tmppath, $jpeg_quality ); // Generate a perceptual hash from the original image $tmd5 = Phash::hash($im_in, $source_w, $source_h); if ($tmd5 === false) { $tmd5 = ''; } // Create the PNG file for inference if ($tensor_png !== null) { $tensor_png_dim = TENSORCHAN_DIM; $tensor_png_out = ImageCreateTrueColor($tensor_png_dim, $tensor_png_dim); ImageCopyResampled($tensor_png_out, $im_in, 0, 0, 0, 0, $tensor_png_dim, $tensor_png_dim, $source_w, $source_h ); $stream = fopen('php://memory','r+'); imagepng($tensor_png_out, $stream); rewind($stream); $tensor_png = stream_get_contents($stream); fclose($stream); ImageDestroy($tensor_png_out); } // Cleanup ImageDestroy( $im_in ); ImageDestroy( $im_out ); optimize_thumb($tmppath); //$tmd5 = md5_file( $tmppath ); rename_across_device( $tmppath, $outpath ); // if PDF was thumbnailed delete the orig jpeg if (isset($pdfjpeg)) { unlink($pdfjpeg); } // delete original webm frame else if (isset($webm_thumb)) { unlink($webm_thumb); } if( $memory_limit_increased ) ini_restore( 'memory_limit' ); $TN_W = $out_w; $TN_H = $out_h; return $outpath; } /* text plastic surgery */ // you can call with skip_bidi=1 if cleaning a paragraph element (like $com) function sanitize_text( $str, $skip_bidi = 0, $allow_html = false ) { global $admin, $html; // stupid unicode-hack removal if( BOARD_DIR != 'jp' && BOARD_DIR != 'a' && BOARD_DIR != 'b' && !SJIS_TAGS ) { $str = preg_replace( '#[\x{00A0}\x{3000}]#u', ' ', $str ); } if( !CODE_TAGS && !SJIS_TAGS) { $str = preg_replace( "/([ \t\f]|\xE2\x80\x8B|\xE2\x80\xA9)+/", " ", $str ); //collapse multiple spaces like HTML does } else { // fix tabs for html compression $str = str_replace( "\t", " ", $str ); } $str = trim( $str ); //blankspace removal if( get_magic_quotes_gpc() ) { //magic quotes is deleted (?) $str = stripslashes( $str ); } if ($allow_html && $html == 1 && (has_level('manager') || has_flag('html') || has_flag('developer'))) { $str = purify_html($str); } else { $str = htmlspecialchars( $str, ENT_QUOTES ); } if( $skip_bidi == 0 ) { // fix malformed bidirectional overrides - insert as many PDFs as RLOs //RLO $str .= str_repeat( "\xE2\x80\xAC", substr_count( $str, "\xE2\x80\xAE" /* U+202E */ ) ); $str .= str_repeat( "‬", substr_count( $str, "‮" ) ); $str .= str_repeat( "‬", substr_count( $str, "‮" ) ); //RLE $str .= str_repeat( "\xE2\x80\xAC", substr_count( $str, "\xE2\x80\xAB" /* U+202B */ ) ); $str .= str_repeat( "‬", substr_count( $str, "‫" ) ); $str .= str_repeat( "‬", substr_count( $str, "‫" ) ); } return $str; } // TODO: rewrite this to use less SQL queries function report() { global $captcha_bypass; $host = $_SERVER['REMOTE_ADDR']; if (isset($_COOKIE['4chan_pass'])) { $userpwd = new UserPwd($host, MAIN_DOMAIN, $_COOKIE['4chan_pass']); } else { $userpwd = new UserPwd($host, MAIN_DOMAIN); } if (BOARD_DIR === 'test') { require_once('forms/report-test.php'); require_once('modes/report-test.php'); } else { require_once('forms/report.php'); require_once('modes/report.php'); } if (!CAN_REPORT_POSTS) { fancydie(S_CANNOTREPORTPOSTS); } $no = (int)$_GET['no']; if ($no <= 0) { fancydie(S_POST_DEAD); } $post = report_check_post(BOARD_DIR, $no); if (!isset($_COOKIE['4chan_auser']) && !isset($_COOKIE['pass_enabled'])) { $no_captcha = report_can_bypass_captcha($host, $userpwd, $post); } else { $no_captcha = false; } if ($_SERVER['REQUEST_METHOD'] == 'GET') { header( 'Cache-Control: private, no-cache, must-revalidate' ); header( 'Expires: -1' ); // Doesn't check bans here report_check_ip( BOARD_DIR, $no, false); form_report(BOARD_DIR, $no, $no_captcha); } else { if (valid_captcha_bypass() !== true && $no_captcha !== true) { if (CAPTCHA_TWISTER) { $_m = create_memcached_instance(); if (isset($_POST['t-challenge']) && $_POST['t-challenge'] === 'noop') { if (use_twister_captcha_credit($_m, $host, $userpwd) === false) { error(S_CAPTCHATIMEOUT); } } else if (is_twister_captcha_valid($_m, $host, $userpwd, BOARD_DIR, 1) === false) { error(S_BADCAPTCHA); } } else { start_recaptcha_verify(); if (!$captcha_bypass) { end_recaptcha_verify(); } } } // Also checks for bans report_check_ip(BOARD_DIR, $no, true); if (!isset($_POST['cat']) && !isset($_POST['cat_id'])) { fancydie('Invalid category selected.'); } if ($_POST['cat']) { $cat_id = (int)$_POST['cat']; } else if ($_POST['cat_id']) { $cat_id = (int)$_POST['cat_id']; } else { $cat_id = null; } if (!$cat_id) { fancydie('Invalid category selected.'); } /* if ($no_captcha) { write_to_event_log('skip_rep_captcha', $host, [ 'board' => BOARD_DIR, 'thread_id' => $post['resto'] ? $post['resto'] : $post['no'], 'post_id' => $post['no'] ]); } */ report_submit(BOARD_DIR, $no, $cat_id); // script dies here } die( '' ); } /** * Archive deletion function * only works on archived posts, for authed users */ function arcdel($no, $redirect = false, $redirect_res = null) { global $onlyimgdel; $delno = array(); $time = $_SERVER['REQUEST_TIME']; reset( $_POST ); while ($item = each($_POST)) { if ($item[1] == 'delete') { $delno[] = $item[0]; } } $numdeletions = count($delno); if (!$numdeletions) { return; } $rebuild_archive_json = false; $rebuild = array(); for ($i = 0; $i < $numdeletions; $i++) { $resto = delete_post($delno[$i], '', $onlyimgdel, 0, 1, $numdeletions == 1, false, true); if ($resto) { $rebuild[$resto] = true; } else if (!$onlyimgdel) { $rebuild_archive_json = true; } } if (!has_level('janitor')) { mysql_global_call("INSERT INTO user_actions (ip,board,action,postno,time) VALUES (%d,'%s','delete',%d,now())", ip2long( $_SERVER["REMOTE_ADDR"] ), BOARD_DIR, $delno[0]); } if ($redirect) { if ($redirect_res) { resredir($redirect_res, 1, true); } else { updating_index(); } fastcgi_finish_request(); } foreach ($rebuild as $thread_id => $true) { rebuild_archived_thread($thread_id); } if ($rebuild_archive_json && ENABLE_JSON_THREADS) { generate_board_archived_json(); } } function user_delete( $no, $pwd, $redirect = false, $redirect_res = null ) { global $pwdc, $onlyimgdel, $captcha_bypass; if (UPLOAD_BOARD && $onlyimgdel) { error("It doesn't make any sense to do a file-only delete on a file board!"); } $delno = array(); $time = $_SERVER['REQUEST_TIME']; $delflag = false; reset( $_POST ); while( $item = each( $_POST ) ) { if( $item[1] == 'delete' ) { array_push( $delno, $item[0] ); $delflag = true; } } $user_is_known = false; if ($pwdc) { $userpwd = new UserPwd($_SERVER['REMOTE_ADDR'], MAIN_DOMAIN, $pwdc); if ($userpwd) { $pwd = $userpwd->getPwd(); $user_is_known = $userpwd->maskLifetime() >= 900; } else { $pwd = null; } } else { $pwd = null; } $numdeletions = count( $delno ); if( !$numdeletions ) return; $flag = false; if( !has_level( 'janitor' ) ) { $n = mysql_global_call( "select count(*)>%d from user_actions where ip=%d and action='delete' and time >= subdate(now(), interval 1 hour)", RENZOKU_DEL_HOURLY, ip2long( $_SERVER['REMOTE_ADDR'] ) ); list( $h ) = mysql_fetch_row( $n ); if( $h ) { //check_fail_floodcheck($no); error(S_FLOOD_DEL); } $n = mysql_global_call( "select count(*)>%d from user_actions where ip=%d and action='delete' and time >= subdate(now(), interval 1 day)", RENZOKU_DEL_DAILY, ip2long( $_SERVER['REMOTE_ADDR'] ) ); list( $h ) = mysql_fetch_row( $n ); if( $h ) { //check_fail_floodcheck($no); error(S_FLOOD_DEL); } } $rebuild = array(); // keys are pages that need to be rebuilt (0 is index, of course) $lazy_rebuild = false; if (isset($_POST['tool']) && $_POST['tool']) { $tool = $_POST['tool']; } else { $tool = null; } // Manual, single post deletion. Only rebuilds one page if deleting a reply. if ($numdeletions == 1) { $resto = delete_post( $delno[0], $pwd, $onlyimgdel, 0, 1, $numdeletions == 1, false, false, $tool, $user_is_known ); if ($resto) { $rebuild[$resto] = 1; calculate_indexes_to_rebuild($resto); $lazy_rebuild = true; } } // Other (multi, automatic, etc...) else { for( $i = 0; $i < $numdeletions; $i++ ) { $resto = delete_post( $delno[$i], $pwd, $onlyimgdel, 0, 1, $numdeletions == 1, false, false, $tool, $user_is_known ); if( $resto ) { $rebuild[$resto] = 1; } } } if (!has_level('janitor')) { mysql_global_call("INSERT INTO user_actions (ip,board,action,postno,time) VALUES (%d,'%s','delete',%d,now())", ip2long( $_SERVER["REMOTE_ADDR"] ), BOARD_DIR, $delno[0]); } if ($redirect) { if ($redirect_res) { resredir($redirect_res, 1, true); } else { updating_index(); } fastcgi_finish_request(); } rebuild_deletions($rebuild, $lazy_rebuild); } function updatelog_remote( $no, $noidx ) { if( !has_level() ) die( '' ); // anti dos $no = intval( $no ); $noidx = !!$noidx; // FIXME, running this when $noidx is true breaks cross-thread quotelinks. updatelog( $no, $noidx ); } function _print( $s, $echo = 1 ) { if( $echo ) { echo $s; return; } ob_flush(); flush(); echo $s; echo str_repeat( ' ', 256 ) . "\n"; ob_flush(); flush(); } function fancystyle() { $style = << body { font-family: Helvetica, Arial, sans-serif; font-size: 12pt; } h1 { margin: 0; padding: 0; } HTML; return $style; } function rebuild_catalog( $shutup = false ) { if( !has_level() ) die(); if( !$shutup ) { echo fancystyle(); echo '

    Rebuilding catalog...

    '; } $start = microtime( true ); generate_catalog(); $time = round( microtime( true ) - $start, 6 ); if( !$shutup ) { echo 'Done!

    Rebuilding took ' . $time . ' seconds.

    Redirecting to catalog...

    '; die(); } } function rebuild_boards_json() { if( !has_level() ) die(); echo fancystyle(); echo '

    Rebuilding boards.json...

    '; $start = microtime( true ); $query = mysql_global_call( "SELECT dir as board,name as title FROM boardlist ORDER BY board ASC" ); $boards = array(); $host = 'https://sys.int'; $post = array( 'mode' => 'cataloginfo' ); while( $row = mysql_fetch_assoc( $query ) ) { if( $row['board'] == 'vp' ) $row['title'] = 'Pokémon'; //cataloginfo $url = "$host/{$row['board']}/imgboard.php"; $ch = rpc_start_request($url, $post, null, true); $response = rpc_finish_request($ch, $error, $httperror); if (!$response) { die( 'Could not generate info for /' . $row['board'] . '/; ' . $error ); } $json = json_decode($response, true); foreach( $json as $key => $val ) { if( $key != 'board' && ctype_digit( $val ) ) $val = (int)$val; $row[$key] = $val; } $boards['boards'][] = $row; } // Dump resulting json on /test/ if (BOARD_DIR === 'test') { echo '
    '; echo json_encode($boards, JSON_HEX_AMP | JSON_PRETTY_PRINT); echo '
    '; } else { $_json = json_encode($boards, JSON_HEX_AMP); print_page(BOARDS_ROOT . 'boards.json', $_json); } $time = round( microtime( true ) - $start, 6 ); echo 'Done!

    Rebuilding took ' . $time . ' seconds.

    No redirect here boss. Off you go.'; die(); } function rebuild( $all = 0 ) { global $rebuildall, $fwritetimer; if( !has_level() ) die( '' ); // anti dos if (has_flag('developer')) { error_reporting(E_ALL); } header( "Pragma: no-cache" ); _print(fancystyle()); $l = $all ? 'all' : 'missing'; _print( "Rebuilding $l replies and pages... Go back

    \n" ); log_cache(); trim_db(); trim_archive(); mysql_board_lock( true ); $starttime = microtime( true ); $query = "SELECT no, resto FROM `" . SQLLOG . "` WHERE resto = 0 AND archived = 0 ORDER BY root DESC"; $treeline = mysql_board_call($query); if (!$treeline) { echo S_SQLFAIL; } mysql_board_unlock(); _print( "Writing...
    \n" ); if( $all ) { while( list( $no, $resto ) = mysql_fetch_row( $treeline ) ) { if( !$resto ) { _print( "Writing No.$no... " ); updatelog( $no, 1 ); $ext = TEST_BOARD ? "rp cache: ".realpath_cache_size() : ""; _print( "DONE!
    $ext\n" ); } } _print( "Writing index pages... " ); updatelog(); _print( "DONE!
    " ); if (ENABLE_CATALOG) { _print( "Writing catalog..." ); generate_catalog( true ); _print( "DONE!
    " ); } if (ENABLE_ARCHIVE) { _print( "Writing archive..." ); rebuild_archive_list(); _print( "DONE!
    " ); } } $totaltime = microtime( true ) - $starttime; $proctimer = $totaltime - $fwritetimer; $peakmem = memory_get_peak_usage(true) / (1024*1024.0); $usedmem = memory_get_usage(true) / (1024*1024.0); $redir = '//boards.' . L::d(BOARD_DIR) . '/' . BOARD_DIR . '/'; echo <<Total running time (lock excluded): $totaltime seconds.
    Composed of:
    Time spent writing files: $fwritetimer seconds.
    Time spent processing: $proctimer seconds.
    Memory: Peak: $peakmem MB Final: $usedmem MB
    Pages created.

    END; /* if (!TEST_BOARD) { echo << END; } */ } function rebuild_after_deletion( $no ) { if( !has_level() ) die(); mysql_board_lock( true ); if( !$treeline = mysql_board_call( "SELECT no FROM `" . SQLLOG . "` WHERE no = %d", $no ) ) { mysql_board_unlock(); die( S_POSTGONE ); } log_cache( 0, $no ); mysql_board_unlock(); updatelog( $no, 1 ); die( $no . ' Rebuilt OK!' ); } function updating_index() { $proto = ( stripos( $_SERVER["HTTP_REFERER"], "https" ) !== false ) ? "https:" : "http:"; echo "" . S_UPDATING_INDEX . "
    " . S_UPDATING_INDEX . "
    "; } function require_request_method( $method ) { if (!isset($_SERVER['REMOTE_ADDR'])) return; $umethod = $_SERVER["REQUEST_METHOD"]; //$req = htmlspecialchars( $_REQUEST["mode"] ); if( $umethod == "OPTIONS" || ( $umethod == "HEAD" && $method == "GET" ) ) return; if( $umethod != $method ) { error( S_REJECTTEXTBAN ); } } function get_catalog_info() { $arr = array( 'ws_board' => (int)(CATEGORY == 'ws'), 'per_page' => (int)DEF_PAGES, 'pages' => (int)PAGE_MAX, 'max_filesize' => ((int)MAX_KB) * 1024, 'max_webm_filesize' => ((int)MAX_WEBM_FILESIZE) * 1024, 'max_comment_chars' => (int)MAX_COM_CHARS, 'max_webm_duration' => (int)MAX_WEBM_DURATION, 'bump_limit' => (int)MAX_RES, 'image_limit' => (int)MAX_IMGRES, 'cooldowns' => array( 'threads' => (int)RENZOKU3, 'replies' => (int)RENZOKU, 'images' => (int)RENZOKU2 ) ); if (defined('META_DESCRIPTION')) { $arr['meta_description'] = META_DESCRIPTION; } if (SPOILERS) { $arr['spoilers'] = 1; if (SPOILER_NUM) { $arr['custom_spoilers'] = (int)SPOILER_NUM; } } if (DISP_ID) { $arr['user_ids'] = 1; } if (ENABLE_ARCHIVE) { $arr['is_archived'] = 1; } if (CODE_TAGS) { $arr['code_tags'] = 1; } if (SJIS_TAGS) { $arr['sjis_tags'] = 1; } if (JSMATH) { $arr['math_tags'] = 1; } if (SHOW_COUNTRY_FLAGS) { $arr['country_flags'] = 1; } if (ENABLE_BOARD_FLAGS) { $arr['board_flags'] = get_board_flags_selector(); } if (ENABLE_WEBM_AUDIO) { $arr['webm_audio'] = 1; } if (MIN_W > 1) { $arr['min_image_width'] = (int)MIN_W; } if (MIN_H > 1) { $arr['min_image_height'] = (int)MIN_H; } if (TEXT_ONLY) { $arr['text_only'] = 1; $arr['require_subject'] = 1; } if (FORCED_ANON) { $arr['forced_anon'] = 1; } if (REQUIRE_SUBJECT) { $arr['require_subject'] = 1; } if (ENABLE_PAINTERJS) { $arr['oekaki'] = 1; } die(json_encode($arr)); } /** * Generates context to append to thread links. */ function generate_href_context($sub, $com) { if (JANITOR_BOARD) { return ''; } $context = ''; if (strpos($sub, 'SPOILER<>') === 0) { $sub = substr($sub, 9); } if ($sub !== '') { $context = cleanup_context_string($sub); } if ($context === '' && $com !== '') { $context = $com; if (strpos($context, '
    ') !== false) { $context = str_replace('
    ', "\n", $context); $has_br = true; } else { $has_br = false; } $context = preg_replace('/(^|\s)https?:\/\/[^\s]{4,}/', '', $context); if (strpos($context, '') !== false) { $context = preg_replace('/.*<\/table>/', '', $context); // ??? } if (strpos($context, ']+>.*<\/strong>/', '', $context); // ??? } if ($has_br) { $context = ltrim($context); $context = explode("\n", $context)[0]; } $context = preg_replace('/<[^>]+>/', ' ', $context); $context = cleanup_context_string($context); } return $context; } /** * Generates page title from subjects and comments * params must be already html-escaped. */ function generate_page_title($thread_id, $sub, $com) { if (JANITOR_BOARD) { return strip_tags(TITLE); } if (UPLOAD_BOARD) { $sub = preg_replace('/^(\d+)\|/', '', $sub); } $context = ''; if (strpos($sub, 'SPOILER<>') === 0) { $sub = substr($sub, 9); } if ($sub !== '') { $context = $sub; } if ($context === '' && $com !== '') { if (SJIS_TAGS && strpos($com, '/', '[SJIS]', $com); } $context = str_replace('
    ', ' ', $com); $context = htmlspecialchars_decode($context, ENT_QUOTES); $context = mb_substr(strip_tags($context), 0, 50); $context = htmlspecialchars($context, ENT_QUOTES); } if ($context === '') { $context = 'No.' . $thread_id; } if (BOARD_DIR === 's4s') { return '[' . BOARD_DIR . '] - ' . $context; } else { return '/' . BOARD_DIR . '/ - ' . $context; } } /** * Generates metatags from subjects and comments * params must be already html-escaped. */ function generate_page_metatags($sub, $com) { if (JANITOR_BOARD) { return null; } $context = ''; if (strpos($sub, 'SPOILER<>') === 0) { $sub = substr($sub, 9); } if ($sub !== '') { $context = $sub; } $ell = ''; if ($context === '' && $com !== '') { $context = preg_replace('/(
    |\s)+/', ' ', $com); $context = htmlspecialchars_decode(strip_tags($context), ENT_QUOTES); if (mb_strlen($context) > 100) { $ell = '...'; $context = mb_substr($context, 0, 100); } } else { $context = htmlspecialchars_decode($context, ENT_QUOTES); } if (empty($context)) { return null; } else { $words = preg_split('/[[:punct:]\s]+/', $context); $keywords = ''; foreach ($words as $word) { if (strlen($word) > 3) { $keywords .= ',' . $word; } } } $context = htmlspecialchars($context, ENT_QUOTES); $keywords = htmlspecialchars($keywords, ENT_QUOTES); return array($context.$ell, $keywords); } function cleanup_context_string($context) { $context = htmlspecialchars_decode($context, ENT_QUOTES); $context = strtolower(preg_replace('/[^a-zA-Z0-9\s]+/', '', $context)); $length = 0; $words = explode(' ', $context); $context = array(); foreach ($words as $word) { if ($word === '') { continue; } $length += strlen($word) + 1; if ($length > 50) { break; } $context[] = $word; } $context = implode('-', $context); return htmlspecialchars($context, ENT_QUOTES); } /** * Embedded data detection * Dies if the PNG or JPG file contains embedded data. * Returns true if the GIF file was modified, otherwise returns false. */ function cleanup_uploaded_file($file, $type) { $full_size = filesize($file); // 50KB $max_delta = 51200; switch ($type) { case '.png': $clean_size = get_clean_png_size($file); break; case '.jpg': if ($full_size < 204800) { return false; } $clean_size = get_clean_jpg_size($file); break; case '.gif': if ($full_size < 204800) { return false; } $clean_size = get_clean_gif_size($file); break; case '.swf': return true; // Why? default: return false; } if ($clean_size === false) { return false; } // PNGs can still fail the check even if no size delta is found if ($clean_size === -1) { if ($type === '.gif') { $file = escapeshellcmd($file); $res = system("/usr/local/bin/gifsicle --no-extensions \"$file\" -o \"$file\" >/dev/null 2>&1"); if ($res !== false) { return true; } else { return false; } } else { error(S_IMGCONTAINSFILE, $file); } } $delta_size = $full_size - $clean_size; if ($delta_size > $max_delta) { if ($type === '.gif') { $file = escapeshellcmd($file); $res = system("/usr/local/bin/gifsicle --no-extensions \"$file\" -o \"$file\" >/dev/null 2>&1"); if ($res !== false) { return true; } } else { error(S_IMGCONTAINSFILE, $file); } } return false; } // Returns the size of critical data or -1 if the file contains extensions. function get_clean_gif_size($file) { $file = escapeshellcmd($file); $binary = '/usr/local/bin/gifsicle'; $res = shell_exec("$binary --sinfo \"$file\" 2>&1"); if ($res !== null) { $size = 0; if (preg_match('/ extensions [0-9]+/', $res)) { return -1; } if (preg_match_all('/compressed size ([0-9]+)/', $res, $m)) { foreach ($m[1] as $frame_size) { $size += (int)$frame_size; } return $size; } } return false; } // Returns the number of bytes in critical chunks or -1 if too many IDAT chunks are found // Returns false on error function get_clean_png_size($file) { $file = escapeshellcmd($file); $binary = '/usr/local/bin/pngcrush'; $res = shell_exec("$binary -m 1 -n -v \"$file\" 2>&1 1>/dev/null"); if ($res !== null) { if (preg_match('/Reading (?:iTXt|tEXt|zTXt) chunk,/', $res, $m)) { if (preg_match('/ [a-z]oo: /i', $res)) { return -1; } else if (preg_match('/ (?:Software|Creation Time): ([=a-zA-Z0-9]{10,})\n/', $res, $ct)) { $_b64 = base64_decode($ct[0]); if ($_b64 && preg_match('/[0-9]/', $_b64)) { write_to_event_log('png_link', $_SERVER['REMOTE_ADDR'], [ 'board' => BOARD_DIR, 'meta' => htmlspecialchars($res) ]); return -1; } } } if (preg_match('/in critical chunks\s+=\s+([0-9]+)/', $res, $m)) { return (int)$m[1]; } } return false; } function get_clean_jpg_size($file) { $eof = false; $img = fopen($file, 'rb'); $data = fread($img, 2); if ($data !== "\xff\xd8") { fclose($img); return false; } while (!feof($img)) { $data = fread($img, 1); if ($data !== "\xff") { continue; } while (!feof($img)) { $data = fread($img, 1); if ($data !== "\xff") { break; } } if (feof($img)) { break; } $byte = unpack('C', $data)[1]; if ($byte === 217) { $eof = ftell($img); break; } if ($byte === 0 || $byte === 1 || ($byte >= 208 && $byte <= 216)) { continue; } $data = fread($img, 2); $length = unpack('n', $data)[1]; if ($length < 1) { break; } fseek($img, $length - 2, SEEK_CUR); } fclose($img); return $eof; } /** * Generates a "Pass User Since YEAR" string for pass users */ function get_since_4chan($pass_id) { $query = "SELECT UNIX_TIMESTAMP(purchase_date) FROM pass_users WHERE user_hash = '%s' ORDER BY purchase_date ASC LIMIT 1"; $res = mysql_global_call($query, $pass_id); if (!$res) { return 0; } $row = mysql_fetch_row($res); $ts = (int)$row[0]; if (!$ts) { return 0; } $ts = date('Y', $ts); if (!$ts) { return 0; } return (int)$ts; } /* // Halloween 2017 function get_halloween_score($pass_id) { $query = "SELECT score FROM halloween_tricks WHERE user_hash = '%s' LIMIT 1"; $res = mysql_global_call($query, $pass_id); if (!$res) { return 0; } $row = mysql_fetch_row($res); if (!$row) { return 0; } return (int)$row[0]; } // Halloween 2017 /* function get_halloween_dummy_pass($name) { if (!$name) { return ''; } $hashed_bits = hash_hmac('sha1', $name, 'CIaPCUkJq9n3fskmKC06tquCV/2edWqbgBeY9pk7RlQ', true); $hashed_name = base64_encode($hashed_bits); if (!$hashed_name) { return ''; } return '_' . substr($hashed_name, 0, 9); } // Halloween 2017 function process_halloween_score($com, $thread_id, $this_pass_id, $this_pwd, $pass_is_bannable) { if ($com === '') { return; } $thread_id = (int)$thread_id; if (!$thread_id) { return; } if (preg_match_all('/>>([0-9]{4,})/', $com, $m) === 1) { $post_id = (int)$m[1][0]; if (!$post_id || $post_id == $thread_id) { return; } $query = 'SELECT host, pwd, 4pass_id FROM `' . SQLLOG . '` WHERE no = ' . $post_id . ' AND resto = ' . $thread_id; $res = mysql_board_call($query); if (!$res) { return; } $row = mysql_fetch_assoc($res); if (!$row) { return; } // Check if same person if (!$row['4pass_id'] || $row['4pass_id'] == $this_pass_id || $row['pwd'] == $this_pwd || $row['host'] == $_SERVER['REMOTE_ADDR']) { return; } $long_ip = ip2long($_SERVER['REMOTE_ADDR']); if (!$long_ip) { return; } // Check if already gave points $query = "SELECT 1 FROM `halloween_votes` WHERE long_ip = $long_ip AND board = '" . BOARD_DIR . "' AND post_id = $post_id"; $res = mysql_global_call($query); if (!$res) { return; } if (mysql_num_rows($res) > 0) { return; } // Check if the user is known if (!spam_filter_is_user_known($long_ip, BOARD_DIR, $pass_is_bannable ? $this_pwd : null)) { return; } // Good to go $query = <<= 1000) { return " n-jol-6"; } if ($trick_count >= 500) { return " n-jol-5"; } if ($trick_count >= 200) { return " n-jol-4"; } if ($trick_count >= 100) { return " n-jol-3"; } if ($trick_count >= 50) { return " n-jol-2"; } if ($trick_count >= 25) { return " n-jol-1"; } return ''; } */ /** * Checks if the user has a recent ban request. * dies with an error if user needs to be blocked. */ function check_for_ban_request($ip, $pwd = null) { $time_lim = BLOCK_ON_BR_LEN; $clauses = []; $clauses[] = "host = '" . mysql_real_escape_string($ip) . "'"; if ($pwd) { $clauses[] = "pwd = '" . mysql_real_escape_string($pwd) . "'"; } $board_sql = mysql_real_escape_string(BOARD_DIR); $clauses = implode(' OR ', $clauses); $query = << DATE_SUB(NOW(), $time_lim) OR ban_template IN (1, 2, 123, 126)) LIMIT 1 SQL; $res = mysql_global_call($query); if (!$res || mysql_num_rows($res) !== 1) { return false; } $row = mysql_fetch_assoc($res); $time = (int)$row['diff']; $tpl_name = rtrim(preg_replace('/\[[^\]]+\]/', '', $row['tpl_name'])); // Non-expiring blocks for some global templates if ($time < 0) { error(sprintf(S_BRBLOCKED_2, $tpl_name)); } $str = $time <= 1 ? '1 minute' : "$time minutes"; error(sprintf(S_BRBLOCKED, $tpl_name, $str)); } /** * Checks if the IP is banned, also checks for ban evasion * return 0 for not banned, 1 for banned, 2 for warned * If the ban has expired, delete the banned thumbnail and de-activate the ban */ function check_for_ban($ip, $fields = array(), $thread_id = 0, $user_verified = false) { global $captcha_bypass; // Skip IP bans when the user has a valid 4chan Pass $skip_ip_bans = isset($fields['4pass_id']) && $captcha_bypass; if (!$skip_ip_bans) { $fields['host'] = $ip; } $expired = []; $is_banned = 0; foreach ($fields as $key => $value) { $query =<< ['pipe', 'w'], 2 => ['pipe', 'w'] ]; $pipes = []; $proc = proc_open($cmd, $desc, $pipes); if ($proc === false || !is_resource($proc)) { error(S_FAILEDUPLOAD, $file); } $stdout = stream_get_contents($pipes[1]); fclose($pipes[1]); $stderr = stream_get_contents($pipes[2]); fclose($pipes[2]); $status = proc_close($proc); if ($status !== 0) { error(S_FAILEDUPLOAD, $file); } // Check stderr if (stripos($stderr, 'invalid') !== false) { error(S_NOREC, $file); } // Check stdout $res = json_decode($stdout, true); if (json_last_error() !== JSON_ERROR_NONE) { error(S_FAILEDUPLOAD, $file); } //print_r($res); if ($res['format']['format_name'] === 'matroska,webm') { $format = 'webm'; } else if ($res['format']['format_name'] === 'mov,mp4,m4a,3gp,3g2,mj2') { $format = 'mp4'; } else { error(S_NOREC, $file); } // container duration, can be forged but we remux the file to fix this $duration = (float)$res['format']['duration']; if ($duration <= 0 || $duration > MAX_WEBM_DURATION) { error(S_VIDEOTOOLONG, $file); // Duration too long } $has_audio = false; $video_dims = false; foreach ($res['streams'] as $stream) { $type = $stream['codec_type']; if ($type === 'audio') { if (!ENABLE_WEBM_AUDIO) { error(S_AUDIODISABLED, $file); // Audio streams are not allowed } // Vorbis or Opus for webm audio if ($format === 'webm') { if ($stream['codec_name'] !== 'vorbis' && $stream['codec_name'] !== 'opus') { error(S_BADAUDIO, $file); // Bad audio stream } } // AAC for mp4 audio else if ($format === 'mp4') { if ($stream['codec_name'] !== 'aac') { error(S_BADAUDIO, $file); // Bad audio stream } } else { error(S_BADAUDIO, $file); // Bad audio stream } $has_audio = true; } else if ($type === 'video') { if ($video_dims) { error(S_BADSTREAM, $file); // Too many video streams } // VP8 or VP9 for webm video if ($format === 'webm') { if ($stream['codec_name'] !== 'vp8' && $stream['codec_name'] !== 'vp9') { error(S_BADVIDEO, $file); // Bad video stream } } // H264 for mp4 video else if ($format === 'mp4') { if ($stream['codec_name'] !== 'h264') { error(S_BADVIDEO, $file); // Bad video stream } // Reject 10 bit streams if ($stream['bits_per_raw_sample'] > 8) { error(S_NOREC, $file); } // Only accept yuv420p streams if ($stream['pix_fmt'] !== 'yuv420p') { error(S_NOREC, $file); } } else { error(S_BADVIDEO, $file); // Bad video stream } $width = (int)$stream['width']; $height = (int)$stream['height']; if (!$width || !$height || $width > MAX_WEBM_DIMENSION || $height > MAX_WEBM_DIMENSION) { error(S_TOOLARGERES, $file); // Dimensions too big } $sar = null; if (isset($stream['sample_aspect_ratio'])) { $tmp_sar = explode(':', $stream['sample_aspect_ratio']); $tmp_sar[0] = (int)$tmp_sar[0]; $tmp_sar[1] = (int)$tmp_sar[1]; if ($tmp_sar[1] && $tmp_sar[0] !== $tmp_sar[1]) { $tmp_sar = $tmp_sar[0] / $tmp_sar[1]; if ($tmp_sar < 2 && $tmp_sar > 0.5) { $sar = $tmp_sar; } } } $video_dims = array($width, $height, $sar); } else { error(S_BADSTREAM, $file); // Bad stream } } if (!$video_dims) { error(S_NOVIDEOSTREAM, $file); // No video streams } return $video_dims; } /** * Generates thumbnails for webm files * Returns the thumbnail filename on success. * Returns false if the thumbnail couldn't be generated. */ function thumb_webm($file, $ext) { $binary = '/usr/local/bin/ffmpeg-mp4'; $out_file = $file . '.tmp.jpg'; if ($ext === '.webm') { $format = 'webm'; } else if ($ext === '.mp4') { $format = 'mp4'; } else { return false; } // $file and $format must be safe for shell_exec $res = shell_exec("$binary -f $format -i \"$file\" -vframes 1 -an -y \"$out_file\" 2>&1"); if (file_exists($out_file)) { return $out_file; } quick_log_to( "/www/perhost/bad-upload.log", "webm failure on $file:\n$res"); return false; } /** * Contest banners 468x60 */ function get_contest_banner() { $query = "SELECT file_id, file_ext, board FROM contest_banners WHERE is_live = 1 ORDER BY RAND() LIMIT 1"; $res = mysql_global_call($query); if (!$res) { return ''; } $banner = mysql_fetch_assoc($res); if (!$banner) { return ''; } $img_url = STATIC_SERVER . "image/contest_banners/{$banner['file_id']}.{$banner['file_ext']}"; $link_url = '//boards.' . L::d($banner['board']) . '/' . $banner['board'] . '/'; return '
    '; } /** * Get latest post number from /j/ * Returns a json { "no": 123 } */ function get_last_post_no() { $no = 0; $query = "SELECT no FROM `j` ORDER BY no DESC LIMIT 1"; $res = mysql_board_call($query); if ($res) { if ($row = mysql_fetch_row($res)) { $no = (int)$row[0]; } } echo "{\"no\":$no}"; } /** * Deletes partial jsons for all live threads */ function purge_json_tails() { $query = 'SELECT no FROM `' . SQLLOG . '` WHERE resto = 0 AND archived = 0'; $res = mysql_board_call($query); if (!$res) { return false; } while ($row = mysql_fetch_row($res)) { $thread_id = (int)$row[0]; update_json_tail_deletion($thread_id, true); } return true; } /** * Deletes, if necessary, the partial json tail file after post deletion. */ function update_json_tail_deletion($thread_id, $force = false) { if (!JSON_TAIL_SIZE && !$force) { return false; } $tail_size = $force ? 0 : get_json_tail_size($thread_id); if (!$tail_size) { $fname = RES_DIR . $thread_id . '-tail.json'; if (USE_GZIP) { $fname = "$fname.gz"; } if (file_exists($fname)) { return unlink($fname); } } return false; } /** * Returns the number of posts in the partial -tail json. * 0 if no tail json is available */ function get_json_tail_size($thread_id) { global $log; $tail_size = (int)JSON_TAIL_SIZE; if (!$tail_size || !isset($log[$thread_id])) { return 0; } $th = $log[$thread_id]; if ($th['sticky'] && $th['undead']) { $tail_size = $tail_size * 2; } $post_count = count($th['children']); if ($post_count >= $tail_size * 2) { return $tail_size; } else { return 0; } } /** * Test function for mobile image resizing */ function resize_mobile_image($path, $w, $h, $fsize, $tim, $ext) { if ($ext !== '.jpg' && $ext !== '.png') { return false; } $MAX_W = 1024; $MAX_H = 1024; $MAX_PXL = 524288; $MAX_PNG_BYTES = 524288; if ($ext === '.png' && $fsize <= $MAX_PNG_BYTES) { return; } if (($w > $MAX_W || $h > $MAX_H) && $w * $h > $MAX_PXL) { $jpeg_quality = 80; $memory_limit_increased = false; if ($w * $h > 3000000) { $memory_limit_increased = true; ini_set('memory_limit', memory_get_usage() + $w * $h * 15); } if ($ext === '.jpg') { $img_in = ImageCreateFromJPEG($path); } else { $img_in = ImageCreateFromPNG($path); } if (!$img_in) { error(S_FAILEDUPLOAD . ' (rmi)', $path); } $ratio = $w / $h; if ($ratio > 1) { $out_w = $MAX_W; $out_h = round($MAX_W / $ratio); } else { $out_w = round($MAX_H * $ratio); $out_h = $MAX_H; } $img_out = ImageCreateTrueColor($out_w, $out_h); ImageCopyResampled($img_out, $img_in, 0, 0, 0, 0, $out_w, $out_h, $w, $h); ImageDestroy($img_in); $out_path = IMG_DIR . $tim . 'm.jpg'; ImageJPEG($img_out, $out_path, $jpeg_quality); ImageDestroy($img_out); if ($memory_limit_increased) { ini_restore('memory_limit'); } return $out_path; } } /** * Returns the number of unique IPs for a given thread id * $thread_id needs to be cached in $log. */ function get_unique_ip_count($thread_id) { global $log; if (!isset($log[$thread_id]) || $log[$thread_id]['archived']) { return false; } $posts = $log[$thread_id]['children']; if (empty($posts)) { return 1; } $ip_map = array(); $ip_count = 1; $ip_map[$log[$thread_id]['host']] = true; foreach ($posts as $pid => $val) { if (!isset($log[$pid])) { continue; } $post = $log[$pid]; if (!isset($ip_map[$post['host']])) { ++$ip_count; $ip_map[$post['host']] = true; } } return $ip_count; } function generate_del_pwd() { return '_' . substr(str_shuffle('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'), 0, 32); } function get_hashed_mod_name($name) { if (!$name) { die('Internal Server Error (ghmn0)'); } $admin_salt = file_get_contents('/www/keys/2014_admin.salt'); if (!$admin_salt) { die('Internal Server Error (ghmn1)'); } $hashed_bits = hash_hmac('sha256', $name, $admin_salt, true); $hashed_name = base64_encode($hashed_bits); if (!$hashed_name) { die('Internal Server Error (ghmn2)'); } return $hashed_name; } function create_memcached_instance() { $m = new Memcached(); //$m->setOption(Memcached::OPT_TCP_NODELAY, true); $m->setOption(Memcached::OPT_SERVER_FAILURE_LIMIT, 1); $m->setOption(Memcached::OPT_SEND_TIMEOUT, 500000); // 500ms $m->setOption(Memcached::OPT_RECV_TIMEOUT, 500000); // 500ms $m->addServer(MEMCACHED_HOST, MEMCACHED_PORT); return $m; } function forcearchive() { if (!has_level()) { error("Can't let you do that."); } if (!ENABLE_ARCHIVE) { error('Archives are disabled on this board.'); } if (!isset($_POST['id'])) { error('Bad Request.'); } $tid = (int)$_POST['id']; $query = 'SELECT resto, sticky, archived, no, name, sub, com, filename, ext FROM `%s` WHERE no = %d'; $res = mysql_board_call($query, BOARD_DIR, $tid); if (!$res) { error('Database error.'); } $thread = mysql_fetch_assoc($res); if (!$thread || $thread['resto']) { error('Thread not found.'); } if ($thread['archived']) { error('This thread is already archived.'); } if ($thread['sticky']) { error(S_MAYNOTDELSTICKY); } archive_thread($tid); // Log the action $action_log_post = array( 'no' => $thread['no'], 'name' => $thread['name'], 'sub' => $thread['sub'], 'com' => $thread['com'], 'filename' => $thread['filename'], 'ext' => $thread['ext'] ); log_mod_action(3, $action_log_post); if (ENABLE_JSON_THREADS) { generate_board_archived_json(); } updating_index(); } // Called remotely by other tools function rebuild_threads_by_id() { header('Content-Type: text/plain'); if (!isset($_POST['ids']) || !is_array($_POST['ids']) || empty($_POST['ids'])) { echo '0'; return; } $live_ids = array(); // Rebuild archived threads first foreach ($_POST['ids'] as $id) { $id = (int)$id; if (!$id) { continue; } $query = "SELECT archived FROM `" . SQLLOG . "` WHERE no = $id LIMIT 1"; $res = mysql_board_call($query); if (!$res) { echo '0'; return; } if (mysql_fetch_row($res)[0] === '1') { rebuild_archived_thread($id); } else { $live_ids[] = $id; } } // Rebuild live threads if (!empty($live_ids)) { foreach ($live_ids as $id) { updatelog($id, 1); } if (STATIC_REBUILD) { return; } updatelog(0, 0); // rebuild indexes } echo '1'; } function rebuild_archive_list($print = false) { $board = BOARD_DIR; $html = ''; $maxlen = 100; $max_age_in_days = 3; $hour_clause = $max_age_in_days * 24; $thread_limit = 3000; $query = <<= DATE_SUB(NOW(), INTERVAL $hour_clause HOUR) ORDER BY root DESC LIMIT $thread_limit SQL; $res = mysql_board_call($query); $thread_count = mysql_num_rows($res); head($html, 0, 0, 0, 0, true); $html .= ''; $html .= '

    '; $html .= '

    Displaying ' . number_format($thread_count) . ' expired thread' . (!$thread_count || $thread_count > 1 ? 's' : '') . ' from the past ' . $max_age_in_days . ' day' . ($max_age_in_days > 1 ? 's' : '') . '

    '; while ($row = mysql_fetch_assoc($res)) { if (strpos($row['sub'], 'SPOILER<>') === 0) { $row['sub'] = substr($row['sub'], 9); } if (!empty($row['sub'])) { if ($row['com'] !== '') { $teaser = '' . $row['sub'] . ': ' . $row['com']; } else { $teaser = $row['sub']; } } else { $teaser = $row['com']; } $teaser = preg_replace('/(?:
    )+/', ' ', str_replace('"', "'", $teaser)); $href_context = generate_href_context($row['sub'], $row['com']); if ($href_context !== '') { $href_context = "/$href_context"; } $html .= ''; } $html .= '
    No. Excerpt
    ' . $row['no'] . ' ' . truncate_comment($teaser, $maxlen) . ' [View]

    '; $html .= '
    '; $html .= '
    '; if (AD_BOTTOM_ENABLE == 1) { $bottomad = ''; if (defined('AD_BOTTOM_TEXT') && AD_BOTTOM_TEXT) { $bottomad .= '
    ' . ad_text_for(AD_BOTTOM_TEXT) . '
    ' . (defined('AD_BOTTOM_PLEA') ? AD_BOTTOM_PLEA : ''); } if ($bottomad) { $html .= "$bottomad
    "; } } $html .= '
    '; if (!defined('CSS_FORCE')) { $html .= 'Style: '; } $html .= '
    '; foot($html, false, true); if ($print) { echo $html; } else { print_page(INDEX_DIR . 'archive'. PHP_EXT, $html); } } function rebuild_syncframe_page($print = false) { $tpl_file = YOTSUBA_DIR . 'views/syncframe.html'; if (!file_exists($tpl_file)) { die('Template file not found'); } $html = file_get_contents($tpl_file); if ($print) { die($html); } else { print_page(BOARDS_ROOT . 'syncframe' . PHP_EXT, $html); } } function rebuild_search_page($print = false) { $html = ''; // board select box $board_select_html = ''; // header --- $cssVersion = $print ? CSS_VERSION_TEST : CSS_VERSION; $defaultcss = 'yotsubanew'; $mobilecss = 'yotsubamobile.' . $cssVersion . '.css'; $styles = array( 'Yotsuba New' => "yotsubanew.$cssVersion.css", 'Yotsuba B New' => "yotsubluenew.$cssVersion.css", 'Futaba New' => "futabanew.$cssVersion.css", 'Burichan New' => "burichannew.$cssVersion.css", 'Photon' => "photon.$cssVersion.css", 'Tomorrow' => "tomorrow.$cssVersion.css" ); $dcssl = $defaultcss . '.' . $cssVersion . '.css'; $css = ''; foreach ($styles as $style => $stylecss) { $css .= ''; } $css .= ''; $scriptjs = ''; $testjs = $print ? 'test/core-8psvqAqszI.' . JS_VERSION_TEST . '.js' : 'core.min.' . JS_VERSION_CORE . '.js'; $testextra = $print ? 'test/extension-8psvqAqszI.' . JS_VERSION_TEST . '.js' : 'extension.min.' . JS_VERSION_EXT . '.js'; $scriptjs .= ''; $scriptjs .= ''; if (defined('FAVICON')) { $favicon = ''; } else { $favicon = ''; } $includenav = file_get_contents_cached(NAV_TXT); $html .= ' ' . $favicon . ' ' . $css . ' Search 4chan' . $scriptjs . ' ' . $stylejs . $includenav . '
    4chan Search
    '; // --- $html .= '
    ' . $board_select_html . '
    '; $html .= '
    '; $html .= '

    '; $html .= '
    '; if (!defined('CSS_FORCE')) { $html .= 'Style: '; } $html .= '
    '; foot($html); if ($print) { echo $html; } else { print_page(BOARDS_ROOT . 'globalsearch' . PHP_EXT, $html); } } /** * Enforce the maximum number of allowed threads per user, per board. * error() if limit has been reached */ function validate_user_thread_limit($ip, $password = null, $pass_id = null) { $clauses = array(); $clauses[] = "host = '" . mysql_real_escape_string($ip) . "'"; if ($password) { $clauses[] = "pwd = '" . mysql_real_escape_string($password) . "'"; } if ($pass_id) { $clauses[] = "4pass_id = '" . mysql_real_escape_string($pass_id) . "'"; } $ts = $_SERVER['REQUEST_TIME'] - ((int)MAX_USER_THREADS_PERIOD * 3600); $clauses = implode(' OR ', $clauses); $query = 'SELECT COUNT(*) FROM `' . SQLLOG . "` WHERE resto = 0 AND archived = 0 AND time > $ts AND ($clauses)"; $res = mysql_board_call($query); if (!$res) { return true; } $count = (int)mysql_fetch_row($res)[0]; if ($count >= (int)MAX_USER_THREADS) { $plural = MAX_USER_THREADS > 1 ? 's' : ''; error(sprintf(S_TOOMANYTHREADS, MAX_USER_THREADS, $plural)); } return true; } function is_poster_op($host, $hashed_pwd, $resto) { $query = 'SELECT host, pwd FROM `%s` WHERE no = %d'; $res = mysql_board_call($query, SQLLOG, $resto); if (!$res) { return false; } $post = mysql_fetch_assoc($res); if (!$post) { return false; } return $post['host'] === $host || $post['pwd'] === $hashed_pwd; } function spam_filter_check_qa_bot($board, $resto, $ip, $country, $com, $captcha_resp) { if (preg_match('/Edge|Safari|WebKit|Firefox|Mozilla/', $_SERVER['HTTP_USER_AGENT']) && $captcha_resp['hostname'] === 'boards.4chan.org') { return true; } // Check if IP is known $long_ip = ip2long($ip); if (spam_filter_is_user_known($long_ip)) { return false; } if (!preg_match('/Edge|Mobile/', $_SERVER['HTTP_USER_AGENT']) && preg_match('/WebKit/', $_SERVER['HTTP_USER_AGENT']) !== preg_match('/WebKit/', $_SERVER['HTTP_CONTENT_TYPE'])) { return true; } $bot_countries = array( 'AD','AE','AF','AG','AI','AL','AM','AN','AO','AR','AS','AW','AZ', 'BB','BD','BF','BG','BH','BI','BJ','BM','BN','BO','BR','BS','BT','BV','BW','BY','BZ', 'CC','CF','CG','CH','CI','CK','CL','CM','CN','CO','CR','CU','CV','CX','CY','CZ', 'DJ','DM','DO','DZ','EC','EE','EG','EH','ER','ET','FJ','FM','FO', 'GA','GD','GE','GF','GH','GI','GL','GM','GN','GP','GQ','GR','GS','GT','GU','GY', 'HK','HM','HN','HT','HU','HR','ID','IL','IN','IO','IQ','IR','IS','JM','JO', 'KE','KG','KH','KI','KM','KN','KR','KW','KY','KZ', 'LA','LB','LC','LI','LK','LR','LS','LU','LY', 'MA','MD','MG','MH','MK','ML','MM','MN','MO','MP','MQ','MR','MS','MT','MU','MV','MW','MY','MZ','NA', 'NE','NF','NG','NI','NP','NR','NU','NZ','OM','PA','PE','PF','PG','PH','PK','PM','PN','PR','PS','PT','PW', 'QA','RE','RS','RO','RU','RW','SA','SB','SC','SD','SH','SI','SJ','SK','SL','SM','SN','SO','SR','ST','SV','SY','SZ', 'TC','TD','TF','TG','TJ','TM','TN','TO','TP','TR','TT','TV','TW','TZ','UG','UM','UY','UZ', 'VA','VE','VC','VG','VI','VN','VU','WF','WS','YE','YT','ZA','ZM','ZR','ZW' ); // Check country if (!in_array($country, $bot_countries)) { return false; } if ($resto) { $query = 'SELECT time FROM %s WHERE resto = %d ORDER BY no DESC LIMIT 1'; $res = mysql_board_call($query, $board, $resto); if (!$res) { return false; } $last_time = mysql_fetch_row($res); if (!$last_time) { return false; } $last_time = (int)$last_time[0]; if ($_SERVER['REQUEST_TIME'] - $last_time < 14400) { return false; } } return true; } function log_qa_spam_filter($is_hit, $thread_id, $ip, $country, $captcha_resp) { $_bot_headers = ''; foreach ($_SERVER as $_h_name => $_h_val) { if (substr($_h_name, 0, 5) == 'HTTP_') { $_bot_headers .= "$_h_name: $_h_val\n"; } } $_bot_headers .= "_Captcha: " . $captcha_resp['hostname'] . "\n"; $_bot_headers .= "_Country: $country\n"; log_spam_filter_trigger($is_hit ? 'blocked_qa' : 'ok_qa', BOARD_DIR, $thread_id, $ip, 1, $_bot_headers); } function log_spam_filter_trigger($action, $board, $thread_id, $ip, $arg_num, $meta = '') { $query = << 'error', 'message' => 'Nothing to do.'); echo json_encode($data); return; } $html = purify_html($html); $data = array('status' => 'success', 'data' => $html); echo json_encode($data); } function purify_html($html) { static $purifier = null; if ($purifier === null) { require_once 'lib/htmlpurifier/HTMLPurifier.standalone.php'; $config = HTMLPurifier_Config::createDefault(); $config->set('Cache.DefinitionImpl', null); $config->set('HTML.Doctype', 'HTML 4.01 Transitional'); $config->set('URI.AllowedSchemes', array('http' => true, 'https' => true)); $config->set('HTML.SafeIframe', true); $config->set('URI.SafeIframeRegexp', HTML_IFRAME_WHITELIST); $config->set('HTML.Allowed', HTML_WHITELIST); $config->set('CSS.AllowTricky', true); $config->set('CSS.Trusted', true); $config->set('Attr.AllowedFrameTargets', array('_blank')); $def = $config->getHTMLDefinition(true); $def->addAttribute('iframe', 'allowfullscreen', 'Bool'); $def->addElement('video', 'Block', 'Flow', 'Common', array( 'controls' => 'Bool', 'height' => 'Length', 'width' => 'Length', 'poster' => 'URI', 'autoplay' => 'Bool', 'loop' => 'Bool', 'muted' => 'Bool', 'src' => 'URI' )); $css_def = $config->getDefinition('CSS'); $css_def->info['color'] = new HTMLPurifier_AttrDef_CSS_Composite( array( new HTMLPurifier_AttrDef_Enum(array('transparent')), new HTMLPurifier_AttrDef_CSS_Color() ) ); $purifier = new HTMLPurifier($config); if (!$purifier) { error('Internal Server Error'); } } return $purifier->purify($html); } function count_thread_replies($board, $thread_id) { $thread_id = (int)$thread_id; if ($thread_id <= 0) { return 0; } $sql = "SELECT COUNT(*) as cnt FROM `%s` WHERE resto = $thread_id"; $res = mysql_board_call($sql, $board); if (!$res) { return 0; } return (int)mysql_fetch_row($res)[0]; } // TODO: remove later function check_safe_ua_sig($ua, $sig) { if (!$ua || !$sig) { return true; } $thres = 3; $ua_sig = "$ua.$sig"; $sql = "SELECT 1 FROM event_log WHERE type = 'log_safe_ua' AND ua_sig = '%s' LIMIT $thres"; $res = mysql_global_call($sql, $ua_sig); if (!$res) { return true; } if (mysql_num_rows($res) < $thres) { return false; } return true; } // April 2024 function april_2024_parse_email($email) { if ($email[0] != '$') { return 0; } $tag = substr(trim($email), 1); $stocks = april_2024_get_stock_list(); $idx = array_search($tag, $stocks); if ($idx === false) { return 0; } $count = april_2024_get_stock_count($tag); if ($count < 10) { return 0; } return 10000 + $idx; } function april_2024_get_stock_list() { static $stocks = [ 'PEPE', 'WOJK', 'ANIME', 'CHAD', 'CLOWN', 'LOL', 'SICP', 'AUTSM', 'BANE', 'CIA', 'BOOB', 'RDDT', 'DESU', 'JANNY', 'GME', 'CHUCK', 'YTSB', 'GACHI' ]; return $stocks; } function april_2024_get_stock_from_s4p($since4pass) { if ($since4pass < 10000) { return false; } $val = $since4pass - 10000; if ($val < 0) { return false; } $badges = april_2024_get_stock_list(); if ($val >= 0 && $val < count($badges)) { return $badges[$val]; } else { return false; } } function april_2024_get_name() { $net_worth = april_2024_get_net_worth(); if ($net_worth < 500) { return 'Destitute Investor'; } else if ($net_worth < 1500) { return 'Helpless Investor'; } else if ($net_worth < 5000) { return 'Poor Investor'; } else if ($net_worth < 50000) { return 'Fledgling Investor'; } else if ($net_worth < 500000) { return 'Aspiring Investor'; } else if ($net_worth < 2000000) { return 'Rich Investor'; } else if ($net_worth < 5000000) { return 'Anonymous Magnate'; } else { return 'Anonymous Mogul'; } } function april_2024_get_post_cls($since4pass) { $stock = april_2024_get_stock_from_s4p($since4pass); if ($stock) { return " p-xa24-$stock"; } else { return ''; } } function april_2024_get_name_badge($since4pass) { $stock = april_2024_get_stock_from_s4p($since4pass); if ($stock) { return " "; } else { return ''; } } function april_2024_get_stock_count($stock) { $userpwd = UserPwd::getSession(); if (!$userpwd || $userpwd->isNew()) { return 0; } $user_id = $userpwd->getPwd(); $sql =<<isNew()) { return 0; } $user_id = $userpwd->getPwd(); $sql =<< 0 SQL; $res = mysql_global_call($sql, $user_id); if (!$res) { return 0; } $stocks = []; while ($row = mysql_fetch_row($res)) { $stocks[$row[0]] = (int)$row[1]; } $sql =<< $count) { if (isset($prices[$stock])) { $net_worth += ($prices[$stock] * $count); } } return $net_worth; } // --- function clear_no_captcha_token() { setcookie('_ct', null, -3600, '/', '.' . L::d(BOARD_DIR)); } function generate_no_captcha_token() { if (BOARD_DIR === 'pol' || BOARD_DIR === 'b' || BOARD_DIR === 'r9k' || BOARD_DIR === 'bant') { return false; } $long_ip = ip2long($_SERVER['REMOTE_ADDR']); if (!$long_ip) { return false; } if (!spam_filter_is_user_known($long_ip, BOARD_DIR, null, 15)) { return false; } $salt = file_get_contents_cached(SALTFILE); if (!$salt) { return false; } $time = $_SERVER['REQUEST_TIME']; $msg = $_SERVER['REMOTE_ADDR'] . '.' . $time; $msg = hash_hmac('sha1', $msg, $salt); if (!$msg) { return false; } $msg = substr($msg, 0, 20) . '.' . $time; setcookie('_ct', $msg, $time + 300, '/', '.' . L::d(BOARD_DIR)); // 5 minutes } function verify_no_captcha_token($token) { list($hash, $ts) = explode('.', $token); $ts = (int)$ts; if (!$hash || !$ts) { return false; } if ($ts < $_SERVER['REQUEST_TIME'] - 300) { // 5 minutes return false; } $salt = file_get_contents_cached(SALTFILE); if (!$salt) { return false; } $msg = $_SERVER['REMOTE_ADDR'] . '.' . $ts; if (substr(hash_hmac('sha1', $msg, $salt), 0, 20) === $hash) { return true; } return false; } function get_random_real_name() { $first_name_nid = mt_rand(1, 1000); if (mt_rand(0, 999) < 10) { $type = 2; } else { $type = 1; } $query = "SELECT data FROM april_names WHERE nid = $first_name_nid AND type = $type"; $res = mysql_global_call($query); $first_name = mysql_fetch_row($res)[0]; if (!$first_name) { $first_name = 'Alberto'; } $last_name_nid = mt_rand(1, 1000); $query = "SELECT data FROM april_names WHERE nid = $last_name_nid AND type = 3"; $res = mysql_global_call($query); $last_name = mysql_fetch_row($res)[0]; if (!$last_name) { $last_name = 'Barbosa'; } return "$first_name $last_name"; } function log_mod_action($action_type, $post, $vip_capcode = false) { $mask_shift = 128; $action_id = $mask_shift + $action_type; $query =<<verifyCode($dec_secret, $otp, 1)) { error("Incorrect or expired OTP."); } } function validate_csrf() { if ($_SERVER['REQUEST_METHOD'] != 'POST') { error('Bad Request.'); } if (!isset($_COOKIE['_tkn']) || !isset($_POST['_tkn']) || $_COOKIE['_tkn'] == '' || $_POST['_tkn'] == '' || $_COOKIE['_tkn'] !== $_POST['_tkn']) { if (!is_local()) { error('Bad Request.'); } } } function validate_referer($strict = false) { if (!$strict && (!isset($_SERVER['HTTP_REFERER']) || $_SERVER['HTTP_REFERER'] == '')) { return; } if (!preg_match('/^https?:\/\/([_a-z0-9]+)\.(4chan|4channel)\.org(\/|$)/', $_SERVER['HTTP_REFERER'])) { error('Bad Request.'); } } function dev_make_remote_thumbnail() { if (!is_local()) { die('403'); } $infile = $_FILES['file']['tmp_name']; $file_ext = $_POST['file_ext']; $src_width = $_POST['src_width']; $src_height = $_POST['src_height']; $th_width = $_POST['th_width']; $th_height = $_POST['th_height']; if (!$infile || !$file_ext || !$src_width || !$src_height || !$th_width || !$th_height) { return; } $jpeg_quality = 65; switch ($file_ext) { case 'gif': $img_in = ImageCreateFromGIF($infile); break; case 'jpg': $img_in = ImageCreateFromJPEG($infile); break; case 'png': $img_in = ImageCreateFromPNG($infile); break; default : return; } if (!$img_in) { return; } $img_out = ImageCreateTrueColor($th_width, $th_height); if (!$img_out) { return; } ImageCopyResampled($img_out, $img_in, 0, 0, 0, 0, $th_width, $th_height, $src_width, $src_height); ImageDestroy($img_in); ImageJPEG($img_out, NULL, $jpeg_quality); ImageDestroy($img_out); } /*-----------Main-------------*/ switch( $mode ) { case 'make_remote_thumbnail': dev_make_remote_thumbnail(); die(); case 'purgejsontails': if (has_flag('developer')) { echo purge_json_tails() ? 'OK' : 'ERROR'; } die(); case 'listarchive': if (has_flag('developer')) { rebuild_archive_list(true); } die(); case 'search': if (has_flag('developer')) { rebuild_search_page(true); } die(); case 'rebuildsearchpage': if (has_flag('developer') || has_level('manager')) { rebuild_search_page(); echo 'done'; } die(); case 'rebuildsyncframepage': if (has_flag('developer') || has_level('manager')) { rebuild_syncframe_page(isset($_GET['print'])); echo 'done'; } die(); case 'rebuildarchivedthread': if (has_flag('developer') || has_level('manager')) { rebuild_archived_thread((int)$_GET['id']); echo 'done'; } die(); case 'regist': case 'post': require_request_method( "POST" ); validate_referer(); new_post( $name, $email, $sub, $com, '', $pwd, $upfile, $upfile_name, $resto, $age, $filetag ); break; case 'report': report(); break; case 'preview_html': preview_html(); break; case 'rebuild': require_request_method( "GET" ); rebuild(); break; case 'rebuildall': rebuild( 1 ); break; case 'rebuildadmin': rebuild_deletions( array($no => '1') ); echo 'Rebuilt OK!'; // shut rpc up break; case 'rebuildcatalog': if (ENABLE_CATALOG) { rebuild_catalog(); } break; case 'rebuildboardsjson': if (has_flag('developer')) { rebuild_boards_json(); } break; case 'rebuildthumb': rebuildallthumb(isset($_GET['archiveonly']) || isset($_ENV['archiveonly'])); break; case 'cataloginfo': get_catalog_info(); break; case 'admindel': case 'admindelete': user_delete( $no, $pwd ); echo ""; break; case 'updatelog': updatelog_remote( $no, $noidx ); break; case 'nothing': break; case 'arcdel': require_request_method( "POST" ); validate_referer(); arcdel($no, true, $res); break; case 'usrdel': case 'delete': require_request_method( "POST" ); validate_referer(); user_delete( $no, $pwd, true, $res ); break; case 'rebuild_threads_by_id': require_request_method( "POST" ); if (is_local()) { rebuild_threads_by_id(); } else { updating_index(); } break; case 'forcearchive': require_request_method( "POST" ); validate_referer(); //validate_csrf(); forcearchive(); break; case 'copythreads': require_request_method( "GET" ); do_copy_threads(); break; case 'movethread': validate_csrf(); do_move_thread(); break; case 'latest': if (has_level('janitor')) { get_last_post_no(); } die(); case 'rake_post': //april_rake_commit(); die('0\nNo'); break; default: require_request_method( "GET" ); if( JANITOR_BOARD == 1 && !has_level( 'janitor' ) ) { die( '' ); } if( $res ) { resredir( $res ); } else { updating_index(); } }