seria/public/announce.php
2022-07-03 23:44:11 +00:00

145 lines
4.4 KiB
PHP

<?php
require_once __DIR__ . '/../seria.php';
function announce_fail(string $reason): never {
die(bencode(['failure reason' => $reason]));
}
if(empty($_GET))
die('There is nothing here.');
header('Content-Type: text/plain; charset=us-ascii');
header('X-Tracker-Version: Seria/' . SERIA_VERSION);
if(!$sIsIPv4)
announce_fail('Tracker is only supported over IPv4, please reset your DNS cache.');
//file_put_contents(SERIA_ERRORS, $_SERVER['REQUEST_URI'] . PHP_EOL, FILE_APPEND);
$urlParts = explode('/', trim($_SERVER['PATH_INFO'], '/'));
$cInfoHash = (string)filter_input(INPUT_GET, 'info_hash');
if(strlen($cInfoHash) !== 20)
announce_fail('Invalid info hash.');
$cPeerId = (string)filter_input(INPUT_GET, 'peer_id');
if(strlen($cPeerId) !== 20)
announce_fail('Invalid peer id format.');
$cPeerAddress = $_SERVER['REMOTE_ADDR'];
$cPeerPort = (int)filter_input(INPUT_GET, 'port', FILTER_SANITIZE_NUMBER_INT);
if($cPeerPort < 1 || $cPeerPort > 0xFFFF)
announce_fail('Invalid port number.');
$cPeerKey = (string)filter_input(INPUT_GET, 'key');
if(strlen($cPeerKey) > 128)
announce_fail('Key is ridiculous.');
$cPeerEvent = (string)filter_input(INPUT_GET, 'event');
if(strlen($cPeerEvent) > 128)
announce_fail('Event is fucked up.');
$cPeerAgent = (string)filter_input(INPUT_SERVER, 'HTTP_USER_AGENT');
if(strlen($cPeerAgent) > 255)
announce_fail('Agent is stupid.');
$cCompactPeers = !empty($_GET['compact']);
$cNoPeerId = !empty($_GET['no_peer_id']);
$cShortAnnounce = !empty($_GET['short']);
$cBytesUploaded = (int)filter_input(INPUT_GET, 'uploaded', FILTER_SANITIZE_NUMBER_INT);
$cBytesDownloaded = (int)filter_input(INPUT_GET, 'downloaded', FILTER_SANITIZE_NUMBER_INT);
$cBytesRemaining = (int)filter_input(INPUT_GET, 'left', FILTER_SANITIZE_NUMBER_INT);
$cPeerWant = (int)filter_input(INPUT_GET, 'numwant', FILTER_SANITIZE_NUMBER_INT);
$cPassKey = $urlParts[0] ?? '';
if(!empty($cPassKey)) {
try {
$cUserInfo = SeriaUser::byPassKey($pdo, $cPassKey);
} catch(SeriaUserNotFoundException $ex) {
sleep(3);
announce_fail('Authentication failed.');
}
} else $cUserInfo = SeriaUser::anonymous();
$interval = SERIA_ANNOUNCE_INTERVAL;
$minInterval = SERIA_ANNOUNCE_INTERVAL_MIN;
if($cShortAnnounce && SERIA_ANNOUNCE_SHORT) {
$interval = SERIA_ANNOUNCE_SHORT_INTERVAL;
$minInterval = SERIA_ANNOUNCE_SHORT_INTERVAL_MIN;
}
try {
$cTorrentInfo = SeriaTorrent::byHash($pdo, $cInfoHash);
} catch(SeriaTorrentNotFoundException $ex) {
announce_fail('Info hash not found.');
}
$cCanDownload = $cTorrentInfo->canDownload($cUserInfo);
if($cCanDownload !== '')
switch($cCanDownload) {
case 'inactive':
announce_fail('This download is inactive.');
case 'private':
announce_fail('You must be logged in for this download.');
case 'pending':
announce_fail('This download is pending approval.');
default:
announce_fail($cCanDownload);
}
$cPeerInfo = SeriaTorrentPeer::byPeerId($pdo, $cTorrentInfo, $cPeerId);
if(empty($cPeerInfo)) {
// could probably skip this is the event is 'stopped'
$cPeerInfo = SeriaTorrentPeer::create(
$pdo, $cTorrentInfo, $cUserInfo, $cPeerId, $cPeerAddress, $cPeerPort, $interval,
$cPeerAgent, $cPeerKey, $cBytesUploaded, $cBytesDownloaded, $cBytesRemaining
);
} else {
if(!$cPeerInfo->verifyKey($cPeerKey)) {
sleep(3);
announce_fail('Peer verification failed.');
}
if(!$cPeerInfo->verifyUser($cUserInfo)) {
sleep(3);
announce_fail('User verification failed.');
}
$cUserInfo->incrementTransferCounts(
$cBytesDownloaded - $cPeerInfo->getBytesDownloaded(),
$cBytesUploaded - $cPeerInfo->getBytesUploaded()
);
$cPeerInfo->update(
$cUserInfo, $cPeerAddress, $cPeerPort, $interval, $cPeerAgent,
$cBytesUploaded, $cBytesDownloaded, $cBytesRemaining
);
}
if($cPeerEvent === 'stopped') {
$cPeerInfo->delete();
die(bencode(new SeriaAnnounceResponse));
}
SeriaTorrentPeer::deleteExpired($pdo);
$response = new SeriaAnnounceResponse(
$interval + mt_rand(0, 10),
$minInterval,
$cTorrentInfo,
$cNoPeerId && SERIA_ANNOUNCE_NO_PEER_ID,
$cCompactPeers
);
if(!$cPeerInfo->isSeed() || !SERIA_ANNOUNCE_NO_SEED_P2P) {
$peers = $cTorrentInfo->getSeeds($cPeerInfo);
foreach($peers as $peer)
$response->addPeer($peer);
}
echo bencode($response);