/ json.php
json.php
1 <?php 2 /** 3 * JSON Dumper 4 * Dumps a thread into a .json file. 5 */ 6 7 if( !isset( $in_imgboard ) ) die( 'No direct access' ); 8 9 if( META_BOARD ) { 10 include_once 'plugins/enhance_q.php'; 11 } 12 13 function generate_thread_json( $threadid, $return = false, $replies = false, $for_catalogue = false, $tail = 0 ) 14 { 15 global $log, $thread_unique_ips; 16 if( !$log[$threadid] ) log_cache( 0, $threadid ); 17 $carr = array(); 18 19 $op = $log[$threadid]; 20 foreach( $op['children'] as $key => $val ) { 21 $carr[] = $key; 22 } 23 24 sort( $carr, SORT_NATURAL ); 25 26 $count = count( $carr ) + 1; 27 $replycount = $op['replycount']; 28 29 $build = ''; 30 $json = array(); 31 $extra = array(); 32 $imagecount = $op['imgreplycount']; 33 $tail_images = 0; 34 35 $extra['replies'] = $replycount; 36 $extra['images'] = $imagecount; 37 38 // Not inside a thread 39 if ($replies !== false) { 40 if ($replycount - $replies > 0) { 41 for ($i = $replycount - $replies; $i < $replycount; $i++) { 42 if ($log[$carr[$i]]['fsize'] && !$log[$carr[$i]]['filedeleted']) { 43 $tail_images++; 44 } 45 } 46 47 $extra['omitted_posts'] = $replycount - $replies; 48 $extra['omitted_images'] = $imagecount - $tail_images; 49 } 50 } 51 // Inside a thread 52 else if (SHOW_THREAD_UNIQUES) { 53 if ($thread_unique_ips) { 54 $unique_ips = (int)$thread_unique_ips; 55 } 56 else { 57 $unique_ips = get_unique_ip_count($threadid); 58 } 59 60 if ($unique_ips) { 61 $extra['unique_ips'] = $unique_ips; 62 } 63 } 64 65 //if( $replycount >= MAX_RES && !$op['permaage'] ) $extra['bumplimit'] = 1; 66 //if( $imagecount >= MAX_IMGRES && !$op['sticky'] ) $extra['imagelimit'] = 1; 67 68 $i = 1; 69 70 if ($op['semantic_url'] !== '') { 71 $extra['semantic_url'] = $op['semantic_url']; 72 } 73 74 if ($replies !== false) { 75 $i = $count - $replies; 76 77 if ($i < 1) { 78 $i = 1; 79 } 80 } 81 82 if ($for_catalogue) { 83 $json = generate_post_json( $op, $threadid, $extra ); // we do op before replies 84 if ($replycount > 0) { 85 $json['last_replies'] = array(); 86 for( ; $i < $count; $i++ ) { 87 if( isset( $log[$carr[$i - 1]] ) ) { 88 $var = $log[$carr[$i - 1]]; 89 $json['last_replies'][] = generate_post_json( $var, $threadid ); 90 } 91 } 92 if (META_BOARD) { 93 $capcode_replies = generate_capcode_replies($op['children']); 94 95 if ($capcode_replies) { 96 $json['capcode_replies'] = $capcode_replies; 97 } 98 } 99 } 100 101 $json['last_modified'] = (int)$log[$threadid]['last_modified']; 102 103 return $json; 104 } 105 else if ($tail) { 106 $json[] = generate_op_tail_json($op, $extra); 107 108 $lim = $count - $tail; 109 110 $tail_id = 0; 111 112 for (; $i < $count; $i++ ) { 113 if (isset($log[$carr[$i - 1]])) { 114 $var = $log[$carr[$i - 1]]; 115 if ($i < $lim) { 116 $tail_id = (int)$var['no']; 117 } 118 else { 119 $json[] = generate_post_json($var, $threadid); 120 } 121 } 122 } 123 124 $json[0]['tail_size'] = $tail; 125 $json[0]['tail_id'] = $tail_id; 126 127 $temp = array('posts' => $json); 128 } 129 else { 130 $json[] = generate_post_json( $op, $threadid, $extra ); // we do op before replies 131 132 $tailSize = get_json_tail_size($threadid); 133 134 if ($tailSize) { 135 $json[0]['tail_size'] = $tailSize; 136 } 137 138 for( ; $i < $count; $i++ ) { 139 if( isset( $log[$carr[$i - 1]] ) ) { 140 $var = $log[$carr[$i - 1]]; 141 $json[] = generate_post_json( $var, $threadid ); 142 } 143 } 144 145 if (META_BOARD) { 146 $capcode_replies = generate_capcode_replies($op['children']); 147 148 if ($capcode_replies) { 149 $json[0]['capcode_replies'] = $capcode_replies; 150 } 151 } 152 153 $temp = array('posts' => $json); 154 } 155 156 if( $return ) return $temp; 157 158 unset( $json ); 159 160 if (!$tail) { 161 $filename = RES_DIR . $threadid . '.json'; 162 } 163 else { 164 $filename = RES_DIR . $threadid . '-tail.json'; 165 } 166 $page = json_encode($temp); 167 print_page( $filename, $page, 0, 0 ); 168 169 return true; 170 } 171 172 function generate_capcode_replies($replies) { 173 global $log; 174 175 $capcode_replies = array( 176 'admin' => array(), 177 'developer' => array(), 178 'mod' => array() 179 ); 180 181 $has_capcode_replies = false; 182 183 foreach ($replies as $no => $val) { 184 if (!isset($log[$no])) { 185 continue; 186 } 187 $json_post = $log[$no]; 188 if ($json_post['capcode'] === 'none') { 189 continue; 190 } 191 if ($json_post['capcode'] === 'admin_highlight') { 192 $json_capcode = 'admin'; 193 } 194 else { 195 $json_capcode = $json_post['capcode']; 196 } 197 $capcode_replies[$json_capcode][] = (int)$no; 198 $has_capcode_replies = true; 199 } 200 201 if ($has_capcode_replies) { 202 $ret = array(); 203 foreach ($capcode_replies as $key => $value) { 204 if (empty($value)) { 205 continue; 206 } 207 $ret[$key] = $value; 208 } 209 return $ret; 210 } 211 else { 212 return null; 213 } 214 } 215 216 function post_json_force_type( &$post ) 217 { 218 static $post_intern = array( 219 'no' => 'integer', 220 'resto' => 'integer', 221 'sticky' => 'integer', 222 'closed' => 'integer', 223 'archived' => 'integer', 224 'now' => 'string', 225 'time' => 'integer', 226 'name' => 'string', 227 'trip' => 'string', 228 'id' => 'string', 229 'capcode' => 'string', 230 'country' => 'string', 231 'country_name' => 'string', 232 'sub' => 'string', 233 'com' => 'string', 234 'tim' => 'integer', 235 'filename' => 'string', 236 'ext' => 'string', 237 'fsize' => 'integer', 238 'md5' => 'string', 239 'w' => 'integer', 240 'h' => 'integer', 241 'tn_w' => 'integer', 242 'tn_h' => 'integer', 243 'filedeleted' => 'integer', 244 'spoiler' => 'integer', 245 'custom_spoiler' => 'integer', 246 'omitted_posts' => 'integer', 247 'omitted_images' => 'integer', 248 'replies' => 'integer', 249 'images' => 'integer', 250 'bumplimit' => 'integer', 251 'imagelimit' => 'integer', 252 'last_modified' => 'integer', 253 'archived_on' => 'integer', 254 'since4pass' => 'integer', 255 'm_img' => 'integer' 256 ); 257 258 259 foreach( $post as $key => $val) { 260 if( isset( $post_intern[$key] ) ) { 261 settype( $post[$key], $post_intern[$key] ); 262 } 263 } 264 } 265 266 function generate_op_tail_json($op, $extra) { 267 $ary = array(); 268 269 $ary['no'] = (int)$op['no']; 270 271 $ary['bumplimit'] = (int)$op['bumplimit']; 272 $ary['imagelimit'] = (int)$op['imagelimit']; 273 274 if ($op['sticky']) { 275 $ary['sticky'] = 1; 276 if ($op['undead']) { 277 $ary['sticky_cap'] = STICKY_CAP; 278 } 279 } 280 281 if ($op['closed']) { 282 $ary['closed'] = 1; 283 } 284 285 if ($op['archived']) { 286 $ary['archived'] = 1; 287 } 288 289 $ary['replies'] = (int)$extra['replies']; 290 $ary['images'] = (int)$extra['images']; 291 292 if (isset($extra['unique_ips'])) { 293 $ary['unique_ips'] = (int)$extra['unique_ips']; 294 } 295 296 if (SPOILERS) { 297 $ary['custom_spoiler'] = (int)SPOILER_NUM; 298 } 299 300 return $ary; 301 } 302 303 function generate_post_json( $var, $threadid, $extra = array(), $banskip = false ) 304 { 305 $COUNTRY_FLAG_ARR = array( 306 'sp', 307 'int', 308 ); 309 310 $FORCED_ANON_ARR = array( 311 'b', 312 'soc' 313 ); 314 315 $META_BOARD_ARR = array( 316 'q' 317 ); 318 319 if (UPLOAD_BOARD) { 320 $FLASH_TAGS = array( 321 0 => 'Hentai', 322 1 => 'Japanese', 323 2 => 'Anime', 324 3 => 'Game', 325 4 => 'Other', 326 5 => 'Loop', 327 6 => 'Porn', 328 ); 329 } 330 331 $SHOW_COUNTRY_FLAGS = $banskip ? in_array( $var['board'], $COUNTRY_FLAG_ARR ) : SHOW_COUNTRY_FLAGS; 332 $ENABLE_BOARD_FLAGS = $banskip ? false : ENABLE_BOARD_FLAGS; 333 $META_BOARD = $banskip ? in_array( $var['board'], $META_BOARD_ARR ) : META_BOARD; 334 $FORCED_ANON = $banskip ? in_array( $var['board'], $FORCED_ANON_ARR ) : FORCED_ANON; 335 336 if ($ENABLE_BOARD_FLAGS) { 337 $board_flags_array = get_board_flags_array(); 338 } 339 340 $country = $var['country']; 341 342 if ($ENABLE_BOARD_FLAGS && isset($board_flags_array[$var['board_flag']])) { 343 $board_flag = $var['board_flag']; 344 } 345 else { 346 $board_flag = ''; 347 } 348 349 if( $banskip && $var['ext'] ) { 350 $salt = file_get_contents( '/www/keys/legacy.salt' ); 351 $no = $var['no']; 352 353 $hash = sha1( $var['board'] . $no . $salt ); 354 $var['thumb'] = $hash; 355 } 356 357 $intern_host = $var['host']; 358 359 $unset = array( 360 'permasage', 361 'host', 362 'pwd', 363 'children', 364 'imgreplycount', 365 'replycount', 366 'last_modified', 367 'root', 368 '4pass_id' 369 ); 370 371 if ($banskip) { 372 $unset[] = 'protected'; 373 $unset[] = 'ua'; 374 } 375 376 $nunset = $unset; 377 378 $var['tim'] = (int)$var['tim']; 379 380 if( $var['filedeleted'] == 1 || !$var['ext'] ) { 381 $arr = array( 382 'tim', 383 'w', 384 'h', 385 'tn_w', 386 'tn_h', 387 'filename', 388 'ext', 389 'md5', 390 'fsize', 391 'tmd5' 392 ); 393 394 foreach( $arr as $key ) { 395 unset( $var[$key] ); 396 } 397 } 398 else { 399 // FIXME 400 $var['filename'] = mb_convert_encoding($var['filename'], 'UTF-8', 'UTF-8'); 401 } 402 403 // FIXME 404 $var['com'] = mb_convert_encoding($var['com'], 'UTF-8', 'UTF-8'); 405 406 if( !$var['filedeleted'] ) $nunset[] = 'filedeleted'; 407 408 // trim it up 409 foreach( $nunset as $key ) { 410 unset( $var[$key] ); 411 } 412 413 $is_archived = $var['archived']; 414 415 if( $var['resto'] ) { 416 unset( $var['sticky'] ); 417 unset( $var['closed'] ); 418 unset( $var['archived'] ); 419 } 420 else { 421 if (!$var['archived']) { 422 unset($var['archived']); 423 unset($var['archived_on']); 424 } 425 426 if (!$var['closed']) { 427 unset($var['closed']); 428 } 429 430 if (!$var['sticky']) { 431 unset($var['sticky']); 432 } 433 else { 434 if ($var['undead']) { 435 $var['sticky_cap'] = (int)STICKY_CAP; 436 } 437 unset( $var['bumplimit'], $var['imagelimit'] ); 438 } 439 440 if ($var['permaage']) { 441 unset( $var['bumplimit'] ); 442 } 443 } 444 445 if (!$var['m_img']) { 446 unset($var['m_img']); 447 } 448 449 if (!$var['since4pass']) { 450 unset($var['since4pass']); 451 } 452 // April 2024 453 else if ($var['since4pass'] >= 10000) { 454 unset($var['since4pass']); 455 } 456 457 unset($var['permaage'], $var['undead']); 458 459 // clean up names 460 if( strpos( $var['name'], '</span> <span class="postertrip">' ) !== false ) { 461 $name = explode( '</span> <span class="postertrip">', $var['name'] ); 462 $var['name'] = $name[0]; 463 $var['trip'] = $name[1]; 464 465 if( $var['trip'] && !$var['name'] ) unset( $var['name'] ); 466 } 467 468 if( !$banskip && SPOILERS && !$var['resto'] ) $var['custom_spoiler'] = (int)SPOILER_NUM; 469 470 $var['spoiler'] = 0; 471 if( strpos( $var['sub'], 'SPOILER<>' ) === 0 ) { 472 $var['sub'] = substr( $var['sub'], 9 ); 473 if( !$var['sub'] ) $var['sub'] = ''; 474 $var['spoiler'] = 1; 475 } else { 476 unset( $var['spoiler'] ); 477 } 478 479 if( $var['sub'] && !$var['resto'] && UPLOAD_BOARD ) { 480 if( preg_match( '/^(\d+)\|/', $var['sub'], $tag_matches ) ) { 481 $var['tag'] = $FLASH_TAGS[(int)$tag_matches[1]]; 482 $var['sub'] = preg_replace( '/^(\d+)\|/', '', $var['sub'] ); 483 } 484 } 485 486 if ( !$banskip ) { 487 if( !$var['id'] || $is_archived ) { 488 unset( $var['id'] ); 489 } elseif( $var['id'] && $var['no'] == $threadid && $var['capcode'] === 'none') { 490 $var['id'] = generate_uid( $var['no'], $var['time'], $intern_host ); 491 } 492 } 493 494 if ($var['capcode'] == 'none') { 495 if ($ENABLE_BOARD_FLAGS && $board_flag) { 496 unset($var['country']); 497 $var['flag_name'] = board_flag_code_to_name($board_flag); 498 } 499 else if ($SHOW_COUNTRY_FLAGS) { 500 unset( $var['board_flag'] ); 501 $var['country_name'] = country_code_to_name($country); 502 } 503 else { 504 unset($var['country']); 505 unset($var['country_name']); 506 unset($var['board_flag']); 507 } 508 } 509 else { 510 unset($var['country']); 511 unset($var['country_name']); 512 unset($var['board_flag']); 513 } 514 515 if( ( $FORCED_ANON || $META_BOARD ) && ( $var['capcode'] != 'admin' && $var['capcode'] != 'admin_hl' ) ) { 516 unset( $var['trip'] ); 517 $var['name'] = 'Anonymous'; 518 } 519 520 if( $var['capcode'] == 'none' ) unset( $var['capcode'] ); 521 if( !$banskip ) $var['com'] = auto_link( $var['com'], $threadid ); 522 if( !$banskip && isset( $var['md5'] ) ) $var['md5'] = base64_encode( pack( 'H*', $var['md5'] ) ); 523 524 if( $var['com'] == '' ) unset( $var['com'] ); 525 if(isset($var['email'])) unset( $var['email'] ); 526 if( $var['sub'] == '' ) unset( $var['sub'] ); 527 528 if( !empty( $extra ) ) { 529 foreach( $extra as $key => $val ) { 530 $var[$key] = $val; 531 } 532 } 533 534 post_json_force_type( $var ); 535 536 /// XXX: CHANGE TO TRIM WHITESPACE 537 return $var; 538 } 539 540 function generate_index_json( $return = false ) 541 { 542 543 global $log, $index_rbl; 544 log_cache( 0, 0 ); // generate all threads 545 546 $threads = $log['THREADS']; 547 548 $threadcount = count( $threads ); 549 550 // figure out how many replies to print 551 if (defined('REPLIES_SHOWN')) { 552 $replies_shown = REPLIES_SHOWN; 553 } 554 else { 555 $replies_shown = 5; 556 } 557 558 // Loop through every page 559 for( $page = 0; $page < $threadcount; $page += DEF_PAGES ) { 560 $file_page_num = $page / DEF_PAGES + 1; 561 562 if (PAGE_MAX && $file_page_num > PAGE_MAX) { 563 break; 564 } 565 566 $thread = $page; 567 $json = array(); 568 569 if( floor( $page / DEF_PAGES ) > $index_rbl ) return; 570 571 for( $i = $thread; $i < $thread + DEF_PAGES; $i++ ) { 572 list( $_unused, $threadid ) = each( $threads ); 573 574 if( !$threadid ) break; 575 576 if ($log[$threadid]['sticky'] == 1) { 577 $replies = min(1, $replies_shown); 578 } 579 else { 580 $replies = $replies_shown; 581 } 582 583 $json[] = generate_thread_json( $threadid, true, $replies ); 584 } 585 586 if( empty( $json ) ) return true; // we've reached the point of no return 587 588 $temp = json_encode( array('threads' => $json) ); 589 590 $filename = INDEX_DIR . ($page / DEF_PAGES + 1) . '.json'; 591 print_page( $filename, $temp, 0, 0 ); 592 } 593 594 return true; 595 } 596 597 // ↓ STICK IT TO THE MAN 598 function generate_board_catalogue() 599 { 600 //$json = generate_index_json( true ); 601 602 global $log, $index_rbl; 603 log_cache( 0, 0 ); // generate all threads 604 605 $threads = $log['THREADS']; 606 $threadcount = count( $threads ); 607 $curpage = 0; 608 609 $json = array(); 610 611 if (defined('REPLIES_SHOWN')) { 612 $replies_shown = REPLIES_SHOWN; 613 } 614 else { 615 $replies_shown = 5; 616 } 617 618 for( $page = 0; $page < $threadcount; $page += DEF_PAGES ) { 619 // Loop through each page... 620 621 $thispage = $page; 622 623 for( $i = $thispage; $i < $thispage + DEF_PAGES; $i++ ) { 624 list( $_unused, $threadid ) = each( $threads ); 625 626 if( !$threadid ) break; 627 628 if ($log[$threadid]['sticky'] == 1) { 629 $replies = min(1, $replies_shown); 630 } 631 else { 632 $replies = $replies_shown; 633 } 634 635 $json[$curpage][] = generate_thread_json( $threadid, false, $replies, true ); 636 637 } 638 639 $curpage++; 640 } 641 642 $build = array(); 643 644 foreach( $json as $page => $threads ) { 645 $thread = array( 646 'page' => $page + 1, 647 'threads' => $threads 648 ); 649 $build[] = $thread; 650 } 651 652 $page = json_encode($build); 653 print_page( INDEX_DIR . 'catalog.json', $page, 0, 0 ); 654 } 655 656 function generate_board_threads_json() 657 { 658 global $log; 659 log_cache( 0, 0 ); 660 661 $threads = $log['THREADS']; 662 $threadcount = count( $threads ); 663 $curpage = 0; 664 $json = array(); 665 666 for( $page = 0; $page < $threadcount; $page += DEF_PAGES ) { 667 $thispage = $page; 668 669 for( $i = $thispage; $i < $thispage + DEF_PAGES; $i++ ) { 670 list( $_unused, $threadid ) = each( $threads ); 671 672 if( !$threadid ) break; 673 674 $arr = array( 675 'no' => $threadid, 676 'last_modified' => $log[$threadid]['last_modified'], 677 'replies' => $log[$threadid]['replycount'] 678 ); 679 680 post_json_force_type($arr); 681 682 $json[$curpage][] = $arr; 683 684 } 685 686 $curpage++; 687 } 688 689 foreach( $json as $page => $threads ) { 690 691 $build[] = array( 692 'page' => $page + 1, 693 'threads' => $threads 694 ); 695 } 696 697 $page = json_encode( $build ); 698 print_page( INDEX_DIR . 'threads.json', $page, 0, 0 ); 699 } 700 701 function generate_board_archived_json() { 702 $query = "SELECT no FROM `" . BOARD_DIR . "` WHERE archived = 1 AND resto = 0 ORDER BY no ASC"; 703 704 $res = mysql_board_call($query); 705 706 if (!$res) { 707 return false; 708 } 709 710 $threads = array(); 711 712 while ($tid = mysql_fetch_row($res)[0]) { 713 $threads[] = $tid; 714 } 715 716 $page = '[' . implode(',', $threads) . ']'; 717 718 print_page(INDEX_DIR . 'archive.json', $page, 0, 0); 719 }