misuzu/public/index.php
2025-03-28 20:19:54 +00:00

194 lines
7.6 KiB
PHP

<?php
namespace Misuzu;
use RuntimeException;
use Index\MediaType;
use Index\Http\Content\MultipartFormContent;
use Index\Http\Content\Multipart\ValueMultipartFormData;
use Index\Http\Routing\Router;
use Index\Http\Routing\Routes\RouteInfo;
use Misuzu\Auth\{AuthTokenBuilder,AuthTokenCookie,AuthTokenInfo};
require_once __DIR__ . '/../misuzu.php';
if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
die('Misuzu is not initialised.');
// The whole wall of shit before the router setup and dispatch should be worked away
// Lockdown things should be middleware when there's no more legacy files
ob_start();
if(is_file($msz->dbCtx->getMigrateLockPath())) {
http_response_code(503);
header('Content-Type: text/html; charset=utf-8');
echo file_get_contents(sprintf('%s/error-503.html', Misuzu::PATH_PUBLIC));
exit;
}
$request = \Index\Http\HttpRequest::fromRequest();
$tokenPacker = $msz->authCtx->createAuthTokenPacker();
if(!empty($_COOKIE['msz_auth']) && is_string($_COOKIE['msz_auth']))
$tokenInfo = $tokenPacker->unpack($_COOKIE['msz_auth']);
elseif(!empty($_COOKIE['msz_uid']) && !empty($_COOKIE['msz_sid']) && is_string($_COOKIE['msz_uid']) && is_string($_COOKIE['msz_sid'])) {
$tokenBuilder = new AuthTokenBuilder;
$tokenBuilder->setUserId($_COOKIE['msz_uid']);
$tokenBuilder->setSessionToken($_COOKIE['msz_sid']);
$tokenInfo = $tokenBuilder->toInfo();
$tokenBuilder = null;
} else
$tokenInfo = AuthTokenInfo::empty();
$userInfo = null;
$sessionInfo = null;
$userInfoReal = null;
$remoteAddr = $_SERVER['REMOTE_ADDR'];
if($tokenInfo->hasUserId && $tokenInfo->hasSessionToken) {
$tokenBuilder = new AuthTokenBuilder($tokenInfo);
try {
$sessionInfo = $msz->authCtx->sessions->getSession(sessionToken: $tokenInfo->sessionToken);
if($sessionInfo->expired) {
$tokenBuilder->removeUserId();
$tokenBuilder->removeSessionToken();
} elseif($sessionInfo->userId === $tokenInfo->userId) {
$userInfo = $msz->usersCtx->users->getUser($tokenInfo->userId, 'id');
if($userInfo->deleted) {
$tokenBuilder->removeUserId();
$tokenBuilder->removeSessionToken();
} else {
$msz->usersCtx->users->recordUserActivity($userInfo, remoteAddr: $remoteAddr);
$msz->authCtx->sessions->recordSessionActivity(sessionInfo: $sessionInfo, remoteAddr: $remoteAddr);
if($sessionInfo->shouldBumpExpires)
$tokenBuilder->setEdited();
if($tokenInfo->hasImpersonatedUserId) {
$allowToImpersonate = $userInfo->super;
$impersonatedUserId = $tokenInfo->impersonatedUserId;
if(!$allowToImpersonate) {
$allowImpersonateUsers = $msz->config->getArray(sprintf('impersonate.allow.u%s', $userInfo->id));
$allowToImpersonate = in_array((string)$impersonatedUserId, $allowImpersonateUsers, true);
}
if($allowToImpersonate) {
$userInfoReal = $userInfo;
try {
$userInfo = $msz->usersCtx->users->getUser($impersonatedUserId, 'id');
} catch(RuntimeException $ex) {
$userInfo = $userInfoReal;
$userInfoReal = null;
$tokenBuilder->removeImpersonatedUserId();
}
} else $tokenBuilder->removeImpersonatedUserId();
}
}
}
} catch(RuntimeException $ex) {
$tokenBuilder->removeUserId();
$tokenBuilder->removeSessionToken();
$tokenBuilder->removeImpersonatedUserId();
$userInfo = null;
$sessionInfo = null;
$userInfoReal = null;
}
if($tokenBuilder->isEdited()) {
$tokenInfo = $tokenBuilder->toInfo();
AuthTokenCookie::apply($tokenPacker->pack($tokenInfo));
}
}
$msz->authInfo->setInfo($tokenInfo, $userInfo, $sessionInfo, $userInfoReal);
CSRF::init(
$msz->config->getString('csrf.secret', 'soup'),
($msz->authInfo->loggedIn ? $sessionInfo->token : $remoteAddr)
);
// order for these two currently matters i think: it shouldn't.
$router = $msz->createRouting($request);
$msz->startTemplating();
if($msz->domainRoles->hasRole($request->getHeaderLine('Host'), 'main')) {
$mszRequestPath = substr($request->requestTarget, 1);
$mszLegacyPathPrefix = Misuzu::PATH_PUBLIC_LEGACY . '/';
$mszLegacyPath = $mszLegacyPathPrefix . $mszRequestPath;
if(str_starts_with($mszLegacyPath, $mszLegacyPathPrefix)) {
$mszLegacyPathReal = realpath($mszLegacyPath);
if($mszLegacyPath === $mszLegacyPathReal || $mszLegacyPath === $mszLegacyPathReal . '/') {
// this is here so filters can run...
$router->router->route(RouteInfo::exact($request->method, $request->requestTarget, function() {}));
$response = $router->router->handle($request);
if($response->getBody()->getSize() > 0) {
Router::output($response);
exit;
}
if(str_starts_with($mszRequestPath, 'manage') && !$msz->hasManageAccess())
Template::throwError(403);
if(is_dir($mszLegacyPath))
$mszLegacyPath .= '/index.php';
if(is_file($mszLegacyPath)) {
// Reconstruct $_POST since PHP no longer makes it for us
if($request->getBody()->isReadable() && empty($_POST)) {
$mszRequestContent = (function($contentType, $stream) {
if($contentType->equals('application/x-www-form-urlencoded')) {
parse_str((string)$stream, $postVars);
return $postVars;
}
if($contentType->equals('multipart/form-data'))
try {
return MultipartFormContent::parseStream($stream, $contentType->boundary);
} catch(RuntimeException $ex) {}
return null;
})(MediaType::parse($request->getHeaderLine('Content-Type')), $request->getBody());
$_POST = (function($requestContent) {
if(is_array($requestContent))
return $requestContent;
if($requestContent instanceof MultipartFormContent) {
$postVars = [];
foreach($requestContent->params as $name => $values) {
if(count($values) === 0)
$postVars[$name] = '';
elseif(count($values) === 1)
$postVars[$name] = $values[0] instanceof ValueMultipartFormData ? (string)$values[0] : '';
else {
$postVar = [];
foreach($values as $value)
$postVars[] = $value instanceof ValueMultipartFormData ? (string)$value : '';
$postVars[$name] = $postVar;
}
}
return $postVars;
}
return [];
})($mszRequestContent);
$_REQUEST = array_merge($_GET, $_POST);
}
require_once $mszLegacyPath;
return;
}
}
}
}
$router->dispatch($request);