misuzu/src/Users/Assets/AssetsRoutes.php

113 lines
4.3 KiB
PHP

<?php
namespace Misuzu\Users\Assets;
use InvalidArgumentException;
use RuntimeException;
use Index\Http\Routing\{HttpGet,RouteHandler};
use Misuzu\Perm;
use Misuzu\Auth\AuthInfo;
use Misuzu\URLs\{URLInfo,URLRegistry};
use Misuzu\Users\{UsersContext,UserInfo};
class AssetsRoutes extends RouteHandler {
public function __construct(
private AuthInfo $authInfo,
private URLRegistry $urls,
private UsersContext $usersCtx
) {}
private function canViewAsset($request, UserInfo $assetUser): bool {
if($this->usersCtx->getBans()->countActiveBans($assetUser))
// allow staff viewing profile to still see banned user assets
// should change the Referer check with some query param only applied when needed
return $this->authInfo->getPerms('user')->check(Perm::U_USERS_MANAGE)
&& parse_url($request->getHeaderFirstLine('Referer'), PHP_URL_PATH) === $this->urls->format('user-profile');
return true;
}
#[HttpGet('/assets/avatar')]
#[HttpGet('/assets/avatar/([0-9]+)(?:\.[a-z]+)?')]
#[URLInfo('user-avatar', '/assets/avatar/<user>', ['res' => '<res>'])]
public function getAvatar($response, $request, string $userId = '') {
$assetInfo = new StaticUserImageAsset(MSZ_PUBLIC . '/images/no-avatar.png', MSZ_PUBLIC);
try {
$userInfo = $this->usersCtx->getUserInfo($userId);
if(!$this->canViewAsset($request, $userInfo)) {
$assetInfo = new StaticUserImageAsset(MSZ_PUBLIC . '/images/banned-avatar.png', MSZ_PUBLIC);
} else {
$userAssetInfo = new UserAvatarAsset($userInfo);
if($userAssetInfo->isPresent())
$assetInfo = $userAssetInfo;
}
} catch(RuntimeException $ex) {
} catch(InvalidArgumentException $ex) {}
$this->serveAsset($response, $request, $assetInfo);
}
#[HttpGet('/assets/profile-background')]
#[HttpGet('/assets/profile-background/([0-9]+)(?:\.[a-z]+)?')]
#[URLInfo('user-background', '/assets/profile-background/<user>')]
public function getProfileBackground($response, $request, string $userId = '') {
try {
$userInfo = $this->usersCtx->getUserInfo($userId);
} catch(RuntimeException $ex) {
} catch(InvalidArgumentException $ex) {}
if(!empty($userInfo)) {
$userAssetInfo = new UserBackgroundAsset($userInfo);
if($userAssetInfo->isPresent() && $this->canViewAsset($request, $userInfo))
$assetInfo = $userAssetInfo;
}
if(!isset($assetInfo)) {
// circumvent the default error page
$response->setContent('Not Found');
return 404;
}
$this->serveAsset($response, $request, $assetInfo);
}
#[HttpGet('/user-assets.php')]
public function getUserAssets($response, $request) {
$userId = (string)$request->getParam('u', FILTER_SANITIZE_NUMBER_INT);
$mode = (string)$request->getParam('m');
if($mode === 'avatar')
return $this->getAvatar($response, $request, $userId);
if($mode === 'background')
return $this->getProfileBackground($response, $request, $userId);
// circumvent the default error page
$response->setContent('Not Found');
return 404;
}
private function serveAsset($response, $request, UserImageAssetInterface $assetInfo): void {
$contentType = $assetInfo->getMimeType();
$publicPath = $assetInfo->getPublicPath();
$fileName = $assetInfo->getFileName();
if($assetInfo instanceof UserAssetScalableInterface) {
$dimensions = (int)($request->getParam('res', FILTER_SANITIZE_NUMBER_INT)
?? $request->getParam('r', FILTER_SANITIZE_NUMBER_INT));
if($dimensions > 0) {
$assetInfo->ensureScaledExists($dimensions);
$contentType = $assetInfo->getScaledMimeType($dimensions);
$publicPath = $assetInfo->getPublicScaledPath($dimensions);
$fileName = $assetInfo->getScaledFileName($dimensions);
}
}
$response->accelRedirect($publicPath);
$response->setContentType($contentType);
$response->setFileName($fileName, false);
}
}