mirror of
https://github.com/HDInnovations/UNIT3D-Community-Edition.git
synced 2026-01-21 19:40:47 -06:00
Tildes inside the filename are fine as long as the filename or folder doesn't start with a tilde.
229 lines
7.1 KiB
PHP
229 lines
7.1 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 Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Stringable;
|
|
|
|
class TorrentTools
|
|
{
|
|
/**
|
|
* Moves and decodes the torrent.
|
|
*
|
|
* @return array<mixed>
|
|
*/
|
|
public static function normalizeTorrent(UploadedFile $torrentFile)
|
|
{
|
|
$result = Bencode::bdecode_file($torrentFile->getRealPath());
|
|
|
|
// Whitelisted keys
|
|
$result = array_intersect_key($result, [
|
|
'comment' => '',
|
|
'created by' => '',
|
|
'encoding' => '',
|
|
'info' => '',
|
|
]);
|
|
$result['info'] = array_intersect_key($result['info'], [
|
|
'files' => '',
|
|
'length' => '',
|
|
'name' => '',
|
|
'piece length' => '',
|
|
'pieces' => '',
|
|
]);
|
|
|
|
$result['info']['source'] = config('torrent.source');
|
|
$result['info']['private'] = 1;
|
|
$result['info']['entropy'] = bin2hex(random_bytes(64));
|
|
|
|
if (config('torrent.created_by_append') && \array_key_exists('created by', $result)) {
|
|
$result['created by'] = trim((string) $result['created by'], '. ').'. '.config('torrent.created_by', '');
|
|
} else {
|
|
$result['created by'] = config('torrent.created_by', '');
|
|
}
|
|
|
|
$comment = config('torrent.comment');
|
|
|
|
if ($comment !== null) {
|
|
$result['comment'] = $comment;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Returns the torrent file list.
|
|
*
|
|
* @param array<mixed> $decodedTorrent
|
|
* @return array<
|
|
* int,
|
|
* array{
|
|
* name: string,
|
|
* size: int,
|
|
* }
|
|
* >
|
|
*/
|
|
public static function getTorrentFiles(array $decodedTorrent): array
|
|
{
|
|
$files = [];
|
|
|
|
if (\array_key_exists('files', $decodedTorrent['info']) && (is_countable($decodedTorrent['info']['files']) ? \count($decodedTorrent['info']['files']) : 0)) {
|
|
foreach ($decodedTorrent['info']['files'] as $k => $file) {
|
|
$dir = '';
|
|
$count = is_countable($file['path']) ? \count($file['path']) : 0;
|
|
|
|
for ($i = 0; $i < $count; $i++) {
|
|
if ($i + 1 === $count) {
|
|
$fname = $dir.$file['path'][$i];
|
|
$files[$k]['name'] = $fname;
|
|
} else {
|
|
$dir .= $file['path'][$i].'/';
|
|
$files[$k]['name'] = $dir;
|
|
}
|
|
|
|
$files[$k]['size'] = $file['length'];
|
|
}
|
|
}
|
|
} else {
|
|
$files[0]['name'] = $decodedTorrent['info']['name'];
|
|
$files[0]['size'] = $decodedTorrent['info']['length'];
|
|
}
|
|
|
|
return $files;
|
|
}
|
|
|
|
/**
|
|
* Returns file and folder names from the torrent.
|
|
*
|
|
* @param array<mixed> $decodedTorrent
|
|
* @return array<string>
|
|
*/
|
|
public static function getFilenameArray(array $decodedTorrent): array
|
|
{
|
|
$filenames = [];
|
|
|
|
if (\array_key_exists('files', $decodedTorrent['info']) && (is_countable($decodedTorrent['info']['files']) ? \count($decodedTorrent['info']['files']) : 0)) {
|
|
foreach ($decodedTorrent['info']['files'] as $file) {
|
|
$count = is_countable($file['path']) ? \count($file['path']) : 0;
|
|
|
|
for ($i = 0; $i < $count; $i++) {
|
|
if (!\in_array($file['path'][$i], $filenames)) {
|
|
$filenames[] = $file['path'][$i];
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
$filenames[] = $decodedTorrent['info']['name'];
|
|
}
|
|
|
|
return $filenames;
|
|
}
|
|
|
|
/**
|
|
* Returns the NFO.
|
|
*/
|
|
public static function getNfo(?UploadedFile $inputFile): bool|string|null
|
|
{
|
|
if ($inputFile === null) {
|
|
return null;
|
|
}
|
|
|
|
$fileName = uniqid('', true).'.nfo';
|
|
$path = Storage::disk('temporary-nfos')->path('');
|
|
$inputFile->move($path, $fileName);
|
|
|
|
if (Storage::disk('temporary-nfos')->exists($fileName)) {
|
|
$fileContent = Storage::disk('temporary-nfos')->get($fileName);
|
|
Storage::disk('temporary-nfos')->delete($fileName);
|
|
} else {
|
|
$fileContent = null;
|
|
}
|
|
|
|
return $fileContent;
|
|
}
|
|
|
|
/**
|
|
* Check if the filename is valid or not.
|
|
*/
|
|
public static function isValidFilename(string $filename): bool
|
|
{
|
|
return !(
|
|
\strlen($filename) > 255
|
|
// nodes containing: `\`, `/`, `?`, `<`, `>`, `:`, `8`, `|`, and ascii characters from 0 through 31
|
|
|| preg_match('/[\\\\\\/?<>:*|"\x00-\x1f]/', $filename)
|
|
// nodes only containing one or many: `.`; or only containing one or many `.`, ` `.
|
|
|| preg_match('/(^\\.+|[. ]+)$/', $filename)
|
|
// Special windows filenames.
|
|
|| preg_match('/^(con|prn|aux|nul|com\d|lpt\d)(\\..*)?$/i', $filename)
|
|
// BitComet padding files
|
|
|| preg_match('/^\.?____padding.*$/i', $filename)
|
|
// BEP 47 torrent padding files that many clients aren't able to handle
|
|
|| str_starts_with($filename, '.pad')
|
|
// Tilde home expansion on linux
|
|
|| str_starts_with($filename, '~')
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Anonymize A Torrent Media Info.
|
|
*/
|
|
public static function anonymizeMediainfo(string|Stringable|null $mediainfo): ?string
|
|
{
|
|
if ($mediainfo === null) {
|
|
return null;
|
|
}
|
|
|
|
if ($mediainfo instanceof Stringable) {
|
|
$mediainfo = $mediainfo->toString();
|
|
}
|
|
|
|
$completeNameI = strpos($mediainfo, 'Complete name');
|
|
|
|
if ($completeNameI !== false) {
|
|
$pathI = strpos($mediainfo, ': ', $completeNameI);
|
|
|
|
if ($pathI !== false) {
|
|
$pathI += 2;
|
|
$endI = strpos($mediainfo, "\n", $pathI);
|
|
$path = substr($mediainfo, $pathI, $endI - $pathI);
|
|
$newPath = MediaInfo::stripPath($path);
|
|
|
|
return substr_replace($mediainfo, $newPath, $pathI, \strlen($path));
|
|
}
|
|
}
|
|
|
|
return $mediainfo;
|
|
}
|
|
|
|
/**
|
|
* Parse Torrent Keywords.
|
|
*
|
|
* @return array<string>
|
|
*/
|
|
public static function parseKeywords(string|Stringable $text): array
|
|
{
|
|
if ($text instanceof Stringable) {
|
|
$text = $text->toString();
|
|
}
|
|
|
|
$keywords = array_filter(array_map('trim', explode(',', $text)));
|
|
|
|
// unique keywords only (case insensitive)
|
|
return array_values(array_intersect_key($keywords, array_unique(array_map('strtolower', $keywords))));
|
|
}
|
|
}
|