Files
ops-Gazelle/sections/upload/upload_handle.php
2023-04-04 18:55:57 +02:00

930 lines
37 KiB
PHP

<?php
use Gazelle\Util\Irc;
use OrpheusNET\Logchecker\Logchecker;
ini_set('max_file_uploads', 100);
ini_set('upload_max_filesize', 1_000_000);
define('MAX_FILENAME_LENGTH', 255);
if (!defined('AJAX')) {
authorize();
}
//******************************************************************************//
//--------------- Set $Properties array ----------------------------------------//
// This is used if the form doesn't validate, and when the time comes to enter //
// it into the database. //
$Err = null;
$Properties = [];
$categoryId = (int)$_POST['type'] + 1;
$categoryName = CATEGORY[$categoryId - 1];
$Properties['Title'] = isset($_POST['title']) ? trim($_POST['title']) : null;
// Remastered is an Enum in the DB
$Properties['Remastered'] = !empty($_POST['remaster']) ? '1' : '0';
if ($Properties['Remastered'] || !empty($_POST['unknown'])) {
$Properties['UnknownRelease'] = !empty($_POST['unknown']) ? 1 : 0;
$Properties['RemasterYear'] = isset($_POST['remaster_year']) ? (int)$_POST['remaster_year'] : null;
$_POST['remaster_year'] = $Properties['RemasterYear'];
$Properties['RemasterTitle'] = trim($_POST['remaster_title'] ?? '');
$Properties['RemasterRecordLabel'] = trim($_POST['remaster_record_label'] ?? '');
$Properties['RemasterCatalogueNumber'] = trim($_POST['remaster_catalogue_number'] ?? '');
}
if (!$Properties['Remastered'] || $Properties['UnknownRelease']) {
$Properties['UnknownRelease'] = 1;
$Properties['RemasterYear'] = null;
$Properties['RemasterTitle'] = '';
$Properties['RemasterRecordLabel'] = '';
$Properties['RemasterCatalogueNumber'] = '';
}
$Properties['Year'] = isset($_POST['year']) ? (int)$_POST['year'] : null;
$_POST['year'] = $Properties['Year'];
$Properties['RecordLabel'] = trim($_POST['record_label'] ?? '');
$Properties['CatalogueNumber'] = trim($_POST['catalogue_number'] ?? '');
$Properties['ReleaseType'] = $_POST['releasetype'] ?? null;
$Properties['Scene'] = !empty($_POST['scene']) ? '1' : '0';
$Properties['Format'] = isset($_POST['format']) ? trim($_POST['format']) : null;
$Properties['Media'] = trim($_POST['media'] ?? '');
$Properties['Encoding'] = trim($_POST['bitrate'] ?? '');
if ($Properties['Encoding'] === 'Other') {
$_POST['other_bitrate'] = trim($_POST['other_bitrate'] ?? '');
}
$Properties['MultiDisc'] = $_POST['multi_disc'] ?? null;
if (isset($_POST['tags'])) {
$Properties['TagList'] = array_unique(array_map('trim', explode(',', $_POST['tags']))); // Musicbranes loves to send duplicates
}
$Properties['Image'] = trim($_POST['image'] ?? '');
$Properties['GroupDescription'] = trim($_POST['album_desc'] ?? '');
$Properties['TorrentDescription'] = trim($_POST['release_desc'] ?? '');
if (isset($_POST['album_desc'])) {
$Properties['GroupDescription'] = trim($_POST['album_desc'] ?? '');
} elseif (isset($_POST['desc'])) {
$Properties['GroupDescription'] = trim($_POST['desc'] ?? '');
}
$Properties['GroupID'] = $_POST['groupid'] ?? null;
if (empty($_POST['artists'])) {
$Err = "You didn't enter any artists";
} else {
$Artists = $_POST['artists'];
$Importance = $_POST['importance'];
}
if (!empty($_POST['requestid'])) {
$RequestID = $_POST['requestid'];
$Properties['RequestID'] = $RequestID;
}
//******************************************************************************//
//--------------- Validate data in upload form ---------------------------------//
$isMusicUpload = ($categoryName === 'Music');
// common to all types
$Validate = new Gazelle\Util\Validator;
$Validate->setFields([
['type', '1', 'inarray', 'Please select a valid category.', ['inarray' => array_keys(CATEGORY)]],
['release_desc', '0','string','The release description you entered is too long.', ['maxlength'=>1_000_000]],
['rules', '1','require','Your torrent must abide by the rules.'],
]);
if (!$isMusicUpload || !$Properties['GroupID']) {
$Validate->setFields([
['image', '0','link','The image URL you entered was invalid.', ['range' => [255, 12]]],
['tags', '1','string','You must enter at least one tag. Maximum length is 200 characters.', ['range' => [2, 200]]],
['title', '1','string','Title must be less than 200 characters.', ['maxlength' => 200]],
]);
}
if (isset($_POST['album_desc'])) {
$Validate->setField('album_desc', '1','string','The album description has a minimum length of 10 characters.', ['range' => [10, 1_000_000]]);
} elseif (isset($_POST['desc'])) {
$Validate->setField('desc', '1','string','The description has a minimum length of 10 characters.', ['range' => [10, 1_000_000]]);
}
// audio types
if (in_array($categoryName, ['Music', 'Audiobooks', 'Comedy'])) {
$Validate->setField('format', '1','inarray','Please select a valid format.', ['inarray'=>FORMAT]);
if ($Properties['Encoding'] !== 'Other') {
$Validate->setField('bitrate', '1','inarray','You must choose a bitrate.', ['inarray'=>ENCODING]);
} else {
if ($Properties['Format'] === 'FLAC') {
$Validate->setField('bitrate', '1','string','FLAC bitrate must be lossless.', ['regex'=>'/Lossless/']);
} else {
$Validate->setField('other_bitrate',
'1','string','You must enter the other bitrate (max length: 9 characters).', ['maxlength'=>9]);
$Properties['Encoding'] = trim($_POST['other_bitrate']) . (!empty($_POST['vbr']) ? ' (VBR)' : '');;
}
}
}
$feedType = ['torrents_all'];
$releaseTypes = (new Gazelle\ReleaseType)->list();
switch ($categoryName) {
case 'Music':
$Validate->setFields([
['groupid', '0', 'number', 'Group ID was not numeric'],
['media', '1','inarray','Please select a valid media.', ['inarray'=>MEDIA]],
['remaster_title', '0','string','Remaster title must be between 1 and 80 characters.', ['range' => [1, 80]]],
['remaster_record_label', '0','string','Remaster record label must be between 1 and 80 characters.', ['range' => [1, 80]]],
['remaster_catalogue_number', '0','string','Remaster catalogue number must be between 1 and 80 characters.', ['range' => [1, 80]]],
]);
if (!$Properties['GroupID']) {
$Validate->setFields([
['year', '1','number','The year of the original release must be entered.', ['length'=>40]],
['releasetype', '1','inarray','Please select a valid release type.', ['inarray'=>array_keys($releaseTypes)]],
['record_label', '0','string','Record label must be between 1 and 80 characters.', ['range' => [1, 80]]],
['catalogue_number', '0','string','Catalogue Number must be between 1 and 80 characters.', ['range' => [1, 80]]],
]);
if ($Properties['Media'] == 'CD' && !$Properties['Remastered']) {
$Validate->setField('year', '1', 'number', 'You have selected a year for an album that predates the media you say it was created on.', ['minlength'=>1982]);
}
}
if ($Properties['RemasterTitle'] === 'Original Release') {
$Validate->setField('remaster_title', '0', 'string', '"Orginal Release" is not a valid remaster title.');
}
if (!$Properties['Remastered']) {
$Validate->setField('remaster_year', '0','number','Invalid remaster year.');
} else {
if (!$Properties['UnknownRelease']) {
$Validate->setField('remaster_year', '1','number','Year of remaster/re-issue must be entered.');
}
if ($Properties['Media'] == 'CD' ) {
$Validate->setField('remaster_year', '1', 'number', 'You have selected a year for an album that predates the media you say it was created on.',
['minlength' => 1982]
);
}
}
$feedType[] = 'torrents_music';
if ($Properties['Media'] === 'Vinyl') {
$feedType[] = 'torrents_vinyl';
}
if ($Properties['Encoding'] === 'Lossless') {
$feedType[] = 'torrents_lossless';
} elseif ($Properties['Encoding'] === '24bit Lossless') {
$feedType[] = 'torrents_lossless24';
}
if ($Properties['Format'] === 'MP3') {
$feedType[] = 'torrents_mp3';
} elseif ($Properties['Format'] === 'FLAC') {
$feedType[] = 'torrents_flac';
}
break;
case 'Applications':
$feedType[] = 'torrents_apps';
break;
case 'Audiobooks':
$Validate->setField('year', '1','number','The year of the release must be entered.');
$feedType[] = 'torrents_abooks';
break;
case 'Comedy':
$feedType[] = 'torrents_comedy';
break;
case 'Comics':
$feedType[] = 'torrents_comics';
break;
case 'E-Books':
$feedType[] = 'torrents_ebooks';
break;
case 'E-Learning Videos':
$feedType[] = '';
break;
}
$Err = $Validate->validate($_POST) ? false : $Validate->errorMessage();
$File = $_FILES['file_input']; // This is our torrent file
$TorrentName = $File['tmp_name'];
$LogName = '';
if (!is_uploaded_file($TorrentName) || !filesize($TorrentName)) {
$Err = 'No torrent file uploaded, or file is empty.';
} elseif (substr(strtolower($File['name']), strlen($File['name']) - strlen('.torrent')) !== '.torrent') {
$Err = "You seem to have put something other than a torrent file into the upload field. (".$File['name'].").";
}
if ($Properties['Image']) {
// Strip out Amazon's padding
if (preg_match('/(http:\/\/ecx.images-amazon.com\/images\/.+)(\._.*_\.jpg)/i', $Properties['Image'], $match)) {
$Properties['Image'] = $match[1].'.jpg';
}
if (!preg_match(IMAGE_REGEXP, $Properties['Image'])) {
$Err = display_str($Properties['Image']) . " does not look like a valid image url";
}
$banned = (new Gazelle\Util\ImageProxy($Viewer))->badHost($Properties['Image']);
if ($banned) {
$Err = "Please rehost images from $banned elsewhere.";
}
}
if (!$Err && $isMusicUpload) {
// additional torrent files
$ExtraTorrents = [];
$DupeNames = [$_FILES['file_input']['name']];
if (!empty($_POST['extra_format']) && !empty($_POST['extra_bitrate'])) {
for ($i = 1; $i <= 5; $i++) {
if (!empty($_FILES["extra_file_$i"])) {
$ExtraFile = $_FILES["extra_file_$i"];
$ExtraTorrentName = $ExtraFile['tmp_name'];
if (!is_uploaded_file($ExtraTorrentName) || !filesize($ExtraTorrentName)) {
$Err = 'No extra torrent file uploaded, or file is empty.';
} elseif (substr(strtolower($ExtraFile['name']), strlen($ExtraFile['name']) - strlen('.torrent')) !== '.torrent') {
$Err = 'You seem to have put something other than an extra torrent file into the upload field. (' . $ExtraFile['name'] . ').';
} elseif (in_array($ExtraFile['name'], $DupeNames)) {
$Err = 'One or more torrents has been entered into the form twice.';
} else {
$j = $i - 1;
$ExtraTorrents[$ExtraTorrentName]['Name'] = $ExtraTorrentName;
$ExtraFormat = trim($_POST['extra_format'][$j]);
if (empty($ExtraFormat)) {
$Err = 'Missing format for extra torrent.';
break;
} else {
$ExtraTorrents[$ExtraTorrentName]['Format'] = $ExtraFormat;
}
$ExtraBitrate = trim($_POST['extra_bitrate'][$j]);
if (empty($ExtraBitrate)) {
$Err = 'Missing bitrate for extra torrent.';
break;
} else {
$ExtraTorrents[$ExtraTorrentName]['Encoding'] = $ExtraBitrate;
}
$ExtraReleaseDescription = trim($_POST['extra_release_desc'][$j]);
$ExtraTorrents[$ExtraTorrentName]['TorrentDescription'] = $ExtraReleaseDescription;
$DupeNames[] = $ExtraFile['name'];
}
}
}
}
unset($DupeNames);
// Multiple artists
if (!$Err && empty($Properties['GroupID'])) {
$ArtistForm = [
ARTIST_MAIN => [],
ARTIST_GUEST => [],
ARTIST_REMIXER => [],
ARTIST_COMPOSER => [],
ARTIST_CONDUCTOR => [],
ARTIST_DJ => [],
ARTIST_PRODUCER => [],
ARTIST_ARRANGER => [],
];
$ArtistNameByRole = [
ARTIST_MAIN => [],
ARTIST_GUEST => [],
ARTIST_REMIXER => [],
ARTIST_COMPOSER => [],
ARTIST_CONDUCTOR => [],
ARTIST_DJ => [],
ARTIST_PRODUCER => [],
ARTIST_ARRANGER => [],
];
$ArtistRoleList = [];
$ArtistNameList = [];
if (isset($Artists) && is_array($Artists)) {
for ($i = 0, $end = count($Artists); $i < $end; $i++) {
$name = Gazelle\Artist::sanitize($Artists[$i]);
if ($name === '') {
continue;
}
$role = (int)$Importance[$i];
if (!in_array($name, $ArtistNameByRole[$role])) {
$ArtistNameByRole[$role][] = $name;
$ArtistForm[$role][] = ['name' => $name];
$ArtistRoleList[] = $role;
$ArtistNameList[] = $name;
}
}
}
if (empty($ArtistNameByRole[ARTIST_MAIN])) {
$Err = 'Please enter at least one main artist';
} else {
$LogName .= Artists::display_artists($ArtistForm, false, true, false);
}
}
}
if ($Err) { // Show the upload form, with the data the user entered
if (defined('AJAX')) {
json_error($Err);
} else {
require(__DIR__ . '/upload.php');
die();
}
}
//******************************************************************************//
//--------------- Generate torrent file ----------------------------------------//
$torMan = new Gazelle\Manager\Torrent;
$tgMan = new Gazelle\Manager\TGroup;
$torrentFiler = new Gazelle\File\Torrent;
$bencoder = new OrpheusNET\BencodeTorrent\BencodeTorrent;
$bencoder->decodeFile($TorrentName);
$PublicTorrent = $bencoder->makePrivate(); // The torrent is now private.
$UnsourcedTorrent = $torMan->setSourceFlag($bencoder);
$InfoHash = $bencoder->getHexInfoHash();
$TorData = $bencoder->getData();
$torrent = $torMan->findByInfohash(bin2hex($InfoHash));
if ($torrent) {
$torrentId = $torrent->id();
if ($torrentFiler->exists($torrentId)) {
$Err = defined('AJAX')
? "The exact same torrent file already exists on the site! (torrentid=$torrentId)"
: "<a href=\"torrents.php?torrentid=$torrentId\">The exact same torrent file already exists on the site!</a>";
} else {
// A lost torrent
$torrentFiler->put($bencoder->getEncode(), $torrentId);
$Err = defined('AJAX')
? "Thank you for fixing this torrent (torrentid=$torrentId)"
: "<a href=\"torrents.php?torrentid=$torrentId\">Thank you for fixing this torrent</a>";
}
}
if (isset($TorData['encrypted_files'])) {
$Err = 'This torrent contains an encrypted file list which is not supported here.';
}
if (isset($TorData['info']['meta version'])) {
$Err = 'This torrent is not a V1 torrent. V2 and Hybrid torrents are not supported here.';
}
$checker = new Gazelle\Util\FileChecker;
// File list and size
['total_size' => $TotalSize, 'files' => $FileList] = $bencoder->getFileList();
$HasLog = '0';
$HasCue = '0';
$TmpFileList = [];
$TooLongPaths = [];
$DirName = (isset($TorData['info']['files']) ? make_utf8($bencoder->getName()) : '');
$folderCheck = [$DirName];
$IgnoredLogFileNames = ['audiochecker.log', 'sox.log'];
if (!$Err) {
$Err = $checker->checkName($DirName); // check the folder name against the blacklist
}
foreach ($FileList as $FileInfo) {
['path' => $Name, 'size' => $Size] = $FileInfo;
// add +log to encoding
if ($Properties['Media'] == 'CD' && $Properties['Encoding'] == "Lossless" && !in_array(strtolower($Name), $IgnoredLogFileNames) && preg_match('/\.log$/i', $Name)) {
$HasLog = '1';
}
// add +cue to encoding
if ($Properties['Encoding'] == "Lossless" && preg_match('/\.cue$/i', $Name)) {
$HasCue = '1';
}
// Check file name and extension against blacklist/whitelist
if (!$Err) {
$Err = $checker->checkFile($categoryName, $Name);
}
// Make sure the filename is not too long
if (mb_strlen($Name, 'UTF-8') + mb_strlen($DirName, 'UTF-8') + 1 > MAX_FILENAME_LENGTH) {
$TooLongPaths[] = "<li>$DirName/$Name</li>";
}
// Add file info to array
$TmpFileList[] = $torMan->metaFilename($Name, $Size);
}
if (count($TooLongPaths) > 0) {
$Err = defined('AJAX')
? ['The torrent contained one or more files with too long a name', ['list' => $TooLongPaths]]
: ('The torrent contained one or more files with too long a name: <ul>' . implode('', $TooLongPaths) . '</ul><br />');
}
$Debug->set_flag('upload: torrent decoded');
$ExtraTorrentsInsert = [];
// disable extra torrents when using ajax, just have them re-submit multiple times
if ($isMusicUpload) {
foreach ($ExtraTorrents as $ExtraTorrent) {
$Name = $ExtraTorrent['Name'];
$ExtraTorrentsInsert[$Name] = $ExtraTorrent;
$ThisInsert =& $ExtraTorrentsInsert[$Name];
$xbencoder = new OrpheusNET\BencodeTorrent\BencodeTorrent;
$xbencoder->decodeFile($Name);
$ExtraTorData = $xbencoder->getData();
if (isset($ExtraTorData['encrypted_files'])) {
$Err = 'At least one of the torrents contain an encrypted file list which is not supported here';
break;
}
if (!$xbencoder->isPrivate()) {
$xbencoder->makePrivate(); // The torrent is now private.
$PublicTorrent = true;
}
if ($torMan->setSourceFlag($xbencoder)) {
$UnsourcedTorrent = true;
}
// File list and size
['total_size' => $ExtraTotalSize, 'files' => $ExtraFileList] = $xbencoder->getFileList();
$ExtraDirName = isset($ExtraTorData['info']['files']) ? make_utf8($xbencoder->getName()) : '';
$ExtraTmpFileList = [];
foreach ($ExtraFileList as $ExtraFile) {
['path' => $ExtraName, 'size' => $ExtraSize] = $ExtraFile;
if (!$Err) {
$Err = $checker->checkFile($categoryName, $ExtraName);
}
if (mb_strlen($ExtraName, 'UTF-8') + mb_strlen($ExtraDirName, 'UTF-8') + 1 > MAX_FILENAME_LENGTH) {
$Err = defined('AJAX')
? "The torrent contained one or more files with too long a name: $ExtraDirName/$ExtraName"
: "The torrent contained one or more files with too long a name: <br />$ExtraDirName/$ExtraName";
break;
}
$ExtraTmpFileList[] = $torMan->metaFilename($ExtraName, $ExtraSize);
}
// To be stored in the database
$ThisInsert['FilePath'] = $ExtraDirName;
$ThisInsert['FileString'] = implode("\n", $ExtraTmpFileList);
$ThisInsert['InfoHash'] = $xbencoder->getHexInfoHash();
$ThisInsert['NumFiles'] = count($ExtraFileList);
$ThisInsert['TorEnc'] = $xbencoder->getEncode();
$ThisInsert['TotalSize'] = $ExtraTotalSize;
$Debug->set_flag('upload: torrent decoded');
$torrent = $torMan->findByInfohash(bin2hex($ThisInsert['InfoHash']));
if ($torrent) {
$torrentId = $torrent->id();
if ($torrentFiler->exists($torrentId)) {
$Err = defined('AJAX')
? "The exact same torrent file already exists on the site! (torrentid=$torrentId)"
: "<a href=\"torrents.php?torrentid=$torrentId\">The exact same torrent file already exists on the site!</a>";
} else {
$torrentFiler->put($ThisInsert['TorEnc'], $torrentId);
$Err = defined('AJAX')
? "Thank you for fixing this torrent (torrentid=$torrentId)"
: "<a href=\"torrents.php?torrentid=$torrentId\">Thank you for fixing this torrent</a>";
}
}
}
unset($ThisInsert);
}
if ($Err) {
if (defined('AJAX')) {
json_error($Err);
} else {
// TODO: Repopulate the form correctly
require(__DIR__ . '/upload.php');
die();
}
}
$tgroup = null;
$NoRevision = false;
if ($isMusicUpload) {
// Does it belong in a group?
if ($Properties['GroupID']) {
$tgroup = $tgMan->findById($Properties['GroupID']);
}
if (is_null($tgroup)) {
foreach ($ArtistForm[ARTIST_MAIN] as $Artist) {
$tgroup = $tgMan->findByArtistReleaseYear($Artist['name'], $Properties['Title'], $Properties['ReleaseType'], $Properties['Year']);
if ($tgroup) {
break;
}
}
}
if ($tgroup) {
$Properties['ReleaseType'] = $tgroup->releaseType();
$Properties['Year'] = $tgroup->year();
$Properties['TagList'] = $tgroup->tagNameList();
if (!$Properties['Image'] && $tgroup->image()) {
$Properties['Image'] = $tgroup->image();
}
if ($Properties['GroupDescription'] != $tgroup->description()) {
$Properties['GroupDescription'] = $tgroup->description();
if (!$Properties['Image'] || $Properties['Image'] == $tgroup->image()) {
$NoRevision = true;
}
}
}
}
//For notifications--take note now whether it's a new group
$IsNewGroup = is_null($tgroup);
//Needs to be here as it isn't set for add format until now
$LogName .= $Properties['Title'];
$logfileSummary = new Gazelle\LogfileSummary($_FILES['logfiles']);
$LogInDB = $logfileSummary->total() ? '1' : '0';
//******************************************************************************//
//--------------- Start database stuff -----------------------------------------//
$Debug->set_flag('upload: database begin transaction');
$db = Gazelle\DB::DB();
$db->begin_transaction();
if ($tgroup) {
$tgroup->touch();
} else {
$tgroup = $tgMan->create(
categoryId: $categoryId,
name: $Properties['Title'],
year: $Properties['Year'],
recordLabel: $Properties['RecordLabel'],
catalogueNumber: $Properties['CatalogueNumber'],
description: $Properties['GroupDescription'],
image: $Properties['Image'],
releaseType: $Properties['ReleaseType'],
showcase: (bool)($Viewer->permitted('torrents_edit_vanityhouse') && isset($_POST['vanity_house'])),
);
if ($isMusicUpload) {
$tgroup->addArtists($Viewer, $ArtistRoleList, $ArtistNameList);
$Cache->increment_value('stats_album_count', count($ArtistNameList));
}
$Viewer->stats()->increment('unique_group_total');
}
$GroupID = $tgroup->id();
// Description
if ($NoRevision) {
$tgroup->createRevision($Properties['GroupDescription'], $Properties['Image'], 'Uploaded new torrent', $Viewer);
}
// Tags
$tagMan = new Gazelle\Manager\Tag;
$tagList = [];
if (!$Properties['GroupID']) {
foreach ($Properties['TagList'] as $tag) {
$tag = $tagMan->resolve($tagMan->sanitize($tag));
if (!empty($tag)) {
$TagID = $tagMan->create($tag, $Viewer->id());
$tagMan->createTorrentTag($TagID, $GroupID, $Viewer->id(), 10);
}
$tagList[] = $tag;
}
}
// Torrent
$db->prepared_query("
INSERT INTO torrents
(GroupID, UserID, Media, Format, Encoding,
Remastered, RemasterYear, RemasterTitle, RemasterRecordLabel, RemasterCatalogueNumber,
Scene, HasLog, HasCue, HasLogDB, LogScore,
LogChecksum, info_hash, FileCount, FileList, FilePath,
Size, Description, Time, FreeTorrent, FreeLeechType)
VALUES
(?, ?, ?, ?, ?,
?, ?, ?, ?, ?,
?, ?, ?, ?, ?,
?, ?, ?, ?, ?,
?, ?, now(), '0', '0')
", $GroupID, $Viewer->id(), $Properties['Media'], $Properties['Format'], $Properties['Encoding'],
$Properties['Remastered'], $Properties['RemasterYear'], $Properties['RemasterTitle'], $Properties['RemasterRecordLabel'], $Properties['RemasterCatalogueNumber'],
$Properties['Scene'], $HasLog, $HasCue, $LogInDB, $logfileSummary->overallScore(),
$logfileSummary->checksumStatus(), $InfoHash, count($FileList), implode("\n", $TmpFileList), $DirName,
$TotalSize, $Properties['TorrentDescription']
);
$TorrentID = $db->inserted_id();
$db->prepared_query('
INSERT INTO torrents_leech_stats (TorrentID)
VALUES (?)
', $TorrentID
);
$torrent = $torMan->findById($TorrentID);
$torrent->lockUpload();
$bonus = new Gazelle\User\Bonus($Viewer);
$bonusTotal = $bonus->torrentValue($torrent);
//******************************************************************************//
//--------------- Upload Extra torrents ----------------------------------------//
$extraFile = [];
$trackerUpdate = [];
foreach ($ExtraTorrentsInsert as $ExtraTorrent) {
$torrentExtra = $torMan->create(
tgroupId: $GroupID,
userId: $Viewer->id(),
description: $ExtraTorrent['TorrentDescription'],
media: $Properties['Media'],
format: $ExtraTorrent['Format'],
encoding: $ExtraTorrent['Encoding'],
infohash: $ExtraTorrent['InfoHash'],
filePath: $ExtraTorrent['FilePath'],
fileList: $ExtraTorrent['FileString'],
size: $ExtraTorrent['TotalSize'],
isScene: $Properties['Scene'],
isRemaster: $Properties['Remastered'],
remasterYear: $Properties['RemasterYear'],
remasterTitle: $Properties['RemasterTitle'],
remasterRecordLabel: $Properties['RemasterRecordLabel'],
remasterCatalogueNumber: $Properties['RemasterCatalogueNumber'],
);
$torMan->flushFoldernameCache($torrentExtra->path());
$folderCheck[] = $torrentExtra->path();
$trackerUpdate[$torrentExtra->id()] = rawurlencode($torrentExtra->infohash());
$bonusTotal += $bonus->torrentValue($torrentExtra);
$extraFile[$torrentExtra->id()] = [
'payload' => $ExtraTorrent['TorEnc'],
'size' => number_format($torrentExtra->size() / (1024 * 1024), 2)
];
}
//******************************************************************************//
//--------------- Write Files To Disk ------------------------------------------//
if ($logfileSummary->total()) {
$torrentLogManager = new Gazelle\Manager\TorrentLog(new Gazelle\File\RipLog, new Gazelle\File\RipLogHTML);
$checkerVersion = Logchecker::getLogcheckerVersion();
foreach($logfileSummary->all() as $logfile) {
$torrentLogManager->create($torrent, $logfile, $checkerVersion);
}
}
$log = new Gazelle\Log;
$torrentFiler->put($bencoder->getEncode(), $TorrentID);
$log->torrent($GroupID, $TorrentID, $Viewer->id(), 'uploaded ('.number_format($TotalSize / (1024 * 1024), 2).' MiB)')
->general("Torrent $TorrentID ($LogName) (".number_format($TotalSize / (1024 * 1024), 2).' MiB) was uploaded by ' . $Viewer->username());
foreach ($extraFile as $id => $info) {
$torrentFiler->put($info['payload'], $id);
$log->torrent($GroupID, $id, $Viewer->id(), "uploaded ({$info['size']} MiB)")
->general("Torrent $id ($LogName) ({$info['size']} MiB) was uploaded by " . $Viewer->username());
}
$db->commit(); // We have a usable upload, any subsequent failures can be repaired ex post facto
$Debug->set_flag('upload: database committed');
//******************************************************************************//
//--------------- Finalize -----------------------------------------------------//
$tracker = new \Gazelle\Tracker;
$trackerUpdate[$TorrentID] = rawurlencode($InfoHash);
foreach ($trackerUpdate as $id => $hash) {
$tracker->update_tracker('add_torrent', ['id' => $id, 'info_hash' => $hash, 'freetorrent' => 0]);
}
$Debug->set_flag('upload: ocelot updated');
if (!$Viewer->disableBonusPoints()) {
$bonus->addPoints($bonusTotal);
}
$tgroup->refresh();
$torMan->flushFoldernameCache($DirName);
if (in_array($Properties['Encoding'], ['Lossless', '24bit Lossless'])) {
$torMan->flushLatestUploads(5);
}
$totalNew = 1 + count($ExtraTorrentsInsert);
$Viewer->stats()->increment('upload_total', $totalNew);
if ($torrent->isPerfectFlac()) {
$Viewer->stats()->increment('perfect_flac_total');
} elseif ($torrent->isPerfecterFlac()) {
$Viewer->stats()->increment('perfecter_flac_total');
}
// Update the various cache keys affected by this
$Cache->increment_value('stats_torrent_count', $totalNew);
if ($Properties['Image'] != '') {
$Cache->delete_value('user_recent_up_' . $Viewer->id());
}
$torrent->flush();
$torrent->unlockUpload();
//******************************************************************************//
//---------------IRC announce and feeds ---------------------------------------//
$Announce = '';
if ($isMusicUpload) {
$Announce .= $tgroup->artistName() . ' - ';
}
$Announce .= $Properties['Title'] . ' ';
$Details = "";
if ($isMusicUpload) {
$Announce .= '['.$Properties['Year'].']';
if ($Properties['ReleaseType'] > 0) {
$Announce .= ' [' . $releaseTypes[$Properties['ReleaseType']] . ']';
}
$Details .= $Properties['Format'].' / '.$Properties['Encoding'];
if ($HasLog == 1) {
$Details .= ' / Log'.($LogInDB ? " ({$logfileSummary->overallScore()}%)" : "");
}
if ($HasCue == 1) {
$Details .= ' / Cue';
}
$Details .= ' / '.$Properties['Media'];
if ($Properties['Scene'] == '1') {
$Details .= ' / Scene';
}
}
$Title = $Announce;
if ($Details !== "") {
$Title .= " - ".$Details;
$Announce .= "\003 - \00310" . $Details . "\003";
}
$AnnounceSSL = "\002TORRENT:\002 \00303{$Announce}\003"
. " - \00312" . implode(',', $tagList) . "\003"
. " - \00304".SITE_URL."/torrents.php?id=$GroupID\003 / \00304".SITE_URL."/torrents.php?action=download&id=$TorrentID\003";
// ENT_QUOTES is needed to decode single quotes/apostrophes
Irc::sendMessage('#ANNOUNCE', html_entity_decode($AnnounceSSL, ENT_QUOTES));
$Debug->set_flag('upload: announced on irc');
//******************************************************************************//
//--------------- Post-processing ----------------------------------------------//
/* Because tracker updates and notifications can be slow, we're
* redirecting the user to the destination page and flushing the buffers
* to make it seem like the PHP process is working in the background.
*/
if ($Properties['Image'] != '') {
$Viewer->flushRecentUpload();
}
if (defined('AJAX')) {
$Response = [
'groupId' => $GroupID,
'torrentId' => $TorrentID,
'private' => !$PublicTorrent,
'source' => !$UnsourcedTorrent,
];
if (isset($RequestID)) {
define('NO_AJAX_ERROR', true);
$FillResponse = require_once(__DIR__ . '/../requests/take_fill.php');
if (!isset($FillResponse['requestId'])) {
$FillResponse = [
'status' => 400,
'error' => $FillResponse,
];
}
$Response['fillRequest'] = $FillResponse;
}
// TODO: this is copy-pasted
$Feed = new Gazelle\Feed;
$Item = $Feed->item(
$Title,
Text::strip_bbcode($Properties['GroupDescription']),
"torrents.php?action=download&id={$TorrentID}&torrent_pass=[[PASSKEY]]",
date('r'),
$Viewer->username(),
'torrents.php?id=' . $GroupID,
implode(',', $tagList)
);
$notification = (new Gazelle\Notification\Upload)
->addFormat($Properties['Format'])
->addEncodings($Properties['Encoding'])
->addMedia($Properties['Media'])
->addYear($Properties['Year'], $Properties['RemasterYear'])
->addArtists($tgroup->artistRole()->roleList())
->addTags($tagList)
->addCategory($categoryName)
->addUser($Viewer)
->setDebug(DEBUG_UPLOAD_NOTIFICATION);
if ($isMusicUpload) {
$notification->addReleaseType($releaseTypes[$Properties['ReleaseType']]);
}
$notification->trigger($GroupID, $TorrentID, $Feed, $Item);
// RSS for bookmarks
$db->prepared_query('
SELECT u.torrent_pass
FROM users_main AS u
INNER JOIN bookmarks_torrents AS b ON (b.UserID = u.ID)
WHERE b.GroupID = ?
', $GroupID
);
while ([$Passkey] = $db->next_record()) {
$feedType[] = "torrents_bookmarks_t_$Passkey";
}
foreach ($feedType as $subFeed) {
$Feed->populate($subFeed, $Item);
}
$Debug->set_flag('upload: notifications handled');
$notification->trigger($GroupID, $TorrentID, $Feed, $Item);
json_print('success', $Response);
} else {
$folderClash = 0;
if ($isMusicUpload) {
foreach ($folderCheck as $foldername) {
// This also has the nice side effect of warming the cache immediately
$list = $torMan->findAllByFoldername($foldername);
if (count($list) > 1) {
++$folderClash;
}
}
}
if ($PublicTorrent || $UnsourcedTorrent || $folderClash) {
View::show_header('Warning');
echo $Twig->render('upload/result_warnings.twig', [
'clash' => $folderClash,
'group_id' => $GroupID,
'public' => $PublicTorrent,
'unsourced' => $UnsourcedTorrent,
'wiki_id' => SOURCE_FLAG_WIKI_PAGE_ID,
]);
View::show_footer();
} elseif (isset($RequestID)) {
header("Location: requests.php?action=takefill&requestid=$RequestID&torrentid=$TorrentID&auth=" . $Viewer->auth());
} else {
header("Location: torrents.php?id=$GroupID");
}
}
if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
} else {
ignore_user_abort(true);
ob_flush();
flush();
ob_start(); // So we don't keep sending data to the client
}
if ($Viewer->option('AutoSubscribe')) {
(new Gazelle\User\Subscription($Viewer))->subscribeComments('torrents', $GroupID);
}
// Manage notifications
$seenFormatEncoding = [];
if (!$IsNewGroup) {
// maybe there are torrents in the same release as the new torrent. Let's find out (for notifications)
$GroupInfo = get_group_info($GroupID, 0, false);
$ThisMedia = display_str($Properties['Media']);
$ThisRemastered = display_str($Properties['Remastered']);
$ThisRemasterYear = display_str($Properties['RemasterYear']);
$ThisRemasterTitle = display_str($Properties['RemasterTitle']);
$ThisRemasterRecordLabel = display_str($Properties['RemasterRecordLabel']);
$ThisRemasterCatalogueNumber = display_str($Properties['RemasterCatalogueNumber']);
foreach ($GroupInfo[1] as $TorrentInfo) {
if (($TorrentInfo['Media'] == $ThisMedia)
&& ($TorrentInfo['Remastered'] == $ThisRemastered)
&& ($TorrentInfo['RemasterYear'] == (int)$ThisRemasterYear)
&& ($TorrentInfo['RemasterTitle'] == $ThisRemasterTitle)
&& ($TorrentInfo['RemasterRecordLabel'] == $ThisRemasterRecordLabel)
&& ($TorrentInfo['RemasterCatalogueNumber'] == $ThisRemasterCatalogueNumber)
&& ($TorrentInfo['ID'] != $TorrentID)) {
$seenFormatEncoding[] = ['format' => $TorrentInfo['Format'], 'bitrate' => $TorrentInfo['Encoding']];
}
}
}
if (!in_array('notifications', $Viewer->paranoia())) {
// For RSS
$Feed = new Gazelle\Feed;
$Item = $Feed->item(
$Title,
Text::strip_bbcode($Properties['GroupDescription']),
"torrents.php?action=download&id={$TorrentID}&torrent_pass=[[PASSKEY]]",
date('r'),
$Viewer->username(),
'torrents.php?id=' . $GroupID,
implode(',', $tagList)
);
$notification = (new Gazelle\Notification\Upload)
->addFormat($Properties['Format'])
->addEncodings($Properties['Encoding'])
->addMedia($Properties['Media'])
->addYear($Properties['Year'], $Properties['RemasterYear'])
->addArtists($tgroup->artistRole()->roleList())
->addTags($tagList)
->addCategory($categoryName)
->addUser($Viewer)
->setDebug(DEBUG_UPLOAD_NOTIFICATION);
if ($isMusicUpload) {
$notification->addReleaseType($releaseTypes[$Properties['ReleaseType']]);
}
$notification->trigger($GroupID, $TorrentID, $Feed, $Item);
// RSS for bookmarks
$db->prepared_query('
SELECT u.torrent_pass
FROM users_main AS u
INNER JOIN bookmarks_torrents AS b ON (b.UserID = u.ID)
WHERE b.GroupID = ?
', $GroupID
);
while ([$Passkey] = $db->next_record()) {
$feedType[] = "torrents_bookmarks_t_$Passkey";
}
foreach ($feedType as $subFeed) {
$Feed->populate($subFeed, $Item);
}
$Debug->set_flag('upload: notifications handled');
}