report.php
1 <?php 2 3 function report_get_style($board) { 4 $styles = array( 5 'Yotsuba' => STATIC_SERVER.'css/yotsuba.css', 6 'Yotsuba B' => STATIC_SERVER.'css/yotsublue.css', 7 'Futaba' => STATIC_SERVER.'css/futaba.css', 8 'Burichan' => STATIC_SERVER.'css/burichan.css', 9 ); 10 $board = mysql_real_escape_string($board); 11 $query = mysql_global_call("SELECT domain FROM boardlist where dir='$board'"); 12 list($domain) = mysql_fetch_row($query); 13 if(DEFAULT_BURICHAN == 1) 14 $styletitle = ($_COOKIE['ws_style']?$_COOKIE['ws_style']:'Yotsuba B'); 15 elseif($domain == 'may') 16 $styletitle = 'not4chan'; 17 else 18 $styletitle = ($_COOKIE['nws_style']?$_COOKIE['nws_style']:'Yotsuba'); 19 return $styles[$styletitle]; 20 } 21 22 function log_cleared_reporter($long_ip, $pwd, $pass_id, $cat_id, $weight) { 23 $sql = <<<SQL 24 INSERT INTO report_clear_log(long_ip, pwd, pass_id, category, weight) 25 VALUES(%d, '%s', '%s', %d, %F) 26 SQL; 27 28 return !!mysql_global_call($sql, $long_ip, $pwd, $pass_id, $cat_id, $weight); 29 } 30 31 function report_can_bypass_captcha($ip, $userpwd, $post) { 32 if (!$userpwd || !$post) { 33 return false; 34 } 35 36 if ($userpwd->ipLifetime() < 604800) { // 7 days 37 return false; 38 } 39 40 if (!$post['fsize']) { // only posts with images 41 return false; 42 } 43 44 $allowance = 3; 45 46 $long_ip = ip2long($ip); 47 48 if (!$long_ip) { 49 return false; 50 } 51 52 // Allow $allowance no-captcha reports for every hour of inactivity 53 $sql = <<<SQL 54 SELECT COUNT(*) as cnt FROM user_actions WHERE ip = $long_ip 55 AND action = 'report' 56 AND time > DATE_SUB(NOW(), INTERVAL 1 HOUR) 57 SQL; 58 59 $res = mysql_global_call($sql); 60 61 if (!$res) { 62 return false; 63 } 64 65 $row = mysql_fetch_row($res); 66 67 if (!$row || $row[0] >= $allowance) { 68 return false; 69 } 70 71 // Don't allow ips with 1 cleared reports in the past 72 hours 72 $sql = <<<SQL 73 SELECT COUNT(*) FROM report_clear_log 74 WHERE long_ip = $long_ip AND created_on > DATE_SUB(NOW(), INTERVAL 72 HOUR) 75 SQL; 76 77 $res = mysql_global_call($sql); 78 79 if (!$res) { 80 return false; 81 } 82 83 $count = (int)mysql_fetch_row($res)[0]; 84 85 if ($count >= 1) { 86 return false; 87 } 88 89 // Don't allow ips with recent warn/ban history 90 $sql = <<<SQL 91 SELECT no FROM banned_users 92 WHERE host = '%s' 93 AND now > DATE_SUB(NOW(), INTERVAL 30 DAY) 94 LIMIT 1 95 SQL; 96 97 $res = mysql_global_call($sql, $ip); 98 99 if (!$res) { 100 return false; 101 } 102 103 if (mysql_num_rows($res) > 0) { 104 return false; 105 } 106 107 return true; 108 } 109 110 function report_check_ip($board, $no, $check_ban = false, $is_illegal = false) { 111 global $captcha_bypass, $passid; 112 113 $board = mysql_real_escape_string($board); 114 115 $no = mysql_real_escape_string($no); 116 117 $ip = ip2long($_SERVER['REMOTE_ADDR']); 118 119 $pass_sql = false; 120 121 $pwd_sql = false; 122 123 // Check if already reported 124 // by IP 125 $rep_clauses = array("ip = '$ip'"); 126 127 // by 4chan pass 128 if ($captcha_bypass && $passid) { 129 $pass_sql = mysql_real_escape_string($passid); 130 $rep_clauses[] = "4pass_id = '$pass_sql'"; 131 } 132 133 // by password 134 $userpwd = UserPwd::getSession(); 135 136 if ($userpwd && $userpwd->getPwd()) { 137 $pwd_sql = mysql_real_escape_string($userpwd->getPwd()); 138 $rep_clauses[] = "pwd = '$pwd_sql'"; 139 } 140 141 $rep_clauses_sql = implode(' OR ', $rep_clauses); 142 143 $res = mysql_global_call("SELECT no FROM reports WHERE ($rep_clauses_sql) AND board = '$board' AND no = '$no'"); 144 145 if ($res && mysql_num_rows($res) > 0) { 146 fancydie('You have already reported this post.'); 147 } 148 149 // Check cooldown 150 $res = mysql_global_call("SELECT no FROM reports WHERE ($rep_clauses_sql) AND ts > DATE_SUB(NOW(), INTERVAL 15 SECOND) LIMIT 1"); 151 152 if ($res && mysql_num_rows($res) > 0) { 153 fancydie('You have to wait a while before reporting another post.'); 154 } 155 156 // Check hourly limits 157 $res = mysql_global_call("SELECT COUNT(*) FROM reports WHERE ($rep_clauses_sql) AND ts > DATE_SUB(NOW(), INTERVAL 1 HOUR) LIMIT 1"); 158 159 if ($res && mysql_fetch_row($res)[0] >= RENZOKU_REP_HOURLY) { 160 fancydie('You have to wait a while before reporting another post.'); 161 } 162 163 // Check daily limits 164 $res = mysql_global_call("SELECT COUNT(*) FROM reports WHERE ($rep_clauses_sql) AND ts > DATE_SUB(NOW(), INTERVAL 24 HOUR) LIMIT 1"); 165 166 if ($res && mysql_fetch_row($res)[0] >= RENZOKU_REP_DAILY) { 167 fancydie('You have to wait a while before reporting another post.'); 168 } 169 170 // Check if banned 171 if ($check_ban) { 172 $ip_sql = mysql_real_escape_string($_SERVER['REMOTE_ADDR']); 173 174 // by ip 175 $ban_clauses = array("host = '$ip_sql'"); 176 177 // by 4chan pass 178 if ($pass_sql) { 179 $ban_clauses[] = "4pass_id = '$pass_sql'"; 180 } 181 182 // by password 183 if ($pwd_sql) { 184 $ban_clauses[] = "password = '$pwd_sql'"; 185 } 186 187 $ban_clauses_sql = implode(' OR ', $ban_clauses); 188 189 $res = mysql_global_call("SELECT COUNT(*) FROM banned_users WHERE ($ban_clauses_sql) AND active = 1 AND (global = 1 OR board = '$board')"); 190 191 if ($res && mysql_fetch_row($res)[0] > 0) { 192 fancydie('You can\'t report posts because you are <a href="https://www.' . 193 L::d(BOARD_DIR) . 194 '/banned" target="_blank">banned</a>.'); 195 } 196 197 if ($captcha_bypass !== true) { 198 $longip = ip2long($_SERVER['REMOTE_ADDR']); 199 200 if (isset($_SERVER['HTTP_X_GEO_ASN'])) { 201 $asn = (int)$_SERVER['HTTP_X_GEO_ASN']; 202 } 203 else { 204 $_asninfo = GeoIP2::get_asn($_SERVER['REMOTE_ADDR']); 205 206 if ($_asninfo) { 207 $asn = (int)$_asninfo['asn']; 208 } 209 else { 210 $asn = 0; 211 } 212 } 213 214 if (isIPRangeBannedReport($longip, $asn, BOARD_DIR, $userpwd)) { 215 fancydie('Reporting from this IP range has been blocked due to abuse. [<a href="//www.' . 216 L::d(BOARD_DIR) . 217 '/faq#blocked" target="_blank">More Info</a>]<br>4chan Pass users can bypass this block. [<a href="https://www.4chan.org/pass" target="_blank">Learn More</a>]'); 218 } 219 } 220 } 221 } 222 223 function report_increment_counter() { 224 return; // broken lol 225 $count = @file_get_contents('reports/report.count'); 226 if(!$count) $count = 0; 227 $count++; 228 file_put_contents('reports/report.count',$count); 229 } 230 231 function report_post_exists($no) { 232 $query=mysql_board_call("SELECT COUNT(*) FROM `".SQLLOG."` WHERE no='$no'"); 233 return mysql_result($query,0,0); 234 } 235 236 function report_is_capcoded_post( $no ) 237 { 238 $query = mysql_board_call( "SELECT COUNT(*) FROM `%s` WHERE capcode != 'none' AND no=%d", SQLLOG, $no ); 239 return mysql_result( $query, 0, 0 ); 240 } 241 242 function report_check_autodelete($board,$no) { 243 $query = mysql_global_do("SELECT COUNT(*) FROM reports WHERE board='$board' AND no='$no'"); 244 $count = mysql_result($query,0,0); 245 246 if(defined('REPORTS_AUTODELETE') && $count >= REPORTS_AUTODELETE) { 247 report_do_autodelete($board,$no,1); 248 return; 249 } 250 251 $query = mysql_global_do("SELECT COUNT(*) FROM reports WHERE cat='2' AND board='$board' AND no='$no'"); 252 $count = mysql_result($query,0,0); 253 if(defined('REPORTS_AUTODELETE_ILLEGAL') && $count >= REPORTS_AUTODELETE_ILLEGAL) { 254 report_do_autodelete($board,$no,2); 255 return; 256 } 257 } 258 function report_do_autodelete($board,$no,$cat) { 259 $query = mysql_board_call("SELECT * FROM `".SQLLOG."` WHERE no='$no'"); 260 $row = mysql_fetch_assoc($query); 261 if(!$row) return; 262 $auser = 'Auto-del'; 263 $adfsize=($row['fsize']>0)?1:0; 264 $adname=str_replace('</span> <span class="postertrip">!','#',$row['name']); 265 $imgonly = 0; 266 $row['sub'] = mysql_escape_string($row['sub']); 267 $row['com'] = mysql_escape_string($row['com']); 268 $row['filename'] = mysql_escape_string($row['filename']); 269 mysql_global_do("INSERT INTO ".SQLLOGDEL." (imgonly,postno,board,name,sub,com,img,filename,admin) values('$imgonly','$no','".SQLLOG."','$adname','{$row['sub']}','{$row['com']}','$adfsize','{$row['filename']}','$auser')"); 270 delete_post($no, '', 0, 1, 1); 271 } 272 function report_log_action($board,$no) { 273 mysql_global_call("insert into user_actions (ip,board,action,postno,time) values (%d,'%s','report',%d,now())", ip2long($_SERVER["REMOTE_ADDR"]), $board, $no); 274 } 275 276 function report_post_sticky($no) { 277 $query=mysql_board_call("SELECT sticky FROM `".SQLLOG."` WHERE no='$no'"); 278 return mysql_result($query,0,0); 279 } 280 281 function report_check_post($board, $post_id) { 282 $sql = "SELECT * FROM `%s` WHERE no = %d"; 283 284 $res = mysql_board_call($sql, $board, $post_id); 285 286 if (!$res) { 287 fancydie(S_POST_DEAD); 288 } 289 290 $post = mysql_fetch_assoc($res); 291 292 if (!$post) { 293 fancydie(S_POST_DEAD); 294 } 295 296 if ($post['sticky']) { 297 fancydie(S_CANNOTREPORTSTICKY); 298 } 299 300 if ($post['capcode'] !== 'none') { 301 fancydie(S_CANNOTREPORT); 302 } 303 304 return $post; 305 } 306 307 function get_report_categories($board, $post_id, $is_worksafe) { 308 $query = "SELECT * FROM report_categories ORDER BY board ASC"; 309 310 $res = mysql_global_call($query); 311 312 if (!$res) { 313 return false; 314 } 315 316 $query = "SELECT resto, fsize, filedeleted FROM `%s` WHERE no = %d"; 317 318 $res2 = mysql_board_call($query, $board, $post_id); 319 320 if (!$res2) { 321 return false; 322 } 323 324 $post = mysql_fetch_assoc($res2); 325 326 if (!$post) { 327 return false; 328 } 329 330 $is_op = !$post['resto']; 331 $has_image = $post['fsize'] && !$post['filedeleted']; 332 333 // ID of the category which will be used for the Illegal radio button 334 $illegal_cat_id = 31; 335 336 // Rule violations + one illegal category 337 $data = array('rule' => null, 'illegal' => null); 338 339 // Sorting, board specific categories go on top 340 $data_rule_top = array(); 341 $data_rule_bottom = array(); 342 343 $match_board = ',' . $board . ','; 344 345 while ($cat = mysql_fetch_assoc($res)) { 346 if ($cat['id'] == $illegal_cat_id) { 347 $data['illegal'] = $cat; 348 continue; 349 } 350 351 if ($cat['board'] !== '') { 352 if ($cat['board'] === '_ws_') { 353 if (!$is_worksafe) { 354 continue; 355 } 356 } 357 else if ($cat['board'] === '_nws_') { 358 if ($is_worksafe) { 359 continue; 360 } 361 } 362 else if ($cat['board'] !== $board) { 363 continue; 364 } 365 } 366 367 if ($cat['op_only'] && !$is_op) { 368 continue; 369 } 370 371 if ($cat['reply_only'] && $is_op) { 372 continue; 373 } 374 375 if ($cat['image_only'] && !$has_image) { 376 continue; 377 } 378 379 if ($cat['exclude_boards'] && strpos(",{$cat['exclude_boards']},", $match_board) !== false) { 380 continue; 381 } 382 383 if ($cat['board']) { 384 $data_rule_top[$cat['id']] = $cat; 385 } 386 else { 387 $data_rule_bottom[$cat['id']] = $cat; 388 } 389 } 390 391 $data['rule'] = $data_rule_top + $data_rule_bottom; 392 393 return $data; 394 } 395 396 /** 397 * Checks if the report should have a different priority 398 * based on the number of cleared reports in the past X days and ban history. 399 */ 400 function is_report_filtered($filter_thres, $ip, $long_ip, $pass_id = null, $pwd = null) { 401 if ($filter_thres < 1) { 402 return false; 403 } 404 405 // only count reports made in the past X days 406 $cleared_days_lim = 2; 407 // number of cleared reports for the IP to be considered 'abusive' 408 $cleared_count_lim = (int)$filter_thres; 409 // only count bans made in the past X days 410 $ban_days_lim = 30; 411 // number of bans/warnings for the IP to be considered 'abusive' 412 $ban_count_lim = 3; 413 414 $rep_abuse_tpl = 190; // ban template for report abusing 415 416 $long_ip = (int)$long_ip; 417 418 $ban_clauses = array(); 419 $rep_clauses = array(); 420 421 // 4chan Pass 422 if ($pass_id) { 423 $pass_id_sql = mysql_real_escape_string($pass_id); 424 $ban_clauses[] = "4pass_id = '$pass_id_sql'"; 425 $rep_clauses[] = "pass_id = '$pass_id_sql'"; 426 427 $pwd_and_ban = "4pass_id != '$pass_id_sql'"; 428 $pwd_and_rep = "pass_id != '$pass_id_sql'"; 429 } 430 // IP 431 else { 432 $ip_sql = mysql_real_escape_string($ip); 433 $ban_clauses[] = "host = '$ip_sql'"; 434 $rep_clauses[] = "long_ip = $long_ip"; 435 436 $pwd_and_ban = "host != '$ip_sql'"; 437 $pwd_and_rep = "long_ip != $long_ip"; 438 } 439 440 // Password 441 if ($pwd) { 442 $pwd_sql = mysql_real_escape_string($pwd); 443 $ban_clauses[] = "password = '$pwd_sql' AND $pwd_and_ban"; 444 $rep_clauses[] = "pwd = '$pwd_sql' AND $pwd_and_rep"; 445 } 446 447 // --- 448 // Check cleared reports 449 // --- 450 $clear_count = 0; 451 452 foreach ($rep_clauses as $clause) { 453 $query = <<<SQL 454 SELECT COUNT(*) FROM report_clear_log 455 WHERE $clause AND created_on > DATE_SUB(NOW(), INTERVAL $cleared_days_lim DAY) 456 SQL; 457 458 $res = mysql_global_call($query); 459 460 if (!$res) { 461 return false; 462 } 463 464 $clear_count += (int)mysql_fetch_row($res)[0]; 465 466 if ($clear_count >= $cleared_count_lim) { 467 return true; 468 } 469 } 470 471 // --- 472 // Check ban history 473 // --- 474 $ban_count = 0; 475 476 foreach ($ban_clauses as $clause) { 477 $query = <<<SQL 478 SELECT COUNT(*) FROM banned_users 479 WHERE active = 0 AND $clause AND template_id = $rep_abuse_tpl 480 AND now > DATE_SUB(NOW(), INTERVAL $ban_days_lim DAY) 481 SQL; 482 483 $res = mysql_global_call($query); 484 485 if (!$res) { 486 return false; 487 } 488 489 $ban_count += (int)mysql_fetch_row($res)[0]; 490 491 if ($ban_count >= $ban_count_lim) { 492 return true; 493 } 494 } 495 496 return false; 497 } 498 499 function report_get_rel_sub($board, $thread_id) { 500 if (!$board || !$thread_id) { 501 return ''; 502 } 503 504 $thread_id = (int)$thread_id; 505 506 $query = "SELECT sub FROM `%s` WHERE no = $thread_id"; 507 508 $res = mysql_board_call($query, $board); 509 510 if (!$res || mysql_num_rows($res) !== 1) { 511 return ''; 512 } 513 514 return mysql_fetch_row($res)[0]; 515 } 516 517 function report_submit($board, $no, $cat_id) { 518 global $log, $passid; 519 520 $board = mysql_real_escape_string($board); 521 $no = (int)$no; 522 $long_ip = ip2long($_SERVER['REMOTE_ADDR']); 523 524 // check if the category is valid 525 $cats = get_report_categories($board, $no, DEFAULT_BURICHAN == 1); 526 527 if ($cats['illegal']['id'] == $cat_id) { 528 $old_cat = 2; // todo: remove later 529 $old_field = 'num_illegal'; // todo: remove later 530 $rep_cat = $cats['illegal']; 531 } 532 else if (isset($cats['rule'][$cat_id])) { 533 $old_cat = 1; 534 $old_field = 'num_rule'; 535 $rep_cat = $cats['rule'][$cat_id]; 536 } 537 else { 538 fancydie('Invalid category selected.'); 539 } 540 541 if (!$no) { 542 fancydie(S_POST_DEAD); 543 } 544 545 log_cache(0, $no, 2); 546 547 if ($log[$no]['archived']) { 548 $extra = array('archived' => 1); 549 } 550 else { 551 $extra = array(); 552 } 553 554 $resto = (int)$log[$no]['resto']; 555 556 $post_data = generate_post_json($log[$no], $log[$no]['resto'] ? $log[$no]['resto'] : $no, $extra); 557 558 if ($log[$no]['resto']) { 559 $rel_sub = report_get_rel_sub($board, $log[$no]['resto']); 560 561 if ($rel_sub !== '') { 562 $post_data['rel_sub'] = $rel_sub; 563 } 564 } 565 566 $json = json_encode($post_data); 567 568 $weight = $rep_cat['weight']; 569 570 $is_staff = has_level('janitor'); 571 572 $req_sig = spam_filter_get_req_sig(); 573 574 $userpwd = UserPwd::getSession(); 575 576 if ($userpwd) { 577 $pwd = $userpwd->getPwd(); 578 $is_new_pwd = $userpwd->isNew(); 579 $is_known_pwd = $userpwd->isUserKnownOrVerified(60); 580 } 581 else { 582 $pwd = null; 583 $is_new_pwd = true; 584 $is_known_pwd = false; 585 } 586 587 if (!$is_staff) { 588 $ignore_reason = 0; 589 590 $_threat_score = spam_filter_get_threat_score(null, true, false); 591 592 if (!$is_known_pwd) { 593 $ignore_reason = 1; 594 } 595 else if ($_threat_score >= 0.4) { 596 $ignore_reason = 2; 597 } 598 else if ($rep_cat['filtered']) { 599 if (is_report_filtered($rep_cat['filtered'], $_SERVER['REMOTE_ADDR'], $long_ip, $passid, $is_new_pwd ? null : $pwd)) { 600 $ignore_reason = 3; 601 } 602 } 603 } 604 605 if ($ignore_reason > 0) { 606 $weight = 0.5; 607 if ($ignore_reason == 2) { 608 $_bot_headers = spam_filter_format_http_headers($log[$no]['com'], '', '', $_threat_score, $req_sig); 609 log_spam_filter_trigger('ignore_report_score', BOARD_DIR, $no, $_SERVER['REMOTE_ADDR'], $ignore_reason, $_bot_headers); 610 } 611 } 612 613 // Check if the post was already reported and cleared 614 $is_cleared = 0; 615 $cleared_by = ''; 616 617 $query = "SELECT cleared_by FROM reports WHERE board = '$board' AND no = $no AND cleared = 1 LIMIT 1"; 618 619 $res = mysql_global_call($query); 620 621 if ($res) { 622 $row = mysql_fetch_row($res); 623 624 if ($row) { 625 $is_cleared = 1; 626 $cleared_by = $row[0]; 627 log_cleared_reporter($long_ip, $pwd, $passid, $rep_cat['id'], $weight); 628 } 629 } 630 631 $is_ws = DEFAULT_BURICHAN == 1 ? 1 : 0; 632 633 $query = <<<SQL 634 INSERT IGNORE INTO reports 635 SET ip = %d, pwd = '%s', 4pass_id = '%s', req_sig = '%s', board = '%s', no = %d, resto = %d, 636 cat = %d, weight = %F, report_category = %d, ws = $is_ws, post_ip = %d, post_json = '%s', 637 cleared = $is_cleared, cleared_by = '%s' 638 SQL; 639 640 $res = mysql_global_call($query, 641 $long_ip, $pwd, $passid, $req_sig, $board, $no, $resto, 642 $old_cat, $weight, $rep_cat['id'], ip2long($log[$no]['host']), $json, $cleared_by 643 ); 644 645 if (!$res) { 646 fancydie('There was an error submitting your report. Please try again.'); 647 } 648 649 $query = <<<SQL 650 INSERT INTO `reports_for_posts` (`board`, `postid`, `threadid`, `$old_field`, `max_cat`) 651 VALUES ('$board', $no, $resto, 1, $old_cat) 652 ON DUPLICATE KEY UPDATE $old_field = $old_field + 1, max_cat = IF(num_illegal >= num_rule, 2, 1) 653 SQL; 654 655 $res = mysql_global_call($query); 656 657 report_log_action($board, $no); 658 659 if ($userpwd) { 660 $userpwd->updateReportActivity(); 661 $userpwd->setCookie('.' . MAIN_DOMAIN); 662 } 663 664 fancydie('Report submitted! This window will close in 3 seconds...', 1); 665 }