like_score-test.php
1 <?php 2 3 define('LIKE_MAX_LIKES', 1048576); 4 define('LIKE_LIKE_SCORE', 10); 5 define('LIKE_POST_SCORE', 2); 6 define('LIKE_GIVE_SCORE', 1); 7 define('LIKE_COOLDOWN_SEC', 120); 8 9 /** 10 * April 2019 11 */ 12 function like_commit() { 13 global $captcha_bypass, $passid; 14 15 // FIXME 16 //$_POST['post_id'] = $_GET['post_id']; 17 18 // Basic validation of inputs 19 if (!isset($_POST['post_id']) || !isset($_COOKIE['xa19'])) { 20 die("0\nBad Request."); 21 } 22 23 $post_id = (int)$_POST['post_id']; 24 25 26 if (!$post_id) { 27 die("0\nBad Post ID."); 28 } 29 30 if ($_COOKIE['xa19'] !== $_POST['post_id']) { 31 die("0\nBad Request."); 32 } 33 34 $is_suspicious = 0; 35 36 $user_id = $_SERVER['REMOTE_ADDR']; 37 38 // Check captcha 39 if ($passid || isset($_COOKIE['4chan_auser']) || !like_is_ip_known($_SERVER['REMOTE_ADDR'])) { 40 start_auth_captcha(); 41 42 43 if (!$captcha_bypass) { 44 end_recaptcha_verify(); 45 /* 46 if (like_is_ip_suspicious($ip)) { 47 $is_suspicious = 1; 48 } 49 */ 50 } 51 else if ($passid) { 52 $user_id = $passid; 53 } 54 } 55 56 // Check if post exists and not trying to like own posts 57 $target_info = like_get_target_user_id(BOARD_DIR, $post_id); 58 59 if (!$target_info) { 60 die("0\nYou can't like this post."); 61 } 62 63 list($target_user_id, $target_ip) = $target_info; 64 65 if ($target_user_id == $user_id || $target_ip == $_SERVER['REMOTE_ADDR']) { 66 die("0\nYou can't like your own posts."); 67 } 68 69 // Check if duplicate 70 if (like_is_duplicate($user_id, BOARD_DIR, $post_id)) { 71 die("0\nYou already like this post."); 72 } 73 74 // Check cooldowns and other abuse 75 if (like_is_abusive($_SERVER['REMOTE_ADDR'], $user_id, $target_user_id, $post_id, $captcha_bypass)) { 76 die("0\nYou have to wait a while before liking this post."); 77 } 78 79 // Update the score 80 $new_like_count = like_update_like_score($user_id, $target_user_id, BOARD_DIR, $post_id, $is_suspicious); 81 82 if (!$new_like_count) { 83 die("0\nInternal Server Error."); 84 } 85 86 $user_score = like_get_user_score(); 87 $user_perks = implode(' ', like_get_perks_state($user_score, true)); 88 89 log_cache(0, $post_id); 90 updatelog($post_id, 1); 91 92 echo "1\n$new_like_count\n$user_score\n$user_perks"; 93 } 94 95 // Called after a user makes a new post 96 // Increases the user score by LIKE_POST_SCORE 97 function like_update_post_score() { 98 global $captcha_bypass, $passid; 99 100 $skip_boards = array('b', 'qa', 's4s', 'bant', 'vip'); 101 102 if (in_array(BOARD_DIR, $skip_boards)) { 103 return false; 104 } 105 106 $add_score = LIKE_POST_SCORE; 107 108 if ($captcha_bypass && $passid) { 109 $user_id = $passid; 110 } 111 else { 112 $user_id = $_SERVER['REMOTE_ADDR']; 113 } 114 115 $query = <<<SQL 116 INSERT INTO `like_user_scores` (`user_id`, `user_score`) 117 VALUES ('%s', $add_score) 118 ON DUPLICATE KEY UPDATE user_score = user_score + $add_score 119 SQL; 120 121 $res = mysql_global_call($query, $user_id); 122 123 if (!$res) { 124 return false; 125 } 126 127 return true; 128 } 129 130 function like_update_like_score($user_id, $target_user_id, $board, $post_id, $is_suspicious) { 131 $add_score = LIKE_LIKE_SCORE; 132 $add_score_giver = LIKE_GIVE_SCORE; 133 134 // Insert log entry 135 $query = <<<SQL 136 INSERT INTO `like_user_log` (`user_id`, `target_user_id`, `suspicious`, `board`, `post_id`) 137 VALUES ('%s', '%s', $is_suspicious, '$board', $post_id) 138 SQL; 139 140 $res = mysql_global_call($query, $user_id, $target_user_id); 141 142 if (!$res) { 143 die("0\nDatabase Error (luls2)."); 144 } 145 146 // Update post like count 147 $query = "SELECT email, resto, archived FROM `$board` WHERE no = $post_id LIMIT 1"; 148 149 $res = mysql_board_call($query); 150 151 if (!$res) { 152 die("0\nDatabase Error (luls0)."); 153 } 154 155 $post = mysql_fetch_assoc($res); 156 157 if (!$post) { 158 die("0\nThis post doesn't exist anymore."); 159 } 160 161 if ($post['resto'] == 0 || $post['archived']) { 162 die("0\nYou can't like this post."); 163 } 164 165 list($user_score, $post_likes) = explode('.', $post['email']); 166 167 $user_score = (int)$user_score; 168 $post_likes = ((int)$post_likes) + 1; 169 170 if ($post_likes >= LIKE_MAX_LIKES) { 171 die("0\nYou can't like this post anymore."); 172 } 173 174 $email = "$user_score.$post_likes"; 175 176 $query = "UPDATE `$board` SET email = '$email' WHERE no = $post_id LIMIT 1"; 177 178 $res = mysql_board_call($query); 179 180 if (!$res) { 181 die("0\nDatabase Error (luls1)."); 182 } 183 184 // --- 185 186 $skip_boards = array('b', 'qa', 's4s', 'bant', 'vip'); 187 188 if (in_array($board, $skip_boards)) { 189 return $post_likes; 190 } 191 192 // Update user score 193 $query = <<<SQL 194 INSERT INTO `like_user_scores` (`user_id`, `user_score`) 195 VALUES ('%s', $add_score_giver) 196 ON DUPLICATE KEY UPDATE user_score = user_score + $add_score_giver 197 SQL; 198 199 $res = mysql_global_call($query, $user_id); 200 201 if (!$res) { 202 die("0\nDatabase Error (luls2)."); 203 } 204 205 // Update target user score 206 $query = <<<SQL 207 INSERT INTO `like_user_scores` (`user_id`, `user_score`) 208 VALUES ('%s', $add_score) 209 ON DUPLICATE KEY UPDATE user_score = user_score + $add_score 210 SQL; 211 212 $res = mysql_global_call($query, $target_user_id); 213 214 if (!$res) { 215 die("0\nDatabase Error (luls3)."); 216 } 217 218 return $post_likes; 219 } 220 221 function like_is_abusive($ip, $user_id, $target_user_id, $post_id, $pass_user = false) { 222 // Check cooldown 223 if ($user_id !== $ip) { 224 $or_clause = "OR user_id = '%s'"; 225 } 226 else { 227 $or_clause = ''; 228 } 229 230 $cd = LIKE_COOLDOWN_SEC; 231 232 $query = <<<SQL 233 SELECT id FROM like_user_log 234 WHERE (user_id = '%s' $or_clause) 235 AND created_on > DATE_SUB(NOW(), INTERVAL $cd SECOND) 236 LIMIT 1 237 SQL; 238 239 if ($or_clause) { 240 $res = mysql_global_call($query, $user_id, $ip); 241 } 242 else { 243 $res = mysql_global_call($query, $user_id); 244 } 245 246 if (!$res) { 247 die("0\nDabase Error (lia0)"); 248 } 249 250 if (mysql_num_rows($res)) { 251 return true; 252 } 253 254 // Rangebans 255 if (!$pass_user) { 256 $long_ip = ip2long($ip); 257 258 if ($long_ip) { 259 if (isIPRangeBanned($long_ip)) { 260 return true; 261 } 262 } 263 } 264 265 // IP cycling 266 /* 267 $user_mask = explode('.', $ip); 268 269 $user_mask = ((int)$user_mask[0]) . '.' . ((int)$user_mask[1]); 270 271 $query = <<<SQL 272 SELECT COUNT(*) as cnt FROM like_user_log 273 WHERE post_id = $post_id AND user_id LIKE '$user_mask.%%' 274 AND created_on > DATE_SUB(NOW(), INTERVAL 10 MINUTE) 275 SQL; 276 277 $res = mysql_global_call($query); 278 279 if (!$res) { 280 die("0\nDabase Error (lia1)"); 281 } 282 283 $row = mysql_fetch_row($res); 284 285 if ($row && (int)$row[0] > 10) { 286 return true; 287 } 288 */ 289 // Proxies 290 /* 291 $query = <<<SQL 292 SELECT COUNT(*) as cnt FROM like_user_log 293 WHERE target_user_id = '%s' AND suspicious = 1 294 AND created_on > DATE_SUB(NOW(), INTERVAL 5 MINUTE) 295 SQL; 296 297 $res = mysql_global_call($query, $target_user_id); 298 299 if (!$res) { 300 die("0\nDabase Error (lia1)"); 301 } 302 303 $row = mysql_fetch_row($res); 304 305 if ($row && (int)$row[0] > 10) { 306 return true; 307 } 308 */ 309 return false; 310 } 311 312 function like_get_target_user_id($board, $post_id) { 313 // board and post_id params should already be escaped 314 $query = "SELECT host, 4pass_id FROM `$board` WHERE no = $post_id AND resto > 0 AND archived = 0 LIMIT 1"; 315 316 $res = mysql_board_call($query); 317 318 if (!$res) { 319 die("0\nDabase Error (lgtu)"); 320 } 321 322 $row = mysql_fetch_row($res); 323 324 if (!$row) { 325 return false; 326 } 327 328 if ($row[1]) { 329 return array($row[1], $row[0]); 330 } 331 else if ($row[0]) { 332 return array($row[0], $row[0]); 333 } 334 335 return false; 336 } 337 338 function like_is_duplicate($user_id, $board, $post_id) { 339 // board and post_id params should already be escaped 340 $query = "SELECT id FROM like_user_log WHERE user_id = '%s' AND board = '$board' AND post_id = $post_id LIMIT 1"; 341 342 $res = mysql_global_call($query, $user_id); 343 344 if (!$res) { 345 die("0\nDabase Error (lid)"); 346 } 347 348 return mysql_num_rows($res) === 1; 349 } 350 351 function like_is_ip_suspicious($ip) { 352 $bot_countries = array( 353 'AD','AE','AF','AG','AI','AL','AM','AN','AO','AR','AS','AW','AZ', 354 'BB','BD','BF','BG','BH','BI','BJ','BM','BN','BO','BR','BS','BT','BV','BW','BY','BZ', 355 'CC','CF','CG','CI','CK','CL','CM','CN','CO','CR','CU','CV','CX','CY','CZ', 356 'DJ','DM','DO','DZ','EC','EE','EG','EH','ER','ET','FJ','FM','FO', 357 'GA','GD','GE','GF','GH','GI','GL','GM','GN','GP','GQ','GR','GS','GT','GU','GY', 358 'HK','HM','HN','HT','HU','HR','ID','IL','IN','IO','IQ','IR','IS','JM','JO','JP', 359 'KE','KG','KH','KI','KM','KN','KR','KW','KY','KZ', 360 'LA','LB','LC','LI','LK','LR','LS','LU','LY', 361 'MA','MD','MG','MH','MK','ML','MM','MN','MO','MP','MQ','MR','MS','MT','MU','MV','MW','MY','MZ','NA', 362 'NE','NF','NG','NI','NP','NR','NU','NZ','OM','PA','PE','PF','PG','PH','PK','PM','PN','PR','PS','PT','PW', 363 'QA','RE','RS','RO','RU','RW','SA','SB','SC','SD','SH','SI','SJ','SK','SL','SM','SN','SO','SR','ST','SV','SY','SZ', 364 'TC','TD','TF','TG','TJ','TM','TN','TO','TP','TR','TT','TV','TW','TZ','UG','UM','UY','UZ', 365 'VA','VE','VC','VG','VI','VN','VU','WF','WS','YE','YT','ZA','ZM','ZR','ZW' 366 ); 367 368 if (!isset($_COOKIE['__cfduid'])) { 369 return true; 370 } 371 372 $country = geoip_country_code_by_addr($ip); 373 374 if (!$country) { 375 return true; 376 } 377 378 if (in_array($country, $bot_countries)) { 379 return true; 380 } 381 382 return false; 383 } 384 385 function like_is_ip_known($ip) { 386 $query = "SELECT id FROM like_user_log WHERE user_id = '%s' LIMIT 1"; 387 388 $res = mysql_global_call($query, $ip); 389 390 if (!$res) { 391 die("0\nDabase Error (lik)"); 392 } 393 394 return mysql_num_rows($res) === 1; 395 } 396 397 function like_get_user_score($no_cache = false) { 398 global $captcha_bypass, $passid; 399 400 // FIXME 401 return 0; 402 403 static $current_score = -1; 404 405 if ($current_score !== -1 && $no_cache !== true) { 406 return $current_score; 407 } 408 409 if ($captcha_bypass && $passid) { 410 $user_id = $passid; 411 } 412 else { 413 $user_id = $_SERVER['REMOTE_ADDR']; 414 } 415 416 $query = "SELECT user_score FROM like_user_scores WHERE user_id = '%s'"; 417 418 $res = mysql_global_call($query, $user_id); 419 420 if (!$res) { 421 return 0; 422 } 423 424 $row = mysql_fetch_row($res); 425 426 if ($row) { 427 $current_score = (int)$row[0]; 428 } 429 else { 430 $current_score = 0; 431 } 432 433 return $current_score; 434 } 435 436 function like_decrease_user_score($ip, $passid, $multiplier) { 437 if ($passid) { 438 $user_id = $passid; 439 } 440 else { 441 $user_id = $ip; 442 } 443 444 $query = <<<SQL 445 UPDATE like_user_scores SET user_score = CEIL(user_score * $multiplier) 446 WHERE user_id = '%s' LIMIT 1 447 SQL; 448 449 return !!mysql_global_call($query, $user_id); 450 } 451 452 function like_get_perks_state($current_score = null, $only_unlocked = false) { 453 if ($current_score === null) { 454 $current_score = like_get_user_score(); 455 } 456 457 $req_points = array( 458 //'showscore' => 0, 459 'smiley' => 0, // single emoji 460 'sad' => 0, // single emoji 461 //'coinflip' => 0, 462 //'dice+1d6' => 0, 463 'ok' => 0, // single emoji 464 'animal' => 0, // random animal emoji 465 'food' => 0, // random food emoji 466 'check' => 0, 467 'cross' => 0, 468 //'nofile' => 0, 469 //'card' => 0, // random playing card emoji 470 //'wflag' => 0, 471 //'bflag' => 0, 472 'like' => 0, // red heart emoji 473 //'rabbit' => 0, // single emoji 474 'unlove' => 0, // broken heart emoji 475 'rage' => 0, 476 'perfect' => 0, 477 //'fortune' => 0, 478 //'dice+1d100' => 0, 479 //'bricks' => 0, 480 'onsen' => 0, 481 //'party' => 0, // partyhat image 482 //'verified' => 0, 483 //'partyhat' => 0, // partyhat image, adjusted 484 //'pickle' => 0, // pickle rick image 485 //'trash' => 0, // trashcan image 486 'heart' => 0, // random heart emoji (different colors) 487 //'santa' => 0, // santa hat image 488 'joy' => 0, // single emoji 489 //'marquee' => 0, 490 'pig' => 0, // single emoji 491 'dog' => 0, // single emoji 492 'cat' => 0, // single emoji 493 'rainbow' => 0, 494 'frog' => 0, // single emoji 495 //'dino' => 750, // dinosaur gif from /fit/ 496 //'spooky' => 1000, // random skeleton 497 ); 498 499 $perks = array(); 500 501 if ($only_unlocked) { 502 foreach ($req_points as $perk => $score) { 503 if ($current_score >= $score) { 504 $perks[] = $perk; 505 } 506 } 507 } 508 else { 509 foreach ($req_points as $perk => $score) { 510 $perks[$perk] = $current_score >= $score; 511 } 512 } 513 514 return $perks; 515 } 516 517 function like_parse_options_field($options) { 518 $show_score = false; 519 $active_perk = null; 520 521 if (strlen($options) > 100) { 522 return array($show_score, $active_perk); 523 } 524 525 $user_perks = like_get_perks_state(); 526 527 $opts = explode(' ', $options); 528 529 foreach ($opts as $opt) { 530 if ($user_perks[$opt] === true) { 531 if ($opt === 'showscore') { 532 $show_score = true; 533 } 534 else { 535 $active_perk = $opt; 536 break; 537 } 538 } 539 } 540 541 return array($show_score, $active_perk); 542 } 543 544 function like_build_perk_html($perk) { 545 $cnt_attrs = ''; 546 547 switch ($perk) { 548 case 'animal': 549 $ary = array('🐭','🐹','🐰','🐶','🐺','🦊','🐵','🐸','🙈','🙉','🙊','🐯','🦁','🦓','🦒','🐴','🐮','🐷','🐻','🐼','🐲','🦄','🐱','😸','😹','😺','😻','😼','😽','😾','😿','🙀','🐅','🐆','🐘','🦏','🐂','🐃','🐄','🐎','🦌','🐐','🐏','🐑','🐖','🐗','🐪','🐫','🦍','🐉','🦖','🦕','🐈','🐀','🐁','🐇','🐒','🐕','🐩','🐨','🐿','🦔','🦇','🐍','🦅','🦉','🦆','🐓','🐔','🦃','🕊','🐣','🐤','🐥','🐦','🐧','🐋','🐳','🐬','🦈','🐟','🐠','🐡','🐙','🦑','🦐','🦀','🐚','🐌','🐢','🦎','🐊','🏇','🎠','♘','♞','🐽','🐾','👣','🐀','🐃','🐅','🐇','🐉','🐍','🐎','🐐','🐒','🐓','🐕','🐖'); 550 $html = $ary[array_rand($ary)]; 551 break; 552 case 'food': 553 $ary = array('🧀','🥚','🍳','🥞','🍠','🍞','🥐','🥖','🥨','🍔','🍕','🍝','🍟','🍤','🌭','🌮','🌯','🍛','🥙','🥘','🥗','🥪','🥫','🥓','🍖','🍗','🥩','🥢','🥡','🥟','🍚','🍜','🍲','🥠','🍘','🍙','🍣','🍥','🍱','🍡','🍢','🍇','🍈','🍉','🍊','🍋','🍌','🍍','🍎','🍏','🍐','🍑','🍒','🍓','🥝','🥥','🥦','🍄','🍅','🍆','🌶','🥑','🥕','🥒','🥔','🥜','🍰','🎂','🥧','🍨','🍦','🍩','🍪','🍿','🍮','🍯','🍧','🍫','🍬','🍭','🍺','🍻','🍷','🍸','🍹','🍶','🥂','🥃','🍾','☕','🍵','🥛','🍼','🥤','🍴','🍽','🥣','🥄'); 554 $html = $ary[array_rand($ary)]; 555 break; 556 case 'marquee': 557 $ary = array('🦖','⚽','🏀','⚾'); 558 $ico = $ary[array_rand($ary)]; 559 $html = <<<HTML 560 <marquee direction="left" width="250" height="50" behavior="alternate"> 561 <marquee direction="down" height="50" behavior="alternate">$ico</marquee> 562 </marquee> 563 HTML; 564 break; 565 case 'rainbow': 566 $html = '🌈'; 567 break; 568 case 'wflag': 569 $html = '🏳️'; 570 break; 571 case 'bflag': 572 $html = '🏴'; 573 break; 574 case 'onsen': 575 $html = '♨️'; 576 break; 577 case 'rage': 578 $html = '💢'; 579 break; 580 case 'perfect': 581 $html = '💯'; 582 break; 583 case 'check': 584 $html = '✔️'; 585 break; 586 case 'cross': 587 $html = '❌'; 588 break; 589 case 'heart': 590 $ary = array('❤️','💙','💜','💛','🖤','💚'); 591 $html = $ary[array_rand($ary)]; 592 break; 593 case 'card': 594 $ary = array('♠️','♥️','♦️','♣️'); 595 $html = $ary[array_rand($ary)]; 596 break; 597 case 'like': 598 $html = '❤️'; 599 break; 600 case 'unlove': 601 $html = '💔'; 602 break; 603 case 'smiley': 604 $html = '😃'; 605 break; 606 case 'sad': 607 $html = '🙁'; 608 break; 609 case 'ok': 610 $html = '👌'; 611 break; 612 case 'coinflip': 613 $html = '<b style="font-size: 14px">Coin Flip: ' . (mt_rand(0, 1) === 1 ? 'Heads' : 'Tails') . '</b>'; 614 break; 615 case 'party': 616 $html = '<img alt="" width="160" height="160" src="//s.4cdn.org/image/partyhat.gif">'; 617 break; 618 case 'partyhat': 619 $cnt_attrs = ' style="position:absolute"'; 620 $html = '<img alt="" style="position:absolute;margin-left:-25px;margin-top:-80px;pointer-events:none;" width="80" height="80" src="//s.4cdn.org/image/partyhat.gif">'; 621 break; 622 case 'pickle': 623 $html = '<img alt="" width="32" height="32" src="//s.4cdn.org/image/pckl.png">'; 624 break; 625 case 'nofile': 626 $html = '<img alt="" width="77" height="13" src="//s.4cdn.org/image/nofile.png">'; 627 break; 628 case 'trash': 629 $html = '<img alt="" width="32" height="32" src="//s.4cdn.org/image/trash@2x.gif">'; 630 break; 631 case 'bricks': 632 $html = '<img alt="" width="60" height="60" src="//s.4cdn.org/image/ba.gif">'; 633 break; 634 case 'pig': 635 $html = '🐷'; 636 break; 637 case 'santa': 638 $html = '<img alt="" width="160" height="160" src="//s.4cdn.org/image/xmashat.gif">'; 639 break; 640 case 'verified': 641 $html = '<div style="text-align:right"><img alt="" width="32" height="32" src="//s.4cdn.org/image/temp/verified.png"></div>'; 642 break; 643 case 'joy': 644 $html = '😂'; 645 break; 646 case 'rabbit': 647 $html = '🐰'; 648 break; 649 case 'frog': 650 $html = '🐸'; 651 break; 652 case 'dog': 653 $html = '🐶'; 654 break; 655 case 'cat': 656 $html = '🐱'; 657 break; 658 case 'dino': 659 $html = '<img alt="" width="451" height="75" src="//s.4cdn.org/image/temp/dinosaur.gif">'; 660 break; 661 case 'spooky': 662 $id = mt_rand(1, 23); 663 $html = '<img alt="" src="//s.4cdn.org/image/skeletons/' . $id . '.gif">'; 664 break; 665 default: 666 return null; 667 break; 668 } 669 670 return '<div' . $cnt_attrs . ' class="like-perk-cnt">' . $html . '</div>'; 671 }