/ 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  }