diff --git a/bt/announce.php b/bt/announce.php index 8b98fc756..1bccc6dca 100644 --- a/bt/announce.php +++ b/bt/announce.php @@ -188,7 +188,11 @@ if ($lp_info) { } else { // Verify if torrent registered on tracker and user authorized $info_hash_sql = rtrim(DB()->escape($info_hash), ' '); - $info_hash_where = $is_bt_v2 ? "WHERE tor.info_hash_v2 = '$info_hash_sql'" : "WHERE tor.info_hash = '$info_hash_sql'"; + /** + * Поскольку торрент-клиенты в настоящее время обрезают инфо-хэш до 20 символов (независимо от его типа, как известно v1 = 20 символов, а v2 = 32 символа), + * то результатов $is_bt_v2 (исходя из длины строки определяем тип инфо-хэша) проверки нам будет мало, именно поэтому происходит поиск v2 хэша, если торрент является v1 (по длине) и если в tor.info_hash столбце нету v1 хэша. + */ + $info_hash_where = $is_bt_v2 ? "WHERE tor.info_hash_v2 = '$info_hash_sql'" : "WHERE tor.info_hash = '$info_hash_sql' OR tor.info_hash_v2 LIKE '$info_hash_sql%'"; $passkey_sql = DB()->escape($passkey); $sql = " diff --git a/bt/scrape.php b/bt/scrape.php index 531758a57..c82011e60 100644 --- a/bt/scrape.php +++ b/bt/scrape.php @@ -53,7 +53,11 @@ function msg_die($msg) require __DIR__ . '/includes/init_tr.php'; $info_hash_sql = rtrim(DB()->escape($info_hash), ' '); -$info_hash_where = $is_bt_v2 ? "WHERE tor.info_hash_v2 = '$info_hash_sql'" : "WHERE tor.info_hash = '$info_hash_sql'"; +/** + * Поскольку торрент-клиенты в настоящее время обрезают инфо-хэш до 20 символов (независимо от его типа, как известно v1 = 20 символов, а v2 = 32 символа), + * то результатов $is_bt_v2 (исходя из длины строки определяем тип инфо-хэша) проверки нам будет мало, именно поэтому происходит поиск v2 хэша, если торрент является v1 (по длинне) и если в tor.info_hash столбце нету v1 хэша. + */ +$info_hash_where = $is_bt_v2 ? "WHERE tor.info_hash_v2 = '$info_hash_sql'" : "WHERE tor.info_hash = '$info_hash_sql' OR tor.info_hash_v2 LIKE '$info_hash_sql%'"; $row = DB()->fetch_row(" SELECT tor.complete_count, snap.seeders, snap.leechers diff --git a/install/sql/mysql.sql b/install/sql/mysql.sql index d93b6dbab..e4c6eca6a 100644 --- a/install/sql/mysql.sql +++ b/install/sql/mysql.sql @@ -291,7 +291,7 @@ CREATE TABLE IF NOT EXISTS `bb_bt_torrents` `tor_type` TINYINT(1) NOT NULL DEFAULT '0', `speed_up` INT(11) NOT NULL DEFAULT '0', `speed_down` INT(11) NOT NULL DEFAULT '0', - PRIMARY KEY (`info_hash`), + PRIMARY KEY (`topic_id`), UNIQUE KEY `post_id` (`post_id`), UNIQUE KEY `topic_id` (`topic_id`), UNIQUE KEY `attach_id` (`attach_id`), diff --git a/library/ajax/view_torrent.php b/library/ajax/view_torrent.php index eb4ab4005..36b48d96a 100644 --- a/library/ajax/view_torrent.php +++ b/library/ajax/view_torrent.php @@ -37,6 +37,9 @@ if (!$tor = \SandFox\Bencode\Bencode::decode($file_contents)) { } $torrent = new TorrentPier\Legacy\TorrentFileList($tor); -$tor_filelist = $torrent->get_filelist(); - +if (($tor['info']['meta version'] ?? null) == 2 && is_array($tor['info']['file tree'] ?? null)) { + $tor_filelist = $torrent->fileTreeList($tor['info']['file tree'], $tor['info']['name'] ?? ''); // v2 +} else { + $tor_filelist = $torrent->get_filelist(); // v1 +} $this->response['html'] = $tor_filelist; diff --git a/library/attach_mod/displaying_torrent.php b/library/attach_mod/displaying_torrent.php index a058b74db..5ebb8351b 100644 --- a/library/attach_mod/displaying_torrent.php +++ b/library/attach_mod/displaying_torrent.php @@ -211,7 +211,7 @@ if ($tor_reged && $tor_info) { 'DL_TITLE_CLASS' => (isset($bt_userdata['user_status'])) ? $dl_status_css[$bt_userdata['user_status']] : 'gen', 'FILESIZE' => $tor_file_size, 'MAGNET' => $tor_magnet, - 'HASH' => strtoupper(bin2hex($tor_info['info_hash'])), + 'HASH' => !empty($tor_info['info_hash']) ? strtoupper(bin2hex($tor_info['info_hash'])) : false, 'HASH_V2' => !empty($tor_info['info_hash_v2']) ? strtoupper(bin2hex($tor_info['info_hash_v2'])) : false, 'DOWNLOAD_COUNT' => declension((int)$download_count, 'times'), 'REGED_TIME' => bb_date($tor_info['reg_time']), diff --git a/library/config.php b/library/config.php index b7cdd5b51..aea7df96c 100644 --- a/library/config.php +++ b/library/config.php @@ -647,8 +647,9 @@ $bb_cfg['tracker'] = [ 'retracker_host' => 'http://retracker.local/announce', 'guest_tracker' => true, 'search_by_tor_status' => true, - 'freeleech' => false, // freelech mode (If enabled, then disable "gold_silver_enabled") - 'gold_silver_enabled' => true // golden / silver days mode (If enabled, then disable "freeleech") + 'freeleech' => false, // freeleech mode (If enabled, then disable "gold_silver_enabled") + 'gold_silver_enabled' => true, // golden / silver days mode (If enabled, then disable "freeleech") + 'disabled_v2_torrents' => false // allow registration of v2-only torrents ]; // Ratio settings diff --git a/library/includes/functions.php b/library/includes/functions.php index c64a417f2..dd25ecd68 100644 --- a/library/includes/functions.php +++ b/library/includes/functions.php @@ -1787,7 +1787,20 @@ function create_magnet(string $infohash, string $infohash_v2, string $auth_key): return false; } - return ''; + $magnet = 'magnet:?'; + + if (!empty($infohash)) { + $magnet .= 'xt=urn:btih:' . bin2hex($infohash); + } + + if (!empty($infohash_v2)) { + if (!empty($infohash)) { + $magnet .= '&'; + } + $magnet .= 'xt=urn:btmh:1220' . bin2hex($infohash_v2); + } + + return ''; } function set_die_append_msg($forum_id = null, $topic_id = null, $group_id = null) @@ -1876,20 +1889,17 @@ function profile_url($data) return $profile; } -function get_avatar($user_id, $ext_id, $allow_avatar = true, $height = 100, $width = 100) +function get_avatar($user_id, $ext_id, $allow_avatar = true) { global $bb_cfg; - $height = $height ? 'height="' . $height . '"' : ''; - $width = $width ? 'width="' . $width . '"' : ''; - - $user_avatar = '' . $user_id . ''; + $user_avatar = '' . $user_id . ''; if ($user_id == BOT_UID && $bb_cfg['avatars']['bot_avatar']) { - $user_avatar = '' . $user_id . ''; + $user_avatar = '' . $user_id . ''; } elseif ($allow_avatar && $ext_id) { if (file_exists(get_avatar_path($user_id, $ext_id))) { - $user_avatar = '' . $user_id . ''; + $user_avatar = '' . $user_id . ''; } } diff --git a/library/language/source/main.php b/library/language/source/main.php index 1f406fa9c..2c5e0fbde 100644 --- a/library/language/source/main.php +++ b/library/language/source/main.php @@ -1082,8 +1082,6 @@ $lang['SEEDING'] = 'Seed'; $lang['LEECHING'] = 'Leech'; $lang['IS_REGISTERED'] = 'Registered'; $lang['MAGNET'] = 'Magnet'; -$lang['DC_MAGNET'] = 'Search in DC++ by filename'; -$lang['DC_MAGNET_EXT'] = 'Search in DC++ by extension'; //torrent status mod $lang['TOR_STATUS'] = 'Status'; diff --git a/src/Legacy/Torrent.php b/src/Legacy/Torrent.php index c082b7a67..91dfa8a89 100644 --- a/src/Legacy/Torrent.php +++ b/src/Legacy/Torrent.php @@ -287,8 +287,10 @@ class Torrent $topic_id = $torrent['topic_id']; $forum_id = $torrent['forum_id']; $poster_id = $torrent['poster_id']; + $info_hash = $info_hash_v2 = null; - $info_hash_sql = $info_hash_v2_sql = null; + $info_hash_sql = $info_hash_v2_sql = $info_hash_where = null; + $v2_hash = null; if ($torrent['extension'] !== TORRENT_EXT) { return self::torrent_error_exit($lang['NOT_TORRENT']); @@ -331,7 +333,7 @@ class Torrent if ($bb_cfg['bt_check_announce_url']) { include INC_DIR . '/torrent_announce_urls.php'; - $ann = (@$tor['announce']) ? $tor['announce'] : ''; + $ann = isset($tor['announce']) ? $tor['announce'] : ''; $announce_urls['main_url'] = $bb_cfg['bt_announce_url']; if (!$ann || !\in_array($ann, $announce_urls)) { @@ -340,35 +342,45 @@ class Torrent } } - $info = (@$tor['info']) ? $tor['info'] : []; + $info = isset($tor['info']) ? $tor['info'] : []; - if (!isset($info['name'], $info['piece length'], $info['pieces']) || \strlen($info['pieces']) % 20 != 0) { + if (!isset($info['name'], $info['piece length'])) { return self::torrent_error_exit($lang['TORFILE_INVALID']); } - // Check if torrent contains info_hash v2 - $bt_v2 = false; + // Check if torrent contains info_hash v2 or v1 + $bt_v1 = $bt_v2 = false; if (($info['meta version'] ?? null) == 2 && is_array($info['file tree'] ?? null)) { $bt_v2 = true; } + if (isset($info['pieces'])) { + $bt_v1 = true; + } + if ($bb_cfg['tracker']['disabled_v2_torrents'] && $bt_v2 && !$bt_v1) { + return self::torrent_error_exit('v2-only torrents were disabled, allowed: v1 and hybrids'); + } // Getting info_hash v1 - $info_hash = pack('H*', sha1(\SandFox\Bencode\Bencode::encode($info))); - $info_hash_sql = rtrim(DB()->escape($info_hash), ' '); - $info_hash_md5 = md5($info_hash); + if ($bt_v1) { + $info_hash = pack('H*', sha1(\SandFox\Bencode\Bencode::encode($info))); + $info_hash_sql = rtrim(DB()->escape($info_hash), ' '); + $info_hash_where = "WHERE info_hash = '$info_hash_sql'"; + } // Getting info_hash v2 if ($bt_v2) { - $info_hash_v2 = pack('H*', hash('sha256', \SandFox\Bencode\Bencode::encode($info))); + $v2_hash = hash('sha256', \SandFox\Bencode\Bencode::encode($info)); + $info_hash_v2 = pack('H*', $v2_hash); $info_hash_v2_sql = rtrim(DB()->escape($info_hash_v2), ' '); + $info_hash_where = "WHERE info_hash_v2 = '$info_hash_v2_sql'"; } // Ocelot if ($bb_cfg['ocelot']['enabled']) { - self::ocelot_update_tracker('add_torrent', ['info_hash' => rawurlencode($info_hash), 'id' => $topic_id, 'freetorrent' => 0]); + self::ocelot_update_tracker('add_torrent', ['info_hash' => rawurlencode($info_hash ?? hex2bin(substr($v2_hash, 0, 40))), 'id' => $topic_id, 'freetorrent' => 0]); } - if ($row = DB()->fetch_row("SELECT topic_id FROM " . BB_BT_TORRENTS . " WHERE info_hash = '$info_hash_sql' LIMIT 1")) { + if ($row = DB()->fetch_row("SELECT topic_id FROM " . BB_BT_TORRENTS . " $info_hash_where LIMIT 1")) { $msg = sprintf($lang['BT_REG_FAIL_SAME_HASH'], TOPIC_URL . $row['topic_id']); bb_die($msg); set_die_append_msg($forum_id, $topic_id); @@ -378,13 +390,29 @@ class Torrent if (isset($info['length'])) { $totallen = (float)$info['length']; - } elseif (isset($info['files']) && \is_array($info['files'])) { + } elseif ($bt_v1 && isset($info['files']) && \is_array($info['files'])) { foreach ($info['files'] as $fn => $f) { // Exclude padding files if (($f['attr'] ?? null) !== 'p') { $totallen += (float)$f['length']; } } + } elseif ($bt_v2) { + $fileTreeSize = function (array $array, string $name = '') use (&$fileTreeSize) { + $size = 0; + + foreach ($array as $key => $value) { + if (!isset($value[''])) { + $size += $fileTreeSize($value); + } else { + $size += (int)$value['']['length']; + } + } + + return $size; + }; + + $totallen = (float)$fileTreeSize($info['file tree']); } else { return self::torrent_error_exit($lang['TORFILE_INVALID']); } diff --git a/src/Legacy/TorrentFileList.php b/src/Legacy/TorrentFileList.php index b9bc54ac7..fc294e465 100644 --- a/src/Legacy/TorrentFileList.php +++ b/src/Legacy/TorrentFileList.php @@ -43,45 +43,18 @@ class TorrentFileList { global $html; - if (($this->tor_decoded['info']['meta version'] ?? null) == 2 && is_array($this->tor_decoded['info']['file tree'] ?? null)) { - // v2 - function fileTree($array, $name = '') - { - $folders = []; - $rootFiles = []; + $this->build_filelist_array(); - foreach ($array as $key => $value) { - if (is_array($value) && !isset($value[''])) { - $html_v2 = fileTree($value); - $folders[] = "
  • $key
  • "; - } else { - $length = $value['']['length']; - $root = bin2hex($value['']['pieces root'] ?? ''); - $rootFiles[] = "
  • $key$length $root
  • "; - } - } - - $allItems = array_merge($folders, $rootFiles); - - return '
    ' . (empty($folders) ? '' : $name) . '
    '; + if ($this->multiple) { + if ($this->files_ary['/'] !== '') { + $this->files_ary = array_merge($this->files_ary, $this->files_ary['/']); + unset($this->files_ary['/']); } - - return fileTree($this->tor_decoded['info']['file tree'], $this->tor_decoded['info']['name']); - } else { - // v1 - $this->build_filelist_array(); - - if ($this->multiple) { - if ($this->files_ary['/'] !== '') { - $this->files_ary = array_merge($this->files_ary, $this->files_ary['/']); - unset($this->files_ary['/']); - } - $filelist = $html->array2html($this->files_ary); - return "
    {$this->root_dir}
    $filelist"; - } - - return implode('', $this->files_ary['/']); + $filelist = $html->array2html($this->files_ary); + return "
    {$this->root_dir}
    $filelist"; } + + return implode('', $this->files_ary['/']); } /** @@ -134,42 +107,76 @@ class TorrentFileList $GLOBALS['bnc_error'] = 1; break; } - $cur_files_ary[] = $this->build_file_item($name, $length); + $cur_files_ary[] = "$name $length"; } } asort($cur_files_ary); } else { $name = $f['path'][0]; - $this->files_ary['/'][] = $this->build_file_item($name, $length); + $this->files_ary['/'][] = "$name $length"; natsort($this->files_ary['/']); } } } else { $name = clean_tor_dirname($info['name']); $length = (float)$info['length']; - $this->files_ary['/'][] = $this->build_file_item($name, $length); + $this->files_ary['/'][] = "$name $length"; natsort($this->files_ary['/']); } } /** - * Формирование файла + * File list generation for v2 supported torrents * - * @param $name - * @param $length + * @param array $array + * @param string $name * @return string */ - private function build_file_item($name, $length): string + public function fileTreeList(array $array, string $name = ''): string { - global $bb_cfg, $images, $lang; + $folders = []; + $rootFiles = []; - $magnet_name = $magnet_ext = ''; - - if ($bb_cfg['magnet_links_enabled']) { - $magnet_name = ''; - $magnet_ext = ''; + foreach ($array as $key => $value) { + $key = htmlCHR($key); + if (!isset($value[''])) { + $html_v2 = $this->fileTreeList($value); + $folders[] = "
  • $key
  • "; + } else { + $length = (int)$value['']['length']; + $root = bin2hex($value['']['pieces root'] ?? ''); + $rootFiles[] = "
  • $key$length

    $root

  • "; + } } - return "$name $length $magnet_name $magnet_ext"; + $allFiles = implode('', [...$folders, ...$rootFiles]); + + return '
    ' . (empty($folders) ? '' : htmlCHR($name)) . '
    '; + } + + /** + * Table generation for BitTorrent v2 compatible torrents + * + * @param array $array + * @param string $parent + * @return array + */ + public function fileTreeTable(array $array, string $parent = ''): array + { + $filesList = []; + + foreach ($array as $key => $value) { + $key = htmlCHR($key); + $current = "$parent/$key"; + if (!isset($value[''])) { + $this->fileTreeTable($value, $current); + } else { + $length = (int)$value['']['length']; + $root = bin2hex($value['']['pieces root'] ?? ''); + $filesList[] = '' . $current . '' . humn_size($length, 2) . '' . $root . ''; + } + } + + return $filesList; } } diff --git a/styles/images/dc_magnet.png b/styles/images/dc_magnet.png deleted file mode 100644 index 19a7cd5dc..000000000 Binary files a/styles/images/dc_magnet.png and /dev/null differ diff --git a/styles/images/dc_magnet_ext.png b/styles/images/dc_magnet_ext.png deleted file mode 100644 index ee6b2e467..000000000 Binary files a/styles/images/dc_magnet_ext.png and /dev/null differ diff --git a/styles/templates/default/tpl_config.php b/styles/templates/default/tpl_config.php index c51bafaa6..ccb6a8a37 100644 --- a/styles/templates/default/tpl_config.php +++ b/styles/templates/default/tpl_config.php @@ -72,8 +72,6 @@ $images['folder_dl_hot_new'] = $_main . 'folder_dl_hot_new.gif'; $images['icon_clip'] = $_img . 'icon_clip.gif'; $images['icon_dn'] = $_img . 'icon_dn.gif'; $images['icon_magnet'] = $_img . 'magnet.png'; -$images['icon_dc_magnet'] = $_img . 'dc_magnet.png'; -$images['icon_dc_magnet_ext'] = $_img . 'dc_magnet_ext.png'; // posting_icons $images['post_new'] = $_lang . 'post.gif'; diff --git a/styles/templates/default/viewtopic_attach.tpl b/styles/templates/default/viewtopic_attach.tpl index e2b7d89f3..425a13b1f 100644 --- a/styles/templates/default/viewtopic_attach.tpl +++ b/styles/templates/default/viewtopic_attach.tpl @@ -144,7 +144,7 @@ - + @@ -156,7 +156,7 @@
    {postrow.attach.tor_reged.DOWNLOAD_NAME} {postrow.attach.tor_reged.MAGNET}{postrow.attach.tor_reged.DOWNLOAD_NAME} {postrow.attach.tor_reged.MAGNET}
    {postrow.attach.tor_reged.TRACKER_LINK} [ {postrow.attach.tor_reged.REGED_TIME} ] -

    info_hash: {postrow.attach.tor_reged.HASH} +

    info_hash: {postrow.attach.tor_reged.HASH}
    info_hash v2: {postrow.attach.tor_reged.HASH_V2}
    @@ -326,6 +326,7 @@ $('#tor-filelist-btn').click(function(){ } #tor-filelist i { color: #7A7A7A; padding-left: 4px; } #tor-filelist s { color: #0000FF; text-decoration: none; } +#tor-filelist p { color: #7C7C7C; text-decoration: none; } #tor-filelist .b > s { color: #800000; } #tor-filelist .b { font-weight: bold; padding-left: 20px; background: transparent url('styles/images/folder.gif') no-repeat 3px 50%;} #tor-filelist ul li span { padding-left: 20px; background: transparent url('styles/images/page.gif') no-repeat 3px 50%;}