4chan/lib/oekaki.php
2025-04-17 14:46:47 -05:00

160 lines
3.5 KiB
PHP

<?php
// $file: $_FILES array entry for the .tgkr file
// returns true if the replay file is valid,
// false if the replay should be ignored, and errors out in all other cases.
function oekaki_validate_replay($file) {
$max_size = 8 * 1024 * 1024;
$max_data_size = 18 * 1024 * 1024; // uncompresed size
if ($file['error'] > 0) {
error(S_FAILEDUPLOAD);
}
if ($file['size'] === 0) {
error(S_NOREC);
}
if ($file['size'] > $max_size) {
error(S_TOOLARGE);
}
$tmp_file = $file['tmp_name'];
if (is_uploaded_file($tmp_file) !== true) {
error(S_FAILEDUPLOAD);
}
// Check the actual data now
$f = fopen($tmp_file, 'rb');
$magic = fread($f, 4);
$decompressed_size = fread($f, 4);
fread($f, 4); // version numbers
$compressed_data = fread($f, $file['size'] - 12);
fclose($f);
if ($magic !== "\x54\x47\x4B\x01") { // TGK 0x01
error(S_NOREC);
}
$decompressed_size = (int)unpack('N', $decompressed_size)[1];
if (!$decompressed_size || $decompressed_size <= 0) {
error(S_NOREC);
}
if ($decompressed_size > $max_data_size) {
return false;
}
if (!$compressed_data) {
error(S_NOREC);
}
$data = gzinflate($compressed_data, $decompressed_size);
if ($data === false) {
error(S_NOREC);
}
$meta_size = (int)unpack('n', $data)[1];
if (!$meta_size) {
return false;
}
// tool count (byte), tool entry size (byte)
$tool_meta = unpack('C2', substr($data, $meta_size, 2));
if (!$tool_meta || !(int)$tool_meta[1] || !(int)$tool_meta[2]) {
return false;
}
$events_pos = $meta_size + ((int)$tool_meta[1]) * ((int)$tool_meta[2]) + 2;
$event_count = (int)unpack('N', substr($data, $events_pos, 4))[1];
if (!$event_count || $event_count > 12000000) {
return false;
}
$prelude_type = unpack('C', substr($data, $events_pos + 4, 1));
if ($prelude_type === false || (int)$prelude_type[1] !== 0) {
return false;
}
$conclusion_type = unpack('C', substr($data, -5, 1));
if ($conclusion_type === false || (int)$conclusion_type[1] !== 255) {
return false;
}
return true;
}
function oekaki_get_valid_src_pid($src_pid, $board, $thread_id) {
$src_pid = (int)$src_pid;
if ($src_pid < 1) {
return null;
}
$thread_id = (int)$thread_id;
if ($thread_id < 1) {
return null;
}
$sql = "SELECT no FROM `%s` WHERE no = $src_pid AND (resto = $thread_id OR resto = 0) AND tim != 0 LIMIT 1";
$res = mysql_board_call($sql, $board);
if (!$res || mysql_num_rows($res) !== 1) {
return null;
}
return $src_pid;
}
function oekaki_format_info($time, $replay_tim, $src_pid) {
$time = (int)$time;
if ($time < 1 || $time > 5184000) { // 60 days
return '';
}
if ($time < 60) {
$time_str = $time . 's';
}
else if ($time < 3600) {
$time_str = round($time / 60) . 'm';
}
else {
$time_str = (int)($time / 3600) . 'h ' . round(($time % 3600) / 60) . 'm';
}
if ($replay_tim && !$src_pid) {
$replay_tim = (int)$replay_tim;
$replay_link = ", Replay: <a href=\"javascript:oeReplay($replay_tim);\">View</a>";
}
else {
$replay_link = '';
}
if ($src_pid) {
$src_pid = (int)$src_pid;
$src_link = ", Source: &gt;&gt;$src_pid";
}
else {
$src_link = '';
}
return "<br><br><small><b>Oekaki Post</b> (Time: $time_str" . $replay_link . $src_link . ")</small>";
}