Files
UNIT3D-Community-Edition/app/Helpers/Bencode.php
Roardom 604b7e1937 add: spell check ci
Just an initial portion for now fixing incorrect text and preventing future incorrect text. Eventually, I'd like to fix everything still listed under "ignoredWords" in cspell.json.
2025-01-24 11:30:34 +00:00

267 lines
6.0 KiB
PHP

<?php
declare(strict_types=1);
/**
* NOTICE OF LICENSE.
*
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
* The details is bundled with this project in the file LICENSE.txt.
*
* @project UNIT3D Community Edition
*
* @author HDVinnie <hdinnovations@protonmail.com>
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
*/
namespace App\Helpers;
use function theodorejb\polycast\safe_int;
class Bencode
{
public static function parse_integer($s, &$pos)
{
$len = \strlen((string) $s);
if ($len === 0 || $s[$pos] != 'i') {
return;
}
$pos++;
$result = '';
while ($pos < $len && $s[$pos] != 'e') {
if (is_numeric($s[$pos]) || $s[$pos] === '-') {
$result .= $s[$pos];
} else {
// We have an invalid character in the string.
return;
}
$pos++;
}
if ($pos >= $len) {
// No end marker, hence we return null.
return;
}
$pos++;
if (safe_int($result)) {
return (int) $result;
}
}
public static function parse_string($s, &$pos)
{
$len = \strlen((string) $s);
$lengthStr = '';
while ($pos < $len && $s[$pos] != ':') {
if (is_numeric($s[$pos])) {
$lengthStr .= $s[$pos];
} else {
// Non-numeric character, we return null in this case.
return;
}
$pos++;
}
if ($pos >= $len) {
// We need a colon here, but there's none.
return;
}
$pos++;
if (!safe_int($lengthStr)) {
return;
}
$length = (int) $lengthStr;
$result = '';
while ($pos < $len && $length > 0) {
$result .= $s[$pos];
$length--;
$pos++;
}
if ($length > 0) {
// Input ended, but the string is longer than that.
return;
}
return $result;
}
public static function bdecode($s, &$pos = 0)
{
$len = \strlen((string) $s);
if ($pos >= $len) {
return;
}
$c = $s[$pos];
if ($c == 'i') {
return self::parse_integer($s, $pos);
}
if (is_numeric($c)) {
return self::parse_string($s, $pos);
}
if ($c == 'd') {
$dict = [];
$pos++;
while ($pos < $len && $s[$pos] != 'e') {
$key = self::bdecode($s, $pos);
$value = self::bdecode($s, $pos);
if (null === $key || null === $value) {
return;
}
$dict[$key] = $value;
}
if ($pos >= $len) {
// We need a end marker here
return;
}
$pos++;
return $dict;
}
if ($c == 'l') {
$list = [];
$pos++;
while ($pos < $len && $s[$pos] != 'e') {
$next = self::bdecode($s, $pos);
if (null !== $next) {
$list[] = $next;
} else {
return;
}
}
if ($pos >= $len) {
// We need a end marker here
return;
}
$pos++;
return $list;
}
}
public static function bencode($d)
{
if (\is_array($d)) {
$ret = 'l';
$isDict = !array_is_list($d);
if ($isDict) {
$ret = 'd';
// this is required by the specs, and BitTornado actually chokes on unsorted dictionaries
ksort($d, SORT_STRING);
}
foreach ($d as $key => $value) {
if ($isDict) {
$ret .= \strlen((string) $key).':'.$key;
}
if (\is_int($value) || \is_float($value)) {
$ret .= \sprintf('i%de', $value);
} elseif (\is_string($value)) {
$ret .= \strlen($value).':'.$value;
} else {
$ret .= self::bencode($value);
}
}
return $ret.'e';
}
if (\is_string($d)) {
return \strlen($d).':'.$d;
}
if (\is_int($d) || \is_float($d)) {
return \sprintf('i%de', $d);
}
}
public static function bdecode_file($filename)
{
$f = file_get_contents($filename);
return self::bdecode($f);
}
public static function get_infohash($t): string
{
return sha1((string) self::bencode($t['info']), true);
}
public static function get_meta($t): array
{
$result = [];
$size = 0;
$count = 0;
// Multi-file
if (isset($t['info']['files']) && \is_array($t['info']['files'])) {
foreach ($t['info']['files'] as $file) {
$count++;
$size += $file['length'];
}
} else {
$size = $t['info']['length'];
$count = 1;
$t['info']['files'][0]['path'] = $t['info']['name'];
$t['info']['files'][0]['length'] = $t['info']['length'];
}
$result['count'] = $count;
$result['size'] = $size;
return $result;
}
public static function get_name($t): ?string
{
$name = null;
if (
\array_key_exists('info', $t)
&& \is_array($t['info'])
&& \array_key_exists('name', $t['info'])
&& \is_string($t['info']['name'])
&& \array_key_exists('files', $t['info'])
) {
$name = $t['info']['name'];
}
return $name;
}
public static function is_v2_or_hybrid($t): bool
{
return isset($t['piece layers']);
}
}