diff --git a/common.php b/common.php index c58637eb8..caaf7e207 100644 --- a/common.php +++ b/common.php @@ -302,8 +302,18 @@ function make_rand_str(int $length = 10): string return $randomString; } -function array_deep(&$var, $fn, $one_dimensional = false, $array_only = false) +function array_deep(&$var, $fn, $one_dimensional = false, $array_only = false, $timeout = false) { + if ($timeout) { + static $recursions = 0; + if (time() > (TIMENOW + $timeout)) { + return [ + 'timeout' => true, + 'recs' => $recursions + ]; + } + $recursions++; + } if (is_array($var)) { foreach ($var as $k => $v) { if (is_array($v)) { @@ -312,7 +322,7 @@ function array_deep(&$var, $fn, $one_dimensional = false, $array_only = false) } elseif ($array_only) { $var[$k] = $fn($v); } else { - array_deep($var[$k], $fn); + array_deep($var[$k], $fn, timeout: $timeout); } } elseif (!$array_only) { $var[$k] = $fn($v); diff --git a/feed.php b/feed.php index 0f1736032..6409116f7 100644 --- a/feed.php +++ b/feed.php @@ -27,7 +27,6 @@ if ($mode === 'get_feed_url' && ($type === 'f' || $type === 'u') && $id >= 0) { if ($type == 'f') { // Check if the user has actually sent a forum ID $sql = "SELECT allow_reg_tracker, forum_name FROM " . BB_FORUMS . " WHERE forum_id = $id LIMIT 1"; - //DIE($sql); if (!$forum_data = DB()->fetch_row($sql)) { if ($id == 0) { $forum_data = []; diff --git a/library/config.php b/library/config.php index d018ec6fe..2dc6fd77d 100644 --- a/library/config.php +++ b/library/config.php @@ -540,7 +540,7 @@ $bb_cfg['mem_on_start'] = memory_get_usage(); $bb_cfg['translate_dates'] = true; // in displaying time $bb_cfg['use_word_censor'] = true; $bb_cfg['show_jumpbox'] = true; // Whether to show jumpbox (on viewtopic.php and viewforum.php) - +$bb_cfg['flist_time_limit'] = 15; // Max number of seconds to process file lists before throwing an error $bb_cfg['last_visit_date_format'] = 'd-M H:i'; $bb_cfg['last_post_date_format'] = 'd-M-y H:i'; $bb_cfg['poll_max_days'] = 180; // How many days will the poll be active diff --git a/src/Legacy/TorrentFileList.php b/src/Legacy/TorrentFileList.php index 25e152096..19a126f90 100644 --- a/src/Legacy/TorrentFileList.php +++ b/src/Legacy/TorrentFileList.php @@ -41,10 +41,12 @@ class TorrentFileList */ public function get_filelist() { - global $html; - if (($this->tor_decoded['info']['meta version'] ?? 1) === 2) { - if (is_array($this->tor_decoded['info']['file tree'] ?? null)) { - return $this->fileTreeList($this->tor_decoded['info']['file tree'], $this->tor_decoded['info']['name'] ?? ''); //v2 + global $bb_cfg, $html; + + $info = &$this->tor_decoded['info']; + if (isset($info['meta version'], $info['file tree'])) { //v2 + if (($info['meta version']) === 2 && is_array($info['file tree'])) { + return $this->fileTreeList($info['file tree'], $info['name'] ?? '', $bb_cfg['flist_time_limit']); } } @@ -69,7 +71,9 @@ class TorrentFileList */ private function build_filelist_array() { - $info = $this->tor_decoded['info']; + global $bb_cfg; + + $info = &$this->tor_decoded['info']; if (isset($info['name.utf-8'])) { $info['name'] =& $info['name.utf-8']; @@ -90,7 +94,11 @@ class TorrentFileList if (isset($f['attr']) && $f['attr'] === 'p') { continue; } - array_deep($f['path'], 'clean_tor_dirname'); + + $structure = array_deep($f['path'], 'clean_tor_dirname', timeout: $bb_cfg['flist_time_limit']); + if (isset($structure['timeout'])) { + bb_die("Timeout, too many nested files/directories for file listing, aborting after \n{$structure['recs']} recursive calls.\nNesting level: " . count($info['files'], COUNT_RECURSIVE)); + } $length = isset($f['length']) ? (float)$f['length'] : 0; $subdir_count = \count($f['path']) - 1; @@ -137,14 +145,22 @@ class TorrentFileList * @param string $name * @return string */ - public function fileTreeList(array $array, string $name = ''): string + public function fileTreeList(array $array, string $name = '', int $timeout = 0, bool $child = false): string { $allItems = ''; + if ($timeout) { + static $recursions = 0; + if (time() > (TIMENOW + $timeout)) { + bb_die("Timeout, too many nested files/directories for file listing, aborting after \n$recursions recursive calls.\nNesting level: " . count($this->tor_decoded['info']['file tree'], COUNT_RECURSIVE)); + } + $recursions++; + } + foreach ($array as $key => $value) { - $key = htmlCHR($key); + $key = clean_tor_dirname($key); if (!isset($value[''])) { - $html_v2 = $this->fileTreeList($value); + $html_v2 = $this->fileTreeList($value, timeout: $timeout, child: true); $allItems .= "
  • $key
  • "; } else { $length = $value['']['length']; @@ -152,7 +168,11 @@ class TorrentFileList } } - return '
    ' . (empty($allItems) ? '' : htmlCHR($name)) . '
    '; + if (!$child) { + return '
    ' . (empty($allItems) ? '' : htmlCHR($name)) . '
    '; + } + + return $allItems; } /** @@ -166,7 +186,7 @@ class TorrentFileList { static $filesList = ['list' => '', 'count' => 0]; foreach ($array as $key => $value) { - $key = htmlCHR($key); + $key = clean_tor_dirname($key); $current = "$parent/$key"; if (!isset($value[''])) { $this->fileTreeTable($value, $current); diff --git a/styles/templates/default/css/ajax.css b/styles/templates/default/css/ajax.css index 1c599b18b..b031b212d 100644 --- a/styles/templates/default/css/ajax.css +++ b/styles/templates/default/css/ajax.css @@ -46,4 +46,5 @@ var.ajax-params { .loading-1 { background: transparent url(../images/loading.gif) no-repeat left center; padding-left: 22px; + padding-block: inherit; } diff --git a/styles/templates/default/viewtopic_attach.tpl b/styles/templates/default/viewtopic_attach.tpl index e5de1a2a7..3f24847e1 100644 --- a/styles/templates/default/viewtopic_attach.tpl +++ b/styles/templates/default/viewtopic_attach.tpl @@ -297,34 +297,44 @@ function humn_size (size) { } ajax.tor_filelist_loaded = false; -$('#tor-filelist-btn').click(function(){ - if (ajax.tor_filelist_loaded) { - $('#tor-fl-wrap').toggle(); - return false; - } - $('#tor-fl-wrap').show(); +$('#tor-filelist-btn').click(function () { + if (ajax.tor_filelist_loaded) { + $('#tor-fl-wrap').toggle(); + return false; + } else { + $("#tor-filelist-btn").attr('disabled', true); + } + $('#tor-fl-wrap').show(); - ajax.exec({action: 'view_torrent', attach_id: {postrow.attach.tor_reged.ATTACH_ID} }); - ajax.callback.view_torrent = function(data) { - $('#tor-filelist').html(data.html); - $('#tor-filelist > ul.tree-root').treeview({ - control: "#tor-fl-treecontrol" - }); - $('#tor-filelist li.collapsable').each(function(){ - var $li = $(this); - var dir_size = 0; - $('i', $li).each(function(){ dir_size += parseInt(this.innerHTML) }); - $('span.b:first', $li).append(' · ' + humn_size(dir_size) + ''); - }); - $('#tor-filelist i').each(function(){ - var size_bytes = this.innerHTML; - this.innerHTML = '('+ size_bytes +')'; - $(this).prepend(''+ humn_size(size_bytes) +' '); - }); - ajax.tor_filelist_loaded = true; - }; - $('#tor-fl-treecontrol a').click(function(){ this.blur(); }); - return false; + ajax.exec({ + action: 'view_torrent', + attach_id: {postrow.attach.tor_reged.ATTACH_ID} + }); + ajax.callback.view_torrent = function (data) { + $('#tor-filelist').html(data.html); + $('#tor-filelist > ul.tree-root').treeview({ + control: "#tor-fl-treecontrol" + }); + $('#tor-filelist li.collapsable').each(function () { + var $li = $(this); + var dir_size = 0; + $('i', $li).each(function () { + dir_size += parseInt(this.innerHTML) + }); + $('span.b:first', $li).append(' · ' + humn_size(dir_size) + ''); + }); + $('#tor-filelist i').each(function () { + var size_bytes = this.innerHTML; + this.innerHTML = '(' + size_bytes + ')'; + $(this).prepend('' + humn_size(size_bytes) + ' '); + }); + ajax.tor_filelist_loaded = true; + $("#tor-filelist-btn").attr('disabled', false); + }; + $('#tor-fl-treecontrol a').click(function () { + this.blur(); + }); + return false; });