mirror of
https://github.com/OPSnet/Gazelle.git
synced 2026-01-16 18:04:34 -05:00
Prevent duplicate logs being added for torrent
This commit is contained in:
11
app/File.php
11
app/File.php
@@ -45,4 +45,15 @@ abstract class File extends BaseObject {
|
||||
public function remove(): int {
|
||||
return (int)unlink($this->path());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hash of the file
|
||||
*/
|
||||
public function hash(): string {
|
||||
$hash = hash_file(DIGEST_ALGO, $this->path());
|
||||
if (!$hash) {
|
||||
throw new \Exception("Failed to compute hash for file: {$this->path()}");
|
||||
}
|
||||
return $hash;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,15 @@ class LogfileSummary {
|
||||
protected bool $allChecksum = true;
|
||||
protected int $lowestScore = 100;
|
||||
|
||||
public function __construct(array $fileList = []) {
|
||||
public function __construct(array $fileList = [], array $hashes = []) {
|
||||
$this->list = [];
|
||||
for ($n = 0, $end = count($fileList['error']); $n < $end; ++$n) {
|
||||
if ($fileList['error'][$n] == UPLOAD_ERR_OK) {
|
||||
if ($fileList['error'][$n] === UPLOAD_ERR_OK) {
|
||||
$hash = hash_file(DIGEST_ALGO, $fileList['tmp_name'][$n]);
|
||||
if (in_array($hash, $hashes)) {
|
||||
continue;
|
||||
}
|
||||
$hashes[] = $hash;
|
||||
$log = new Logfile($fileList['tmp_name'][$n], $fileList['name'][$n]);
|
||||
$this->allChecksum = $this->allChecksum && $log->checksum();
|
||||
$this->lowestScore = min($this->lowestScore, $log->score());
|
||||
@@ -31,6 +36,9 @@ class LogfileSummary {
|
||||
return $this->lowestScore;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<Logfile>
|
||||
*/
|
||||
public function all(): array {
|
||||
return $this->list;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Gazelle\Manager;
|
||||
|
||||
use Gazelle\Logfile as Logfile;
|
||||
@@ -21,7 +23,7 @@ class TorrentLog extends \Gazelle\Base {
|
||||
$logId = self::$db->inserted_id();
|
||||
new RipLog($torrent->id, $logId)->put($logfile->filepath());
|
||||
new RipLogHTML($torrent->id, $logId)->put($logfile->text());
|
||||
return $this->findById($torrent, $logId);
|
||||
return new \Gazelle\TorrentLog($torrent, $logId);
|
||||
}
|
||||
|
||||
public function findById(Torrent $torrent, int $id): ?\Gazelle\TorrentLog {
|
||||
|
||||
@@ -818,4 +818,20 @@ abstract class TorrentAbstract extends BaseAttrObject {
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array of the hashes of all logfiles for a torrent
|
||||
* @return array<string>
|
||||
*/
|
||||
public function logfileHashList(): array {
|
||||
self::$db->prepared_query("
|
||||
SELECT LogID FROM torrents_logs WHERE TorrentID = ?
|
||||
", $this->id
|
||||
);
|
||||
$hashes = [];
|
||||
foreach (self::$db->collect(0) as $logId) {
|
||||
$hashes[] = new RipLog($this->id, $logId)->hash();
|
||||
}
|
||||
return $hashes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,13 @@ if (empty($_FILES) || empty($_FILES['logfiles'])) {
|
||||
json_error('no log files uploaded');
|
||||
}
|
||||
|
||||
$manager = new Manager\TorrentLog();
|
||||
|
||||
echo new Json\AddLog(
|
||||
$torrent,
|
||||
$Viewer,
|
||||
new Manager\TorrentLog(),
|
||||
new LogfileSummary($_FILES['logfiles']),
|
||||
$manager,
|
||||
new LogfileSummary($_FILES['logfiles'], $torrent->logfileHashList()),
|
||||
)
|
||||
->setVersion(1)
|
||||
->response();
|
||||
|
||||
@@ -22,16 +22,16 @@ if ($torrent->uploaderId() != $Viewer->id && !$Viewer->permitted('admin_add_log'
|
||||
}
|
||||
|
||||
$action = in_array($_POST['from_action'], ['upload', 'update']) ? $_POST['from_action'] : 'upload';
|
||||
$logfileSummary = new LogfileSummary($_FILES['logfiles']);
|
||||
$logfileSummary = new LogfileSummary($_FILES['logfiles'], $torrent->logfileHashList());
|
||||
|
||||
if (!$logfileSummary->total()) {
|
||||
Error400::error("No logfiles uploaded.");
|
||||
Error400::error("No (new) logfiles uploaded.");
|
||||
} else {
|
||||
$torrent->removeLogDb();
|
||||
new File\RipLog($torrent->id, '*')->remove();
|
||||
new File\RipLogHTML($torrent->id, '*')->remove();
|
||||
$torrentLogManager = new Manager\TorrentLog();
|
||||
|
||||
$torrentLogManager = new Manager\TorrentLog();
|
||||
$checkerVersion = Logchecker::getLogcheckerVersion();
|
||||
foreach ($logfileSummary->all() as $logfile) {
|
||||
$torrentLogManager->create($torrent, $logfile, $checkerVersion);
|
||||
|
||||
@@ -207,7 +207,7 @@ $db = DB::DB();
|
||||
$db->begin_transaction(); // It's all or nothing
|
||||
|
||||
if (isset($_FILES['logfiles'])) {
|
||||
$logfileSummary = new LogfileSummary($_FILES['logfiles']);
|
||||
$logfileSummary = new LogfileSummary($_FILES['logfiles'], $torrent->logfileHashList());
|
||||
if ($logfileSummary->total()) {
|
||||
$torrentLogManager = new Manager\TorrentLog();
|
||||
$checkerVersion = Logchecker::getLogcheckerVersion();
|
||||
|
||||
@@ -144,4 +144,10 @@ class FileStorageTest extends TestCase {
|
||||
$this->assertEquals('', $file->link(), 'file-t-link');
|
||||
$this->assertEquals('', $file->location(), 'file-t-location');
|
||||
}
|
||||
|
||||
public function testFileHashFailure(): void {
|
||||
$file = new File\Torrent(906623);
|
||||
$this->expectException(\Exception::class);
|
||||
$file->hash();
|
||||
}
|
||||
}
|
||||
|
||||
46
tests/phpunit/LogfileSummaryTest.php
Normal file
46
tests/phpunit/LogfileSummaryTest.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Gazelle;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class LogfileSummaryTest extends TestCase {
|
||||
public function testSummary(): void {
|
||||
$logfileSummary = new LogfileSummary([
|
||||
'error' => [UPLOAD_ERR_OK],
|
||||
'name' => ['valid_log_eac.log'],
|
||||
'tmp_name' => [__DIR__ . '/../fixture/valid_log_eac.log'],
|
||||
]);
|
||||
|
||||
$this->assertTrue($logfileSummary->checksum(), 'logfilesummary-checksum');
|
||||
$this->assertEquals('1', $logfileSummary->checksumStatus(), 'logfilesummary-checksum-status');
|
||||
$this->assertEquals(100, $logfileSummary->overallScore(), 'logfilesummary-overall-score');
|
||||
$this->assertCount(1, $logfileSummary->all(), 'logfilesummary-all');
|
||||
$this->assertEquals(1, $logfileSummary->total(), 'logfilesummary-total');
|
||||
}
|
||||
|
||||
public function testDuplicateFilesIgnored(): void {
|
||||
$logfileSummary = new LogfileSummary([
|
||||
'error' => [UPLOAD_ERR_OK, UPLOAD_ERR_OK],
|
||||
'name' => ['valid_log_eac.log', 'valid_log_eac.log'],
|
||||
'tmp_name' => [__DIR__ . '/../fixture/valid_log_eac.log', __DIR__ . '/../fixture/valid_log_eac.log'],
|
||||
]);
|
||||
|
||||
$this->assertTrue($logfileSummary->checksum(), 'logfilesummary-checksum');
|
||||
$this->assertEquals('1', $logfileSummary->checksumStatus(), 'logfilesummary-checksum-status');
|
||||
$this->assertEquals(100, $logfileSummary->overallScore(), 'logfilesummary-overall-score');
|
||||
$this->assertCount(1, $logfileSummary->all(), 'logfilesummary-all');
|
||||
$this->assertEquals(1, $logfileSummary->total(), 'logfilesummary-total');
|
||||
}
|
||||
|
||||
public function testDuplicateHashesIgnored(): void {
|
||||
$logfileSummary = new LogfileSummary([
|
||||
'error' => [UPLOAD_ERR_OK],
|
||||
'name' => ['valid_log_eac.log'],
|
||||
'tmp_name' => [__DIR__ . '/../fixture/valid_log_eac.log'],
|
||||
], [hash_file(DIGEST_ALGO, __DIR__ . '/../fixture/valid_log_eac.log')]);
|
||||
|
||||
$this->assertCount(0, $logfileSummary->all(), 'logfilesummary-all');
|
||||
$this->assertEquals(0, $logfileSummary->total(), 'logfilesummary-total');
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ use GazelleUnitTest\Helper;
|
||||
use Gazelle\Enum\DownloadStatus;
|
||||
use Gazelle\Enum\TorrentFlag;
|
||||
use Gazelle\Enum\UserTorrentSearch;
|
||||
use OrpheusNET\Logchecker\Logchecker;
|
||||
|
||||
class TorrentTest extends TestCase {
|
||||
protected Torrent $torrent;
|
||||
@@ -365,4 +366,26 @@ class TorrentTest extends TestCase {
|
||||
"collector-tlist-summary",
|
||||
);
|
||||
}
|
||||
|
||||
public function testLogfileHashList(): void {
|
||||
try {
|
||||
$logfileSummary = new LogfileSummary([
|
||||
'error' => [UPLOAD_ERR_OK],
|
||||
'name' => ['valid_log_eac.log'],
|
||||
'tmp_name' => [__DIR__ . '/../fixture/valid_log_eac.log'],
|
||||
]);
|
||||
$torrentLogManager = new Manager\TorrentLog();
|
||||
$checkerVersion = Logchecker::getLogcheckerVersion();
|
||||
foreach ($logfileSummary->all() as $logfile) {
|
||||
$torrentLog = $torrentLogManager->create($this->torrent, $logfile, $checkerVersion);
|
||||
// Because RipLog::put relies on move_uploaded_file, the create method above fails to put the log file
|
||||
// into place, so we do this copy afterwards.
|
||||
$ripLog = new File\RipLog($torrentLog->torrentId(), $torrentLog->id());
|
||||
copy(__DIR__ . '/../fixture/valid_log_eac.log', $ripLog->path());
|
||||
}
|
||||
$this->assertEquals([hash_file('sha256', __DIR__ . '/../fixture/valid_log_eac.log')], $this->torrent->logfileHashList());
|
||||
} finally {
|
||||
new File\RipLog($this->torrent->id, '*')->remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
57
tests/phpunit/manager/TorrentLogTest.php
Normal file
57
tests/phpunit/manager/TorrentLogTest.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Gazelle;
|
||||
|
||||
use GazelleUnitTest\Helper;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use OrpheusNET\Logchecker\Logchecker;
|
||||
|
||||
class TorrentLogTest extends TestCase {
|
||||
public function testFindById(): void {
|
||||
$user = null;
|
||||
$tgroup = null;
|
||||
try {
|
||||
$user = Helper::makeUser('torrentlog.' . randomString(6), 'torrentlog');
|
||||
$user->requestContext()->setViewer($user);
|
||||
$tgroup = Helper::makeTGroupMusic(
|
||||
$user,
|
||||
'phpunit torrentlog ' . randomString(6),
|
||||
[[ARTIST_MAIN], ['phpunit torrentlog artist ' . randomString(6)]],
|
||||
['czech']
|
||||
);
|
||||
$torrent = Helper::makeTorrentMusic(
|
||||
tgroup: $tgroup,
|
||||
user: $user,
|
||||
title: randomString(10),
|
||||
);
|
||||
$logfileSummary = new LogfileSummary([
|
||||
'error' => [UPLOAD_ERR_OK],
|
||||
'name' => ['valid_log_eac.log'],
|
||||
'tmp_name' => [__DIR__ . '/../fixture/valid_log_eac.log'],
|
||||
]);
|
||||
|
||||
$torrentLogManager = new Manager\TorrentLog();
|
||||
$checkerVersion = Logchecker::getLogcheckerVersion();
|
||||
$torrentLog = null;
|
||||
foreach ($logfileSummary->all() as $logfile) {
|
||||
$torrentLog = $torrentLogManager->create($torrent, $logfile, $checkerVersion);
|
||||
}
|
||||
$torrentLog2 = $torrentLogManager->findById($torrent, $torrentLog->id());
|
||||
$this->assertInstanceOf(TorrentLog::class, $torrentLog2);
|
||||
$this->assertEquals($torrentLog->id(), $torrentLog2->id());
|
||||
$this->assertEquals($torrentLog->link(), $torrentLog2->link());
|
||||
} finally {
|
||||
if (isset($tgroup)) {
|
||||
Helper::removeTGroup($tgroup, $user);
|
||||
}
|
||||
if (isset($user)) {
|
||||
$user->remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testFindByIdNull(): void {
|
||||
$manager = new Manager\TorrentLog();
|
||||
$this->assertNull($manager->findById(new Torrent(1), 1));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user