145 lines
4.4 KiB
PHP
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);
|