Updated to latest Index as well as some minor bug fixes.
This commit is contained in:
parent
ad89d45cf0
commit
5ba8b30047
77 changed files with 900 additions and 937 deletions
assets
composer.jsoncomposer.lockpublic-legacy
public
src
Auth
CSRF.phpChangelog
Comments
DatabaseContext.phpForum
Home
Info
LegacyRoutes.phpMessages
MisuzuContext.phpNews
OAuth2
Pagination.phpPerm.phpRedirects
AliasRedirectsRoutes.phpIncrementalRedirectsRoutes.phpLandingRedirectsRoutes.phpNamedRedirectsRoutes.phpSocialRedirectsRoutes.php
Routing
Satori
SharpChat
Tools.phpUsers/Assets
WebFinger
templates
auth
forum
manage/users
profile
tools
|
@ -1,4 +1,12 @@
|
|||
const MszCommentsApi = (() => {
|
||||
const argsToFormData = args => {
|
||||
const formData = new FormData;
|
||||
for(const name in args)
|
||||
formData.append(name, args[name]);
|
||||
|
||||
return formData;
|
||||
};
|
||||
|
||||
return {
|
||||
getCategory: async name => {
|
||||
if(typeof name !== 'string' || name.trim() === '')
|
||||
|
@ -62,7 +70,7 @@ const MszCommentsApi = (() => {
|
|||
const { status, body } = await $xhr.post(
|
||||
'/comments/posts',
|
||||
{ csrf: true, type: 'json' },
|
||||
args
|
||||
argsToFormData(args)
|
||||
);
|
||||
if(status !== 201)
|
||||
throw new Error(body.error?.text ?? 'something went wrong', { cause: body.error?.name ?? 'something' });
|
||||
|
@ -75,10 +83,10 @@ const MszCommentsApi = (() => {
|
|||
if(typeof args !== 'object' || args === null)
|
||||
throw new Error('args must be a non-null object');
|
||||
|
||||
const { status, body } = await $xhr.post(
|
||||
const { status, body } = await $xhr.patch(
|
||||
`/comments/posts/${post}`,
|
||||
{ csrf: true, type: 'json' },
|
||||
args
|
||||
argsToFormData(args)
|
||||
);
|
||||
if(status !== 200)
|
||||
throw new Error(body.error?.text ?? 'something went wrong', { cause: body.error?.name ?? 'something' });
|
||||
|
|
|
@ -52,7 +52,7 @@ const MszMessages = () => {
|
|||
formData.append('format', format);
|
||||
formData.append('draft', draft);
|
||||
|
||||
const { body } = await $xhr.post(`/messages/${encodeURIComponent(messageId)}`, { type: 'json', csrf: true }, formData);
|
||||
const { body } = await $xhr.patch(`/messages/${encodeURIComponent(messageId)}`, { type: 'json', csrf: true }, formData);
|
||||
if(body.error !== undefined)
|
||||
throw body.error;
|
||||
|
||||
|
|
|
@ -188,7 +188,7 @@ const MszOAuth2Authorise = async () => {
|
|||
params.scope = scope;
|
||||
|
||||
try {
|
||||
const { body } = await $xhr.post('/oauth2/authorise', { authed: true, csrf: true, type: 'json' }, params);
|
||||
const { body } = await $xhr.post('/oauth2/authorize', { authed: true, csrf: true, type: 'json' }, params);
|
||||
if(!body)
|
||||
throw 'authorisation failed';
|
||||
if(typeof body.error === 'string')
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
"require": {
|
||||
"php": ">=8.4",
|
||||
"ext-mbstring": "*",
|
||||
"flashwave/index": "^0.2501",
|
||||
"flashii/rpcii": "~4.0",
|
||||
"flashwave/index": "^0.2503",
|
||||
"flashii/rpcii": "~5.0",
|
||||
"erusev/parsedown": "~1.7",
|
||||
"chillerlan/php-qrcode": "~5.0",
|
||||
"symfony/mailer": "~7.2",
|
||||
|
@ -11,9 +11,9 @@
|
|||
"sentry/sdk": "~4.0",
|
||||
"nesbot/carbon": "~3.8",
|
||||
"vlucas/phpdotenv": "~5.6",
|
||||
"filp/whoops": "~2.17",
|
||||
"filp/whoops": "~2.18",
|
||||
"phpseclib/phpseclib": "~3.0",
|
||||
"guzzlehttp/guzzle": "~7.0"
|
||||
"guzzlehttp/guzzle": "~7.9"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
|
|
135
composer.lock
generated
135
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "1045c8f605203fae19361d659fb64c54",
|
||||
"content-hash": "baa15cf491eb4500360fa9c20fd42a4d",
|
||||
"packages": [
|
||||
{
|
||||
"name": "carbonphp/carbon-doctrine-types",
|
||||
|
@ -313,16 +313,16 @@
|
|||
},
|
||||
{
|
||||
"name": "egulias/email-validator",
|
||||
"version": "4.0.3",
|
||||
"version": "4.0.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/egulias/EmailValidator.git",
|
||||
"reference": "b115554301161fa21467629f1e1391c1936de517"
|
||||
"reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/b115554301161fa21467629f1e1391c1936de517",
|
||||
"reference": "b115554301161fa21467629f1e1391c1936de517",
|
||||
"url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa",
|
||||
"reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -368,7 +368,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/egulias/EmailValidator/issues",
|
||||
"source": "https://github.com/egulias/EmailValidator/tree/4.0.3"
|
||||
"source": "https://github.com/egulias/EmailValidator/tree/4.0.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -376,7 +376,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-12-27T00:36:43+00:00"
|
||||
"time": "2025-03-06T22:45:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "erusev/parsedown",
|
||||
|
@ -430,16 +430,16 @@
|
|||
},
|
||||
{
|
||||
"name": "filp/whoops",
|
||||
"version": "2.17.0",
|
||||
"version": "2.18.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/filp/whoops.git",
|
||||
"reference": "075bc0c26631110584175de6523ab3f1652eb28e"
|
||||
"reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/filp/whoops/zipball/075bc0c26631110584175de6523ab3f1652eb28e",
|
||||
"reference": "075bc0c26631110584175de6523ab3f1652eb28e",
|
||||
"url": "https://api.github.com/repos/filp/whoops/zipball/a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e",
|
||||
"reference": "a7de6c3c6c3c022f5cfc337f8ede6a14460cf77e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -489,7 +489,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/filp/whoops/issues",
|
||||
"source": "https://github.com/filp/whoops/tree/2.17.0"
|
||||
"source": "https://github.com/filp/whoops/tree/2.18.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -497,24 +497,26 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-01-25T12:00:00+00:00"
|
||||
"time": "2025-03-15T12:00:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "flashii/rpcii",
|
||||
"version": "v4.0.0",
|
||||
"version": "v5.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://patchii.net/flashii/rpcii-php.git",
|
||||
"reference": "7849b2effd7ac878e7cf174eef7e27914799e60d"
|
||||
"reference": "28c25e0a342173524f8894d03158663842e03252"
|
||||
},
|
||||
"require": {
|
||||
"ext-msgpack": ">=2.2",
|
||||
"flashwave/index": "^0.2501",
|
||||
"php": ">=8.4"
|
||||
"flashwave/index": "^0.2503",
|
||||
"guzzlehttp/guzzle": "~7.0",
|
||||
"php": ">=8.4",
|
||||
"psr/http-client": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpunit/phpunit": "^11.5"
|
||||
"phpunit/phpunit": "^12.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
|
@ -536,25 +538,27 @@
|
|||
],
|
||||
"description": "HTTP RPC client/server library.",
|
||||
"homepage": "https://railgun.sh/rpcii",
|
||||
"time": "2025-01-29T22:00:17+00:00"
|
||||
"time": "2025-03-21T19:38:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "flashwave/index",
|
||||
"version": "v0.2501.221237",
|
||||
"version": "v0.2503.230355",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://patchii.net/flash/index.git",
|
||||
"reference": "fee9a65e3bca341be7401fe2e21b795630f15f2a"
|
||||
"reference": "2372a113d26380176994f64ab99c42aaf2e9d98e"
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"php": ">=8.4",
|
||||
"twig/html-extra": "^3.18",
|
||||
"twig/twig": "^3.18"
|
||||
"psr/http-message": "^2.0",
|
||||
"psr/http-server-handler": "^1.0",
|
||||
"twig/html-extra": "^3.20",
|
||||
"twig/twig": "^3.20"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpunit/phpunit": "^11.5"
|
||||
"phpunit/phpunit": "^12.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-memcache": "Support for the Index\\Cache\\Memcached namespace (only if you can't use ext-memcached for some reason).",
|
||||
|
@ -591,7 +595,7 @@
|
|||
],
|
||||
"description": "Composer package for the common library for my projects.",
|
||||
"homepage": "https://railgun.sh/index",
|
||||
"time": "2025-01-22T12:38:11+00:00"
|
||||
"time": "2025-03-23T03:45:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "graham-campbell/result-type",
|
||||
|
@ -982,16 +986,16 @@
|
|||
},
|
||||
{
|
||||
"name": "jean85/pretty-package-versions",
|
||||
"version": "2.1.0",
|
||||
"version": "2.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Jean85/pretty-package-versions.git",
|
||||
"reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10"
|
||||
"reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/3c4e5f62ba8d7de1734312e4fff32f67a8daaf10",
|
||||
"reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10",
|
||||
"url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a",
|
||||
"reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1001,8 +1005,9 @@
|
|||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.2",
|
||||
"jean85/composer-provided-replaced-stub-package": "^1.0",
|
||||
"phpstan/phpstan": "^1.4",
|
||||
"phpstan/phpstan": "^2.0",
|
||||
"phpunit/phpunit": "^7.5|^8.5|^9.6",
|
||||
"rector/rector": "^2.0",
|
||||
"vimeo/psalm": "^4.3 || ^5.0"
|
||||
},
|
||||
"type": "library",
|
||||
|
@ -1035,9 +1040,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/Jean85/pretty-package-versions/issues",
|
||||
"source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.0"
|
||||
"source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1"
|
||||
},
|
||||
"time": "2024-11-18T16:19:46+00:00"
|
||||
"time": "2025-03-19T14:43:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "matomo/device-detector",
|
||||
|
@ -1882,6 +1887,62 @@
|
|||
},
|
||||
"time": "2023-04-04T09:54:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-server-handler",
|
||||
"version": "1.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-server-handler.git",
|
||||
"reference": "84c4fb66179be4caaf8e97bd239203245302e7d4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/84c4fb66179be4caaf8e97bd239203245302e7d4",
|
||||
"reference": "84c4fb66179be4caaf8e97bd239203245302e7d4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.0",
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Server\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP server-side request handler",
|
||||
"keywords": [
|
||||
"handler",
|
||||
"http",
|
||||
"http-interop",
|
||||
"psr",
|
||||
"psr-15",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response",
|
||||
"server"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-server-handler/tree/1.0.2"
|
||||
},
|
||||
"time": "2023-04-10T20:06:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "3.0.2",
|
||||
|
@ -3618,16 +3679,16 @@
|
|||
"packages-dev": [
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "2.1.6",
|
||||
"version": "2.1.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c"
|
||||
"reference": "051a3b6b9b80df4ba3a7f801a8b53ad7d8f1c15f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c",
|
||||
"reference": "6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/051a3b6b9b80df4ba3a7f801a8b53ad7d8f1c15f",
|
||||
"reference": "051a3b6b9b80df4ba3a7f801a8b53ad7d8f1c15f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -3672,7 +3733,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-02-19T15:46:42+00:00"
|
||||
"time": "2025-03-23T14:57:55+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
|
|
@ -15,10 +15,15 @@ if($msz->authInfo->loggedIn) {
|
|||
if(!empty($_GET['resolve'])) {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
try {
|
||||
// Only works for usernames, this is by design
|
||||
$userInfo = $msz->usersCtx->users->getUser((string)filter_input(INPUT_GET, 'name'), 'name');
|
||||
} catch(Exception $ex) {
|
||||
if(!empty($_GET['name']) && is_scalar($_GET['name']))
|
||||
try {
|
||||
// Only works for usernames, this is by design
|
||||
$userInfo = $msz->usersCtx->users->getUser((string)$_GET['name'], 'name');
|
||||
} catch(Exception $ex) {
|
||||
unset($userInfo);
|
||||
}
|
||||
|
||||
if(empty($userInfo)) {
|
||||
echo json_encode([
|
||||
'id' => 0,
|
||||
'name' => '',
|
||||
|
@ -62,16 +67,16 @@ if($siteIsPrivate) {
|
|||
$canResetPassword = true;
|
||||
}
|
||||
|
||||
while(!empty($_POST['login']) && is_array($_POST['login'])) {
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if(!CSRF::validateRequest()) {
|
||||
$notices[] = 'Was unable to verify the request, please try again!';
|
||||
break;
|
||||
}
|
||||
|
||||
$loginRedirect = empty($_POST['login']['redirect']) || !is_string($_POST['login']['redirect']) ? '' : $_POST['login']['redirect'];
|
||||
$loginRedirect = empty($_POST['redirect']) || !is_string($_POST['redirect']) ? '' : $_POST['redirect'];
|
||||
|
||||
if(empty($_POST['login']['username']) || empty($_POST['login']['password'])
|
||||
|| !is_string($_POST['login']['username']) || !is_string($_POST['login']['password'])) {
|
||||
if(empty($_POST['username']) || empty($_POST['password'])
|
||||
|| !is_string($_POST['username']) || !is_string($_POST['password'])) {
|
||||
$notices[] = "You didn't fill in a username and/or password.";
|
||||
break;
|
||||
}
|
||||
|
@ -91,7 +96,7 @@ while(!empty($_POST['login']) && is_array($_POST['login'])) {
|
|||
$loginFailedError = "Invalid username or password, {$attemptsRemainingError}.";
|
||||
|
||||
try {
|
||||
$userInfo = $msz->usersCtx->users->getUser($_POST['login']['username'], 'login');
|
||||
$userInfo = $msz->usersCtx->users->getUser($_POST['username'], 'login');
|
||||
} catch(Exception $ex) {
|
||||
$msz->authCtx->loginAttempts->recordAttempt(false, $ipAddress, $countryCode, $userAgent, $clientInfo);
|
||||
$notices[] = $loginFailedError;
|
||||
|
@ -110,14 +115,14 @@ while(!empty($_POST['login']) && is_array($_POST['login'])) {
|
|||
break;
|
||||
}
|
||||
|
||||
if(!$pwInfo->verifyPassword($_POST['login']['password'])) {
|
||||
if(!$pwInfo->verifyPassword($_POST['password'])) {
|
||||
$msz->authCtx->loginAttempts->recordAttempt(false, $ipAddress, $countryCode, $userAgent, $clientInfo, $userInfo);
|
||||
$notices[] = $loginFailedError;
|
||||
break;
|
||||
}
|
||||
|
||||
if($pwInfo->needsRehash)
|
||||
$msz->usersCtx->passwords->updateUserPassword($userInfo, $_POST['login']['password']);
|
||||
$msz->usersCtx->passwords->updateUserPassword($userInfo, $_POST['password']);
|
||||
|
||||
if(!empty($loginPermCat) && $loginPermVal > 0 && !$msz->perms->checkPermissions($loginPermCat, $loginPermVal, $userInfo)) {
|
||||
$notices[] = "Login succeeded, but you're not allowed to browse the site right now.";
|
||||
|
@ -157,7 +162,7 @@ while(!empty($_POST['login']) && is_array($_POST['login'])) {
|
|||
|
||||
$welcomeMode = !empty($_GET['welcome']);
|
||||
$oauth2Mode = !empty($_GET['oauth2']);
|
||||
$loginUsername = !empty($_POST['login']['username']) && is_string($_POST['login']['username']) ? $_POST['login']['username'] : (
|
||||
$loginUsername = !empty($_POST['username']) && is_string($_POST['username']) ? $_POST['username'] : (
|
||||
!empty($_GET['username']) && is_string($_GET['username']) ? $_GET['username'] : ''
|
||||
);
|
||||
$loginRedirect = $welcomeMode ? $msz->urls->format('index') : (!empty($_GET['redirect']) && is_string($_GET['redirect']) ? $_GET['redirect'] : null) ?? $_SERVER['HTTP_REFERER'] ?? $msz->urls->format('index');
|
||||
|
|
|
@ -12,11 +12,10 @@ if($msz->authInfo->loggedIn) {
|
|||
return;
|
||||
}
|
||||
|
||||
$reset = !empty($_POST['reset']) && is_array($_POST['reset']) ? $_POST['reset'] : [];
|
||||
$forgot = !empty($_POST['forgot']) && is_array($_POST['forgot']) ? $_POST['forgot'] : [];
|
||||
$userId = !empty($reset['user']) ? (int)$reset['user'] : (
|
||||
!empty($_GET['user']) ? (int)$_GET['user'] : 0
|
||||
);
|
||||
$userId = !empty($_POST['user']) && is_scalar($_POST['user'])
|
||||
? (int)$_POST['user'] : (
|
||||
!empty($_GET['user']) && is_scalar($_GET['user']) ? (int)$_GET['user'] : 0
|
||||
);
|
||||
|
||||
if($userId > 0)
|
||||
try {
|
||||
|
@ -30,17 +29,16 @@ $notices = [];
|
|||
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
||||
$siteIsPrivate = $msz->config->getBoolean('private.enable');
|
||||
$canResetPassword = $siteIsPrivate ? $msz->config->getBoolean('private.allow_password_reset', true) : true;
|
||||
|
||||
$remainingAttempts = $msz->authCtx->loginAttempts->countRemainingAttempts($ipAddress);
|
||||
|
||||
while($canResetPassword) {
|
||||
if(!empty($reset) && $userId > 0) {
|
||||
if(!empty($_POST['verification']) && is_scalar($_POST['verification']) && !empty($userInfo)) {
|
||||
if(!CSRF::validateRequest()) {
|
||||
$notices[] = 'Was unable to verify the request, please try again!';
|
||||
break;
|
||||
}
|
||||
|
||||
$verifyCode = !empty($reset['verification']) && is_string($reset['verification']) ? $reset['verification'] : '';
|
||||
$verifyCode = (string)$_POST['verification'];
|
||||
|
||||
try {
|
||||
$tokenInfo = $msz->authCtx->recoveryTokens->getToken(verifyCode: $verifyCode);
|
||||
|
@ -53,9 +51,8 @@ while($canResetPassword) {
|
|||
break;
|
||||
}
|
||||
|
||||
$password = !empty($reset['password']) && is_array($reset['password']) ? $reset['password'] : [];
|
||||
$passwordNew = !empty($password['new']) && is_string($password['new']) ? $password['new'] : '';
|
||||
$passwordConfirm = !empty($password['confirm']) && is_string($password['confirm']) ? $password['confirm'] : '';
|
||||
$passwordNew = !empty($_POST['password_new']) && is_scalar($_POST['password_new']) ? $_POST['password_new'] : '';
|
||||
$passwordConfirm = !empty($_POST['password_confirm']) && is_scalar($_POST['password_confirm']) ? $_POST['password_confirm'] : '';
|
||||
|
||||
if(empty($passwordNew) || empty($passwordConfirm)
|
||||
|| $passwordNew !== $passwordConfirm) {
|
||||
|
@ -82,24 +79,19 @@ while($canResetPassword) {
|
|||
return;
|
||||
}
|
||||
|
||||
if(!empty($forgot)) {
|
||||
if(!empty($_POST['email']) && is_scalar($_POST['email'])) {
|
||||
if(!CSRF::validateRequest()) {
|
||||
$notices[] = 'Was unable to verify the request, please try again!';
|
||||
break;
|
||||
}
|
||||
|
||||
if(empty($forgot['email']) || !is_string($forgot['email'])) {
|
||||
$notices[] = "You didn't supply an e-mail address.";
|
||||
break;
|
||||
}
|
||||
|
||||
if($remainingAttempts < 1) {
|
||||
$notices[] = "There are too many failed login attempts from your IP address, please try again later.";
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
$forgotUser = $msz->usersCtx->users->getUser($forgot['email'], 'email');
|
||||
$forgotUser = $msz->usersCtx->users->getUser((string)$_POST['email'], 'email');
|
||||
} catch(RuntimeException $ex) {
|
||||
unset($forgotUser);
|
||||
}
|
||||
|
@ -142,7 +134,7 @@ while($canResetPassword) {
|
|||
|
||||
Template::render(isset($userInfo) ? 'auth.password_reset' : 'auth.password_forgot', [
|
||||
'password_notices' => $notices,
|
||||
'password_email' => !empty($forget['email']) && is_string($forget['email']) ? $forget['email'] : '',
|
||||
'password_email' => !empty($_POST['email']) && is_scalar($_POST['email']) ? (string)$_POST['email'] : '',
|
||||
'password_attempts_remaining' => $remainingAttempts,
|
||||
'password_user' => $userInfo ?? null,
|
||||
'password_verification' => $verifyCode ?? '',
|
||||
|
|
|
@ -12,14 +12,13 @@ if($msz->authInfo->loggedIn) {
|
|||
return;
|
||||
}
|
||||
|
||||
$register = !empty($_POST['register']) && is_array($_POST['register']) ? $_POST['register'] : [];
|
||||
$notices = [];
|
||||
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
||||
$countryCode = $_SERVER['COUNTRY_CODE'] ?? 'XX';
|
||||
|
||||
$remainingAttempts = $msz->authCtx->loginAttempts->countRemainingAttempts($ipAddress);
|
||||
|
||||
while(!empty($register)) {
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if(!CSRF::validateRequest()) {
|
||||
$notices[] = 'Was unable to verify the request, please try again!';
|
||||
break;
|
||||
|
@ -30,13 +29,13 @@ while(!empty($register)) {
|
|||
break;
|
||||
}
|
||||
|
||||
if(empty($register['username']) || empty($register['password']) || empty($register['email']) || empty($register['question'])
|
||||
|| !is_string($register['username']) || !is_string($register['password']) || !is_string($register['email']) || !is_string($register['question'])) {
|
||||
if(empty($_POST['username']) || empty($_POST['password']) || empty($_POST['email']) || empty($_POST['question'])
|
||||
|| !is_scalar($_POST['username']) || !is_scalar($_POST['password']) || !is_scalar($_POST['email']) || !is_scalar($_POST['question'])) {
|
||||
$notices[] = "You haven't filled in all fields.";
|
||||
break;
|
||||
}
|
||||
|
||||
$checkSpamBot = mb_strtolower($register['question']);
|
||||
$checkSpamBot = mb_strtolower($_POST['question']);
|
||||
$spamBotValid = [
|
||||
'21', 'twentyone', 'twenty-one', 'twenty one',
|
||||
];
|
||||
|
@ -52,18 +51,18 @@ while(!empty($register)) {
|
|||
break;
|
||||
}
|
||||
|
||||
$usernameValidation = $msz->usersCtx->users->validateName($register['username']);
|
||||
$usernameValidation = $msz->usersCtx->users->validateName($_POST['username']);
|
||||
if($usernameValidation !== '')
|
||||
$notices[] = $msz->usersCtx->users->validateNameText($usernameValidation);
|
||||
|
||||
$emailValidation = $msz->usersCtx->users->validateEMailAddress($register['email']);
|
||||
$emailValidation = $msz->usersCtx->users->validateEMailAddress($_POST['email']);
|
||||
if($emailValidation !== '')
|
||||
$notices[] = $msz->usersCtx->users->validateEMailAddressText($emailValidation);
|
||||
|
||||
if($register['password_confirm'] !== $register['password'])
|
||||
if($_POST['password_confirm'] !== $_POST['password'])
|
||||
$notices[] = "The given passwords don't match.";
|
||||
|
||||
$passwordValidation = UserPasswordsData::validateUserPassword($register['password']);
|
||||
$passwordValidation = UserPasswordsData::validateUserPassword($_POST['password']);
|
||||
if($passwordValidation !== '')
|
||||
$notices[] = UserPasswordsData::validateUserPasswordText($passwordValidation);
|
||||
|
||||
|
@ -74,13 +73,13 @@ while(!empty($register)) {
|
|||
|
||||
try {
|
||||
$userInfo = $msz->usersCtx->users->createUser(
|
||||
$register['username'],
|
||||
$register['email'],
|
||||
$_POST['username'],
|
||||
$_POST['email'],
|
||||
$ipAddress,
|
||||
$countryCode,
|
||||
$defaultRoleInfo
|
||||
);
|
||||
$msz->usersCtx->passwords->updateUserPassword($userInfo, $register['password']);
|
||||
$msz->usersCtx->passwords->updateUserPassword($userInfo, $_POST['password']);
|
||||
} catch(RuntimeException $ex) {
|
||||
$notices[] = 'Something went wrong while creating your account, please alert an administrator or a developer about this!';
|
||||
break;
|
||||
|
@ -99,7 +98,7 @@ while(!empty($register)) {
|
|||
|
||||
Template::render('auth.register', [
|
||||
'register_notices' => $notices,
|
||||
'register_username' => !empty($register['username']) && is_string($register['username']) ? $register['username'] : '',
|
||||
'register_email' => !empty($register['email']) && is_string($register['email']) ? $register['email'] : '',
|
||||
'register_username' => !empty($_POST['username']) && is_scalar($_POST['username']) ? (string)$_POST['username'] : '',
|
||||
'register_email' => !empty($_POST['email']) && is_scalar($_POST['email']) ? (string)$_POST['email'] : '',
|
||||
'register_restricted' => '',
|
||||
]);
|
||||
|
|
|
@ -16,13 +16,12 @@ if($msz->authInfo->loggedIn) {
|
|||
$ipAddress = $_SERVER['REMOTE_ADDR'];
|
||||
$countryCode = $_SERVER['COUNTRY_CODE'] ?? 'XX';
|
||||
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
$twofactor = !empty($_POST['twofactor']) && is_array($_POST['twofactor']) ? $_POST['twofactor'] : [];
|
||||
$notices = [];
|
||||
|
||||
$remainingAttempts = $msz->authCtx->loginAttempts->countRemainingAttempts($ipAddress);
|
||||
|
||||
$tokenString = !empty($_GET['token']) && is_string($_GET['token']) ? $_GET['token'] : (
|
||||
!empty($twofactor['token']) && is_string($twofactor['token']) ? $twofactor['token'] : ''
|
||||
$tokenString = !empty($_GET['token']) && is_scalar($_GET['token']) ? (string)$_GET['token'] : (
|
||||
!empty($_POST['token']) && is_scalar($_POST['token']) ? (string)$_POST['token'] : ''
|
||||
);
|
||||
|
||||
$tokenUserId = $msz->authCtx->tfaSessions->getTokenUserId($tokenString);
|
||||
|
@ -37,16 +36,16 @@ if($totpInfo === null) {
|
|||
return;
|
||||
}
|
||||
|
||||
while(!empty($twofactor)) {
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
if(!CSRF::validateRequest()) {
|
||||
$notices[] = 'Was unable to verify the request, please try again!';
|
||||
break;
|
||||
}
|
||||
|
||||
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
$redirect = !empty($twofactor['redirect']) && is_string($twofactor['redirect']) ? $twofactor['redirect'] : '';
|
||||
$redirect = !empty($_POST['redirect']) && is_scalar($_POST['redirect']) ? (string)$_POST['redirect'] : '';
|
||||
|
||||
if(empty($twofactor['code']) || !is_string($twofactor['code'])) {
|
||||
if(empty($_POST['code']) || !is_string($_POST['code'])) {
|
||||
$notices[] = 'Code field was empty.';
|
||||
break;
|
||||
}
|
||||
|
@ -59,7 +58,7 @@ while(!empty($twofactor)) {
|
|||
$clientInfo = ClientInfo::fromRequest();
|
||||
$generator = $totpInfo->createGenerator();
|
||||
|
||||
if(!in_array($twofactor['code'], $generator->generateRange())) {
|
||||
if(!in_array($_POST['code'], $generator->generateRange())) {
|
||||
$notices[] = sprintf(
|
||||
"Invalid two factor code, %d attempt%s remaining",
|
||||
$remainingAttempts - 1,
|
||||
|
|
|
@ -16,8 +16,8 @@ $config = $msz->config->getValues([
|
|||
'forum_leader.unranked.topic:a',
|
||||
]);
|
||||
|
||||
$mode = (string)filter_input(INPUT_GET, 'mode');
|
||||
$yearMonth = (string)filter_input(INPUT_GET, 'id');
|
||||
$mode = isset($_GET['mode']) && is_scalar($_GET['mode']) ? (string)$_GET['mode'] : '';
|
||||
$yearMonth = isset($_GET['id']) && is_scalar($_GET['id']) ? (string)$_GET['id'] : '';
|
||||
$year = $month = 0;
|
||||
|
||||
$currentYear = (int)date('Y');
|
||||
|
@ -39,7 +39,7 @@ if(!empty($yearMonth)) {
|
|||
}
|
||||
}
|
||||
|
||||
if(filter_has_var(INPUT_GET, 'allow_unranked')) {
|
||||
if(isset($_GET['allow_unranked'])) {
|
||||
$unrankedForums = $unrankedTopics = [];
|
||||
} else {
|
||||
$unrankedForums = $config['forum_leader.unranked.forum'];
|
||||
|
|
|
@ -19,11 +19,11 @@ $currentUserId = $currentUser->id;
|
|||
if($msz->usersCtx->hasActiveBan($currentUser))
|
||||
Template::throwError(403);
|
||||
|
||||
if(filter_has_var(INPUT_POST, 'preview')) {
|
||||
if(!empty($_POST['preview'])) {
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
|
||||
$text = (string)filter_input(INPUT_POST, 'text');
|
||||
$format = TextFormat::tryFrom((string)filter_input(INPUT_POST, 'format'));
|
||||
$text = isset($_POST['text']) && is_scalar($_POST['text']) ? (string)$_POST['text'] : '';
|
||||
$format = TextFormat::tryFrom(isset($_POST['format']) && is_scalar($_POST['format']) ? (string)$_POST['format'] : '');
|
||||
if($format === null) {
|
||||
http_response_code(400);
|
||||
return;
|
||||
|
@ -39,10 +39,10 @@ $forumPostingModes = [
|
|||
];
|
||||
|
||||
if(!empty($_POST)) {
|
||||
$mode = !empty($_POST['post']['mode']) && is_string($_POST['post']['mode']) ? $_POST['post']['mode'] : 'create';
|
||||
$postId = !empty($_POST['post']['id']) && is_string($_POST['post']['id']) ? (int)$_POST['post']['id'] : 0;
|
||||
$topicId = !empty($_POST['post']['topic']) && is_string($_POST['post']['topic']) ? (int)$_POST['post']['topic'] : 0;
|
||||
$forumId = !empty($_POST['post']['forum']) && is_string($_POST['post']['forum']) ? (int)$_POST['post']['forum'] : 0;
|
||||
$mode = !empty($_POST['mode']) && is_string($_POST['mode']) ? $_POST['mode'] : 'create';
|
||||
$postId = !empty($_POST['id']) && is_string($_POST['id']) ? (int)$_POST['id'] : 0;
|
||||
$topicId = !empty($_POST['topic']) && is_string($_POST['topic']) ? (int)$_POST['topic'] : 0;
|
||||
$forumId = !empty($_POST['forum']) && is_string($_POST['forum']) ? (int)$_POST['forum'] : 0;
|
||||
} else {
|
||||
$mode = !empty($_GET['m']) && is_string($_GET['m']) ? $_GET['m'] : 'create';
|
||||
$postId = !empty($_GET['p']) && is_string($_GET['p']) ? (int)$_GET['p'] : 0;
|
||||
|
@ -141,11 +141,11 @@ if($mode === 'edit') {
|
|||
$notices = [];
|
||||
|
||||
if(!empty($_POST)) {
|
||||
$topicTitle = $_POST['post']['title'] ?? '';
|
||||
$postText = $_POST['post']['text'] ?? '';
|
||||
$postParser = TextFormat::tryFrom((string)($_POST['post']['parser'] ?? '')) ?? TextFormat::BBCode;
|
||||
$topicType = isset($_POST['post']['type']) ? $_POST['post']['type'] : null;
|
||||
$postSignature = isset($_POST['post']['signature']);
|
||||
$topicTitle = $_POST['title'] ?? '';
|
||||
$postText = $_POST['text'] ?? '';
|
||||
$postParser = TextFormat::tryFrom((string)($_POST['parser'] ?? '')) ?? TextFormat::BBCode;
|
||||
$topicType = isset($_POST['type']) ? $_POST['type'] : null;
|
||||
$postSignature = isset($_POST['signature']);
|
||||
|
||||
if(!CSRF::validateRequest()) {
|
||||
$notices[] = 'Could not verify request.';
|
||||
|
|
|
@ -17,7 +17,7 @@ $changeActions = [];
|
|||
foreach(ChangelogData::ACTIONS as $action)
|
||||
$changeActions[$action] = ChangelogData::actionText($action);
|
||||
|
||||
$changeId = (string)filter_input(INPUT_GET, 'c', FILTER_SANITIZE_NUMBER_INT);
|
||||
$changeId = !empty($_GET['c']) && is_scalar($_GET['c']) ? (string)$_GET['c'] : '';
|
||||
$changeInfo = null;
|
||||
$changeTagIds = [];
|
||||
$tagInfos = $msz->changelog->getTags();
|
||||
|
@ -45,12 +45,12 @@ if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) {
|
|||
|
||||
// make errors not echos lol
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
$action = trim((string)filter_input(INPUT_POST, 'cl_action'));
|
||||
$summary = trim((string)filter_input(INPUT_POST, 'cl_summary'));
|
||||
$body = trim((string)filter_input(INPUT_POST, 'cl_body'));
|
||||
$userId = (int)filter_input(INPUT_POST, 'cl_user', FILTER_SANITIZE_NUMBER_INT);
|
||||
$createdAt = trim((string)filter_input(INPUT_POST, 'cl_created'));
|
||||
$tags = filter_input(INPUT_POST, 'cl_tags', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
|
||||
$action = !empty($_POST['cl_action']) && is_scalar($_POST['cl_action']) ? trim((string)$_POST['cl_action']) : '';
|
||||
$summary = !empty($_POST['cl_summary']) && is_scalar($_POST['cl_summary']) ? trim((string)$_POST['cl_summary']) : '';
|
||||
$body = !empty($_POST['cl_body']) && is_scalar($_POST['cl_body']) ? trim((string)$_POST['cl_body']) : '';
|
||||
$userId = !empty($_POST['cl_user']) && is_scalar($_POST['cl_user']) ? (int)$_POST['cl_user'] : 0;
|
||||
$createdAt = !empty($_POST['cl_created']) && is_scalar($_POST['cl_created']) ? trim((string)$_POST['cl_created']) : '';
|
||||
$tags = !empty($_POST['cl_tags']) && is_array($_POST['cl_tags']) ? $_POST['cl_tags'] : [];
|
||||
|
||||
if($userId < 1) $userId = null;
|
||||
else $userId = (string)$userId;
|
||||
|
|
|
@ -9,7 +9,7 @@ if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
|
|||
if(!$msz->authInfo->getPerms('global')->check(Perm::G_CL_TAGS_MANAGE))
|
||||
Template::throwError(403);
|
||||
|
||||
$tagId = (string)filter_input(INPUT_GET, 't', FILTER_SANITIZE_NUMBER_INT);
|
||||
$tagId = !empty($_GET['t']) && is_scalar($_GET['t']) ? (string)$_GET['t'] : '';
|
||||
$loadTagInfo = fn() => $msz->changelog->getTag($tagId);
|
||||
|
||||
if(empty($tagId))
|
||||
|
@ -33,8 +33,8 @@ if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) {
|
|||
}
|
||||
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
$name = trim((string)filter_input(INPUT_POST, 'ct_name'));
|
||||
$description = trim((string)filter_input(INPUT_POST, 'ct_desc'));
|
||||
$name = !empty($_POST['ct_name']) && is_scalar($_POST['ct_name']) ? trim((string)$_POST['ct_name']) : '';
|
||||
$description = !empty($_POST['ct_desc']) && is_scalar($_POST['ct_desc']) ? trim((string)$_POST['ct_desc']) : '';
|
||||
$archive = !empty($_POST['ct_archive']);
|
||||
|
||||
if($isNew) {
|
||||
|
|
|
@ -12,11 +12,8 @@ if(!$msz->authInfo->getPerms('global')->check(Perm::G_FORUM_CATEGORIES_MANAGE))
|
|||
$permsInfos = $msz->perms->getPermissionInfo(categoryNames: Perm::INFO_FOR_FORUM_CATEGORY);
|
||||
$permsLists = Perm::createList(Perm::LISTS_FOR_FORUM_CATEGORY);
|
||||
|
||||
if(filter_has_var(INPUT_POST, 'perms'))
|
||||
Template::set('calculated_perms', Perm::convertSubmission(
|
||||
filter_input(INPUT_POST, 'perms', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY),
|
||||
Perm::INFO_FOR_FORUM_CATEGORY
|
||||
));
|
||||
if(!empty($_POST))
|
||||
Template::set('calculated_perms', Perm::convertSubmission($_POST, Perm::INFO_FOR_FORUM_CATEGORY));
|
||||
|
||||
Template::render('manage.forum.listing', [
|
||||
'perms_lists' => $permsLists,
|
||||
|
|
|
@ -11,8 +11,8 @@ if($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
if(!CSRF::validateRequest())
|
||||
throw new \Exception("Request verification failed.");
|
||||
|
||||
$rTopicId = (string)filter_input(INPUT_POST, 'topic_redir_id');
|
||||
$rTopicURL = trim((string)filter_input(INPUT_POST, 'topic_redir_url'));
|
||||
$rTopicId = !empty($_POST['topic_redir_id']) && is_scalar($_POST['topic_redir_id']) ? trim((string)$_POST['topic_redir_id']) : '';
|
||||
$rTopicURL = !empty($_POST['topic_redir_url']) && is_scalar($_POST['topic_redir_url']) ? trim((string)$_POST['topic_redir_url']) : '';
|
||||
|
||||
$msz->createAuditLog('FORUM_TOPIC_REDIR_CREATE', [$rTopicId]);
|
||||
$msz->forumCtx->topicRedirects->createTopicRedirect($rTopicId, $msz->authInfo->userInfo, $rTopicURL);
|
||||
|
@ -20,11 +20,11 @@ if($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||
return;
|
||||
}
|
||||
|
||||
if(filter_input(INPUT_GET, 'm') === 'explode') {
|
||||
if(!empty($_GET['m']) && $_GET['m'] === 'explode') {
|
||||
if(!CSRF::validateRequest())
|
||||
throw new \Exception("Request verification failed.");
|
||||
|
||||
$rTopicId = (string)filter_input(INPUT_GET, 't');
|
||||
$rTopicId = !empty($_GET['t']) && is_scalar($_GET['t']) ? (string)$_GET['t'] : '';
|
||||
$msz->createAuditLog('FORUM_TOPIC_REDIR_REMOVE', [$rTopicId]);
|
||||
$msz->forumCtx->topicRedirects->deleteTopicRedirect($rTopicId);
|
||||
Tools::redirect($msz->urls->format('manage-forum-topic-redirs'));
|
||||
|
|
|
@ -10,7 +10,7 @@ if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
|
|||
if(!$msz->authInfo->getPerms('global')->check(Perm::G_EMOTES_MANAGE))
|
||||
Template::throwError(403);
|
||||
|
||||
$emoteId = (string)filter_input(INPUT_GET, 'e', FILTER_SANITIZE_NUMBER_INT);
|
||||
$emoteId = !empty($_GET['e']) && is_scalar($_GET['e']) ? (string)$_GET['e'] : '';
|
||||
$emoteInfo = [];
|
||||
$emoteStrings = [];
|
||||
|
||||
|
@ -27,10 +27,10 @@ else
|
|||
|
||||
// make errors not echos lol
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
$order = (int)filter_input(INPUT_POST, 'em_order', FILTER_SANITIZE_NUMBER_INT);
|
||||
$minRank = (int)filter_input(INPUT_POST, 'em_minrank', FILTER_SANITIZE_NUMBER_INT);
|
||||
$url = trim((string)filter_input(INPUT_POST, 'em_url'));
|
||||
$strings = explode(' ', trim((string)filter_input(INPUT_POST, 'em_strings')));
|
||||
$order = !empty($_POST['em_order']) && is_scalar($_POST['em_order']) ? (int)$_POST['em_order'] : '';
|
||||
$minRank = !empty($_POST['em_minrank']) && is_scalar($_POST['em_minrank']) ? (int)$_POST['em_minrank'] : '';
|
||||
$url = !empty($_POST['em_url']) && is_scalar($_POST['em_url']) ? trim((string)$_POST['em_url']) : '';
|
||||
$strings = explode(' ', !empty($_POST['em_strings']) && is_scalar($_POST['em_strings']) ? trim((string)$_POST['em_strings']) : '');
|
||||
|
||||
if($isNew || $url !== $emoteInfo->url) {
|
||||
$checkUrl = $msz->emotes->checkEmoteUrl($url);
|
||||
|
|
|
@ -10,7 +10,7 @@ if(!$msz->authInfo->getPerms('global')->check(Perm::G_EMOTES_MANAGE))
|
|||
Template::throwError(403);
|
||||
|
||||
if(CSRF::validateRequest() && !empty($_GET['emote'])) {
|
||||
$emoteId = (string)filter_input(INPUT_GET, 'emote', FILTER_SANITIZE_NUMBER_INT);
|
||||
$emoteId = !empty($_GET['emote']) && is_scalar($_GET['emote']) ? (string)$_GET['emote'] : '';
|
||||
|
||||
try {
|
||||
$emoteInfo = $msz->emotes->getEmote($emoteId);
|
||||
|
@ -23,14 +23,14 @@ if(CSRF::validateRequest() && !empty($_GET['emote'])) {
|
|||
$msz->createAuditLog('EMOTICON_DELETE', [$emoteInfo->id]);
|
||||
} else {
|
||||
if(isset($_GET['order'])) {
|
||||
$order = filter_input(INPUT_GET, 'order');
|
||||
$order = !empty($_GET['order']) && is_scalar($_GET['order']) ? (string)$_GET['order'] : '';
|
||||
$offset = $order === 'i' ? 10 : ($order === 'd' ? -10 : 0);
|
||||
$msz->emotes->updateEmoteOrderOffset($emoteInfo, $offset);
|
||||
$msz->createAuditLog('EMOTICON_ORDER', [$emoteInfo->id]);
|
||||
}
|
||||
|
||||
if(isset($_GET['alias'])) {
|
||||
$alias = (string)filter_input(INPUT_GET, 'alias');
|
||||
$alias = !empty($_GET['alias']) && is_scalar($_GET['alias']) ? (string)$_GET['alias'] : '';
|
||||
if($msz->emotes->checkEmoteString($alias) === '') {
|
||||
$msz->emotes->addEmoteString($emoteInfo, $alias);
|
||||
$msz->createAuditLog('EMOTICON_ALIAS', [$emoteInfo->id, $alias]);
|
||||
|
|
|
@ -7,7 +7,7 @@ if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
|
|||
if(!$msz->authInfo->getPerms('global')->check(Perm::G_CONFIG_MANAGE))
|
||||
Template::throwError(403);
|
||||
|
||||
$valueInfo = $msz->config->getValueInfo((string)filter_input(INPUT_GET, 'name'));
|
||||
$valueInfo = $msz->config->getValueInfo(!empty($_GET['name']) && is_scalar($_GET['name']) ? (string)$_GET['name'] : '');
|
||||
if($valueInfo === null)
|
||||
Template::throwError(404);
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ if(!$msz->authInfo->getPerms('global')->check(Perm::G_CONFIG_MANAGE))
|
|||
Template::throwError(403);
|
||||
|
||||
$isNew = true;
|
||||
$sName = (string)filter_input(INPUT_GET, 'name');
|
||||
$sType = (string)filter_input(INPUT_GET, 'type');
|
||||
$sName = !empty($_GET['name']) && is_scalar($_GET['name']) ? (string)$_GET['name'] : '';
|
||||
$sType = !empty($_GET['type']) && is_scalar($_GET['type']) ? (string)$_GET['type'] : '';
|
||||
$sValue = null;
|
||||
$loadValueInfo = fn() => $msz->config->getValueInfo($sName);
|
||||
|
||||
|
@ -27,13 +27,13 @@ if(!empty($sName)) {
|
|||
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
if($isNew) {
|
||||
$sName = trim((string)filter_input(INPUT_POST, 'conf_name'));
|
||||
$sName = !empty($_POST['conf_name']) && is_scalar($_POST['conf_name']) ? trim((string)$_POST['conf_name']) : '';
|
||||
if(!DbConfig::validateName($sName)) {
|
||||
echo 'Name contains invalid characters.';
|
||||
break;
|
||||
}
|
||||
|
||||
$sType = trim((string)filter_input(INPUT_POST, 'conf_type'));
|
||||
$sType = !empty($_POST['conf_type']) && is_scalar($_POST['conf_type']) ? trim((string)$_POST['conf_type']) : '';
|
||||
if(!in_array($sType, ['string', 'int', 'float', 'bool', 'array'])) {
|
||||
echo 'Invalid type specified.';
|
||||
break;
|
||||
|
@ -43,7 +43,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
|||
if($sType === 'array') {
|
||||
$applyFunc = $msz->config->setArray(...);
|
||||
$sValue = [];
|
||||
$sRaw = filter_input(INPUT_POST, 'conf_value', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
|
||||
$sRaw = !empty($_POST['conf_value']) && is_array($_POST['conf_value']) ? $_POST['conf_value'] : [];
|
||||
foreach($sRaw as $rValue) {
|
||||
if(strpos($rValue, ':') === 1) {
|
||||
$rType = $rValue[0];
|
||||
|
@ -63,7 +63,7 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
|||
$sValue = !empty($_POST['conf_value']);
|
||||
$applyFunc = $msz->config->setBoolean(...);
|
||||
} else {
|
||||
$sValue = filter_input(INPUT_POST, 'conf_value');
|
||||
$sValue = !empty($_POST['conf_value']) && is_scalar($_POST['conf_value']) ? trim((string)$_POST['conf_value']) : '';
|
||||
if($sType === 'int') {
|
||||
$applyFunc = $msz->config->setInteger(...);
|
||||
$sValue = (int)$sValue;
|
||||
|
|
|
@ -9,7 +9,7 @@ if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
|
|||
if(!$msz->authInfo->getPerms('global')->check(Perm::G_NEWS_CATEGORIES_MANAGE))
|
||||
Template::throwError(403);
|
||||
|
||||
$categoryId = (string)filter_input(INPUT_GET, 'c', FILTER_SANITIZE_NUMBER_INT);
|
||||
$categoryId = !empty($_GET['c']) && is_scalar($_GET['c']) ? (string)$_GET['c'] : '';
|
||||
$loadCategoryInfo = fn() => $msz->news->getCategory(categoryId: $categoryId);
|
||||
|
||||
if(empty($categoryId))
|
||||
|
@ -33,8 +33,8 @@ if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) {
|
|||
}
|
||||
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
$name = trim((string)filter_input(INPUT_POST, 'nc_name'));
|
||||
$description = trim((string)filter_input(INPUT_POST, 'nc_desc'));
|
||||
$name = !empty($_POST['nc_name']) && is_scalar($_POST['nc_name']) ? trim((string)$_POST['nc_name']) : '';
|
||||
$description = !empty($_POST['nc_desc']) && is_scalar($_POST['nc_desc']) ? trim((string)$_POST['nc_desc']) : '';
|
||||
$hidden = !empty($_POST['nc_hidden']);
|
||||
|
||||
if($isNew) {
|
||||
|
|
|
@ -10,7 +10,7 @@ if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
|
|||
if(!$msz->authInfo->getPerms('global')->check(Perm::G_NEWS_POSTS_MANAGE))
|
||||
Template::throwError(403);
|
||||
|
||||
$postId = (string)filter_input(INPUT_GET, 'p', FILTER_SANITIZE_NUMBER_INT);
|
||||
$postId = !empty($_GET['p']) && is_scalar($_GET['p']) ? (string)$_GET['p'] : '';
|
||||
$loadPostInfo = fn() => $msz->news->getPost($postId);
|
||||
|
||||
if(empty($postId))
|
||||
|
@ -34,10 +34,10 @@ if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) {
|
|||
}
|
||||
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
$title = trim((string)filter_input(INPUT_POST, 'np_title'));
|
||||
$category = (string)filter_input(INPUT_POST, 'np_category', FILTER_SANITIZE_NUMBER_INT);
|
||||
$title = !empty($_POST['np_title']) && is_scalar($_POST['np_title']) ? trim((string)$_POST['np_title']) : '';
|
||||
$category = !empty($_POST['np_category']) && is_scalar($_POST['np_category']) ? trim((string)$_POST['np_category']) : '';
|
||||
$featured = !empty($_POST['np_featured']);
|
||||
$body = trim((string)filter_input(INPUT_POST, 'np_body'));
|
||||
$body = !empty($_POST['np_body']) && is_scalar($_POST['np_body']) ? trim((string)$_POST['np_body']) : '';
|
||||
|
||||
if($isNew) {
|
||||
$postInfo = $msz->news->createPost($category, $title, $body, $featured, $msz->authInfo->userInfo);
|
||||
|
|
|
@ -11,12 +11,12 @@ if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
|
|||
if(!$msz->authInfo->getPerms('user')->check(Perm::U_BANS_MANAGE))
|
||||
Template::throwError(403);
|
||||
|
||||
if($_SERVER['REQUEST_METHOD'] === 'GET' && filter_has_var(INPUT_GET, 'delete')) {
|
||||
if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) {
|
||||
if(!CSRF::validateRequest())
|
||||
Template::throwError(403);
|
||||
|
||||
try {
|
||||
$banInfo = $msz->usersCtx->bans->getBan((string)filter_input(INPUT_GET, 'b'));
|
||||
$banInfo = $msz->usersCtx->bans->getBan(!empty($_GET['b']) && is_scalar($_GET['b']) ? (string)$_GET['b'] : '');
|
||||
} catch(RuntimeException $ex) {
|
||||
Template::throwError(404);
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ if($_SERVER['REQUEST_METHOD'] === 'GET' && filter_has_var(INPUT_GET, 'delete'))
|
|||
}
|
||||
|
||||
try {
|
||||
$userInfo = $msz->usersCtx->getUserInfo(filter_input(INPUT_GET, 'u', FILTER_SANITIZE_NUMBER_INT), 'id');
|
||||
$userInfo = $msz->usersCtx->getUserInfo(!empty($_GET['u']) && is_scalar($_GET['u']) ? (string)$_GET['u'] : '', 'id');
|
||||
} catch(RuntimeException $ex) {
|
||||
Template::throwError(404);
|
||||
}
|
||||
|
@ -36,11 +36,11 @@ try {
|
|||
$modInfo = $msz->authInfo->userInfo;
|
||||
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
$expires = (int)filter_input(INPUT_POST, 'ub_expires', FILTER_SANITIZE_NUMBER_INT);
|
||||
$expiresCustom = (string)filter_input(INPUT_POST, 'ub_expires_custom');
|
||||
$publicReason = trim((string)filter_input(INPUT_POST, 'ub_reason_pub'));
|
||||
$privateReason = trim((string)filter_input(INPUT_POST, 'ub_reason_priv'));
|
||||
$severity = (int)filter_input(INPUT_POST, 'ub_severity', FILTER_SANITIZE_NUMBER_INT);
|
||||
$expires = !empty($_POST['ub_expires']) && is_scalar($_POST['ub_expires']) ? (int)$_POST['ub_expires'] : 0;
|
||||
$expiresCustom = !empty($_POST['ub_expires_custom']) && is_scalar($_POST['ub_expires_custom']) ? trim((string)$_POST['ub_expires_custom']) : '';
|
||||
$publicReason = !empty($_POST['ub_reason_pub']) && is_scalar($_POST['ub_reason_pub']) ? trim((string)$_POST['ub_reason_pub']) : '';
|
||||
$privateReason = !empty($_POST['ub_reason_priv']) && is_scalar($_POST['ub_reason_priv']) ? trim((string)$_POST['ub_reason_priv']) : '';
|
||||
$severity = !empty($_POST['ub_severity']) && is_scalar($_POST['ub_severity']) ? (int)$_POST['ub_severity'] : 0;
|
||||
|
||||
Template::set([
|
||||
'ban_value_expires' => $expires,
|
||||
|
|
|
@ -10,8 +10,8 @@ if(!$msz->authInfo->getPerms('user')->check(Perm::U_BANS_MANAGE))
|
|||
Template::throwError(403);
|
||||
|
||||
$filterUser = null;
|
||||
if(filter_has_var(INPUT_GET, 'u')) {
|
||||
$filterUserId = filter_input(INPUT_GET, 'u', FILTER_SANITIZE_NUMBER_INT);
|
||||
if(!empty($_GET['u'])) {
|
||||
$filterUserId = !empty($_GET['u']) && is_scalar($_GET['u']) ? (string)$_GET['u'] : '';
|
||||
try {
|
||||
$filterUser = $msz->usersCtx->getUserInfo($filterUserId);
|
||||
} catch(RuntimeException $ex) {
|
||||
|
@ -20,7 +20,7 @@ if(filter_has_var(INPUT_GET, 'u')) {
|
|||
}
|
||||
|
||||
$pagination = Pagination::fromInput($msz->usersCtx->bans->countBans(userInfo: $filterUser), 10);
|
||||
if(!$pagination->validOffset)
|
||||
if(!$pagination->validOffset && $pagination->count > 0)
|
||||
Template::throwError(404);
|
||||
|
||||
$banList = [];
|
||||
|
|
|
@ -9,8 +9,8 @@ if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
|
|||
if(!$msz->authInfo->getPerms('user')->check(Perm::U_NOTES_MANAGE))
|
||||
Template::throwError(403);
|
||||
|
||||
$hasNoteId = filter_has_var(INPUT_GET, 'n');
|
||||
$hasUserId = filter_has_var(INPUT_GET, 'u');
|
||||
$hasNoteId = !empty($_GET['n']);
|
||||
$hasUserId = !empty($_GET['u']);
|
||||
|
||||
if((!$hasNoteId && !$hasUserId) || ($hasNoteId && $hasUserId))
|
||||
Template::throwError(400);
|
||||
|
@ -19,7 +19,7 @@ if($hasUserId) {
|
|||
$isNew = true;
|
||||
|
||||
try {
|
||||
$userInfo = $msz->usersCtx->getUserInfo(filter_input(INPUT_GET, 'u', FILTER_SANITIZE_NUMBER_INT));
|
||||
$userInfo = $msz->usersCtx->getUserInfo(!empty($_GET['u']) && is_scalar($_GET['u']) ? (string)$_GET['u'] : '');
|
||||
} catch(RuntimeException $ex) {
|
||||
Template::throwError(404);
|
||||
}
|
||||
|
@ -29,12 +29,12 @@ if($hasUserId) {
|
|||
$isNew = false;
|
||||
|
||||
try {
|
||||
$noteInfo = $msz->usersCtx->modNotes->getNote((string)filter_input(INPUT_GET, 'n', FILTER_SANITIZE_NUMBER_INT));
|
||||
$noteInfo = $msz->usersCtx->modNotes->getNote(!empty($_GET['n']) && is_scalar($_GET['n']) ? (string)$_GET['n'] : '');
|
||||
} catch(RuntimeException $ex) {
|
||||
Template::throwError(404);
|
||||
}
|
||||
|
||||
if($_SERVER['REQUEST_METHOD'] === 'GET' && filter_has_var(INPUT_GET, 'delete')) {
|
||||
if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) {
|
||||
if(!CSRF::validateRequest())
|
||||
Template::throwError(403);
|
||||
|
||||
|
@ -49,8 +49,8 @@ if($hasUserId) {
|
|||
}
|
||||
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
$title = trim((string)filter_input(INPUT_POST, 'mn_title'));
|
||||
$body = trim((string)filter_input(INPUT_POST, 'mn_body'));
|
||||
$title = trim((string)($_POST['mn_title'] ?? ''));
|
||||
$body = trim((string)($_POST['mn_body'] ?? ''));
|
||||
|
||||
if($isNew) {
|
||||
$noteInfo = $msz->usersCtx->modNotes->createNote($userInfo, $title, $body, $authorInfo);
|
||||
|
|
|
@ -10,8 +10,8 @@ if(!$msz->authInfo->getPerms('user')->check(Perm::U_NOTES_MANAGE))
|
|||
Template::throwError(403);
|
||||
|
||||
$filterUser = null;
|
||||
if(filter_has_var(INPUT_GET, 'u')) {
|
||||
$filterUserId = filter_input(INPUT_GET, 'u', FILTER_SANITIZE_NUMBER_INT);
|
||||
if(!empty($_GET['u'])) {
|
||||
$filterUserId = !empty($_GET['u']) && is_scalar($_GET['u']) ? (string)$_GET['u'] : '';
|
||||
try {
|
||||
$filterUser = $msz->usersCtx->getUserInfo($filterUserId);
|
||||
} catch(RuntimeException $ex) {
|
||||
|
@ -20,7 +20,7 @@ if(filter_has_var(INPUT_GET, 'u')) {
|
|||
}
|
||||
|
||||
$pagination = Pagination::fromInput($msz->usersCtx->modNotes->countNotes(userInfo: $filterUser), 10);
|
||||
if(!$pagination->validOffset)
|
||||
if(!$pagination->validOffset && $pagination->count > 0)
|
||||
Template::throwError(404);
|
||||
|
||||
$notes = [];
|
||||
|
|
|
@ -15,8 +15,8 @@ if(!$viewerPerms->check(Perm::U_ROLES_MANAGE))
|
|||
|
||||
$roleInfo = null;
|
||||
|
||||
if(filter_has_var(INPUT_GET, 'r')) {
|
||||
$roleId = (string)filter_input(INPUT_GET, 'r', FILTER_SANITIZE_NUMBER_INT);
|
||||
if(!empty($_GET['r'])) {
|
||||
$roleId = !empty($_GET['r']) && is_scalar($_GET['r']) ? (string)$_GET['r'] : '';
|
||||
|
||||
try {
|
||||
$isNew = false;
|
||||
|
@ -40,17 +40,17 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
|||
break;
|
||||
}
|
||||
|
||||
$roleString = (string)filter_input(INPUT_POST, 'ur_string');
|
||||
$roleName = (string)filter_input(INPUT_POST, 'ur_name');
|
||||
$roleString = !empty($_POST['ur_string']) && is_scalar($_POST['ur_string']) ? trim((string)$_POST['ur_string']) : '';
|
||||
$roleName = !empty($_POST['ur_name']) && is_scalar($_POST['ur_name']) ? trim((string)$_POST['ur_name']) : '';
|
||||
$roleHide = !empty($_POST['ur_hidden']);
|
||||
$roleLeavable = !empty($_POST['ur_leavable']);
|
||||
$roleRank = (int)filter_input(INPUT_POST, 'ur_rank', FILTER_SANITIZE_NUMBER_INT);
|
||||
$roleTitle = (string)filter_input(INPUT_POST, 'ur_title');
|
||||
$roleDesc = (string)filter_input(INPUT_POST, 'ur_desc');
|
||||
$roleRank = !empty($_POST['ur_rank']) && is_scalar($_POST['ur_rank']) ? (int)$_POST['ur_rank'] : 0;
|
||||
$roleTitle = !empty($_POST['ur_title']) && is_scalar($_POST['ur_title']) ? trim((string)$_POST['ur_title']) : '';
|
||||
$roleDesc = !empty($_POST['ur_desc']) && is_scalar($_POST['ur_desc']) ? trim((string)$_POST['ur_desc']) : '';
|
||||
$colourInherit = !empty($_POST['ur_col_inherit']);
|
||||
$colourRed = (int)filter_input(INPUT_POST, 'ur_col_red', FILTER_SANITIZE_NUMBER_INT);
|
||||
$colourGreen = (int)filter_input(INPUT_POST, 'ur_col_green', FILTER_SANITIZE_NUMBER_INT);
|
||||
$colourBlue = (int)filter_input(INPUT_POST, 'ur_col_blue', FILTER_SANITIZE_NUMBER_INT);
|
||||
$colourRed = !empty($_POST['ur_col_red']) && is_scalar($_POST['ur_col_red']) ? (int)$_POST['ur_col_red'] : 0;
|
||||
$colourGreen = !empty($_POST['ur_col_green']) && is_scalar($_POST['ur_col_green']) ? (int)$_POST['ur_col_green'] : 0;
|
||||
$colourBlue = !empty($_POST['ur_col_blue']) && is_scalar($_POST['ur_col_blue']) ? (int)$_POST['ur_col_blue'] : 0;
|
||||
|
||||
Template::set([
|
||||
'role_ur_string' => $roleString,
|
||||
|
@ -153,12 +153,8 @@ while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
|||
[$roleInfo->id]
|
||||
);
|
||||
|
||||
if($canEditPerms && filter_has_var(INPUT_POST, 'perms')) {
|
||||
$permsApply = Perm::convertSubmission(
|
||||
filter_input(INPUT_POST, 'perms', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY),
|
||||
Perm::INFO_FOR_ROLE
|
||||
);
|
||||
|
||||
if($canEditPerms) {
|
||||
$permsApply = Perm::convertSubmission($_POST, Perm::INFO_FOR_ROLE);
|
||||
foreach($permsApply as $categoryName => $values)
|
||||
$msz->perms->setPermissions($categoryName, $values['allow'], $values['deny'], roleInfo: $roleInfo);
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ if(!$hasAccess)
|
|||
Template::throwError(403);
|
||||
|
||||
$notices = [];
|
||||
$userId = (string)filter_input(INPUT_GET, 'u', FILTER_SANITIZE_NUMBER_INT);
|
||||
$userId = !empty($_GET['u']) && is_scalar($_GET['u']) ? (string)$_GET['u'] : '';
|
||||
|
||||
try {
|
||||
$userInfo = $msz->usersCtx->users->getUser($userId, 'id');
|
||||
|
@ -201,12 +201,8 @@ if(CSRF::validateRequest() && $canEdit) {
|
|||
}
|
||||
}
|
||||
|
||||
if($canEditPerms && filter_has_var(INPUT_POST, 'perms')) {
|
||||
$permsApply = Perm::convertSubmission(
|
||||
filter_input(INPUT_POST, 'perms', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY),
|
||||
Perm::INFO_FOR_USER
|
||||
);
|
||||
|
||||
if($canEditPerms) {
|
||||
$permsApply = Perm::convertSubmission($_POST, Perm::INFO_FOR_USER);
|
||||
foreach($permsApply as $categoryName => $values)
|
||||
$msz->perms->setPermissions($categoryName, $values['allow'], $values['deny'], userInfo: $userInfo);
|
||||
|
||||
|
|
|
@ -9,12 +9,12 @@ if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
|
|||
if(!$msz->authInfo->getPerms('user')->check(Perm::U_WARNINGS_MANAGE))
|
||||
Template::throwError(403);
|
||||
|
||||
if($_SERVER['REQUEST_METHOD'] === 'GET' && filter_has_var(INPUT_GET, 'delete')) {
|
||||
if($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['delete'])) {
|
||||
if(!CSRF::validateRequest())
|
||||
Template::throwError(403);
|
||||
|
||||
try {
|
||||
$warnInfo = $msz->usersCtx->warnings->getWarning((string)filter_input(INPUT_GET, 'w'));
|
||||
$warnInfo = $msz->usersCtx->warnings->getWarning(!empty($_GET['w']) && is_scalar($_GET['w']) ? (string)$_GET['w'] : '');
|
||||
} catch(RuntimeException $ex) {
|
||||
Template::throwError(404);
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ if($_SERVER['REQUEST_METHOD'] === 'GET' && filter_has_var(INPUT_GET, 'delete'))
|
|||
}
|
||||
|
||||
try {
|
||||
$userInfo = $msz->usersCtx->users->getUser(filter_input(INPUT_GET, 'u', FILTER_SANITIZE_NUMBER_INT), 'id');
|
||||
$userInfo = $msz->usersCtx->users->getUser(!empty($_GET['u']) && is_scalar($_GET['u']) ? (string)$_GET['u'] : '', 'id');
|
||||
} catch(RuntimeException $ex) {
|
||||
Template::throwError(404);
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ try {
|
|||
$modInfo = $msz->authInfo->userInfo;
|
||||
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
$body = trim((string)filter_input(INPUT_POST, 'uw_body'));
|
||||
$body = trim((string)($_POST['uw_body'] ?? ''));
|
||||
Template::set('warn_value_body', $body);
|
||||
|
||||
$warnInfo = $msz->usersCtx->warnings->createWarning(
|
||||
|
|
|
@ -10,8 +10,8 @@ if(!$msz->authInfo->getPerms('user')->check(Perm::U_WARNINGS_MANAGE))
|
|||
Template::throwError(403);
|
||||
|
||||
$filterUser = null;
|
||||
if(filter_has_var(INPUT_GET, 'u')) {
|
||||
$filterUserId = filter_input(INPUT_GET, 'u', FILTER_SANITIZE_NUMBER_INT);
|
||||
if(!empty($_GET['u'])) {
|
||||
$filterUserId = !empty($_GET['u']) && is_scalar($_GET['u']) ? (string)$_GET['u'] : '';
|
||||
try {
|
||||
$filterUser = $msz->usersCtx->getUserInfo($filterUserId);
|
||||
} catch(RuntimeException $ex) {
|
||||
|
@ -20,7 +20,7 @@ if(filter_has_var(INPUT_GET, 'u')) {
|
|||
}
|
||||
|
||||
$pagination = Pagination::fromInput($msz->usersCtx->warnings->countWarnings(userInfo: $filterUser), 10);
|
||||
if(!$pagination->validOffset)
|
||||
if(!$pagination->validOffset && $pagination->count > 0)
|
||||
Template::throwError(404);
|
||||
|
||||
$warnList = [];
|
||||
|
|
|
@ -11,9 +11,9 @@ if(!$msz->authInfo->loggedIn)
|
|||
|
||||
// TODO: restore forum-topics and forum-posts orderings
|
||||
|
||||
$roleId = filter_has_var(INPUT_GET, 'r') ? (string)filter_input(INPUT_GET, 'r') : null;
|
||||
$orderBy = strtolower((string)filter_input(INPUT_GET, 'ss'));
|
||||
$orderDir = strtolower((string)filter_input(INPUT_GET, 'sd'));
|
||||
$roleId = !empty($_GET['r']) && is_string($_GET['r']) ? $_GET['r'] : null;
|
||||
$orderBy = strtolower(!empty($_GET['ss']) && is_string($_GET['ss']) ? $_GET['ss'] : '');
|
||||
$orderDir = strtolower(!empty($_GET['sd']) && is_string($_GET['sd']) ? $_GET['sd'] : '');
|
||||
|
||||
$orderDirs = [
|
||||
'asc' => 'In Order',
|
||||
|
|
|
@ -5,6 +5,8 @@ use stdClass;
|
|||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use Index\ByteFormat;
|
||||
use Index\Http\Content\MultipartFormContent;
|
||||
use Index\Http\Content\Multipart\FileMultipartFormData;
|
||||
use Misuzu\Forum\ForumSignaturesData;
|
||||
use Misuzu\Parsers\TextFormat;
|
||||
use Misuzu\Profile\{ProfileAboutData,ProfileBackgroundAttach};
|
||||
|
@ -111,52 +113,49 @@ if($isEditing) {
|
|||
if(!CSRF::validateRequest()) {
|
||||
$notices[] = "Couldn't verify you, please refresh the page and retry.";
|
||||
} else {
|
||||
$profileFieldsSubmit = filter_input(INPUT_POST, 'profile', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
|
||||
if(!$perms->edit_profile) {
|
||||
$notices[] = "You're not allowed to edit your profile.";
|
||||
} else {
|
||||
$profileFieldInfos = iterator_to_array($msz->profileCtx->fields->getFields());
|
||||
$profileFieldsSetInfos = [];
|
||||
$profileFieldsSetValues = [];
|
||||
$profileFieldsRemove = [];
|
||||
|
||||
if(!empty($profileFieldsSubmit)) {
|
||||
if(!$perms->edit_profile) {
|
||||
$notices[] = "You're not allowed to edit your profile.";
|
||||
} else {
|
||||
$profileFieldInfos = iterator_to_array($msz->profileCtx->fields->getFields());
|
||||
$profileFieldsSetInfos = [];
|
||||
$profileFieldsSetValues = [];
|
||||
$profileFieldsRemove = [];
|
||||
foreach($profileFieldInfos as $fieldInfo) {
|
||||
$fieldName = sprintf('profile_%s', $fieldInfo->name);
|
||||
$fieldValue = empty($_POST[$fieldName]) || !is_scalar($_POST[$fieldName])
|
||||
? '' : (string)filter_var($_POST[$fieldName]);
|
||||
|
||||
foreach($profileFieldInfos as $fieldInfo) {
|
||||
$fieldName = $fieldInfo->name;
|
||||
$fieldValue = empty($profileFieldsSubmit[$fieldName]) ? '' : (string)filter_var($profileFieldsSubmit[$fieldName]);
|
||||
|
||||
if(empty($profileFieldsSubmit[$fieldName])) {
|
||||
$profileFieldsRemove[] = $fieldInfo;
|
||||
continue;
|
||||
}
|
||||
|
||||
if($fieldInfo->checkValue($fieldValue)) {
|
||||
$profileFieldsSetInfos[] = $fieldInfo;
|
||||
$profileFieldsSetValues[] = $fieldValue;
|
||||
} else
|
||||
$notices[] = sprintf("%s isn't properly formatted.", $fieldInfo->title);
|
||||
|
||||
unset($fieldName, $fieldValue, $fieldInfo);
|
||||
if(empty($_POST[$fieldName])) {
|
||||
$profileFieldsRemove[] = $fieldInfo;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!empty($profileFieldsRemove))
|
||||
$msz->profileCtx->fields->removeFieldValues($userInfo, $profileFieldsRemove);
|
||||
if(!empty($profileFieldsSetInfos))
|
||||
$msz->profileCtx->fields->setFieldValues($userInfo, $profileFieldsSetInfos, $profileFieldsSetValues);
|
||||
if($fieldInfo->checkValue($fieldValue)) {
|
||||
$profileFieldsSetInfos[] = $fieldInfo;
|
||||
$profileFieldsSetValues[] = $fieldValue;
|
||||
} else
|
||||
$notices[] = sprintf("%s isn't properly formatted.", $fieldInfo->title);
|
||||
|
||||
unset($fieldName, $fieldValue, $fieldInfo);
|
||||
}
|
||||
|
||||
if(!empty($profileFieldsRemove))
|
||||
$msz->profileCtx->fields->removeFieldValues($userInfo, $profileFieldsRemove);
|
||||
if(!empty($profileFieldsSetInfos))
|
||||
$msz->profileCtx->fields->setFieldValues($userInfo, $profileFieldsSetInfos, $profileFieldsSetValues);
|
||||
}
|
||||
|
||||
if(filter_has_var(INPUT_POST, 'about_body')) {
|
||||
if(isset($_POST['about_body']) && is_scalar($_POST['about_body'])) {
|
||||
if(!$perms->edit_about) {
|
||||
$notices[] = "You're not allowed to edit your about page.";
|
||||
} else {
|
||||
$aboutBody = (string)filter_input(INPUT_POST, 'about_body');
|
||||
$aboutBody = (string)$_POST['about_body'];
|
||||
if(trim($aboutBody) === '') {
|
||||
$msz->profileCtx->about->deleteProfileAbout($userInfo);
|
||||
$aboutInfo = null;
|
||||
} else {
|
||||
$aboutFormat = TextFormat::tryFrom(filter_input(INPUT_POST, 'about_format'));
|
||||
$aboutFormat = TextFormat::tryFrom(isset($_POST['about_format']) && is_scalar($_POST['about_format']) ? (string)$_POST['about_format'] : '');
|
||||
$aboutValid = ProfileAboutData::validateProfileAbout($aboutFormat, $aboutBody);
|
||||
if($aboutValid === '')
|
||||
$aboutInfo = $msz->profileCtx->about->updateProfileAbout($userInfo, $aboutBody, $aboutFormat);
|
||||
|
@ -166,16 +165,16 @@ if($isEditing) {
|
|||
}
|
||||
}
|
||||
|
||||
if(filter_has_var(INPUT_POST, 'sig_body')) {
|
||||
if(isset($_POST['sig_body']) && is_scalar($_POST['sig_body'])) {
|
||||
if(!$perms->edit_signature) {
|
||||
$notices[] = "You're not allowed to edit your forum signature.";
|
||||
} else {
|
||||
$sigBody = (string)filter_input(INPUT_POST, 'sig_body');
|
||||
$sigBody = (string)$_POST['sig_body'];
|
||||
if(trim($sigBody) === '') {
|
||||
$msz->forumCtx->signatures->deleteSignature($userInfo);
|
||||
$sigInfo = null;
|
||||
} else {
|
||||
$sigFormat = TextFormat::tryFrom(filter_input(INPUT_POST, 'sig_format'));
|
||||
$sigFormat = TextFormat::tryFrom(isset($_POST['sig_format']) && is_scalar($_POST['sig_format']) ? (string)$_POST['sig_format'] : '');
|
||||
$sigValid = ForumSignaturesData::validateSignature($sigFormat, $sigBody);
|
||||
if($sigValid === '')
|
||||
$sigInfo = $msz->forumCtx->signatures->updateSignature($userInfo, $sigBody, $sigFormat);
|
||||
|
@ -185,13 +184,13 @@ if($isEditing) {
|
|||
}
|
||||
}
|
||||
|
||||
if(!empty($_POST['birthdate']) && is_array($_POST['birthdate'])) {
|
||||
if(!empty($_POST['birth_day']) && !empty($_POST['birth_month'])) {
|
||||
if(!$perms->edit_birthdate) {
|
||||
$notices[] = "You aren't allow to change your birthdate.";
|
||||
} else {
|
||||
$birthYear = (int)($_POST['birthdate']['year'] ?? 0);
|
||||
$birthMonth = (int)($_POST['birthdate']['month'] ?? 0);
|
||||
$birthDay = (int)($_POST['birthdate']['day'] ?? 0);
|
||||
$birthYear = (int)($_POST['birth_year'] ?? 0);
|
||||
$birthMonth = (int)$_POST['birth_month'];
|
||||
$birthDay = (int)$_POST['birth_day'];
|
||||
$birthValid = UserBirthdatesData::validateBirthdate($birthYear, $birthMonth, $birthDay);
|
||||
|
||||
if($birthValid === '') {
|
||||
|
@ -204,53 +203,39 @@ if($isEditing) {
|
|||
}
|
||||
}
|
||||
|
||||
if(!empty($_FILES['avatar'])) {
|
||||
if(!empty($_POST['avatar']['delete'])) {
|
||||
$avatarAsset->delete();
|
||||
} else {
|
||||
if(!empty($_POST['avatar_delete'])) {
|
||||
$avatarAsset->delete();
|
||||
} elseif(isset($mszRequestContent) && $mszRequestContent instanceof MultipartFormContent) {
|
||||
$avatarInfo = $mszRequestContent->getParamData('avatar_file');
|
||||
if($avatarInfo instanceof FileMultipartFormData) {
|
||||
if(!$perms->edit_avatar) {
|
||||
$notices[] = "You aren't allow to change your avatar.";
|
||||
} elseif(!empty($_FILES['avatar'])
|
||||
&& is_array($_FILES['avatar'])
|
||||
&& !empty($_FILES['avatar']['name']['file'])) {
|
||||
if($_FILES['avatar']['error']['file'] !== UPLOAD_ERR_OK) {
|
||||
switch($_FILES['avatar']['error']['file']) {
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
$notices[] = 'Select a file before hitting upload!';
|
||||
break;
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
$notices[] = 'The upload was interrupted, please try again!';
|
||||
break;
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
$notices[] = sprintf('Your avatar is not allowed to be larger in file size than %s!', ByteFormat::format($avatarAsset->getMaxBytes()));
|
||||
break;
|
||||
default:
|
||||
$notices[] = 'Unable to save your avatar, contact an administator!';
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$avatarAsset->setFromPath($_FILES['avatar']['tmp_name']['file']);
|
||||
} catch(InvalidArgumentException $ex) {
|
||||
$exMessage = $ex->getMessage();
|
||||
$notices[] = match($exMessage) {
|
||||
'$path is not a valid image.' => 'The file you uploaded was not an image!',
|
||||
'$path is not an allowed image file.' => 'This type of image is not supported, keep to PNG, JPG or GIF!',
|
||||
'Dimensions of $path are too large.' => sprintf("Your avatar can't be larger than %dx%d!", $avatarAsset->getMaxWidth(), $avatarAsset->getMaxHeight()),
|
||||
'File size of $path is too large.' => sprintf('Your avatar is not allowed to be larger in file size than %s!', ByteFormat::format($avatarAsset->getMaxBytes())),
|
||||
default => $exMessage,
|
||||
};
|
||||
} catch(RuntimeException $ex) {
|
||||
$notices[] = 'Unable to save your avatar, contact an administator!';
|
||||
}
|
||||
} elseif($avatarInfo->getSize() > 0) {
|
||||
$avatarTemp = tempnam(sys_get_temp_dir(), 'msz-legacy-avatar-');
|
||||
try {
|
||||
$avatarInfo->moveTo($avatarTemp);
|
||||
$avatarAsset->setFromPath($avatarTemp);
|
||||
} catch(InvalidArgumentException $ex) {
|
||||
$exMessage = $ex->getMessage();
|
||||
$notices[] = match($exMessage) {
|
||||
'$path is not a valid image.' => 'The file you uploaded was not an image!',
|
||||
'$path is not an allowed image file.' => 'This type of image is not supported, keep to PNG, JPG or GIF!',
|
||||
'Dimensions of $path are too large.' => sprintf("Your avatar can't be larger than %dx%d!", $avatarAsset->getMaxWidth(), $avatarAsset->getMaxHeight()),
|
||||
'File size of $path is too large.' => sprintf('Your avatar is not allowed to be larger in file size than %s!', ByteFormat::format($avatarAsset->getMaxBytes())),
|
||||
default => $exMessage,
|
||||
};
|
||||
} catch(RuntimeException $ex) {
|
||||
$notices[] = 'Unable to save your avatar, contact an administator!';
|
||||
} finally {
|
||||
if(is_file($avatarTemp))
|
||||
unlink($avatarTemp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(filter_has_var(INPUT_POST, 'bg_attach')) {
|
||||
$bgFormat = ProfileBackgroundAttach::tryFrom((string)filter_input(INPUT_POST, 'bg_attach'));
|
||||
if(isset($_POST['bg_attach']) && is_scalar($_POST['bg_attach'])) {
|
||||
$bgFormat = ProfileBackgroundAttach::tryFrom((string)$_POST['bg_attach']);
|
||||
|
||||
if($bgFormat === null) {
|
||||
$backgroundAsset->delete();
|
||||
|
@ -259,47 +244,35 @@ if($isEditing) {
|
|||
} else {
|
||||
if(!$perms->edit_background) {
|
||||
$notices[] = "You aren't allow to change your background.";
|
||||
} elseif(!empty($_FILES['bg_file']) && is_array($_FILES['bg_file'])) {
|
||||
if(!empty($_FILES['bg_file']['name'])) {
|
||||
if($_FILES['bg_file']['error'] !== UPLOAD_ERR_OK) {
|
||||
switch($_FILES['bg_file']['error']) {
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
$notices[] = 'Select a file before hitting upload!';
|
||||
break;
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
$notices[] = 'The upload was interrupted, please try again!';
|
||||
break;
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
$notices[] = sprintf('Your background is not allowed to be larger in file size than %s!', ByteFormat::format(isset($backgroundProps) && is_array($backgroundProps) ? $backgroundProps['max_size'] : 0));
|
||||
break;
|
||||
default:
|
||||
$notices[] = 'Unable to save your background, contact an administator!';
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$backgroundAsset->setFromPath($_FILES['bg_file']['tmp_name']);
|
||||
} catch(InvalidArgumentException $ex) {
|
||||
$exMessage = $ex->getMessage();
|
||||
$notices[] = match($exMessage) {
|
||||
'$path is not a valid image.' => 'The file you uploaded was not an image!',
|
||||
'$path is not an allowed image file.' => 'This type of image is not supported, keep to PNG, JPG or GIF!',
|
||||
'Dimensions of $path are too large.' => sprintf("Your background can't be larger than %dx%d!", $backgroundAsset->getMaxWidth(), $backgroundAsset->getMaxHeight()),
|
||||
'File size of $path is too large.' => sprintf('Your background is not allowed to be larger in file size than %s!', ByteFormat::format($backgroundAsset->getMaxBytes())),
|
||||
default => $exMessage,
|
||||
};
|
||||
} catch(RuntimeException $ex) {
|
||||
$notices[] = 'Unable to save your background, contact an administator!';
|
||||
}
|
||||
} elseif(isset($mszRequestContent) && $mszRequestContent instanceof MultipartFormContent) {
|
||||
$bgInfo = $mszRequestContent->getParamData('bg_file');
|
||||
if($bgInfo instanceof FileMultipartFormData && $bgInfo->getSize() > 0) {
|
||||
$bgTemp = tempnam(sys_get_temp_dir(), 'msz-legacy-profile-background-');
|
||||
try {
|
||||
$bgInfo->moveTo($bgTemp);
|
||||
$backgroundAsset->setFromPath($bgTemp);
|
||||
} catch(InvalidArgumentException $ex) {
|
||||
$exMessage = $ex->getMessage();
|
||||
$notices[] = match($exMessage) {
|
||||
'$path is not a valid image.' => 'The file you uploaded was not an image!',
|
||||
'$path is not an allowed image file.' => 'This type of image is not supported, keep to PNG, JPG or GIF!',
|
||||
'Dimensions of $path are too large.' => sprintf("Your background can't be larger than %dx%d!", $backgroundAsset->getMaxWidth(), $backgroundAsset->getMaxHeight()),
|
||||
'File size of $path is too large.' => sprintf('Your background is not allowed to be larger in file size than %s!', ByteFormat::format($backgroundAsset->getMaxBytes())),
|
||||
default => $exMessage,
|
||||
};
|
||||
} catch(RuntimeException $ex) {
|
||||
$notices[] = 'Unable to save your background, contact an administator!';
|
||||
} finally {
|
||||
if(is_file($bgTemp))
|
||||
unlink($bgTemp);
|
||||
}
|
||||
}
|
||||
|
||||
$backgroundInfo = $msz->profileCtx->backgrounds->updateProfileBackground(
|
||||
$userInfo,
|
||||
$bgFormat,
|
||||
filter_has_var(INPUT_POST, 'bg_blend'),
|
||||
filter_has_var(INPUT_POST, 'bg_slide')
|
||||
!empty($_POST['bg_blend']),
|
||||
!empty($_POST['bg_slide'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ $currentUser = $msz->authInfo->userInfo;
|
|||
$activeSessionId = $msz->authInfo->sessionId;
|
||||
|
||||
while($_SERVER['REQUEST_METHOD'] === 'POST' && CSRF::validateRequest()) {
|
||||
$sessionId = (string)filter_input(INPUT_POST, 'session');
|
||||
$sessionId = !empty($_POST['session']) && is_scalar($_POST['session']) ? trim((string)$_POST['session']) : '';
|
||||
$activeSessionKilled = false;
|
||||
|
||||
if($sessionId === 'all') {
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
namespace Misuzu;
|
||||
|
||||
use RuntimeException;
|
||||
use Index\MediaType;
|
||||
use Index\Http\Content\MultipartFormContent;
|
||||
use Index\Http\Content\Multipart\ValueMultipartFormData;
|
||||
use Misuzu\Auth\{AuthTokenBuilder,AuthTokenCookie,AuthTokenInfo};
|
||||
|
||||
require_once __DIR__ . '/../misuzu.php';
|
||||
|
@ -25,12 +28,12 @@ $request = \Index\Http\HttpRequest::fromRequest();
|
|||
|
||||
$tokenPacker = $msz->authCtx->createAuthTokenPacker();
|
||||
|
||||
if(filter_has_var(INPUT_COOKIE, 'msz_auth'))
|
||||
$tokenInfo = $tokenPacker->unpack(filter_input(INPUT_COOKIE, 'msz_auth'));
|
||||
elseif(filter_has_var(INPUT_COOKIE, 'msz_uid') && filter_has_var(INPUT_COOKIE, 'msz_sid')) {
|
||||
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((string)filter_input(INPUT_COOKIE, 'msz_uid', FILTER_SANITIZE_NUMBER_INT));
|
||||
$tokenBuilder->setSessionToken((string)filter_input(INPUT_COOKIE, 'msz_sid'));
|
||||
$tokenBuilder->setUserId($_COOKIE['msz_uid']);
|
||||
$tokenBuilder->setSessionToken($_COOKIE['msz_sid']);
|
||||
$tokenInfo = $tokenBuilder->toInfo();
|
||||
$tokenBuilder = null;
|
||||
} else
|
||||
|
@ -39,7 +42,7 @@ elseif(filter_has_var(INPUT_COOKIE, 'msz_uid') && filter_has_var(INPUT_COOKIE, '
|
|||
$userInfo = null;
|
||||
$sessionInfo = null;
|
||||
$userInfoReal = null;
|
||||
$remoteAddr = (string)filter_input(INPUT_SERVER, 'REMOTE_ADDR');
|
||||
$remoteAddr = $_SERVER['REMOTE_ADDR'];
|
||||
|
||||
if($tokenInfo->hasUserId && $tokenInfo->hasSessionToken) {
|
||||
$tokenBuilder = new AuthTokenBuilder($tokenInfo);
|
||||
|
@ -112,7 +115,52 @@ $router = $msz->createRouting($request);
|
|||
$msz->startTemplating();
|
||||
|
||||
if($msz->domainRoles->hasRole($request->getHeaderLine('Host'), 'main')) {
|
||||
$mszRequestPath = substr($request->path, 1);
|
||||
// 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);
|
||||
}
|
||||
|
||||
$mszRequestPath = substr($request->requestTarget, 1);
|
||||
$mszLegacyPathPrefix = Misuzu::PATH_PUBLIC_LEGACY . '/';
|
||||
$mszLegacyPath = $mszLegacyPathPrefix . $mszRequestPath;
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ final class AuthTokenCookie {
|
|||
$threeMonths->format('D, d M Y H:i:s e'),
|
||||
$threeMonths->getTimestamp() - $now->getTimestamp(),
|
||||
self::domain(),
|
||||
filter_has_var(INPUT_SERVER, 'HTTPS') ? ' Secure' : ''
|
||||
!empty($_SERVER['HTTPS']) ? ' Secure' : ''
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ final class AuthTokenCookie {
|
|||
header(sprintf(
|
||||
'Set-Cookie: msz_auth=; Expires=Wed, 31 Dec 1969 21:29:59 UTC; Max-Age=-9001; Domain=%s; Path=/; SameSite=Lax; HttpOnly;%s',
|
||||
self::domain(),
|
||||
filter_has_var(INPUT_SERVER, 'HTTPS') ? ' Secure' : ''
|
||||
!empty($_SERVER['HTTPS']) ? ' Secure' : ''
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,9 +36,9 @@ final class CSRF {
|
|||
if(self::$instance === null)
|
||||
return false;
|
||||
|
||||
$token = (string)filter_input(INPUT_POST, '_csrf');
|
||||
$token = isset($_POST['_csrf']) && is_string($_POST['_csrf']) ? $_POST['_csrf'] : '';
|
||||
if(empty($token))
|
||||
$token = (string)filter_input(INPUT_GET, 'csrf');
|
||||
$token = isset($_GET['csrf']) && is_string($_GET['csrf']) ? $_GET['csrf'] : '';
|
||||
|
||||
return self::$instance->verifyToken($token, $tolerance);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@ namespace Misuzu\Changelog;
|
|||
use ErrorException;
|
||||
use RuntimeException;
|
||||
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\Routes\{ExactRoute,PatternRoute};
|
||||
use Index\Syndication\FeedBuilder;
|
||||
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon};
|
||||
use Misuzu\{Pagination,SiteInfo,Template};
|
||||
|
@ -22,11 +23,11 @@ final class ChangelogRoutes implements RouteHandler, UrlSource {
|
|||
private CommentsContext $commentsCtx,
|
||||
) {}
|
||||
|
||||
#[HttpGet('/changelog')]
|
||||
#[ExactRoute('GET', '/changelog')]
|
||||
#[UrlFormat('changelog-index', '/changelog', ['date' => '<date>', 'user' => '<user>', 'tags' => '<tags>', 'p' => '<page>'])]
|
||||
public function getIndex(HttpResponseBuilder $response, HttpRequest $request): int|string {
|
||||
$filterDate = (string)$request->getParam('date');
|
||||
$filterUser = (string)$request->getParam('user', FILTER_SANITIZE_NUMBER_INT);
|
||||
$filterUser = (string)$request->getFilteredParam('user', FILTER_SANITIZE_NUMBER_INT);
|
||||
$filterTags = (string)$request->getParam('tags');
|
||||
|
||||
if(empty($filterDate))
|
||||
|
@ -96,7 +97,7 @@ final class ChangelogRoutes implements RouteHandler, UrlSource {
|
|||
]);
|
||||
}
|
||||
|
||||
#[HttpGet('/changelog/change/([0-9]+)')]
|
||||
#[PatternRoute('GET', '/changelog/change/([0-9]+)')]
|
||||
#[UrlFormat('changelog-change', '/changelog/change/<change>')]
|
||||
#[UrlFormat('changelog-change-comments', '/changelog/change/<change>', fragment: 'comments')]
|
||||
public function getChange(HttpResponseBuilder $response, HttpRequest $request, string $changeId): int|string {
|
||||
|
@ -120,7 +121,7 @@ final class ChangelogRoutes implements RouteHandler, UrlSource {
|
|||
]);
|
||||
}
|
||||
|
||||
#[HttpGet('/changelog.(xml|rss|atom)')]
|
||||
#[PatternRoute('GET', '/changelog.(xml|rss|atom)')]
|
||||
#[UrlFormat('changelog-feed', '/changelog.xml')]
|
||||
public function getFeed(HttpResponseBuilder $response): string {
|
||||
$response->setContentType('application/rss+xml; charset=utf-8');
|
||||
|
|
|
@ -4,7 +4,11 @@ namespace Misuzu\Comments;
|
|||
use RuntimeException;
|
||||
use Index\XArray;
|
||||
use Index\Http\{FormHttpContent,HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Routing\{HttpDelete,HttpGet,HttpMiddleware,HttpPatch,HttpPost,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Content\FormContent;
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\Filters\PrefixFilter;
|
||||
use Index\Http\Routing\Processors\Before;
|
||||
use Index\Http\Routing\Routes\{ExactRoute,PatternRoute};
|
||||
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon};
|
||||
use Misuzu\{CSRF,Perm};
|
||||
use Misuzu\Auth\AuthInfo;
|
||||
|
@ -186,7 +190,7 @@ class CommentsRoutes implements RouteHandler, UrlSource {
|
|||
}
|
||||
|
||||
/** @return void|array{error: array{name: string, text: string}} */
|
||||
#[HttpMiddleware('/comments')]
|
||||
#[PrefixFilter('/comments')]
|
||||
public function checkCsrf(HttpResponseBuilder $response, HttpRequest $request) {
|
||||
if(in_array($request->method, ['DELETE', 'PATCH', 'POST'])) {
|
||||
if(!$this->authInfo->loggedIn)
|
||||
|
@ -228,8 +232,8 @@ class CommentsRoutes implements RouteHandler, UrlSource {
|
|||
* posts: mixed[]
|
||||
* }|array{error: array{name: string, text: string}}
|
||||
*/
|
||||
#[HttpGet('/comments/categories/([A-Za-z0-9-]+)')]
|
||||
public function getCategory(HttpResponseBuilder $response, HttpRequest $request, string $categoryName): array {
|
||||
#[PatternRoute('GET', '/comments/categories/([A-Za-z0-9-]+)')]
|
||||
public function getCategory(HttpResponseBuilder $response, string $categoryName): array {
|
||||
try {
|
||||
$catInfo = $this->commentsCtx->categories->getCategory(name: $categoryName);
|
||||
} catch(RuntimeException $ex) {
|
||||
|
@ -290,11 +294,9 @@ class CommentsRoutes implements RouteHandler, UrlSource {
|
|||
* locked?: string|false,
|
||||
* }|array{error: array{name: string, text: string}}
|
||||
*/
|
||||
#[HttpPost('/comments/categories/([A-Za-z0-9-]+)')]
|
||||
public function patchCategory(HttpResponseBuilder $response, HttpRequest $request, string $categoryName): array {
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
return self::error($response, 400, 'comments:content', 'Provided content could not be understood.');
|
||||
|
||||
#[PatternRoute('POST', '/comments/categories/([A-Za-z0-9-]+)')]
|
||||
#[Before('input:urlencoded')]
|
||||
public function patchCategory(HttpResponseBuilder $response, FormContent $content, string $categoryName): array {
|
||||
try {
|
||||
$catInfo = $this->commentsCtx->categories->getCategory(name: $categoryName);
|
||||
} catch(RuntimeException $ex) {
|
||||
|
@ -304,11 +306,11 @@ class CommentsRoutes implements RouteHandler, UrlSource {
|
|||
$perms = $this->getGlobalPerms();
|
||||
$locked = null;
|
||||
|
||||
if($request->content->hasParam('lock')) {
|
||||
if($content->hasParam('lock')) {
|
||||
if(!$perms->check(Perm::G_COMMENTS_LOCK))
|
||||
return self::error($response, 403, 'comments:lock-not-allowed', 'You are not allowed to lock this comment section.');
|
||||
|
||||
$locked = !empty($request->content->getParam('lock'));
|
||||
$locked = !empty($content->getParam('lock'));
|
||||
}
|
||||
|
||||
$this->commentsCtx->categories->updateCategory(
|
||||
|
@ -332,34 +334,32 @@ class CommentsRoutes implements RouteHandler, UrlSource {
|
|||
/**
|
||||
* @return mixed[]|array{error: array{name: string, text: string}}
|
||||
*/
|
||||
#[HttpPost('/comments/posts')]
|
||||
public function postPost(HttpResponseBuilder $response, HttpRequest $request): array {
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
return self::error($response, 400, 'comments:content', 'Provided content could not be understood.');
|
||||
|
||||
#[ExactRoute('POST', '/comments/posts')]
|
||||
#[Before('input:multipart')]
|
||||
public function postPost(HttpResponseBuilder $response, FormContent $content): array {
|
||||
$perms = $this->getGlobalPerms();
|
||||
if(!$perms->check(Perm::G_COMMENTS_CREATE))
|
||||
return self::error($response, 403, 'comments:create-not-allowed', 'You are not allowed to post comments.');
|
||||
|
||||
if(!$request->content->hasParam('category') || !$request->content->hasParam('body'))
|
||||
if(!$content->hasParam('category') || !$content->hasParam('body'))
|
||||
return self::error($response, 400, 'comments:missing-fields', 'Required fields are not specified.');
|
||||
|
||||
$pinned = false;
|
||||
$body = preg_replace("/[\r\n]{2,}/", "\n", (string)$request->content->getParam('body'));
|
||||
$body = preg_replace("/[\r\n]{2,}/", "\n", (string)$content->getParam('body'));
|
||||
if(mb_strlen(mb_trim($body)) < 1)
|
||||
return self::error($response, 400, 'comments:body-too-short', 'Your comment must be longer.');
|
||||
if(mb_strlen($body) > 5000)
|
||||
return self::error($response, 400, 'comments:body-too-long', 'Your comment is too long.');
|
||||
|
||||
try {
|
||||
$catInfo = $this->commentsCtx->categories->getCategory(name: (string)$request->content->getParam('category'));
|
||||
$catInfo = $this->commentsCtx->categories->getCategory(name: (string)$content->getParam('category'));
|
||||
} catch(RuntimeException $ex) {
|
||||
return self::error($response, 404, 'comments:category-not-found', 'No comment section with that name exists.');
|
||||
}
|
||||
|
||||
if($request->content->hasParam('reply_to')) {
|
||||
if($content->hasParam('reply_to')) {
|
||||
try {
|
||||
$replyToInfo = $this->commentsCtx->posts->getPost((string)$request->content->getParam('reply_to'));
|
||||
$replyToInfo = $this->commentsCtx->posts->getPost((string)$content->getParam('reply_to'));
|
||||
if($replyToInfo->deleted)
|
||||
return self::error($response, 404, 'comments:parent-not-found', 'The comment you are trying to reply to does not exist.');
|
||||
} catch(RuntimeException $ex) {
|
||||
|
@ -368,13 +368,13 @@ class CommentsRoutes implements RouteHandler, UrlSource {
|
|||
} else
|
||||
$replyToInfo = null;
|
||||
|
||||
if($request->content->hasParam('pin')) {
|
||||
if($content->hasParam('pin')) {
|
||||
if(!$perms->check(Perm::G_COMMENTS_PIN) && $catInfo->ownerId !== $this->authInfo->userId)
|
||||
return self::error($response, 403, 'comments:pin-not-allowed', 'You are not allowed to pin comments.');
|
||||
if($replyToInfo !== null)
|
||||
return self::error($response, 400, 'comments:post-not-root', 'Replies cannot be pinned.');
|
||||
|
||||
$pinned = !empty($request->content->getParam('pin'));
|
||||
$pinned = !empty($content->getParam('pin'));
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -396,8 +396,8 @@ class CommentsRoutes implements RouteHandler, UrlSource {
|
|||
/**
|
||||
* @return mixed[]|array{error: array{name: string, text: string}}
|
||||
*/
|
||||
#[HttpGet('/comments/posts/([0-9]+)')]
|
||||
public function getPost(HttpResponseBuilder $response, HttpRequest $request, string $commentId): array {
|
||||
#[PatternRoute('GET', '/comments/posts/([0-9]+)')]
|
||||
public function getPost(HttpResponseBuilder $response, string $commentId): array {
|
||||
try {
|
||||
$postInfo = $this->commentsCtx->posts->getPost($commentId);
|
||||
$catInfo = $this->commentsCtx->categories->getCategory(postInfo: $postInfo);
|
||||
|
@ -421,8 +421,8 @@ class CommentsRoutes implements RouteHandler, UrlSource {
|
|||
/**
|
||||
* @return mixed[]|array{error: array{name: string, text: string}}
|
||||
*/
|
||||
#[HttpGet('/comments/posts/([0-9]+)/replies')]
|
||||
public function getPostReplies(HttpResponseBuilder $response, HttpRequest $request, string $commentId): array {
|
||||
#[PatternRoute('GET', '/comments/posts/([0-9]+)/replies')]
|
||||
public function getPostReplies(HttpResponseBuilder $response, string $commentId): array {
|
||||
try {
|
||||
$postInfo = $this->commentsCtx->posts->getPost($commentId);
|
||||
$catInfo = $this->commentsCtx->categories->getCategory(postInfo: $postInfo);
|
||||
|
@ -445,13 +445,9 @@ class CommentsRoutes implements RouteHandler, UrlSource {
|
|||
* edited?: string,
|
||||
* }|array{error: array{name: string, text: string}}
|
||||
*/
|
||||
#[HttpPost('/comments/posts/([0-9]+)')]
|
||||
// this should be HttpPatch but PHP doesn't parse into $_POST for PATCH...
|
||||
// fix this in the v3 router for index by just ignoring PHP's parsing altogether
|
||||
public function patchPost(HttpResponseBuilder $response, HttpRequest $request, string $commentId): array {
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
return self::error($response, 400, 'comments:content', 'Provided content could not be understood.');
|
||||
|
||||
#[PatternRoute('PATCH', '/comments/posts/([0-9]+)')]
|
||||
#[Before('input:multipart')]
|
||||
public function patchPost(HttpResponseBuilder $response, FormContent $content, string $commentId): array {
|
||||
try {
|
||||
$postInfo = $this->commentsCtx->posts->getPost($commentId);
|
||||
$catInfo = $this->commentsCtx->categories->getCategory(postInfo: $postInfo);
|
||||
|
@ -467,20 +463,20 @@ class CommentsRoutes implements RouteHandler, UrlSource {
|
|||
$pinned = null;
|
||||
$edited = false;
|
||||
|
||||
if($request->content->hasParam('pin')) {
|
||||
if($content->hasParam('pin')) {
|
||||
if(!$perms->check(Perm::G_COMMENTS_PIN) && $catInfo->ownerId !== $this->authInfo->userId)
|
||||
return self::error($response, 403, 'comments:pin-not-allowed', 'You are not allowed to pin comments.');
|
||||
if($postInfo->reply)
|
||||
return self::error($response, 400, 'comments:post-not-root', 'Replies cannot be pinned.');
|
||||
|
||||
$pinned = !empty($request->content->getParam('pin'));
|
||||
$pinned = !empty($content->getParam('pin'));
|
||||
}
|
||||
|
||||
if($request->content->hasParam('body')) {
|
||||
if($content->hasParam('body')) {
|
||||
if(!$perms->check(Perm::G_COMMENTS_EDIT_ANY) && !($perms->check(Perm::G_COMMENTS_EDIT_OWN) && $this->authInfo->userId === $postInfo->userId))
|
||||
return self::error($response, 403, 'comments:edit-not-allowed', 'You are not allowed to edit comments.');
|
||||
|
||||
$body = preg_replace("/[\r\n]{2,}/", "\n", (string)$request->content->getParam('body'));
|
||||
$body = preg_replace("/[\r\n]{2,}/", "\n", (string)$content->getParam('body'));
|
||||
if(mb_strlen(mb_trim($body)) < 1)
|
||||
return self::error($response, 400, 'comments:body-too-short', 'Your comment must be longer.');
|
||||
if(mb_strlen($body) > 5000)
|
||||
|
@ -518,8 +514,8 @@ class CommentsRoutes implements RouteHandler, UrlSource {
|
|||
/**
|
||||
* @return string|array{error: array{name: string, text: string}}
|
||||
*/
|
||||
#[HttpDelete('/comments/posts/([0-9]+)')]
|
||||
public function deletePost(HttpResponseBuilder $response, HttpRequest $request, string $commentId): array|string {
|
||||
#[PatternRoute('DELETE', '/comments/posts/([0-9]+)')]
|
||||
public function deletePost(HttpResponseBuilder $response, string $commentId): array|string {
|
||||
try {
|
||||
$postInfo = $this->commentsCtx->posts->getPost($commentId);
|
||||
if($postInfo->deleted)
|
||||
|
@ -547,8 +543,8 @@ class CommentsRoutes implements RouteHandler, UrlSource {
|
|||
/**
|
||||
* @return mixed[]|array{error: array{name: string, text: string}}
|
||||
*/
|
||||
#[HttpPost('/comments/posts/([0-9]+)/restore')]
|
||||
public function postPostRestore(HttpResponseBuilder $response, HttpRequest $request, string $commentId): array {
|
||||
#[PatternRoute('POST', '/comments/posts/([0-9]+)/restore')]
|
||||
public function postPostRestore(HttpResponseBuilder $response, string $commentId): array {
|
||||
if(!$this->getGlobalPerms()->check(Perm::G_COMMENTS_DELETE_ANY))
|
||||
return self::error($response, 403, 'comments:restore-not-allowed', 'You are not allowed to restore comments.');
|
||||
|
||||
|
@ -572,8 +568,8 @@ class CommentsRoutes implements RouteHandler, UrlSource {
|
|||
/**
|
||||
* @return mixed[]|array{error: array{name: string, text: string}}
|
||||
*/
|
||||
#[HttpPost('/comments/posts/([0-9]+)/nuke')]
|
||||
public function postPostNuke(HttpResponseBuilder $response, HttpRequest $request, string $commentId): array {
|
||||
#[PatternRoute('POST' ,'/comments/posts/([0-9]+)/nuke')]
|
||||
public function postPostNuke(HttpResponseBuilder $response, string $commentId): array {
|
||||
if(!$this->getGlobalPerms()->check(Perm::G_COMMENTS_DELETE_ANY))
|
||||
return self::error($response, 403, 'comments:nuke-not-allowed', 'You are not allowed to permanently delete comments.');
|
||||
|
||||
|
@ -601,12 +597,10 @@ class CommentsRoutes implements RouteHandler, UrlSource {
|
|||
* negative: int,
|
||||
* }|array{error: array{name: string, text: string}}
|
||||
*/
|
||||
#[HttpPost('/comments/posts/([0-9]+)/vote')]
|
||||
public function postPostVote(HttpResponseBuilder $response, HttpRequest $request, string $commentId): array {
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
return self::error($response, 400, 'comments:content', 'Provided content could not be understood.');
|
||||
|
||||
$vote = (int)$request->content->getParam('vote', FILTER_SANITIZE_NUMBER_INT);
|
||||
#[PatternRoute('POST', '/comments/posts/([0-9]+)/vote')]
|
||||
#[Before('input:urlencoded')]
|
||||
public function postPostVote(HttpResponseBuilder $response, FormContent $content, string $commentId): array {
|
||||
$vote = (int)$content->getFilteredParam('vote', FILTER_SANITIZE_NUMBER_INT);
|
||||
if($vote === 0)
|
||||
return self::error($response, 400, 'comments:vote', 'Could not process vote.');
|
||||
|
||||
|
@ -649,7 +643,7 @@ class CommentsRoutes implements RouteHandler, UrlSource {
|
|||
* negative: int,
|
||||
* }|array{error: array{name: string, text: string}}
|
||||
*/
|
||||
#[HttpDelete('/comments/posts/([0-9]+)/vote')]
|
||||
#[PatternRoute('DELETE', '/comments/posts/([0-9]+)/vote')]
|
||||
public function deletePostVote(HttpResponseBuilder $response, HttpRequest $request, string $commentId): array {
|
||||
if(!$this->getGlobalPerms()->check(Perm::G_COMMENTS_VOTE))
|
||||
return self::error($response, 403, 'comments:vote-not-allowed', 'You are not allowed to vote on comments.');
|
||||
|
|
|
@ -4,7 +4,8 @@ namespace Misuzu;
|
|||
use Index\Config\Config;
|
||||
use Index\Db\{DbBackends,DbConnection};
|
||||
use Index\Db\Migration\{DbMigrationManager,DbMigrationRepo,FsDbMigrationRepo};
|
||||
use Index\Http\Routing\{HttpMiddleware,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\Filters\PrefixFilter;
|
||||
|
||||
class DatabaseContext implements RouteHandler {
|
||||
use RouteHandlerCommon;
|
||||
|
@ -40,8 +41,8 @@ class DatabaseContext implements RouteHandler {
|
|||
}
|
||||
|
||||
/** @return void|int */
|
||||
#[HttpMiddleware('/')]
|
||||
public function middleware() {
|
||||
#[PrefixFilter('/')]
|
||||
public function filterMigrate() {
|
||||
if(is_file($this->getMigrateLockPath()))
|
||||
return 503;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@ namespace Misuzu\Forum;
|
|||
use stdClass;
|
||||
use RuntimeException;
|
||||
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Routing\{HttpGet,HttpPost,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\Routes\{ExactRoute,PatternRoute};
|
||||
use Index\Urls\{UrlFormat,UrlSource,UrlSourceCommon};
|
||||
use Misuzu\{CSRF,Pagination,Perm,Template};
|
||||
use Misuzu\Auth\AuthInfo;
|
||||
|
@ -19,7 +20,7 @@ class ForumCategoriesRoutes implements RouteHandler, UrlSource {
|
|||
private AuthInfo $authInfo,
|
||||
) {}
|
||||
|
||||
#[HttpGet('/forum')]
|
||||
#[ExactRoute('GET', '/forum')]
|
||||
#[UrlFormat('forum-index', '/forum')]
|
||||
#[UrlFormat('forum-category-root', '/forum', fragment: '<forum>')]
|
||||
public function getIndex(): string {
|
||||
|
@ -170,7 +171,7 @@ class ForumCategoriesRoutes implements RouteHandler, UrlSource {
|
|||
]);
|
||||
}
|
||||
|
||||
#[HttpGet('/forum/([0-9]+)')]
|
||||
#[PatternRoute('GET', '/forum/([0-9]+)')]
|
||||
#[UrlFormat('forum-category', '/forum/<forum>', ['page' => '<page>'])]
|
||||
public function getCategory(HttpResponseBuilder $response, HttpRequest $request, string $catId): mixed {
|
||||
try {
|
||||
|
@ -328,7 +329,7 @@ class ForumCategoriesRoutes implements RouteHandler, UrlSource {
|
|||
]);
|
||||
}
|
||||
|
||||
#[HttpPost('/forum/mark-as-read')]
|
||||
#[ExactRoute('POST', '/forum/mark-as-read')]
|
||||
#[UrlFormat('forum-mark-as-read', '/forum/mark-as-read', ['cat' => '<category>', 'rec' => '<recursive>'])]
|
||||
public function postMarkAsRead(HttpResponseBuilder $response, HttpRequest $request): mixed {
|
||||
if(!$this->authInfo->loggedIn)
|
||||
|
@ -338,7 +339,7 @@ class ForumCategoriesRoutes implements RouteHandler, UrlSource {
|
|||
return 403;
|
||||
$response->setHeader('X-CSRF-Token', CSRF::token());
|
||||
|
||||
$catId = (string)$request->getParam('cat', FILTER_SANITIZE_NUMBER_INT);
|
||||
$catId = (string)$request->getFilteredParam('cat', FILTER_SANITIZE_NUMBER_INT);
|
||||
$recursive = !empty($request->getParam('rec'));
|
||||
|
||||
if($catId === '') {
|
||||
|
|
|
@ -3,7 +3,8 @@ namespace Misuzu\Forum;
|
|||
|
||||
use RuntimeException;
|
||||
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Routing\{HttpDelete,HttpGet,HttpPost,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\Routes\PatternRoute;
|
||||
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon};
|
||||
use Misuzu\{CSRF,Perm};
|
||||
use Misuzu\AuditLog\AuditLogData;
|
||||
|
@ -21,7 +22,7 @@ class ForumPostsRoutes implements RouteHandler, UrlSource {
|
|||
private AuthInfo $authInfo,
|
||||
) {}
|
||||
|
||||
#[HttpGet('/forum/posts/([0-9]+)')]
|
||||
#[PatternRoute('GET', '/forum/posts/([0-9]+)')]
|
||||
#[UrlFormat('forum-post', '/forum/posts/<post>')]
|
||||
public function getPost(HttpResponseBuilder $response, HttpRequest $request, string $postId): mixed {
|
||||
try {
|
||||
|
@ -54,7 +55,7 @@ class ForumPostsRoutes implements RouteHandler, UrlSource {
|
|||
return 302;
|
||||
}
|
||||
|
||||
#[HttpDelete('/forum/posts/([0-9]+)')]
|
||||
#[PatternRoute('DELETE', '/forum/posts/([0-9]+)')]
|
||||
#[UrlFormat('forum-post-delete', '/forum/posts/<post>')]
|
||||
public function deletePost(HttpResponseBuilder $response, HttpRequest $request, string $postId): mixed {
|
||||
if(!$this->authInfo->loggedIn)
|
||||
|
@ -189,7 +190,7 @@ class ForumPostsRoutes implements RouteHandler, UrlSource {
|
|||
return 204;
|
||||
}
|
||||
|
||||
#[HttpPost('/forum/posts/([0-9]+)/nuke')]
|
||||
#[PatternRoute('POST', '/forum/posts/([0-9]+)/nuke')]
|
||||
#[UrlFormat('forum-post-nuke', '/forum/posts/<post>/nuke')]
|
||||
public function postPostNuke(HttpResponseBuilder $response, HttpRequest $request, string $postId): mixed {
|
||||
if(!$this->authInfo->loggedIn)
|
||||
|
@ -269,7 +270,7 @@ class ForumPostsRoutes implements RouteHandler, UrlSource {
|
|||
return 204;
|
||||
}
|
||||
|
||||
#[HttpPost('/forum/posts/([0-9]+)/restore')]
|
||||
#[PatternRoute('POST', '/forum/posts/([0-9]+)/restore')]
|
||||
#[UrlFormat('forum-post-restore', '/forum/posts/<post>/restore')]
|
||||
public function postPostRestore(HttpResponseBuilder $response, HttpRequest $request, string $postId): mixed {
|
||||
if(!$this->authInfo->loggedIn)
|
||||
|
|
|
@ -4,7 +4,9 @@ namespace Misuzu\Forum;
|
|||
use stdClass;
|
||||
use RuntimeException;
|
||||
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Routing\{HttpDelete,HttpGet,HttpPost,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Content\FormContent;
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\Routes\PatternRoute;
|
||||
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon};
|
||||
use Misuzu\{CSRF,Pagination,Perm,Template};
|
||||
use Misuzu\AuditLog\AuditLogData;
|
||||
|
@ -21,7 +23,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
|
|||
private AuthInfo $authInfo,
|
||||
) {}
|
||||
|
||||
#[HttpGet('/forum/topics/([0-9]+)')]
|
||||
#[PatternRoute('GET', '/forum/topics/([0-9]+)')]
|
||||
#[UrlFormat('forum-topic', '/forum/topics/<topic>', ['page' => '<page>'], '<topic_fragment>')]
|
||||
public function getTopic(HttpResponseBuilder $response, HttpRequest $request, string $topicId): mixed {
|
||||
$isNuked = $deleted = $canDeleteAny = false;
|
||||
|
@ -152,7 +154,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
|
|||
]);
|
||||
}
|
||||
|
||||
#[HttpDelete('/forum/topics/([0-9]+)')]
|
||||
#[PatternRoute('DELETE', '/forum/topics/([0-9]+)')]
|
||||
#[UrlFormat('forum-topic-delete', '/forum/topics/<topic>')]
|
||||
public function deleteTopic(HttpResponseBuilder $response, HttpRequest $request, string $topicId): mixed {
|
||||
if(!$this->authInfo->loggedIn)
|
||||
|
@ -272,7 +274,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
|
|||
return 204;
|
||||
}
|
||||
|
||||
#[HttpPost('/forum/topics/([0-9]+)/restore')]
|
||||
#[PatternRoute('POST', '/forum/topics/([0-9]+)/restore')]
|
||||
#[UrlFormat('forum-topic-restore', '/forum/topics/<topic>/restore')]
|
||||
public function postTopicRestore(HttpResponseBuilder $response, HttpRequest $request, string $topicId): mixed {
|
||||
if(!$this->authInfo->loggedIn)
|
||||
|
@ -352,7 +354,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
|
|||
return 204;
|
||||
}
|
||||
|
||||
#[HttpPost('/forum/topics/([0-9]+)/nuke')]
|
||||
#[PatternRoute('POST', '/forum/topics/([0-9]+)/nuke')]
|
||||
#[UrlFormat('forum-topic-nuke', '/forum/topics/<topic>/nuke')]
|
||||
public function postTopicNuke(HttpResponseBuilder $response, HttpRequest $request, string $topicId): mixed {
|
||||
if(!$this->authInfo->loggedIn)
|
||||
|
@ -432,7 +434,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
|
|||
return 204;
|
||||
}
|
||||
|
||||
#[HttpPost('/forum/topics/([0-9]+)/bump')]
|
||||
#[PatternRoute('POST', '/forum/topics/([0-9]+)/bump')]
|
||||
#[UrlFormat('forum-topic-bump', '/forum/topics/<topic>/bump')]
|
||||
public function postTopicBump(HttpResponseBuilder $response, HttpRequest $request, string $topicId): mixed {
|
||||
if(!$this->authInfo->loggedIn)
|
||||
|
@ -512,7 +514,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
|
|||
return 204;
|
||||
}
|
||||
|
||||
#[HttpPost('/forum/topics/([0-9]+)/lock')]
|
||||
#[PatternRoute('POST', '/forum/topics/([0-9]+)/lock')]
|
||||
#[UrlFormat('forum-topic-lock', '/forum/topics/<topic>/lock')]
|
||||
public function postTopicLock(HttpResponseBuilder $response, HttpRequest $request, string $topicId): mixed {
|
||||
if(!$this->authInfo->loggedIn)
|
||||
|
@ -602,7 +604,7 @@ class ForumTopicsRoutes implements RouteHandler, UrlSource {
|
|||
return 204;
|
||||
}
|
||||
|
||||
#[HttpPost('/forum/topics/([0-9]+)/unlock')]
|
||||
#[PatternRoute('POST', '/forum/topics/([0-9]+)/unlock')]
|
||||
#[UrlFormat('forum-topic-unlock', '/forum/topics/<topic>/unlock')]
|
||||
public function postTopicUnlock(HttpResponseBuilder $response, HttpRequest $request, string $topicId): mixed {
|
||||
if(!$this->authInfo->loggedIn)
|
||||
|
|
|
@ -7,7 +7,8 @@ use Index\Config\Config;
|
|||
use Index\Colour\Colour;
|
||||
use Index\Db\{DbConnection,DbTools};
|
||||
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\Routes\ExactRoute;
|
||||
use Index\Urls\{UrlFormat,UrlSource,UrlSourceCommon};
|
||||
use Misuzu\{Pagination,SiteInfo,Template};
|
||||
use Misuzu\Auth\AuthInfo;
|
||||
|
@ -198,12 +199,10 @@ class HomeRoutes implements RouteHandler, UrlSource {
|
|||
return $topics;
|
||||
}
|
||||
|
||||
#[HttpGet('/')]
|
||||
#[ExactRoute('GET', '/')]
|
||||
#[UrlFormat('index', '/')]
|
||||
public function getIndex(HttpResponseBuilder $response, HttpRequest $request): string {
|
||||
return $this->authInfo->loggedIn
|
||||
? $this->getHome()
|
||||
: $this->getLanding($response, $request);
|
||||
public function getIndex(): string {
|
||||
return $this->authInfo->loggedIn ? $this->getHome() : $this->getLanding();
|
||||
}
|
||||
|
||||
public function getHome(): string {
|
||||
|
@ -246,8 +245,8 @@ class HomeRoutes implements RouteHandler, UrlSource {
|
|||
]);
|
||||
}
|
||||
|
||||
#[HttpGet('/_landing')]
|
||||
public function getLanding(HttpResponseBuilder $response, HttpRequest $request): string {
|
||||
#[ExactRoute('GET', '/_landing')]
|
||||
public function getLanding(): string {
|
||||
$config = $this->config->getValues([
|
||||
['social.embed_linked:b'],
|
||||
['landing.forum_categories:a'],
|
||||
|
|
|
@ -3,7 +3,8 @@ namespace Misuzu\Info;
|
|||
|
||||
use Index\Index;
|
||||
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\Routes\{ExactRoute,PatternRoute};
|
||||
use Index\Urls\{UrlFormat,UrlSource,UrlSourceCommon};
|
||||
use Misuzu\{Misuzu,Template};
|
||||
use Misuzu\Parsers\{Parsers,TextFormat};
|
||||
|
@ -23,13 +24,13 @@ class InfoRoutes implements RouteHandler, UrlSource {
|
|||
'rpcii' => 'RPCii » %s',
|
||||
];
|
||||
|
||||
#[HttpGet('/info')]
|
||||
#[ExactRoute('GET', '/info')]
|
||||
#[UrlFormat('info-index', '/info')]
|
||||
public function getIndex(): string {
|
||||
return Template::renderRaw('info.index');
|
||||
}
|
||||
|
||||
#[HttpGet('/info/([A-Za-z0-9_]+)')]
|
||||
#[PatternRoute('GET', '/info/([A-Za-z0-9_]+)')]
|
||||
#[UrlFormat('info', '/info/<title>')]
|
||||
#[UrlFormat('info-doc', '/info/<title>')]
|
||||
public function getDocsPage(HttpResponseBuilder $response, HttpRequest $request, string $name): string {
|
||||
|
@ -76,7 +77,7 @@ class InfoRoutes implements RouteHandler, UrlSource {
|
|||
return '';
|
||||
}
|
||||
|
||||
#[HttpGet('/info/([A-Za-z0-9_]+)/([A-Za-z0-9_]+)')]
|
||||
#[PatternRoute('GET', '/info/([A-Za-z0-9_]+)/([A-Za-z0-9_]+)')]
|
||||
#[UrlFormat('info-project-doc', '/info/<project>/<title>')]
|
||||
public function getProjectPage(HttpResponseBuilder $response, HttpRequest $request, string $project, string $name): int|string {
|
||||
if(!array_key_exists($project, self::PROJECT_PATHS))
|
||||
|
|
|
@ -3,6 +3,7 @@ namespace Misuzu;
|
|||
|
||||
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\Routes\{ExactRoute,PatternRoute};
|
||||
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource};
|
||||
|
||||
class LegacyRoutes implements RouteHandler, UrlSource {
|
||||
|
@ -110,126 +111,123 @@ class LegacyRoutes implements RouteHandler, UrlSource {
|
|||
$urls->register('manage-role', '/manage/users/role.php', ['r' => '<role>']);
|
||||
}
|
||||
|
||||
#[HttpGet('/index.php')]
|
||||
#[ExactRoute('GET', '/index.php')]
|
||||
public function getIndexPHP(HttpResponseBuilder $response): void {
|
||||
$response->redirect($this->urls->format('index'), true);
|
||||
}
|
||||
|
||||
#[HttpGet('/info.php')]
|
||||
#[ExactRoute('GET', '/info.php')]
|
||||
public function getInfoPHP(HttpResponseBuilder $response): void {
|
||||
$response->redirect($this->urls->format('info'), true);
|
||||
}
|
||||
|
||||
#[HttpGet('/info.php/([A-Za-z0-9_]+)')]
|
||||
#[PatternRoute('GET', '/info.php/([A-Za-z0-9_]+)')]
|
||||
public function getInfoDocsPHP(HttpResponseBuilder $response, HttpRequest $request, string $name): void {
|
||||
$response->redirect($this->urls->format('info', ['title' => $name]), true);
|
||||
}
|
||||
|
||||
#[HttpGet('/info.php/([A-Za-z0-9_]+)/([A-Za-z0-9_]+)')]
|
||||
#[PatternRoute('GET', '/info.php/([A-Za-z0-9_]+)/([A-Za-z0-9_]+)')]
|
||||
public function getInfoProjectPHP(HttpResponseBuilder $response, HttpRequest $request, string $project, string $name): void {
|
||||
$response->redirect($this->urls->format('info', ['title' => $project . '/' . $name]), true);
|
||||
}
|
||||
|
||||
#[HttpGet('/news.php')]
|
||||
#[ExactRoute('GET', '/news.php')]
|
||||
public function getNewsPHP(HttpResponseBuilder $response, HttpRequest $request): void {
|
||||
$postId = (int)($request->getParam('n', FILTER_SANITIZE_NUMBER_INT) ?? $request->getParam('p', FILTER_SANITIZE_NUMBER_INT));
|
||||
$postId = (int)($request->getFilteredParam('n', FILTER_SANITIZE_NUMBER_INT) ?? $request->getFilteredParam('p', FILTER_SANITIZE_NUMBER_INT));
|
||||
|
||||
if($postId > 0)
|
||||
$location = $this->urls->format('news-post', ['post' => $postId]);
|
||||
else {
|
||||
$catId = (int)$request->getParam('c', FILTER_SANITIZE_NUMBER_INT);
|
||||
$pageId = $request->getParam('page', FILTER_SANITIZE_NUMBER_INT);
|
||||
$catId = (int)$request->getFilteredParam('c', FILTER_SANITIZE_NUMBER_INT);
|
||||
$pageId = $request->getFilteredParam('page', FILTER_SANITIZE_NUMBER_INT);
|
||||
$location = $this->urls->format($catId > 0 ? 'news-category' : 'news-index', ['category' => $catId, 'page' => $pageId]);
|
||||
}
|
||||
|
||||
$response->redirect($location, true);
|
||||
}
|
||||
|
||||
#[HttpGet('/news/index.php')]
|
||||
#[ExactRoute('GET', '/news/index.php')]
|
||||
public function getNewsIndexPHP(HttpResponseBuilder $response, HttpRequest $request): void {
|
||||
$response->redirect($this->urls->format('news-index', [
|
||||
'page' => $request->getParam('page', FILTER_SANITIZE_NUMBER_INT),
|
||||
'page' => $request->getFilteredParam('page', FILTER_SANITIZE_NUMBER_INT),
|
||||
]), true);
|
||||
}
|
||||
|
||||
#[HttpGet('/news/category.php')]
|
||||
#[ExactRoute('GET', '/news/category.php')]
|
||||
public function getNewsCategoryPHP(HttpResponseBuilder $response, HttpRequest $request): void {
|
||||
$response->redirect($this->urls->format('news-category', [
|
||||
'category' => $request->getParam('c', FILTER_SANITIZE_NUMBER_INT),
|
||||
'page' => $request->getParam('p', FILTER_SANITIZE_NUMBER_INT),
|
||||
'category' => $request->getFilteredParam('c', FILTER_SANITIZE_NUMBER_INT),
|
||||
'page' => $request->getFilteredParam('p', FILTER_SANITIZE_NUMBER_INT),
|
||||
]), true);
|
||||
}
|
||||
|
||||
#[HttpGet('/news/post.php')]
|
||||
#[ExactRoute('GET', '/news/post.php')]
|
||||
public function getNewsPostPHP(HttpResponseBuilder $response, HttpRequest $request): void {
|
||||
$response->redirect($this->urls->format('news-post', [
|
||||
'post' => $request->getParam('p', FILTER_SANITIZE_NUMBER_INT),
|
||||
'post' => $request->getFilteredParam('p', FILTER_SANITIZE_NUMBER_INT),
|
||||
]), true);
|
||||
}
|
||||
|
||||
#[HttpGet('/news.php/rss')]
|
||||
#[HttpGet('/news.php/atom')]
|
||||
#[HttpGet('/news/feed.php')]
|
||||
#[HttpGet('/news/feed.php/rss')]
|
||||
#[HttpGet('/news/feed.php/atom')]
|
||||
#[PatternRoute('GET', '/news.php/(?:atom|rss)')]
|
||||
#[PatternRoute('GET', '/news/feed.php(?:/(?:atom|rss)?)?')]
|
||||
public function getNewsFeedPHP(HttpResponseBuilder $response, HttpRequest $request): void {
|
||||
$catId = (int)$request->getParam('c', FILTER_SANITIZE_NUMBER_INT);
|
||||
$catId = (int)$request->getFilteredParam('c', FILTER_SANITIZE_NUMBER_INT);
|
||||
$response->redirect($this->urls->format(
|
||||
$catId > 0 ? 'news-category-feed' : 'news-feed',
|
||||
['category' => $catId]
|
||||
), true);
|
||||
}
|
||||
|
||||
#[HttpGet('/forum/index.php')]
|
||||
#[ExactRoute('GET', '/forum/index.php')]
|
||||
public function getForumIndexPHP(HttpResponseBuilder $response, HttpRequest $request): void {
|
||||
$response->redirect($this->urls->format('forum-index'), true);
|
||||
}
|
||||
|
||||
#[HttpGet('/forum/forum.php')]
|
||||
#[ExactRoute('GET', '/forum/forum.php')]
|
||||
public function getForumForumPHP(HttpResponseBuilder $response, HttpRequest $request): void {
|
||||
$response->redirect($this->urls->format('forum-category', [
|
||||
'forum' => $request->getParam('f', FILTER_SANITIZE_NUMBER_INT),
|
||||
'page' => $request->getParam('p', FILTER_SANITIZE_NUMBER_INT),
|
||||
'forum' => $request->getFilteredParam('f', FILTER_SANITIZE_NUMBER_INT),
|
||||
'page' => $request->getFilteredParam('p', FILTER_SANITIZE_NUMBER_INT),
|
||||
]), true);
|
||||
}
|
||||
|
||||
#[HttpGet('/forum/topic.php')]
|
||||
#[ExactRoute('GET', '/forum/topic.php')]
|
||||
public function getForumTopicPHP(HttpResponseBuilder $response, HttpRequest $request): void {
|
||||
if($request->hasParam('p'))
|
||||
$response->redirect($this->urls->format('forum-post', [
|
||||
'post' => $request->getParam('p', FILTER_SANITIZE_NUMBER_INT),
|
||||
'post' => $request->getFilteredParam('p', FILTER_SANITIZE_NUMBER_INT),
|
||||
]), true);
|
||||
else
|
||||
$response->redirect($this->urls->format('forum-topic', [
|
||||
'topic' => $request->getParam('t', FILTER_SANITIZE_NUMBER_INT),
|
||||
'page' => $request->getParam('page', FILTER_SANITIZE_NUMBER_INT),
|
||||
'topic' => $request->getFilteredParam('t', FILTER_SANITIZE_NUMBER_INT),
|
||||
'page' => $request->getFilteredParam('page', FILTER_SANITIZE_NUMBER_INT),
|
||||
]), true);
|
||||
}
|
||||
|
||||
#[HttpGet('/forum/post.php')]
|
||||
#[ExactRoute('GET', '/forum/post.php')]
|
||||
public function getForumPostPHP(HttpResponseBuilder $response, HttpRequest $request): void {
|
||||
$response->redirect($this->urls->format('forum-post', [
|
||||
'post' => $request->getParam('p', FILTER_SANITIZE_NUMBER_INT),
|
||||
'post' => $request->getFilteredParam('p', FILTER_SANITIZE_NUMBER_INT),
|
||||
]), true);
|
||||
}
|
||||
|
||||
#[HttpGet('/changelog.php')]
|
||||
#[ExactRoute('GET', '/changelog.php')]
|
||||
public function getChangelogPHP(HttpResponseBuilder $response, HttpRequest $request): void {
|
||||
$changeId = $request->getParam('c', FILTER_SANITIZE_NUMBER_INT);
|
||||
$changeId = $request->getFilteredParam('c', FILTER_SANITIZE_NUMBER_INT);
|
||||
if($changeId) {
|
||||
$response->redirect($this->urls->format('changelog-change', ['change' => $changeId]), true);
|
||||
return;
|
||||
}
|
||||
|
||||
$response->redirect($this->urls->format('changelog-index', [
|
||||
'date' => $request->getParam('d'),
|
||||
'user' => $request->getParam('u', FILTER_SANITIZE_NUMBER_INT),
|
||||
'date' => $request->getFilteredParam('d'),
|
||||
'user' => $request->getFilteredParam('u', FILTER_SANITIZE_NUMBER_INT),
|
||||
]), true);
|
||||
}
|
||||
|
||||
#[HttpGet('/auth.php')]
|
||||
#[ExactRoute('GET', '/auth.php')]
|
||||
public function getAuthPHP(HttpResponseBuilder $response, HttpRequest $request): void {
|
||||
$response->redirect($this->urls->format(match($request->getParam('m')) {
|
||||
$response->redirect($this->urls->format(match($request->getFilteredParam('m')) {
|
||||
'logout' => 'auth-logout',
|
||||
'reset' => 'auth-reset',
|
||||
'forgot' => 'auth-forgot',
|
||||
|
@ -238,44 +236,44 @@ class LegacyRoutes implements RouteHandler, UrlSource {
|
|||
}), true);
|
||||
}
|
||||
|
||||
#[HttpGet('/auth')]
|
||||
#[HttpGet('/auth/index.php')]
|
||||
#[ExactRoute('GET', '/auth')]
|
||||
#[ExactRoute('GET', '/auth/index.php')]
|
||||
public function getAuthIndexPHP(HttpResponseBuilder $response, HttpRequest $request): void {
|
||||
$response->redirect($this->urls->format('auth-login'), true);
|
||||
}
|
||||
|
||||
#[HttpGet('/settings.php')]
|
||||
#[ExactRoute('GET', '/settings.php')]
|
||||
public function getSettingsPHP(HttpResponseBuilder $response): void {
|
||||
$response->redirect($this->urls->format('settings-index'), true);
|
||||
}
|
||||
|
||||
#[HttpGet('/settings')]
|
||||
#[ExactRoute('GET', '/settings')]
|
||||
public function getSettingsIndex(HttpResponseBuilder $response): void {
|
||||
$response->redirect($this->urls->format('settings-account'));
|
||||
}
|
||||
|
||||
#[HttpGet('/settings/index.php')]
|
||||
#[ExactRoute('GET', '/settings/index.php')]
|
||||
public function getSettingsIndexPHP(HttpResponseBuilder $response): void {
|
||||
$response->redirect($this->urls->format('settings-account'), true);
|
||||
}
|
||||
|
||||
#[HttpGet('/manage')]
|
||||
#[ExactRoute('GET', '/manage')]
|
||||
#[UrlFormat('manage-index', '/manage')]
|
||||
public function getManageIndex(HttpResponseBuilder $response): void {
|
||||
$response->redirect($this->urls->format('manage-general-overview'));
|
||||
}
|
||||
|
||||
#[HttpGet('/manage/index.php')]
|
||||
#[ExactRoute('GET', '/manage/index.php')]
|
||||
public function getManageIndexPHP(HttpResponseBuilder $response): void {
|
||||
$response->redirect($this->urls->format('manage-general-overview'), true);
|
||||
}
|
||||
|
||||
#[HttpGet('/manage/news')]
|
||||
#[ExactRoute('GET', '/manage/news')]
|
||||
public function getManageNewsIndex(HttpResponseBuilder $response): void {
|
||||
$response->redirect($this->urls->format('manage-news-categories'));
|
||||
}
|
||||
|
||||
#[HttpGet('/manage/news/index.php')]
|
||||
#[ExactRoute('GET', '/manage/news/index.php')]
|
||||
public function getManageNewsIndexPHP(HttpResponseBuilder $response): void {
|
||||
$response->redirect($this->urls->format('manage-news-categories'), true);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,11 @@ use Index\XString;
|
|||
use Index\Config\Config;
|
||||
use Index\Colour\Colour;
|
||||
use Index\Http\{FormHttpContent,HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Routing\{HttpGet,HttpMiddleware,HttpPost,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Content\FormContent;
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\Filters\PrefixFilter;
|
||||
use Index\Http\Routing\Processors\{After,Before};
|
||||
use Index\Http\Routing\Routes\{ExactRoute,PatternRoute};
|
||||
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon};
|
||||
use Misuzu\{CSRF,Misuzu,Pagination,Perm,Template};
|
||||
use Misuzu\Auth\AuthInfo;
|
||||
|
@ -38,7 +42,7 @@ class MessagesRoutes implements RouteHandler, UrlSource {
|
|||
private bool $canSendMessages;
|
||||
|
||||
/** @return void|int|array{error: array{name: string, text: string}} */
|
||||
#[HttpMiddleware('/messages')]
|
||||
#[PrefixFilter('/messages')]
|
||||
public function checkAccess(HttpResponseBuilder $response, HttpRequest $request) {
|
||||
// should probably be a permission or something too
|
||||
if(!$this->authInfo->loggedIn)
|
||||
|
@ -56,9 +60,6 @@ class MessagesRoutes implements RouteHandler, UrlSource {
|
|||
&& !$this->usersCtx->hasActiveBan($this->authInfo->userInfo);
|
||||
|
||||
if($request->method === 'POST') {
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
return 400;
|
||||
|
||||
if(!CSRF::validate($request->getHeaderLine('x-csrf-token')))
|
||||
return [
|
||||
'error' => [
|
||||
|
@ -92,9 +93,9 @@ class MessagesRoutes implements RouteHandler, UrlSource {
|
|||
return $message;
|
||||
}
|
||||
|
||||
#[HttpGet('/messages')]
|
||||
#[ExactRoute('GET', '/messages')]
|
||||
#[UrlFormat('messages-index', '/messages', ['folder' => '<folder>', 'page' => '<page>'])]
|
||||
public function getIndex(HttpResponseBuilder $response, HttpRequest $request, string $folderName = ''): int|string {
|
||||
public function getIndex(HttpRequest $request, string $folderName = ''): int|string {
|
||||
$folderName = (string)$request->getParam('folder');
|
||||
if($folderName === '')
|
||||
$folderName = 'inbox';
|
||||
|
@ -146,7 +147,7 @@ class MessagesRoutes implements RouteHandler, UrlSource {
|
|||
}
|
||||
|
||||
/** @return array{unread: int} */
|
||||
#[HttpGet('/messages/stats')]
|
||||
#[ExactRoute('GET', '/messages/stats')]
|
||||
#[UrlFormat('messages-stats', '/messages/stats')]
|
||||
public function getStats(): array {
|
||||
$selfInfo = $this->authInfo->userInfo;
|
||||
|
@ -171,16 +172,14 @@ class MessagesRoutes implements RouteHandler, UrlSource {
|
|||
* avatar: string
|
||||
* }
|
||||
*/
|
||||
#[HttpPost('/messages/recipient')]
|
||||
#[ExactRoute('POST', '/messages/recipient')]
|
||||
#[Before('input:urlencoded')]
|
||||
#[UrlFormat('messages-recipient', '/messages/recipient')]
|
||||
public function postRecipient(HttpResponseBuilder $response, HttpRequest $request): int|array {
|
||||
public function postRecipient(FormContent $content): int|array {
|
||||
if(!$this->canSendMessages)
|
||||
return 403;
|
||||
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
return 400;
|
||||
|
||||
$name = trim((string)$request->content->getParam('name'));
|
||||
$name = trim((string)$content->getParam('name'));
|
||||
|
||||
// flappy hacks
|
||||
if(str_starts_with(mb_strtolower($name), 'flappyzor'))
|
||||
|
@ -211,9 +210,9 @@ class MessagesRoutes implements RouteHandler, UrlSource {
|
|||
];
|
||||
}
|
||||
|
||||
#[HttpGet('/messages/compose')]
|
||||
#[ExactRoute('GET', '/messages/compose')]
|
||||
#[UrlFormat('messages-compose', '/messages/compose', ['recipient' => '<recipient>'])]
|
||||
public function getEditor(HttpResponseBuilder $response, HttpRequest $request): int|string {
|
||||
public function getEditor(HttpRequest $request): int|string {
|
||||
if(!$this->canSendMessages)
|
||||
return 403;
|
||||
|
||||
|
@ -222,9 +221,9 @@ class MessagesRoutes implements RouteHandler, UrlSource {
|
|||
]);
|
||||
}
|
||||
|
||||
#[HttpGet('/messages/([A-Za-z0-9]+)')]
|
||||
#[PatternRoute('GET', '/messages/([A-Za-z0-9]+)')]
|
||||
#[UrlFormat('messages-view', '/messages/<message>')]
|
||||
public function getView(HttpResponseBuilder $response, HttpRequest $request, string $messageId): int|string {
|
||||
public function getView(string $messageId): int|string {
|
||||
if(strlen($messageId) !== 8)
|
||||
return 404;
|
||||
|
||||
|
@ -355,21 +354,19 @@ class MessagesRoutes implements RouteHandler, UrlSource {
|
|||
}
|
||||
|
||||
/** @return int|array{error: array{name: string, text: string}}|array{id: string, url: string} */
|
||||
#[HttpPost('/messages/create')]
|
||||
#[ExactRoute('POST', '/messages/create')]
|
||||
#[Before('input:multipart')]
|
||||
#[UrlFormat('messages-create', '/messages/create')]
|
||||
public function postCreate(HttpResponseBuilder $response, HttpRequest $request): int|array {
|
||||
public function postCreate(FormContent $content): int|array {
|
||||
if(!$this->canSendMessages)
|
||||
return 403;
|
||||
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
return 400;
|
||||
|
||||
$recipient = (string)$request->content->getParam('recipient');
|
||||
$replyTo = (string)$request->content->getParam('reply');
|
||||
$title = (string)$request->content->getParam('title');
|
||||
$body = (string)$request->content->getParam('body');
|
||||
$format = TextFormat::tryFrom((string)$request->content->getParam('format'));
|
||||
$draft = !empty($request->content->getParam('draft'));
|
||||
$recipient = (string)$content->getParam('recipient');
|
||||
$replyTo = (string)$content->getParam('reply');
|
||||
$title = (string)$content->getParam('title');
|
||||
$body = (string)$content->getParam('body');
|
||||
$format = TextFormat::tryFrom((string)$content->getParam('format'));
|
||||
$draft = !empty($content->getParam('draft'));
|
||||
|
||||
$error = $this->checkMessageFields($title, $body, $format);
|
||||
if($error !== null)
|
||||
|
@ -459,19 +456,17 @@ class MessagesRoutes implements RouteHandler, UrlSource {
|
|||
}
|
||||
|
||||
/** @return int|array{error: array{name: string, text: string}}|array{id: string, url: string} */
|
||||
#[HttpPost('/messages/([A-Za-z0-9]+)')]
|
||||
#[PatternRoute('PATCH', '/messages/([A-Za-z0-9]+)')]
|
||||
#[Before('input:multipart')]
|
||||
#[UrlFormat('messages-update', '/messages/<message>')]
|
||||
public function postUpdate(HttpResponseBuilder $response, HttpRequest $request, string $messageId): int|array {
|
||||
public function patchUpdate(FormContent $content, string $messageId): int|array {
|
||||
if(!$this->canSendMessages)
|
||||
return 403;
|
||||
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
return 400;
|
||||
|
||||
$title = (string)$request->content->getParam('title');
|
||||
$body = (string)$request->content->getParam('body');
|
||||
$format = TextFormat::tryFrom((string)$request->content->getParam('format'));
|
||||
$draft = !empty($request->content->getParam('draft'));
|
||||
$title = (string)$content->getParam('title');
|
||||
$body = (string)$content->getParam('body');
|
||||
$format = TextFormat::tryFrom((string)$content->getParam('format'));
|
||||
$draft = !empty($content->getParam('draft'));
|
||||
|
||||
$error = $this->checkMessageFields($title, $body, $format);
|
||||
if($error !== null)
|
||||
|
@ -551,14 +546,12 @@ class MessagesRoutes implements RouteHandler, UrlSource {
|
|||
}
|
||||
|
||||
/** @return int|array{error: array{name: string, text: string}}|scalar[] */
|
||||
#[HttpPost('/messages/mark')]
|
||||
#[ExactRoute('POST', '/messages/mark')]
|
||||
#[Before('input:urlencoded')]
|
||||
#[UrlFormat('messages-mark', '/messages/mark')]
|
||||
public function postMark(HttpResponseBuilder $response, HttpRequest $request): int|array {
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
return 400;
|
||||
|
||||
$type = (string)$request->content->getParam('type');
|
||||
$messages = explode(',', (string)$request->content->getParam('messages'));
|
||||
public function postMark(FormContent $content): int|array {
|
||||
$type = (string)$content->getParam('type');
|
||||
$messages = explode(',', (string)$content->getParam('messages'));
|
||||
|
||||
if($type !== 'read' && $type !== 'unread')
|
||||
return [
|
||||
|
@ -582,13 +575,11 @@ class MessagesRoutes implements RouteHandler, UrlSource {
|
|||
}
|
||||
|
||||
/** @return int|array{error: array{name: string, text: string}}|scalar[] */
|
||||
#[HttpPost('/messages/delete')]
|
||||
#[ExactRoute('POST', '/messages/delete')]
|
||||
#[Before('input:urlencoded')]
|
||||
#[UrlFormat('messages-delete', '/messages/delete')]
|
||||
public function postDelete(HttpResponseBuilder $response, HttpRequest $request): int|array {
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
return 400;
|
||||
|
||||
$messages = (string)$request->content->getParam('messages');
|
||||
public function postDelete(FormContent $content): int|array {
|
||||
$messages = (string)$content->getParam('messages');
|
||||
if($messages === '')
|
||||
return [
|
||||
'error' => [
|
||||
|
@ -608,13 +599,11 @@ class MessagesRoutes implements RouteHandler, UrlSource {
|
|||
}
|
||||
|
||||
/** @return int|array{error: array{name: string, text: string}}|scalar[] */
|
||||
#[HttpPost('/messages/restore')]
|
||||
#[ExactRoute('POST', '/messages/restore')]
|
||||
#[Before('input:urlencoded')]
|
||||
#[UrlFormat('messages-restore', '/messages/restore')]
|
||||
public function postRestore(HttpResponseBuilder $response, HttpRequest $request) {
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
return 400;
|
||||
|
||||
$messages = (string)$request->content->getParam('messages');
|
||||
public function postRestore(FormContent $content) {
|
||||
$messages = (string)$content->getParam('messages');
|
||||
if($messages === '')
|
||||
return [
|
||||
'error' => [
|
||||
|
@ -634,13 +623,11 @@ class MessagesRoutes implements RouteHandler, UrlSource {
|
|||
}
|
||||
|
||||
/** @return int|array{error: array{name: string, text: string}}|scalar[] */
|
||||
#[HttpPost('/messages/nuke')]
|
||||
#[ExactRoute('POST', '/messages/nuke')]
|
||||
#[Before('input:urlencoded')]
|
||||
#[UrlFormat('messages-nuke', '/messages/nuke')]
|
||||
public function postNuke(HttpResponseBuilder $response, HttpRequest $request) {
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
return 400;
|
||||
|
||||
$messages = (string)$request->content->getParam('messages');
|
||||
public function postNuke(FormContent $content) {
|
||||
$messages = (string)$content->getParam('messages');
|
||||
if($messages === '')
|
||||
return [
|
||||
'error' => [
|
||||
|
|
|
@ -9,7 +9,7 @@ use Index\Db\Migration\{DbMigrationManager,DbMigrationRepo,FsDbMigrationRepo};
|
|||
use Index\Http\HttpRequest;
|
||||
use Index\Templating\TplEnvironment;
|
||||
use Index\Urls\UrlRegistry;
|
||||
use Misuzu\Routing\{BackedRoutingContext,RoutingContext};
|
||||
use Misuzu\Routing\RoutingContext;
|
||||
use Misuzu\Users\UserInfo;
|
||||
use RPCii\HmacVerificationProvider;
|
||||
use RPCii\Server\{HttpRpcServer,RpcServer};
|
||||
|
@ -139,36 +139,30 @@ class MisuzuContext {
|
|||
Template::init($this->templating);
|
||||
}
|
||||
|
||||
public function createRouting(HttpRequest $request): BackedRoutingContext {
|
||||
public function createRouting(HttpRequest $request): RoutingContext {
|
||||
$host = $request->getHeaderLine('Host');
|
||||
$roles = $this->domainRoles->getRoles($host);
|
||||
|
||||
$routingCtx = new BackedRoutingContext;
|
||||
$routingCtx = new RoutingContext;
|
||||
$this->deps->register($this->urls = $routingCtx->urls);
|
||||
|
||||
$rpcServer = new HttpRpcServer;
|
||||
$routingCtx->register($rpcServer->createRouteHandler(
|
||||
new HmacVerificationProvider(fn() => $this->config->getString('aleister.secret'))
|
||||
));
|
||||
|
||||
if(in_array('main', $roles))
|
||||
$this->registerMainRoutes(
|
||||
$routingCtx->scopeTo($this->domainRoles->getPrefix($host, 'main')),
|
||||
$rpcServer
|
||||
);
|
||||
$this->registerMainRoutes($routingCtx);
|
||||
|
||||
if(in_array('redirect', $roles))
|
||||
$this->registerRedirectorRoutes(
|
||||
$routingCtx->scopeTo($this->domainRoles->getPrefix($host, 'redirect'))
|
||||
);
|
||||
$this->registerRedirectorRoutes($routingCtx);
|
||||
|
||||
return $routingCtx;
|
||||
}
|
||||
|
||||
public function registerMainRoutes(
|
||||
RoutingContext $routingCtx,
|
||||
RpcServer $rpcServer
|
||||
RoutingContext $routingCtx
|
||||
): void {
|
||||
$rpcServer = new HttpRpcServer;
|
||||
$routingCtx->register($rpcServer->createRouteHandler(
|
||||
new HmacVerificationProvider(fn() => $this->config->getString('aleister.secret'))
|
||||
));
|
||||
|
||||
$this->deps->register($wf = new WebFinger\WebFingerRegistry);
|
||||
$wf->register($this->deps->constructLazy(
|
||||
Users\UsersWebFingerResolver::class,
|
||||
|
|
|
@ -4,7 +4,8 @@ namespace Misuzu\News;
|
|||
use RuntimeException;
|
||||
use Index\Colour\Colour;
|
||||
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\Routes\{ExactRoute,PatternRoute};
|
||||
use Index\Syndication\FeedBuilder;
|
||||
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon};
|
||||
use Misuzu\{Pagination,SiteInfo,Template};
|
||||
|
@ -98,7 +99,7 @@ class NewsRoutes implements RouteHandler, UrlSource {
|
|||
return $posts;
|
||||
}
|
||||
|
||||
#[HttpGet('/news')]
|
||||
#[ExactRoute('GET', '/news')]
|
||||
#[UrlFormat('news-index', '/news', ['p' => '<page>'])]
|
||||
public function getIndex(HttpResponseBuilder $response, HttpRequest $request): int|string {
|
||||
$categories = $this->news->getCategories(hidden: false);
|
||||
|
@ -116,7 +117,7 @@ class NewsRoutes implements RouteHandler, UrlSource {
|
|||
]);
|
||||
}
|
||||
|
||||
#[HttpGet('/news/([0-9]+)(?:\.(xml|rss|atom))?')]
|
||||
#[PatternRoute('GET', '/news/([0-9]+)(?:\.(xml|rss|atom))?')]
|
||||
#[UrlFormat('news-category', '/news/<category>', ['p' => '<page>'])]
|
||||
public function getCategory(HttpResponseBuilder $response, HttpRequest $request, string $categoryId, string $type = ''): int|string {
|
||||
try {
|
||||
|
@ -141,7 +142,7 @@ class NewsRoutes implements RouteHandler, UrlSource {
|
|||
]);
|
||||
}
|
||||
|
||||
#[HttpGet('/news/post/([0-9]+)')]
|
||||
#[PatternRoute('GET', '/news/post/([0-9]+)')]
|
||||
#[UrlFormat('news-post', '/news/post/<post>')]
|
||||
#[UrlFormat('news-post-comments', '/news/post/<post>', fragment: 'comments')]
|
||||
public function getPost(HttpResponseBuilder $response, HttpRequest $request, string $postId): int|string {
|
||||
|
@ -168,7 +169,7 @@ class NewsRoutes implements RouteHandler, UrlSource {
|
|||
]);
|
||||
}
|
||||
|
||||
#[HttpGet('/news.(?:xml|rss|atom)')]
|
||||
#[PatternRoute('GET', '/news.(?:xml|rss|atom)')]
|
||||
#[UrlFormat('news-feed', '/news.xml')]
|
||||
#[UrlFormat('news-category-feed', '/news/<category>.xml')]
|
||||
public function getFeed(HttpResponseBuilder $response, HttpRequest $request, ?NewsCategoryInfo $categoryInfo = null): string {
|
||||
|
|
|
@ -4,8 +4,12 @@ namespace Misuzu\OAuth2;
|
|||
use RuntimeException;
|
||||
use Index\XArray;
|
||||
use Index\Colour\{Colour,ColourRgb};
|
||||
use Index\Http\{FormHttpContent,HttpResponseBuilder,HttpRequest};
|
||||
use Index\Http\Routing\{HttpGet,HttpOptions,HttpPost,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\{HttpResponseBuilder,HttpRequest};
|
||||
use Index\Http\Content\FormContent;
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\AccessControl\AccessControl;
|
||||
use Index\Http\Routing\Processors\Before;
|
||||
use Index\Http\Routing\Routes\{ExactRoute,PatternRoute};
|
||||
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon};
|
||||
use Misuzu\SiteInfo;
|
||||
use Misuzu\Apps\AppsContext;
|
||||
|
@ -52,20 +56,16 @@ final class OAuth2ApiRoutes implements RouteHandler, UrlSource {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return int|array{
|
||||
* @return array{
|
||||
* resource: string,
|
||||
* authorization_servers: string[],
|
||||
* scopes_supported: string[],
|
||||
* bearer_methods_supported: string[],
|
||||
* }
|
||||
*/
|
||||
#[HttpOptions('/.well-known/oauth-protected-resource')]
|
||||
#[HttpGet('/.well-known/oauth-protected-resource')]
|
||||
public function getWellKnownProtectedResource(HttpResponseBuilder $response, HttpRequest $request): array|int {
|
||||
$response->setHeader('Access-Control-Allow-Origin', '*');
|
||||
if($request->method === 'OPTIONS')
|
||||
return 204;
|
||||
|
||||
#[AccessControl]
|
||||
#[ExactRoute('GET', '/.well-known/oauth-protected-resource')]
|
||||
public function getWellKnownProtectedResource(HttpResponseBuilder $response, HttpRequest $request): array {
|
||||
return [
|
||||
'resource' => $this->siteInfo->url,
|
||||
'authorization_servers' => [$this->siteInfo->url],
|
||||
|
@ -75,7 +75,7 @@ final class OAuth2ApiRoutes implements RouteHandler, UrlSource {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return int|array{
|
||||
* @return array{
|
||||
* issuer: string,
|
||||
* authorization_endpoint: string,
|
||||
* token_endpoint: string,
|
||||
|
@ -92,13 +92,9 @@ final class OAuth2ApiRoutes implements RouteHandler, UrlSource {
|
|||
* code_challenge_methods_supported: string[],
|
||||
* }
|
||||
*/
|
||||
#[HttpOptions('/.well-known/oauth-authorization-server')]
|
||||
#[HttpGet('/.well-known/oauth-authorization-server')]
|
||||
public function getWellKnownAuthorizationServer(HttpResponseBuilder $response, HttpRequest $request): array|int {
|
||||
$response->setHeader('Access-Control-Allow-Origin', '*');
|
||||
if($request->method === 'OPTIONS')
|
||||
return 204;
|
||||
|
||||
#[AccessControl]
|
||||
#[ExactRoute('GET', '/.well-known/oauth-authorization-server')]
|
||||
public function getWellKnownAuthorizationServer(HttpResponseBuilder $response, HttpRequest $request): array {
|
||||
return [
|
||||
'issuer' => $this->siteInfo->url,
|
||||
'authorization_endpoint' => sprintf('%s%s', $this->siteInfo->url, $this->urls->format('oauth2-authorise')),
|
||||
|
@ -134,7 +130,7 @@ final class OAuth2ApiRoutes implements RouteHandler, UrlSource {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return int|array{
|
||||
* @return array{
|
||||
* issuer: string,
|
||||
* authorization_endpoint: string,
|
||||
* token_endpoint: string,
|
||||
|
@ -148,13 +144,9 @@ final class OAuth2ApiRoutes implements RouteHandler, UrlSource {
|
|||
* token_endpoint_auth_methods_supported: string[],
|
||||
* }
|
||||
*/
|
||||
#[HttpOptions('/.well-known/openid-configuration')]
|
||||
#[HttpGet('/.well-known/openid-configuration')]
|
||||
public function getWellKnown(HttpResponseBuilder $response, HttpRequest $request): array|int {
|
||||
$response->setHeader('Access-Control-Allow-Origin', '*');
|
||||
if($request->method === 'OPTIONS')
|
||||
return 204;
|
||||
|
||||
#[AccessControl]
|
||||
#[ExactRoute('GET', '/.well-known/openid-configuration')]
|
||||
public function getWellKnown(HttpResponseBuilder $response, HttpRequest $request): array {
|
||||
$signingAlgs = array_values(array_unique(XArray::select(
|
||||
$this->oauth2Ctx->keys->getPublicKeySet()->keys,
|
||||
fn($key) => $key->algo
|
||||
|
@ -185,11 +177,7 @@ final class OAuth2ApiRoutes implements RouteHandler, UrlSource {
|
|||
#[HttpOptions('/oauth2/jwks.json')]
|
||||
#[HttpGet('/oauth2/jwks.json')]
|
||||
#[UrlFormat('oauth2-jwks', '/oauth2/jwks.json')]
|
||||
public function getJwks(HttpResponseBuilder $response, HttpRequest $request): int|array {
|
||||
$response->setHeader('Access-Control-Allow-Origin', '*');
|
||||
if($request->method === 'OPTIONS')
|
||||
return 204;
|
||||
|
||||
public function getJwks(HttpResponseBuilder $response, HttpRequest $request): array {
|
||||
return $this->oauth2Ctx->keys->getPublicKeysForJson();
|
||||
}
|
||||
|
||||
|
@ -203,13 +191,13 @@ final class OAuth2ApiRoutes implements RouteHandler, UrlSource {
|
|||
* interval?: int
|
||||
* }|array{ error: string, error_description: string }
|
||||
*/
|
||||
#[HttpPost('/oauth2/request-authorise')]
|
||||
#[HttpPost('/oauth2/request-authorize')]
|
||||
#[PatternRoute('POST', '/oauth2/request-authori[sz]e')]
|
||||
#[Before('input:urlencoded', required: false)]
|
||||
#[UrlFormat('oauth2-request-authorise', '/oauth2/request-authorize')]
|
||||
public function postRequestAuthorise(HttpResponseBuilder $response, HttpRequest $request): array {
|
||||
public function postRequestAuthorise(HttpResponseBuilder $response, HttpRequest $request, ?FormContent $content): array {
|
||||
$response->setHeader('Cache-Control', 'no-store');
|
||||
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
if($content === null)
|
||||
return self::filter($response, $request, [
|
||||
'error' => 'invalid_request',
|
||||
'error_description' => 'Your request must use content type application/x-www-form-urlencoded.',
|
||||
|
@ -226,7 +214,7 @@ final class OAuth2ApiRoutes implements RouteHandler, UrlSource {
|
|||
'error_description' => 'You must use the Basic method for Authorization parameters.',
|
||||
], authzHeader: true);
|
||||
} else {
|
||||
$clientId = (string)$request->content->getParam('client_id');
|
||||
$clientId = (string)$content->getParam('client_id');
|
||||
$clientSecret = '';
|
||||
}
|
||||
|
||||
|
@ -250,12 +238,12 @@ final class OAuth2ApiRoutes implements RouteHandler, UrlSource {
|
|||
|
||||
return self::filter($response, $request, $this->oauth2Ctx->createDeviceAuthorisationRequest(
|
||||
$appInfo,
|
||||
$request->content->hasParam('scope') ? (string)$request->content->getParam('scope') : null
|
||||
$content->hasParam('scope') ? (string)$content->getParam('scope') : null
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|array{
|
||||
* @return array{
|
||||
* access_token: string,
|
||||
* token_type: 'Bearer',
|
||||
* expires_in?: int,
|
||||
|
@ -263,36 +251,14 @@ final class OAuth2ApiRoutes implements RouteHandler, UrlSource {
|
|||
* refresh_token?: string,
|
||||
* }|array{ error: string, error_description: string }
|
||||
*/
|
||||
#[HttpOptions('/oauth2/token')]
|
||||
#[HttpPost('/oauth2/token')]
|
||||
#[AccessControl(allowHeaders: ['Authorization'], exposeHeaders: ['WWW-Authenticate'])]
|
||||
#[ExactRoute('POST', '/oauth2/token')]
|
||||
#[Before('input:urlencoded', required: false)]
|
||||
#[UrlFormat('oauth2-token', '/oauth2/token')]
|
||||
public function postToken(HttpResponseBuilder $response, HttpRequest $request): array|int {
|
||||
public function postToken(HttpResponseBuilder $response, HttpRequest $request, ?FormContent $content): array {
|
||||
$response->setHeader('Cache-Control', 'no-store');
|
||||
|
||||
$originHeaders = ['Origin', 'X-Origin', 'Referer'];
|
||||
$origins = [];
|
||||
foreach($originHeaders as $originHeader) {
|
||||
$originHeader = $request->getHeaderFirstLine($originHeader);
|
||||
if($originHeader !== '' && !in_array($originHeader, $origins))
|
||||
$origins[] = $originHeader;
|
||||
}
|
||||
|
||||
if(!empty($origins)) {
|
||||
// TODO: check if none of the provided origins is on a blocklist or something
|
||||
// different origins being specified for each header should probably also be considered suspect...
|
||||
|
||||
$response->setHeader('Access-Control-Allow-Origin', $origins[0]);
|
||||
$response->setHeader('Access-Control-Allow-Methods', 'OPTIONS, POST');
|
||||
$response->setHeader('Access-Control-Allow-Headers', 'Authorization');
|
||||
$response->setHeader('Access-Control-Expose-Headers', 'Vary');
|
||||
foreach($originHeaders as $originHeader)
|
||||
$response->setHeader('Vary', $originHeader);
|
||||
}
|
||||
|
||||
if($request->method === 'OPTIONS')
|
||||
return 204;
|
||||
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
if($content === null)
|
||||
return self::filter($response, $request, [
|
||||
'error' => 'invalid_request',
|
||||
'error_description' => 'Your request must use content type application/x-www-form-urlencoded.',
|
||||
|
@ -310,8 +276,8 @@ final class OAuth2ApiRoutes implements RouteHandler, UrlSource {
|
|||
'error_description' => 'You must either use the Basic method for Authorization or use the client_id and client_secret parameters.',
|
||||
], authzHeader: true);
|
||||
} else {
|
||||
$clientId = (string)$request->content->getParam('client_id');
|
||||
$clientSecret = (string)$request->content->getParam('client_secret');
|
||||
$clientId = (string)$content->getParam('client_id');
|
||||
$clientSecret = (string)$content->getParam('client_secret');
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -334,36 +300,36 @@ final class OAuth2ApiRoutes implements RouteHandler, UrlSource {
|
|||
], authzHeader: $authzHeader[0] !== '');
|
||||
}
|
||||
|
||||
$type = (string)$request->content->getParam('grant_type');
|
||||
$type = (string)$content->getParam('grant_type');
|
||||
|
||||
if($type === 'authorization_code')
|
||||
return self::filter($response, $request, $this->oauth2Ctx->redeemAuthorisationCode(
|
||||
$appInfo,
|
||||
$isAuthed,
|
||||
(string)$request->content->getParam('code'),
|
||||
(string)$request->content->getParam('code_verifier')
|
||||
(string)$content->getParam('code'),
|
||||
(string)$content->getParam('code_verifier')
|
||||
));
|
||||
|
||||
if($type === 'refresh_token')
|
||||
return self::filter($response, $request, $this->oauth2Ctx->redeemRefreshToken(
|
||||
$appInfo,
|
||||
$isAuthed,
|
||||
(string)$request->content->getParam('refresh_token'),
|
||||
$request->content->hasParam('scope') ? (string)$request->content->getParam('scope') : null
|
||||
(string)$content->getParam('refresh_token'),
|
||||
$content->hasParam('scope') ? (string)$content->getParam('scope') : null
|
||||
));
|
||||
|
||||
if($type === 'client_credentials')
|
||||
return self::filter($response, $request, $this->oauth2Ctx->redeemClientCredentials(
|
||||
$appInfo,
|
||||
$isAuthed,
|
||||
$request->content->hasParam('scope') ? (string)$request->content->getParam('scope') : null
|
||||
$content->hasParam('scope') ? (string)$content->getParam('scope') : null
|
||||
));
|
||||
|
||||
if($type === 'urn:ietf:params:oauth:grant-type:device_code' || $type === 'device_code')
|
||||
return self::filter($response, $request, $this->oauth2Ctx->redeemDeviceCode(
|
||||
$appInfo,
|
||||
$isAuthed,
|
||||
(string)$request->content->getParam('device_code')
|
||||
(string)$content->getParam('device_code')
|
||||
));
|
||||
|
||||
return self::filter($response, $request, [
|
||||
|
@ -373,7 +339,7 @@ final class OAuth2ApiRoutes implements RouteHandler, UrlSource {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return int|array{
|
||||
* @return array{
|
||||
* sub: string,
|
||||
* name?: string,
|
||||
* nickname?: string,
|
||||
|
|
|
@ -4,8 +4,11 @@ namespace Misuzu\OAuth2;
|
|||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use Index\XArray;
|
||||
use Index\Http\{FormHttpContent,HttpResponseBuilder,HttpRequest};
|
||||
use Index\Http\Routing\{HttpGet,HttpPost,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\{HttpResponseBuilder,HttpRequest};
|
||||
use Index\Http\Content\FormContent;
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\Processors\Before;
|
||||
use Index\Http\Routing\Routes\{ExactRoute,PatternRoute};
|
||||
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon};
|
||||
use Misuzu\{CSRF,Template};
|
||||
use Misuzu\Auth\AuthInfo;
|
||||
|
@ -24,15 +27,14 @@ final class OAuth2WebRoutes implements RouteHandler, UrlSource {
|
|||
private AuthInfo $authInfo,
|
||||
) {}
|
||||
|
||||
#[HttpGet('/oauth2/authorise')]
|
||||
#[HttpGet('/oauth2/authorize')]
|
||||
#[PatternRoute('GET', '/oauth2/authori[sz]e')]
|
||||
#[UrlFormat('oauth2-authorise', '/oauth2/authorize')]
|
||||
public function getAuthorise(HttpResponseBuilder $response, HttpRequest $request): string {
|
||||
return Template::renderRaw('oauth2.authorise');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|array{
|
||||
* @return array{
|
||||
* error: 'auth'|'csrf'|'method'|'length'|'client'|'scope'|'format'|'required'|'authorise'|'resptype',
|
||||
* scope?: string,
|
||||
* reason?: string,
|
||||
|
@ -41,11 +43,9 @@ final class OAuth2WebRoutes implements RouteHandler, UrlSource {
|
|||
* redirect: string,
|
||||
* }
|
||||
*/
|
||||
#[HttpPost('/oauth2/authorise')]
|
||||
public function postAuthorise(HttpResponseBuilder $response, HttpRequest $request): int|array {
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
return 400;
|
||||
|
||||
#[ExactRoute('POST', '/oauth2/authorize')]
|
||||
#[Before('input:urlencoded')]
|
||||
public function postAuthorise(HttpResponseBuilder $response, HttpRequest $request, FormContent $content): array {
|
||||
// TODO: RATE LIMITING
|
||||
|
||||
if(!CSRF::validate($request->getHeaderLine('X-CSRF-token')))
|
||||
|
@ -56,20 +56,20 @@ final class OAuth2WebRoutes implements RouteHandler, UrlSource {
|
|||
if(!$this->authInfo->loggedIn)
|
||||
return ['error' => 'auth'];
|
||||
|
||||
$responseTypes = explode(' ', (string)$request->content->getParam('rt'), 2);
|
||||
$responseTypes = explode(' ', (string)$content->getParam('rt'), 2);
|
||||
sort($responseTypes);
|
||||
$responseType = implode(' ', $responseTypes);
|
||||
if(!in_array($responseType, self::RESPONSE_TYPES))
|
||||
return ['error' => 'resptype'];
|
||||
|
||||
$codeChallengeMethod = 'plain';
|
||||
if($request->content->hasParam('ccm')) {
|
||||
$codeChallengeMethod = $request->content->getParam('ccm');
|
||||
if($content->hasParam('ccm')) {
|
||||
$codeChallengeMethod = $content->getParam('ccm');
|
||||
if(!in_array($codeChallengeMethod, ['plain', 'S256']))
|
||||
return ['error' => 'method'];
|
||||
}
|
||||
|
||||
$codeChallenge = $request->content->getParam('cc');
|
||||
$codeChallenge = $content->getParam('cc');
|
||||
$codeChallengeLength = strlen($codeChallenge);
|
||||
if($codeChallengeMethod === 'S256') {
|
||||
if($codeChallengeLength !== 43)
|
||||
|
@ -81,16 +81,16 @@ final class OAuth2WebRoutes implements RouteHandler, UrlSource {
|
|||
|
||||
try {
|
||||
$appInfo = $this->oauth2Ctx->appsCtx->apps->getAppInfo(
|
||||
clientId: (string)$request->content->getParam('client'),
|
||||
clientId: (string)$content->getParam('client'),
|
||||
deleted: false
|
||||
);
|
||||
} catch(RuntimeException $ex) {
|
||||
return ['error' => 'client'];
|
||||
}
|
||||
|
||||
if($request->content->hasParam('scope')) {
|
||||
if($content->hasParam('scope')) {
|
||||
$scope = [];
|
||||
$scopeInfos = $this->oauth2Ctx->appsCtx->handleScopeString($appInfo, (string)$request->content->getParam('scope'));
|
||||
$scopeInfos = $this->oauth2Ctx->appsCtx->handleScopeString($appInfo, (string)$content->getParam('scope'));
|
||||
|
||||
foreach($scopeInfos as $scopeName => $scopeInfo) {
|
||||
if(is_string($scopeInfo))
|
||||
|
@ -102,8 +102,8 @@ final class OAuth2WebRoutes implements RouteHandler, UrlSource {
|
|||
$scope = implode(' ', $scope);
|
||||
} else $scope = '';
|
||||
|
||||
if($request->content->hasParam('redirect')) {
|
||||
$redirectUri = (string)$request->content->getParam('redirect');
|
||||
if($content->hasParam('redirect')) {
|
||||
$redirectUri = (string)$content->getParam('redirect');
|
||||
$redirectUriId = $this->oauth2Ctx->appsCtx->apps->getAppUriId($appInfo, $redirectUri);
|
||||
if($redirectUriId === null)
|
||||
return ['error' => 'format'];
|
||||
|
@ -169,7 +169,7 @@ final class OAuth2WebRoutes implements RouteHandler, UrlSource {
|
|||
* scope: string[],
|
||||
* }
|
||||
*/
|
||||
#[HttpGet('/oauth2/resolve-authorise-app')]
|
||||
#[ExactRoute('GET', '/oauth2/resolve-authorise-app')]
|
||||
#[UrlFormat('oauth2-resolve-authorise-app', '/oauth2/resolve-authorise-app')]
|
||||
public function getResolveAuthorise(HttpResponseBuilder $response, HttpRequest $request): array {
|
||||
// TODO: RATE LIMITING
|
||||
|
@ -243,14 +243,14 @@ final class OAuth2WebRoutes implements RouteHandler, UrlSource {
|
|||
return $result;
|
||||
}
|
||||
|
||||
#[HttpGet('/oauth2/verify')]
|
||||
#[ExactRoute('GET', '/oauth2/verify')]
|
||||
#[UrlFormat('oauth2-verify', '/oauth2/verify')]
|
||||
public function getVerify(HttpResponseBuilder $response, HttpRequest $request): string {
|
||||
return Template::renderRaw('oauth2.verify');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|array{
|
||||
* @return array{
|
||||
* error: 'auth'|'csrf'|'invalid'|'code'|'expired'|'approval'|'code'|'scope',
|
||||
* scope?: string,
|
||||
* reason?: string,
|
||||
|
@ -258,11 +258,9 @@ final class OAuth2WebRoutes implements RouteHandler, UrlSource {
|
|||
* approval: 'approved'|'denied',
|
||||
* }
|
||||
*/
|
||||
#[HttpPost('/oauth2/verify')]
|
||||
public function postVerify(HttpResponseBuilder $response, HttpRequest $request): int|array {
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
return 400;
|
||||
|
||||
#[ExactRoute('POST', '/oauth2/verify')]
|
||||
#[Before('input:urlencoded')]
|
||||
public function postVerify(HttpResponseBuilder $response, HttpRequest $request, FormContent $content): array {
|
||||
// TODO: RATE LIMITING
|
||||
|
||||
if(!CSRF::validate($request->getHeaderLine('X-CSRF-token')))
|
||||
|
@ -273,13 +271,13 @@ final class OAuth2WebRoutes implements RouteHandler, UrlSource {
|
|||
if(!$this->authInfo->loggedIn)
|
||||
return ['error' => 'auth'];
|
||||
|
||||
$approve = (string)$request->content->getParam('approve');
|
||||
$approve = (string)$content->getParam('approve');
|
||||
if(!in_array($approve, ['yes', 'no']))
|
||||
return ['error' => 'invalid'];
|
||||
|
||||
try {
|
||||
$deviceInfo = $this->oauth2Ctx->devices->getDeviceInfo(
|
||||
userCode: (string)$request->content->getParam('code')
|
||||
userCode: (string)$content->getParam('code')
|
||||
);
|
||||
} catch(RuntimeException $ex) {
|
||||
return ['error' => 'code'];
|
||||
|
@ -357,7 +355,7 @@ final class OAuth2WebRoutes implements RouteHandler, UrlSource {
|
|||
* scope: string[],
|
||||
* }
|
||||
*/
|
||||
#[HttpGet('/oauth2/resolve-verify')]
|
||||
#[ExactRoute('GET', '/oauth2/resolve-verify')]
|
||||
#[UrlFormat('oauth2-resolve-verify', '/oauth2/resolve-verify')]
|
||||
public function getResolveVerify(HttpResponseBuilder $response, HttpRequest $request) {
|
||||
// TODO: RATE LIMITING
|
||||
|
|
|
@ -57,7 +57,7 @@ final class Pagination {
|
|||
): self {
|
||||
return self::fromPage(
|
||||
$count,
|
||||
(int)(filter_input(INPUT_GET, $pageParam, FILTER_SANITIZE_NUMBER_INT) ?? $firstPage),
|
||||
!empty($_GET[$pageParam]) && is_scalar($_GET[$pageParam]) ? (int)$_GET[$pageParam] : $firstPage,
|
||||
$range,
|
||||
$firstPage
|
||||
);
|
||||
|
@ -72,7 +72,7 @@ final class Pagination {
|
|||
): Pagination {
|
||||
return self::fromPage(
|
||||
$count,
|
||||
(int)($request->getParam($pageParam, FILTER_SANITIZE_NUMBER_INT) ?? $firstPage),
|
||||
(int)($request->getFilteredParam($pageParam, FILTER_SANITIZE_NUMBER_INT) ?? $firstPage),
|
||||
$range,
|
||||
$firstPage
|
||||
);
|
||||
|
|
20
src/Perm.php
20
src/Perm.php
|
@ -423,7 +423,7 @@ final class Perm {
|
|||
|
||||
$item->perms[] = $permItem = new stdClass;
|
||||
$permItem->category = $categoryName;
|
||||
$permItem->name = sprintf('perms[%s:%d]', $categoryName, $perm);
|
||||
$permItem->name = sprintf('perm_%s_%d', $categoryName, $perm);
|
||||
$permItem->title = self::label($categoryName, $perm);
|
||||
$permItem->value = $perm;
|
||||
}
|
||||
|
@ -437,11 +437,18 @@ final class Perm {
|
|||
* @param string[] $categories
|
||||
* @return array<string, array<string, int>>
|
||||
*/
|
||||
public static function convertSubmission(array $raw, array $categories): array {
|
||||
public static function convertSubmission(array $raw, array $categories, string $prefix = 'perm_'): array {
|
||||
$apply = [];
|
||||
|
||||
foreach($raw as $name => $mode) {
|
||||
$nameParts = explode(':', $name, 2);
|
||||
if($prefix !== '') {
|
||||
if(!str_starts_with($name, $prefix))
|
||||
continue;
|
||||
|
||||
$name = substr($name, strlen($prefix));
|
||||
}
|
||||
|
||||
$nameParts = explode('_', $name, 2);
|
||||
if(count($nameParts) !== 2 || !ctype_alpha($nameParts[0]) || !ctype_digit($nameParts[1]))
|
||||
continue;
|
||||
|
||||
|
@ -449,12 +456,11 @@ final class Perm {
|
|||
if(!in_array($category, $categories))
|
||||
continue;
|
||||
|
||||
if($mode === 'yes' || $mode === 'never') {
|
||||
if(!array_key_exists($category, $apply))
|
||||
$apply[$category] = ['allow' => 0, 'deny' => 0];
|
||||
if(!array_key_exists($category, $apply))
|
||||
$apply[$category] = ['allow' => 0, 'deny' => 0];
|
||||
|
||||
if($mode === 'yes' || $mode === 'never')
|
||||
$apply[$category][$mode === 'yes' ? 'allow' : 'deny'] |= (int)$value;
|
||||
}
|
||||
}
|
||||
|
||||
return $apply;
|
||||
|
|
|
@ -3,7 +3,8 @@ namespace Misuzu\Redirects;
|
|||
|
||||
use Index\Config\Config;
|
||||
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\Routes\PatternRoute;
|
||||
|
||||
class AliasRedirectsRoutes implements RouteHandler {
|
||||
use RouteHandlerCommon;
|
||||
|
@ -24,23 +25,23 @@ class AliasRedirectsRoutes implements RouteHandler {
|
|||
$response->redirect($url, true);
|
||||
}
|
||||
|
||||
#[HttpGet('/[up]([0-9]+)')]
|
||||
#[HttpGet('/[up]/([A-Za-z0-9\-_]+)')]
|
||||
#[PatternRoute('GET', '/[up]([0-9]+)')]
|
||||
#[PatternRoute('GET', '/[up]/([A-Za-z0-9\-_]+)')]
|
||||
public function getProfileRedirect(HttpResponseBuilder $response, HttpRequest $request, string $userId): void {
|
||||
$this->redirect($response, $request, 'user_profile', $userId);
|
||||
}
|
||||
|
||||
#[HttpGet('/fc?/?([0-9]+)')]
|
||||
#[PatternRoute('GET', '/fc?/?([0-9]+)')]
|
||||
public function getForumCategoryRedirect(HttpResponseBuilder $response, HttpRequest $request, string $categoryId): void {
|
||||
$this->redirect($response, $request, 'forum_category', $categoryId);
|
||||
}
|
||||
|
||||
#[HttpGet('/ft/?([0-9]+)')]
|
||||
#[PatternRoute('GET', '/ft/?([0-9]+)')]
|
||||
public function getForumTopicRedirect(HttpResponseBuilder $response, HttpRequest $request, string $topicId): void {
|
||||
$this->redirect($response, $request, 'forum_topic', $topicId);
|
||||
}
|
||||
|
||||
#[HttpGet('/fp/?([0-9]+)')]
|
||||
#[PatternRoute('GET', '/fp/?([0-9]+)')]
|
||||
public function getForumPostRedirect(HttpResponseBuilder $response, HttpRequest $request, string $postId): void {
|
||||
$this->redirect($response, $request, 'forum_post', $postId);
|
||||
}
|
||||
|
|
|
@ -3,8 +3,11 @@ namespace Misuzu\Redirects;
|
|||
|
||||
use RuntimeException;
|
||||
use Index\XNumber;
|
||||
use Index\Http\{FormHttpContent,HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Routing\{HttpGet,HttpPost,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\HttpResponseBuilder;
|
||||
use Index\Http\Content\FormContent;
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\Processors\Before;
|
||||
use Index\Http\Routing\Routes\{ExactRoute,PatternRoute};
|
||||
|
||||
class IncrementalRedirectsRoutes implements RouteHandler {
|
||||
use RouteHandlerCommon;
|
||||
|
@ -13,8 +16,8 @@ class IncrementalRedirectsRoutes implements RouteHandler {
|
|||
private RedirectsContext $redirectsCtx,
|
||||
) {}
|
||||
|
||||
#[HttpGet('/[bg]/([A-Za-z0-9]+)')]
|
||||
public function getIncrementalRedirect(HttpResponseBuilder $response, HttpRequest $request, string $linkId): int {
|
||||
#[PatternRoute('GET', '/[bg]/([A-Za-z0-9]+)')]
|
||||
public function getIncrementalRedirect(HttpResponseBuilder $response, string $linkId): int {
|
||||
$linkId = XNumber::fromBase62($linkId);
|
||||
if($linkId === false)
|
||||
return 400;
|
||||
|
@ -30,15 +33,13 @@ class IncrementalRedirectsRoutes implements RouteHandler {
|
|||
}
|
||||
|
||||
/** @return int|array{url: string} */
|
||||
#[HttpPost('/satori/create')]
|
||||
public function postIncrementalRedirect(HttpResponseBuilder $response, HttpRequest $request): int|array {
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
return 400;
|
||||
|
||||
#[ExactRoute('POST', '/satori/create')]
|
||||
#[Before('input:urlencoded')]
|
||||
public function postIncrementalRedirect(FormContent $content): int|array {
|
||||
$config = $this->redirectsCtx->config->scopeTo('incremental');
|
||||
$url = (string)$request->content->getParam('u');
|
||||
$time = (int)$request->content->getParam('t', FILTER_SANITIZE_NUMBER_INT);
|
||||
$sign = base64_decode((string)$request->content->getParam('s'));
|
||||
$url = (string)$content->getParam('u');
|
||||
$time = (int)$content->getFilteredParam('t', FILTER_SANITIZE_NUMBER_INT);
|
||||
$sign = base64_decode((string)$content->getParam('s'));
|
||||
$hash = hash_hmac('sha256', "satori#create#{$time}#{$url}", $config->getString('secret'), true);
|
||||
|
||||
if(!hash_equals($hash, $sign))
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
<?php
|
||||
namespace Misuzu\Redirects;
|
||||
|
||||
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\Routes\ExactRoute;
|
||||
use Misuzu\Template;
|
||||
|
||||
class LandingRedirectsRoutes implements RouteHandler {
|
||||
use RouteHandlerCommon;
|
||||
|
||||
#[HttpGet('/')]
|
||||
#[ExactRoute('GET', '/')]
|
||||
public function getIndex(): string {
|
||||
return Template::renderRaw('redirects.landing');
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
namespace Misuzu\Redirects;
|
||||
|
||||
use RuntimeException;
|
||||
use Index\Config\Config;
|
||||
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\Routes\PatternRoute;
|
||||
|
||||
class NamedRedirectsRoutes implements RouteHandler {
|
||||
use RouteHandlerCommon;
|
||||
|
@ -13,7 +13,7 @@ class NamedRedirectsRoutes implements RouteHandler {
|
|||
private RedirectsContext $redirectsCtx,
|
||||
) {}
|
||||
|
||||
#[HttpGet('/([A-Za-z0-9\-_]+)')]
|
||||
#[PatternRoute('GET', '/([A-Za-z0-9\-_]+)')]
|
||||
public function getNamedRedirect(HttpResponseBuilder $response, HttpRequest $request, string $name): int {
|
||||
try {
|
||||
$redirectInfo = $this->redirectsCtx->named->getNamedRedirect(
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
<?php
|
||||
namespace Misuzu\Redirects;
|
||||
|
||||
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\HttpResponseBuilder;
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\Routes\PatternRoute;
|
||||
use Misuzu\Template;
|
||||
|
||||
class SocialRedirectsRoutes implements RouteHandler {
|
||||
|
@ -12,8 +13,8 @@ class SocialRedirectsRoutes implements RouteHandler {
|
|||
private RedirectsContext $redirectsCtx
|
||||
) {}
|
||||
|
||||
#[HttpGet('/bsky/((did:[a-z0-9]+:[A-Za-z0-9.\-_:%]+)|(([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])))')]
|
||||
public function getBlueskyRedirect(HttpResponseBuilder $response, HttpRequest $request, string $handle): int|string {
|
||||
#[PatternRoute('GET', '/bsky/((did:[a-z0-9]+:[A-Za-z0-9.\-_:%]+)|(([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])))')]
|
||||
public function getBlueskyRedirect(HttpResponseBuilder $response, string $handle): int|string {
|
||||
$did = null;
|
||||
|
||||
if(str_starts_with($handle, 'did:'))
|
||||
|
@ -47,8 +48,8 @@ class SocialRedirectsRoutes implements RouteHandler {
|
|||
]);
|
||||
}
|
||||
|
||||
#[HttpGet('/fedi/([A-Za-z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})')]
|
||||
public function getFediverseRedirect(HttpResponseBuilder $response, HttpRequest $request, string $userName, string $instance): string {
|
||||
#[PatternRoute('GET', '/fedi/([A-Za-z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})')]
|
||||
public function getFediverseRedirect(string $userName, string $instance): string {
|
||||
return Template::renderRaw('redirects.fedi', [
|
||||
'fedi_username' => $userName,
|
||||
'fedi_instance' => $instance,
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu\Routing;
|
||||
|
||||
use Index\Http\HttpRequest;
|
||||
use Index\Http\Routing\{HttpRouter,RouteHandler};
|
||||
use Index\Urls\{ArrayUrlRegistry,UrlRegistry,UrlSource};
|
||||
|
||||
class BackedRoutingContext implements RoutingContext {
|
||||
public private(set) HttpRouter $router;
|
||||
public private(set) UrlRegistry $urls;
|
||||
|
||||
public function __construct(
|
||||
?HttpRouter $router = null,
|
||||
?UrlRegistry $urls = null
|
||||
) {
|
||||
$this->urls = $urls ?? new ArrayUrlRegistry;
|
||||
$this->router = $router ?? new HttpRouter(errorHandler: new RoutingErrorHandler);
|
||||
$this->router->use('/', fn($resp) => $resp->setPoweredBy('Misuzu'));
|
||||
}
|
||||
|
||||
public function register(RouteHandler|UrlSource $handler): void {
|
||||
if($handler instanceof RouteHandler)
|
||||
$this->router->register($handler);
|
||||
if($handler instanceof UrlSource)
|
||||
$this->urls->register($handler);
|
||||
}
|
||||
|
||||
public function scopeTo(string $prefix): RoutingContext {
|
||||
return new ScopedRoutingContext(
|
||||
$this->router->scopeTo($prefix),
|
||||
$this->urls->scopeTo(pathPrefix: $prefix)
|
||||
);
|
||||
}
|
||||
|
||||
/** @param mixed[] $args */
|
||||
public function dispatch(?HttpRequest $request = null, array $args = []): void {
|
||||
$this->router->dispatch($request, $args);
|
||||
}
|
||||
}
|
|
@ -1,11 +1,32 @@
|
|||
<?php
|
||||
namespace Misuzu\Routing;
|
||||
|
||||
use Index\Http\HttpRequest;
|
||||
use Index\Http\Routing\RouteHandler;
|
||||
use Index\Urls\UrlSource;
|
||||
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Routing\{Router,RouteHandler};
|
||||
use Index\Http\Routing\Filters\FilterInfo;
|
||||
use Index\Urls\{ArrayUrlRegistry,UrlRegistry,UrlSource};
|
||||
|
||||
interface RoutingContext {
|
||||
public function register(RouteHandler|UrlSource $handler): void;
|
||||
public function scopeTo(string $prefix): RoutingContext;
|
||||
class RoutingContext {
|
||||
public private(set) Router $router;
|
||||
public private(set) UrlRegistry $urls;
|
||||
|
||||
public function __construct(
|
||||
?Router $router = null,
|
||||
?UrlRegistry $urls = null
|
||||
) {
|
||||
$this->urls = $urls ?? new ArrayUrlRegistry;
|
||||
$this->router = $router ?? new Router(errorHandler: new RoutingErrorHandler);
|
||||
$this->router->filter(FilterInfo::prefix('/', fn(HttpResponseBuilder $response) => $response->setPoweredBy('Misuzu')));
|
||||
}
|
||||
|
||||
public function register(RouteHandler|UrlSource $handler): void {
|
||||
if($handler instanceof RouteHandler)
|
||||
$this->router->register($handler);
|
||||
if($handler instanceof UrlSource)
|
||||
$this->urls->register($handler);
|
||||
}
|
||||
|
||||
public function dispatch(?HttpRequest $request = null): void {
|
||||
$this->router->dispatch($request);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
<?php
|
||||
namespace Misuzu\Routing;
|
||||
|
||||
use Index\Http\{HtmlHttpErrorHandler,HttpResponseBuilder,HttpRequest};
|
||||
use Misuzu\{Misuzu,Template};
|
||||
use Index\Http\Routing\HandlerContext;
|
||||
use Index\Http\Routing\ErrorHandling\HtmlErrorHandler;
|
||||
use Index\Http\Streams\Stream;
|
||||
use Misuzu\Misuzu;
|
||||
|
||||
class RoutingErrorHandler extends HtmlHttpErrorHandler {
|
||||
public function handle(HttpResponseBuilder $response, HttpRequest $request, int $code, string $message): void {
|
||||
if(str_starts_with($request->path, '/_')) {
|
||||
$response->setTypePlain();
|
||||
$response->content = sprintf('HTTP %03d', $code);
|
||||
class RoutingErrorHandler extends HtmlErrorHandler {
|
||||
public function handle(HandlerContext $context): void {
|
||||
if(!$context->response->needsBody)
|
||||
return;
|
||||
|
||||
if(str_starts_with($context->request->requestTarget, '/_')) {
|
||||
$context->response->setTypePlain();
|
||||
$context->response->body = Stream::createStream(sprintf('HTTP %03d', $context->response->statusCode));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -23,11 +28,11 @@ class RoutingErrorHandler extends HtmlHttpErrorHandler {
|
|||
|
||||
$path = sprintf('%s/error-%03d.html', Misuzu::PATH_PUBLIC, $code);
|
||||
if(is_file($path)) {
|
||||
$response->setTypeHTML();
|
||||
$response->content = file_get_contents($path);
|
||||
$context->response->setTypeHTML();
|
||||
$context->response->body = Stream::createStreamFromFile($path, 'rb');
|
||||
return;
|
||||
}
|
||||
|
||||
parent::handle($response, $request, $code, $message);
|
||||
parent::handle($context);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
namespace Misuzu\Routing;
|
||||
|
||||
use Index\Http\HttpRequest;
|
||||
use Index\Http\Routing\{RouteHandler,Router};
|
||||
use Index\Urls\{UrlRegistry,UrlSource};
|
||||
|
||||
class ScopedRoutingContext implements RoutingContext {
|
||||
public function __construct(
|
||||
private Router $router,
|
||||
private UrlRegistry $urls
|
||||
) {}
|
||||
|
||||
public function register(RouteHandler|UrlSource $handler): void {
|
||||
if($handler instanceof RouteHandler)
|
||||
$this->router->register($handler);
|
||||
if($handler instanceof UrlSource)
|
||||
$this->urls->register($handler);
|
||||
}
|
||||
|
||||
public function scopeTo(string $prefix): RoutingContext {
|
||||
return new ScopedRoutingContext(
|
||||
$this->router->scopeTo($prefix),
|
||||
$this->urls->scopeTo(pathPrefix: $prefix)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -5,7 +5,12 @@ use RuntimeException;
|
|||
use Index\Colour\Colour;
|
||||
use Index\Config\Config;
|
||||
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Routing\{HttpGet,HttpMiddleware,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Content\FormContent;
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\AccessControl\AccessControl;
|
||||
use Index\Http\Routing\Filters\PrefixFilter;
|
||||
use Index\Http\Routing\Processors\Before;
|
||||
use Index\Http\Routing\Routes\ExactRoute;
|
||||
use Misuzu\Pagination;
|
||||
use Misuzu\Forum\ForumContext;
|
||||
use Misuzu\Profile\ProfileContext;
|
||||
|
@ -22,8 +27,8 @@ final class SatoriRoutes implements RouteHandler {
|
|||
) {}
|
||||
|
||||
/** @return void|int */
|
||||
#[HttpMiddleware('/_satori')]
|
||||
public function verifyRequest(HttpResponseBuilder $response, HttpRequest $request) {
|
||||
#[PrefixFilter('/_satori')]
|
||||
public function verifyRequest(HttpRequest $request) {
|
||||
$secretKey = $this->config->getString('secret');
|
||||
|
||||
if(!empty($secretKey)) {
|
||||
|
@ -34,7 +39,7 @@ final class SatoriRoutes implements RouteHandler {
|
|||
if(empty($userHash) || $userTime < $currentTime - 60 || $userTime > $currentTime + 60)
|
||||
return 403;
|
||||
|
||||
$verifyText = (string)$userTime . '#' . $request->path . '?' . $request->getParamString();
|
||||
$verifyText = (string)$userTime . '#' . $request->requestTarget . '?' . $request->getParamString(true);
|
||||
$verifyHash = hash_hmac('sha256', $verifyText, $secretKey, true);
|
||||
|
||||
if(!hash_equals($verifyHash, $userHash))
|
||||
|
@ -43,10 +48,10 @@ final class SatoriRoutes implements RouteHandler {
|
|||
}
|
||||
|
||||
/** @return array{error: int}|array{field_value: string} */
|
||||
#[HttpGet('/_satori/get-profile-field')]
|
||||
public function getProfileField(HttpResponseBuilder $response, HttpRequest $request): array {
|
||||
$userId = (string)$request->getParam('user', FILTER_SANITIZE_NUMBER_INT);
|
||||
$fieldId = (string)$request->getParam('field', FILTER_SANITIZE_NUMBER_INT);
|
||||
#[ExactRoute('GET', '/_satori/get-profile-field')]
|
||||
public function getProfileField(HttpRequest $request): array {
|
||||
$userId = (string)$request->getFilteredParam('user', FILTER_SANITIZE_NUMBER_INT);
|
||||
$fieldId = (string)$request->getFilteredParam('field', FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
try {
|
||||
$fieldValue = $this->profileCtx->fields->getFieldValue($fieldId, $userId);
|
||||
|
@ -72,15 +77,15 @@ final class SatoriRoutes implements RouteHandler {
|
|||
* is_opening_post: int
|
||||
* }[]
|
||||
*/
|
||||
#[HttpGet('/_satori/get-recent-forum-posts')]
|
||||
public function getRecentForumPosts(HttpResponseBuilder $response, HttpRequest $request): array {
|
||||
#[ExactRoute('GET', '/_satori/get-recent-forum-posts')]
|
||||
public function getRecentForumPosts(HttpRequest $request): array {
|
||||
$categoryIds = $this->config->getArray('forum.categories');
|
||||
if(empty($categoryIds))
|
||||
return [];
|
||||
|
||||
$batchSize = $this->config->getInteger('forum.batch', 6);
|
||||
$backlogDays = $this->config->getInteger('forum.backlog', 7);
|
||||
$startId = (string)$request->getParam('start', FILTER_SANITIZE_NUMBER_INT);
|
||||
$startId = (string)$request->getFilteredParam('start', FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
$posts = [];
|
||||
$postInfos = $this->forumCtx->posts->getPosts(
|
||||
|
@ -120,11 +125,11 @@ final class SatoriRoutes implements RouteHandler {
|
|||
* username: string
|
||||
* }[]
|
||||
*/
|
||||
#[HttpGet('/_satori/get-recent-registrations')]
|
||||
public function getRecentRegistrations(HttpResponseBuilder $response, HttpRequest $request): array {
|
||||
#[ExactRoute('GET', '/_satori/get-recent-registrations')]
|
||||
public function getRecentRegistrations(HttpRequest $request): array {
|
||||
$batchSize = $this->config->getInteger('users.batch', 10);
|
||||
$backlogDays = $this->config->getInteger('users.backlog', 7);
|
||||
$startId = (string)$request->getParam('start', FILTER_SANITIZE_NUMBER_INT);
|
||||
$startId = (string)$request->getFilteredParam('start', FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
$userInfos = $this->usersCtx->users->getUsers(
|
||||
after: $startId,
|
||||
|
|
|
@ -4,8 +4,12 @@ namespace Misuzu\SharpChat;
|
|||
use RuntimeException;
|
||||
use Index\Colour\Colour;
|
||||
use Index\Config\Config;
|
||||
use Index\Http\{FormHttpContent,HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Routing\{HandlerAttribute,HttpDelete,HttpGet,HttpOptions,HttpPost,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Content\{FormContent,UrlEncodedFormContent};
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\AccessControl\AccessControl;
|
||||
use Index\Http\Routing\Processors\Before;
|
||||
use Index\Http\Routing\Routes\ExactRoute;
|
||||
use Index\Urls\UrlRegistry;
|
||||
use Misuzu\Auth\{AuthContext,AuthInfo,Sessions};
|
||||
use Misuzu\Counters\CountersData;
|
||||
|
@ -34,17 +38,10 @@ final class SharpChatRoutes implements RouteHandler {
|
|||
$this->hashKey = $this->config->getString('hashKey', 'woomy');
|
||||
}
|
||||
|
||||
/** @return int|array{Text: string[], Image: string, Hierarchy: int}[] */
|
||||
#[HttpOptions('/_sockchat/emotes')]
|
||||
#[HttpGet('/_sockchat/emotes')]
|
||||
public function getEmotes(HttpResponseBuilder $response, HttpRequest $request): array|int {
|
||||
$response->setHeader('Access-Control-Allow-Origin', '*');
|
||||
$response->setHeader('Access-Control-Allow-Methods', 'GET');
|
||||
$response->setHeader('Access-Control-Allow-Headers', 'Cache-Control');
|
||||
|
||||
if($request->method === 'OPTIONS')
|
||||
return 204;
|
||||
|
||||
/** @return array{Text: string[], Image: string, Hierarchy: int}[] */
|
||||
#[AccessControl(allowHeaders: ['Cache-Control'])]
|
||||
#[ExactRoute('GET', '/_sockchat/emotes')]
|
||||
public function getEmotes(): array {
|
||||
$this->counters->increment('dev:legacy_emotes_loads');
|
||||
|
||||
$emotes = $this->emotes->getEmotes(orderBy: 'order');
|
||||
|
@ -66,7 +63,7 @@ final class SharpChatRoutes implements RouteHandler {
|
|||
return $out;
|
||||
}
|
||||
|
||||
#[HttpGet('/_sockchat/login')]
|
||||
#[ExactRoute('GET', '/_sockchat/login')]
|
||||
public function getLogin(HttpResponseBuilder $response, HttpRequest $request): void {
|
||||
if(!$this->authInfo->loggedIn) {
|
||||
$response->redirect($this->urls->format('auth-login'));
|
||||
|
@ -87,34 +84,11 @@ final class SharpChatRoutes implements RouteHandler {
|
|||
return in_array($targetId, $whitelist, true);
|
||||
}
|
||||
|
||||
/** @return int|array{ok: false, err: string}|array{ok: true, usr: int, tkn: string} */
|
||||
#[HttpOptions('/_sockchat/token')]
|
||||
#[HttpGet('/_sockchat/token')]
|
||||
public function getToken(HttpResponseBuilder $response, HttpRequest $request): int|array {
|
||||
$host = $request->hasHeader('Host') ? $request->getHeaderFirstLine('Host') : '';
|
||||
$origin = $request->hasHeader('Origin') ? $request->getHeaderFirstLine('Origin') : '';
|
||||
$originHost = strtolower(parse_url($origin, PHP_URL_HOST) ?? '');
|
||||
|
||||
if(!empty($originHost) && $originHost !== $host) {
|
||||
$whitelist = $this->config->getArray('origins', []);
|
||||
|
||||
if(!in_array($originHost, $whitelist))
|
||||
return 403;
|
||||
|
||||
$originProto = strtolower(parse_url($origin, PHP_URL_SCHEME));
|
||||
$origin = $originProto . '://' . $originHost;
|
||||
|
||||
$response->setHeader('Access-Control-Allow-Origin', $origin);
|
||||
$response->setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
|
||||
$response->setHeader('Access-Control-Allow-Credentials', 'true');
|
||||
$response->setHeader('Vary', 'Origin');
|
||||
}
|
||||
|
||||
if($request->method === 'OPTIONS')
|
||||
return 204;
|
||||
|
||||
/** @return array{ok: false, err: string}|array{ok: true, usr: int, tkn: string} */
|
||||
#[AccessControl(credentials: true)]
|
||||
#[ExactRoute('GET', '/_sockchat/token')]
|
||||
public function getToken(HttpRequest $request): array {
|
||||
$tokenInfo = $this->authInfo->tokenInfo;
|
||||
|
||||
if(!$tokenInfo->hasSessionToken)
|
||||
return ['ok' => false, 'err' => 'token'];
|
||||
|
||||
|
@ -144,25 +118,28 @@ final class SharpChatRoutes implements RouteHandler {
|
|||
}
|
||||
|
||||
/** @return int|void */
|
||||
#[HttpPost('/_sockchat/bump')]
|
||||
public function postBump(HttpResponseBuilder $response, HttpRequest $request) {
|
||||
#[ExactRoute('POST', '/_sockchat/bump')]
|
||||
#[Before('input:urlencoded')]
|
||||
public function postBump(HttpRequest $request, UrlEncodedFormContent $content) {
|
||||
if(!$request->hasHeader('X-SharpChat-Signature'))
|
||||
return 400;
|
||||
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
return 400;
|
||||
$bumpList = [];
|
||||
foreach($content->params as $name => $value) {
|
||||
if(count($value) < 1)
|
||||
continue;
|
||||
|
||||
$bumpList = $request->content->getParam('u', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
|
||||
if(!is_array($bumpList))
|
||||
return 400;
|
||||
if(str_starts_with($name, 'u[') && str_ends_with($name, ']'))
|
||||
$bumpList[substr($name, 2, -1)] = $value[0];
|
||||
}
|
||||
|
||||
$userTime = (int)$request->content->getParam('t', FILTER_SANITIZE_NUMBER_INT);
|
||||
$userTime = (int)$content->getFilteredParam('t', FILTER_SANITIZE_NUMBER_INT);
|
||||
$signature = "bump#{$userTime}";
|
||||
|
||||
foreach($bumpList as $userId => $ipAddr)
|
||||
$signature .= "#{$userId}:{$ipAddr}";
|
||||
|
||||
$userHash = (string)$request->getHeaderFirstLine('X-SharpChat-Signature');
|
||||
$userHash = (string)$request->getHeaderLine('X-SharpChat-Signature');
|
||||
$realHash = hash_hmac('sha256', $signature, $this->hashKey);
|
||||
if(!hash_equals($realHash, $userHash))
|
||||
return 403;
|
||||
|
@ -185,19 +162,17 @@ final class SharpChatRoutes implements RouteHandler {
|
|||
* super: bool
|
||||
* }
|
||||
*/
|
||||
#[HttpPost('/_sockchat/verify')]
|
||||
public function postVerify(HttpResponseBuilder $response, HttpRequest $request): int|array {
|
||||
#[ExactRoute('POST', '/_sockchat/verify')]
|
||||
#[Before('input:urlencoded')]
|
||||
public function postVerify(HttpRequest $request, FormContent $content): int|array {
|
||||
if(!$request->hasHeader('X-SharpChat-Signature'))
|
||||
return 400;
|
||||
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
return ['success' => false, 'reason' => 'request'];
|
||||
$authMethod = (string)$content->getParam('method');
|
||||
$authToken = (string)$content->getParam('token');
|
||||
$ipAddress = (string)$content->getParam('ipaddr');
|
||||
|
||||
$authMethod = (string)$request->content->getParam('method');
|
||||
$authToken = (string)$request->content->getParam('token');
|
||||
$ipAddress = (string)$request->content->getParam('ipaddr');
|
||||
|
||||
$userHash = (string)$request->getHeaderFirstLine('X-SharpChat-Signature');
|
||||
$userHash = (string)$request->getHeaderLine('X-SharpChat-Signature');
|
||||
if(strlen($userHash) !== 64)
|
||||
return ['success' => false, 'reason' => 'length'];
|
||||
|
||||
|
@ -291,13 +266,13 @@ final class SharpChatRoutes implements RouteHandler {
|
|||
* expires: string
|
||||
* }[]
|
||||
*/
|
||||
#[HttpGet('/_sockchat/bans/list')]
|
||||
public function getBanList(HttpResponseBuilder $response, HttpRequest $request): int|array {
|
||||
#[ExactRoute('GET', '/_sockchat/bans/list')]
|
||||
public function getBanList(HttpRequest $request): int|array {
|
||||
if(!$request->hasHeader('X-SharpChat-Signature'))
|
||||
return 400;
|
||||
|
||||
$userHash = (string)$request->getHeaderFirstLine('X-SharpChat-Signature');
|
||||
$userTime = (int)$request->getParam('x', FILTER_SANITIZE_NUMBER_INT);
|
||||
$userHash = (string)$request->getHeaderLine('X-SharpChat-Signature');
|
||||
$userTime = (int)$request->getFilteredParam('x', FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
$realHash = hash_hmac('sha256', "list#{$userTime}", $this->hashKey);
|
||||
if(!hash_equals($realHash, $userHash) || $userTime < time() - 60)
|
||||
|
@ -335,16 +310,16 @@ final class SharpChatRoutes implements RouteHandler {
|
|||
* expires: string
|
||||
* }
|
||||
*/
|
||||
#[HttpGet('/_sockchat/bans/check')]
|
||||
public function getBanCheck(HttpResponseBuilder $response, HttpRequest $request): int|array {
|
||||
#[ExactRoute('GET', '/_sockchat/bans/check')]
|
||||
public function getBanCheck(HttpRequest $request): int|array {
|
||||
if(!$request->hasHeader('X-SharpChat-Signature'))
|
||||
return 400;
|
||||
|
||||
$userHash = (string)$request->getHeaderFirstLine('X-SharpChat-Signature');
|
||||
$userTime = (int)$request->getParam('x', FILTER_SANITIZE_NUMBER_INT);
|
||||
$userHash = (string)$request->getHeaderLine('X-SharpChat-Signature');
|
||||
$userTime = (int)$request->getFilteredParam('x', FILTER_SANITIZE_NUMBER_INT);
|
||||
$ipAddress = (string)$request->getParam('a');
|
||||
$userId = (string)$request->getParam('u');
|
||||
$userIdIsName = (int)$request->getParam('n', FILTER_SANITIZE_NUMBER_INT);
|
||||
$userIdIsName = (int)$request->getFilteredParam('n', FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
$realHash = hash_hmac('sha256', "check#{$userTime}#{$userId}#{$ipAddress}#{$userIdIsName}", $this->hashKey);
|
||||
if(!hash_equals($realHash, $userHash) || $userTime < time() - 60)
|
||||
|
@ -371,23 +346,21 @@ final class SharpChatRoutes implements RouteHandler {
|
|||
];
|
||||
}
|
||||
|
||||
#[HttpPost('/_sockchat/bans/create')]
|
||||
public function postBanCreate(HttpResponseBuilder $response, HttpRequest $request): int {
|
||||
#[ExactRoute('POST', '/_sockchat/bans/create')]
|
||||
#[Before('input:urlencoded')]
|
||||
public function postBanCreate(HttpRequest $request, FormContent $content): int {
|
||||
if(!$request->hasHeader('X-SharpChat-Signature'))
|
||||
return 400;
|
||||
|
||||
if(!($request->content instanceof FormHttpContent))
|
||||
return 400;
|
||||
|
||||
$userHash = (string)$request->getHeaderFirstLine('X-SharpChat-Signature');
|
||||
$userTime = (int)$request->content->getParam('t', FILTER_SANITIZE_NUMBER_INT);
|
||||
$userId = (string)$request->content->getParam('ui', FILTER_SANITIZE_NUMBER_INT);
|
||||
$userAddr = (string)$request->content->getParam('ua');
|
||||
$modId = (string)$request->content->getParam('mi', FILTER_SANITIZE_NUMBER_INT);
|
||||
$modAddr = (string)$request->content->getParam('ma');
|
||||
$duration = (int)$request->content->getParam('d', FILTER_SANITIZE_NUMBER_INT);
|
||||
$isPermanent = (int)$request->content->getParam('p', FILTER_SANITIZE_NUMBER_INT);
|
||||
$reason = (string)$request->content->getParam('r');
|
||||
$userHash = (string)$request->getHeaderLine('X-SharpChat-Signature');
|
||||
$userTime = (int)$content->getFilteredParam('t', FILTER_SANITIZE_NUMBER_INT);
|
||||
$userId = (string)$content->getFilteredParam('ui', FILTER_SANITIZE_NUMBER_INT);
|
||||
$userAddr = (string)$content->getParam('ua');
|
||||
$modId = (string)$content->getFilteredParam('mi', FILTER_SANITIZE_NUMBER_INT);
|
||||
$modAddr = (string)$content->getParam('ma');
|
||||
$duration = (int)$content->getFilteredParam('d', FILTER_SANITIZE_NUMBER_INT);
|
||||
$isPermanent = (int)$content->getFilteredParam('p', FILTER_SANITIZE_NUMBER_INT);
|
||||
$reason = (string)$content->getParam('r');
|
||||
|
||||
$signature = implode('#', [
|
||||
'create', $userTime, $userId, $userAddr,
|
||||
|
@ -447,13 +420,13 @@ final class SharpChatRoutes implements RouteHandler {
|
|||
return 201;
|
||||
}
|
||||
|
||||
#[HttpDelete('/_sockchat/bans/revoke')]
|
||||
public function deleteBanRevoke(HttpResponseBuilder $response, HttpRequest $request): int {
|
||||
#[ExactRoute('DELETE', '/_sockchat/bans/revoke')]
|
||||
public function deleteBanRevoke(HttpRequest $request): int {
|
||||
if(!$request->hasHeader('X-SharpChat-Signature'))
|
||||
return 400;
|
||||
|
||||
$userHash = (string)$request->getHeaderFirstLine('X-SharpChat-Signature');
|
||||
$userTime = (int)$request->getParam('x', FILTER_SANITIZE_NUMBER_INT);
|
||||
$userHash = (string)$request->getHeaderLine('X-SharpChat-Signature');
|
||||
$userTime = (int)$request->getFilteredParam('x', FILTER_SANITIZE_NUMBER_INT);
|
||||
$type = (string)$request->getParam('t');
|
||||
$subject = (string)$request->getParam('s');
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ final class Tools {
|
|||
return false;
|
||||
|
||||
if(isset($parsed['scheme'])) {
|
||||
$isSecure ??= filter_has_var(INPUT_SERVER, 'HTTPS');
|
||||
$isSecure ??= !empty($_SERVER['HTTPS']);
|
||||
|
||||
// only allow https when secure
|
||||
if($isSecure && $parsed['scheme'] !== 'https')
|
||||
|
@ -28,7 +28,7 @@ final class Tools {
|
|||
}
|
||||
|
||||
if(isset($parsed['host'])) {
|
||||
$host ??= filter_input(INPUT_SERVER, 'HTTP_HOST');
|
||||
$host ??= $_SERVER['HTTP_HOST'];
|
||||
|
||||
// ensure host is identical
|
||||
if($parsed['host'] !== $host)
|
||||
|
|
|
@ -5,6 +5,7 @@ use InvalidArgumentException;
|
|||
use RuntimeException;
|
||||
use Index\Http\{HttpRequest,HttpResponseBuilder};
|
||||
use Index\Http\Routing\{HttpGet,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\Routes\{ExactRoute,PatternRoute};
|
||||
use Index\Urls\{UrlFormat,UrlRegistry,UrlSource,UrlSourceCommon};
|
||||
use Misuzu\{Misuzu,Perm};
|
||||
use Misuzu\Auth\AuthInfo;
|
||||
|
@ -24,14 +25,14 @@ class AssetsRoutes implements RouteHandler, UrlSource {
|
|||
// 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');
|
||||
&& parse_url($request->getHeaderLine('Referer'), PHP_URL_PATH) === $this->urls->format('user-profile');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @return void */
|
||||
#[HttpGet('/assets/avatar')]
|
||||
#[HttpGet('/assets/avatar/([0-9]+)(?:\.[a-z]+)?')]
|
||||
#[ExactRoute('GET', '/assets/avatar')]
|
||||
#[PatternRoute('GET', '/assets/avatar/([0-9]+)(?:\.[a-z]+)?')]
|
||||
#[UrlFormat('user-avatar', '/assets/avatar/<user>', ['res' => '<res>'])]
|
||||
public function getAvatar(HttpResponseBuilder $response, HttpRequest $request, string $userId = '') {
|
||||
$assetInfo = new StaticUserImageAsset(Misuzu::PATH_PUBLIC . '/images/no-avatar.png', Misuzu::PATH_PUBLIC);
|
||||
|
@ -53,8 +54,8 @@ class AssetsRoutes implements RouteHandler, UrlSource {
|
|||
}
|
||||
|
||||
/** @return string|void */
|
||||
#[HttpGet('/assets/profile-background')]
|
||||
#[HttpGet('/assets/profile-background/([0-9]+)(?:\.[a-z]+)?')]
|
||||
#[ExactRoute('GET', '/assets/profile-background')]
|
||||
#[PatternRoute('GET', '/assets/profile-background/([0-9]+)(?:\.[a-z]+)?')]
|
||||
#[UrlFormat('user-background', '/assets/profile-background/<user>')]
|
||||
public function getProfileBackground(HttpResponseBuilder $response, HttpRequest $request, string $userId = '') {
|
||||
try {
|
||||
|
@ -78,9 +79,9 @@ class AssetsRoutes implements RouteHandler, UrlSource {
|
|||
}
|
||||
|
||||
/** @return string|void */
|
||||
#[HttpGet('/user-assets.php')]
|
||||
#[ExactRoute('GET', '/user-assets.php')]
|
||||
public function getUserAssets(HttpResponseBuilder $response, HttpRequest $request) {
|
||||
$userId = (string)$request->getParam('u', FILTER_SANITIZE_NUMBER_INT);
|
||||
$userId = (string)$request->getFilteredParam('u', FILTER_SANITIZE_NUMBER_INT);
|
||||
$mode = (string)$request->getParam('m');
|
||||
|
||||
if($mode === 'avatar') {
|
||||
|
@ -102,8 +103,8 @@ class AssetsRoutes implements RouteHandler, UrlSource {
|
|||
$fileName = $assetInfo->getFileName();
|
||||
|
||||
if($assetInfo instanceof UserAssetScalableInterface) {
|
||||
$dimensions = (int)($request->getParam('res', FILTER_SANITIZE_NUMBER_INT)
|
||||
?? $request->getParam('r', FILTER_SANITIZE_NUMBER_INT));
|
||||
$dimensions = (int)($request->getFilteredParam('res', FILTER_SANITIZE_NUMBER_INT)
|
||||
?? $request->getFilteredParam('r', FILTER_SANITIZE_NUMBER_INT));
|
||||
|
||||
if($dimensions > 0) {
|
||||
$assetInfo->ensureScaledExists($dimensions);
|
||||
|
|
|
@ -3,7 +3,9 @@ namespace Misuzu\WebFinger;
|
|||
|
||||
use Index\XArray;
|
||||
use Index\Http\{HttpResponseBuilder,HttpRequest};
|
||||
use Index\Http\Routing\{HttpGet,HttpOptions,RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\{RouteHandler,RouteHandlerCommon};
|
||||
use Index\Http\Routing\AccessControl\AccessControl;
|
||||
use Index\Http\Routing\Routes\ExactRoute;
|
||||
|
||||
class WebFingerRoutes implements RouteHandler {
|
||||
use RouteHandlerCommon;
|
||||
|
@ -69,13 +71,9 @@ class WebFingerRoutes implements RouteHandler {
|
|||
* }[]
|
||||
* }
|
||||
*/
|
||||
#[HttpOptions('/.well-known/webfinger')]
|
||||
#[HttpGet('/.well-known/webfinger')]
|
||||
#[AccessControl]
|
||||
#[ExactRoute('GET', '/.well-known/webfinger')]
|
||||
public function getWebFinger(HttpResponseBuilder $response, HttpRequest $request): array|int {
|
||||
$response->setHeader('Access-Control-Allow-Origin', '*');
|
||||
if($request->method === 'OPTIONS')
|
||||
return 204;
|
||||
|
||||
$resInfos = $this->registry->resolve(trim((string)$request->getParam('resource')));
|
||||
if(empty($resInfos))
|
||||
return 404;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
{% block content %}
|
||||
<form class="container auth__container auth__login js-login-form" method="post" action="{{ url('auth-login') }}">
|
||||
{{ input_csrf() }}
|
||||
{{ input_hidden('login[redirect]', login_redirect) }}
|
||||
{{ input_hidden('redirect', login_redirect) }}
|
||||
|
||||
<div class="container__title">
|
||||
<div class="container__title__background"></div>
|
||||
|
@ -45,7 +45,7 @@
|
|||
Username
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('login[username]', 'auth__label__input js-login-username', login_username, 'text', '', true, null, 1, not login_welcome) }}
|
||||
{{ input_text('username', 'auth__label__input js-login-username', login_username, 'text', '', true, null, 1, not login_welcome) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
@ -57,7 +57,7 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('login[password]', 'auth__label__input', '', 'password', '', true, null, 2, login_welcome) }}
|
||||
{{ input_text('password', 'auth__label__input', '', 'password', '', true, null, 2, login_welcome) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
E-mail
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('forgot[email]', 'auth__label__input', password_email, 'text', '', true, null, 1) }}
|
||||
{{ input_text('email', 'auth__label__input', password_email, 'text', '', true, null, 1) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<form class="container auth__container auth__password" method="post" action="{{ url('auth-reset') }}">
|
||||
{{ container_title('<i class="fas fa-user-lock fa-fw"></i> Resetting password for ' ~ password_user.name) }}
|
||||
|
||||
{{ input_hidden('reset[user]', password_user.id) }}
|
||||
{{ input_hidden('user', password_user.id) }}
|
||||
{{ input_csrf() }}
|
||||
|
||||
{% if password_notices|length > 0 %}
|
||||
|
@ -28,14 +28,14 @@
|
|||
{% endif %}
|
||||
|
||||
{% if password_verification|length == 12 %}
|
||||
{{ input_hidden('reset[verification]', password_verification) }}
|
||||
{{ input_hidden('verification', password_verification) }}
|
||||
{% else %}
|
||||
<label class="auth__label">
|
||||
<div class="auth__label__text">
|
||||
Verification Code
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('reset[verification]', 'input__text--monospace auth__label__input', '', 'text', '', true, {'maxlength':12}, 1) }}
|
||||
{{ input_text('verification', 'input__text--monospace auth__label__input', '', 'text', '', true, {'maxlength':12}, 1) }}
|
||||
</div>
|
||||
</label>
|
||||
{% endif %}
|
||||
|
@ -45,7 +45,7 @@
|
|||
New Password
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('reset[password][new]', 'auth__label__input', '', 'password', '', true, null, 2) }}
|
||||
{{ input_text('password_new', 'auth__label__input', '', 'password', '', true, null, 2) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
@ -54,7 +54,7 @@
|
|||
Confirm Password
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('reset[password][confirm]', 'auth__label__input', '', 'password', '', true, null, 3) }}
|
||||
{{ input_text('password_confirm', 'auth__label__input', '', 'password', '', true, null, 3) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
Username
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('register[username]', 'auth__label__input', register_username, 'text', '', true, null, 10, true) }}
|
||||
{{ input_text('username', 'auth__label__input', register_username, 'text', '', true, null, 10, true) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
@ -64,7 +64,7 @@
|
|||
Password
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('register[password]', 'auth__label__input', '', 'password', '', true, null, 20) }}
|
||||
{{ input_text('password', 'auth__label__input', '', 'password', '', true, null, 20) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
@ -73,7 +73,7 @@
|
|||
Confirm Password
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('register[password_confirm]', 'auth__label__input', '', 'password', '', true, null, 30) }}
|
||||
{{ input_text('password_confirm', 'auth__label__input', '', 'password', '', true, null, 30) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
@ -82,7 +82,7 @@
|
|||
E-mail
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('register[email]', 'auth__label__input', register_email, 'text', '', true, null, 40) }}
|
||||
{{ input_text('email', 'auth__label__input', register_email, 'text', '', true, null, 40) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
@ -91,7 +91,7 @@
|
|||
What is the outcome of nine plus ten?
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('register[question]', 'auth__label__input', '', 'text', '', true, null, 50) }}
|
||||
{{ input_text('question', 'auth__label__input', '', 'text', '', true, null, 50) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
{{ container_title('<i class="fas fa-user-shield fa-fw"></i> Two Factor Authentication') }}
|
||||
|
||||
{{ input_csrf() }}
|
||||
{{ input_hidden('twofactor[redirect]', twofactor_redirect) }}
|
||||
{{ input_hidden('twofactor[token]', twofactor_token) }}
|
||||
{{ input_hidden('redirect', twofactor_redirect) }}
|
||||
{{ input_hidden('token', twofactor_token) }}
|
||||
|
||||
{% if twofactor_notices|length > 0 %}
|
||||
<div class="warning auth__warning">
|
||||
|
@ -27,7 +27,7 @@
|
|||
Code
|
||||
</div>
|
||||
<div class="auth__label__value">
|
||||
{{ input_text('twofactor[code]', 'input__text--monospace input__text--centre auth__label__input', '', 'text', '', true, {'maxlength': 6, 'inputmode': 'numeric'}, 1) }}
|
||||
{{ input_text('code', 'input__text--monospace input__text--centre auth__label__input', '', 'text', '', true, {'maxlength': 6, 'inputmode': 'numeric'}, 1) }}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
|
|
@ -8,15 +8,15 @@
|
|||
{% set is_opening = not is_reply or posting_post.isOriginalPost|default(false) %}
|
||||
|
||||
{% block content %}
|
||||
<form method="post" action="{{ url('forum-' ~ (is_reply ? 'post' : 'topic') ~ '-create') }}" class="js-forum-posting">
|
||||
{{ input_hidden('post[' ~ (is_reply ? 'topic' : 'forum') ~ ']', is_reply ? posting_topic.id : posting_forum.id) }}
|
||||
{{ input_hidden('post[mode]', posting_mode) }}
|
||||
<form method="post" action="{{ url('forum-' ~ (is_reply ? 'post' : 'topic') ~ '-create') }}" class="js-forum-posting" enctype="multipart/form-data">
|
||||
{{ input_hidden((is_reply ? 'topic' : 'forum'), is_reply ? posting_topic.id : posting_forum.id) }}
|
||||
{{ input_hidden('mode', posting_mode) }}
|
||||
{{ input_csrf() }}
|
||||
{{ forum_header(
|
||||
is_reply and not is_opening
|
||||
? posting_topic.title
|
||||
: input_text(
|
||||
'post[title]',
|
||||
'title',
|
||||
'forum__header__input',
|
||||
posting_defaults.title|default(posting_topic.title|default('')),
|
||||
'text',
|
||||
|
@ -30,7 +30,7 @@
|
|||
) }}
|
||||
|
||||
{% if posting_post is defined %}
|
||||
{{ input_hidden('post[id]', posting_post.info.id) }}
|
||||
{{ input_hidden('id', posting_post.info.id) }}
|
||||
{% endif %}
|
||||
|
||||
{% if posting_notices|length > 0 %}
|
||||
|
@ -74,25 +74,25 @@
|
|||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<textarea name="post[text]" class="forum__post__text forum__post__text--edit js-forum-posting-text js-ctrl-enter-submit" placeholder="Type your post content here...">{{ posting_defaults.text|default(posting_post.info.body|default('')) }}</textarea>
|
||||
<textarea name="text" class="forum__post__text forum__post__text--edit js-forum-posting-text js-ctrl-enter-submit" placeholder="Type your post content here...">{{ posting_defaults.text|default(posting_post.info.body|default('')) }}</textarea>
|
||||
<div class="forum__post__text js-forum-posting-preview" hidden></div>
|
||||
<div class="forum__post__actions js-forum-posting-actions"></div>
|
||||
<div class="forum__post__options">
|
||||
<div class="forum__post__settings">
|
||||
{{ input_select(
|
||||
'post[parser]', parser_options(),
|
||||
'parser', parser_options(),
|
||||
posting_defaults.parser|default(posting_post.info.bodyFormat|default(posting_user_preferred_parser)).value,
|
||||
null, null, false, 'forum__post__dropdown js-forum-posting-parser'
|
||||
) }}
|
||||
{% if is_opening and posting_types|length > 1 %}
|
||||
<select class="input__select forum__post__dropdown" name="post[type]">
|
||||
<select class="input__select forum__post__dropdown" name="type">
|
||||
{% for type_name, type_title in posting_types %}
|
||||
<option value="{{ type_name }}"{% if type_name == posting_topic.typeString|default('discussion') %} selected{% endif %}>{{ type_title }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endif %}
|
||||
{{ input_checkbox(
|
||||
'post[signature]',
|
||||
'signature',
|
||||
'Display Signature',
|
||||
posting_defaults.signature is not null
|
||||
? posting_defaults.signature : (
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<div class="container">
|
||||
{{ container_title('<i class="fas fa-ban fa-fw"></i> Issuing a ban on user #' ~ ban_user.id ~ ' ' ~ ban_user.name) }}
|
||||
|
||||
<form method="post" enctype="multipart/form-data" action="{{ url('manage-users-ban', {'user': ban_user.id}) }}" class="manage__ban">
|
||||
<form method="post" action="{{ url('manage-users-ban', {'user': ban_user.id}) }}" class="manage__ban">
|
||||
{{ input_csrf() }}
|
||||
|
||||
<div class="manage__ban__field">
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<div class="container">
|
||||
{{ container_title('<i class="fas fa-sticky-note fa-fw"></i> ' ~ (note_new ? ('Adding mod note to ' ~ note_user.name) : ('Editing mod note #' ~ note_info.id))) }}
|
||||
|
||||
<form method="post" enctype="multipart/form-data" action="{{ url('manage-users-note', note_new ? {'user': note_user.id} : {'note': note_info.id}) }}" class="manage__note {{ note_new ? 'manage__note--edit' : 'manage__note--view' }}">
|
||||
<form method="post" action="{{ url('manage-users-note', note_new ? {'user': note_user.id} : {'note': note_info.id}) }}" class="manage__note {{ note_new ? 'manage__note--edit' : 'manage__note--view' }}">
|
||||
{{ input_csrf() }}
|
||||
|
||||
<div class="manage__note__header">
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<div class="container">
|
||||
{{ container_title('<i class="fas fa-exclamation-circle fa-fw"></i> Issuing a warning to user #' ~ warn_user.id ~ ' ' ~ warn_user.name) }}
|
||||
|
||||
<form method="post" enctype="multipart/form-data" action="{{ url('manage-users-warning', {'user': warn_user.id}) }}" class="manage__warning">
|
||||
<form method="post" action="{{ url('manage-users-warning', {'user': warn_user.id}) }}" class="manage__warning">
|
||||
{{ input_csrf() }}
|
||||
|
||||
<div class="manage__warning__field">
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
Select
|
||||
</label>
|
||||
|
||||
{{ input_checkbox_raw('avatar[delete]', false, 'profile__header__avatar__check', '', false, {'id':'avatar-delete'}) }}
|
||||
{{ input_checkbox_raw('avatar_delete', false, 'profile__header__avatar__check', null, false, {'id':'avatar-delete'}) }}
|
||||
<label class="input__button profile__header__avatar__option profile__header__avatar__option--delete"
|
||||
for="avatar-delete">
|
||||
Remove
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
{{ input_csrf() }}
|
||||
|
||||
{% if perms.edit_avatar %}
|
||||
{{ input_file_raw('avatar[file]', 'profile__hidden', ['image/png', 'image/jpeg', 'image/gif'], {'id':'avatar-selection'}) }}
|
||||
{{ input_file_raw('avatar_file', 'profile__hidden', ['image/png', 'image/jpeg', 'image/gif'], {'id':'avatar-selection'}) }}
|
||||
|
||||
<script>
|
||||
function updateAvatarPreview(name, url, preview) {
|
||||
|
@ -120,7 +120,7 @@
|
|||
</div>
|
||||
|
||||
{% if profile_is_editing %}
|
||||
{{ input_text('profile[' ~ fieldInfo.name ~ ']', 'profile__accounts__input', profile_fields_raw_values[fieldInfo.name]|default('')) }}
|
||||
{{ input_text('profile_' ~ fieldInfo.name, 'profile__accounts__input', profile_fields_raw_values[fieldInfo.name]|default('')) }}
|
||||
{% else %}
|
||||
<div class="profile__accounts__value">
|
||||
{% if profile_fields_link_values[fieldInfo.name] is defined %}
|
||||
|
@ -212,14 +212,14 @@
|
|||
<div class="profile__birthdate__title">
|
||||
Day
|
||||
</div>
|
||||
{{ input_select('birthdate[day]', ['-']|merge(range(1, 31)), birthdate_info.day|default(0), '', '', true, 'profile__birthdate__select profile__birthdate__select--day') }}
|
||||
{{ input_select('birth_day', ['-']|merge(range(1, 31)), birthdate_info.day|default(0), '', '', true, 'profile__birthdate__select profile__birthdate__select--day') }}
|
||||
</label>
|
||||
|
||||
<label class="profile__birthdate__label">
|
||||
<div class="profile__birthdate__title">
|
||||
Month
|
||||
</div>
|
||||
{{ input_select('birthdate[month]', ['-']|merge(range(1, 12)), birthdate_info.month|default(0), '', '', true, 'profile__birthdate__select profile__birthdate__select--month') }}
|
||||
{{ input_select('birth_month', ['-']|merge(range(1, 12)), birthdate_info.month|default(0), '', '', true, 'profile__birthdate__select profile__birthdate__select--month') }}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
@ -228,7 +228,7 @@
|
|||
<div class="profile__birthdate__title">
|
||||
Year (may be left empty)
|
||||
</div>
|
||||
{{ input_select('birthdate[year]', ['-']|merge(range(null|date('Y'), null|date('Y') - 100)), birthdate_info.year|default(0), '', '', true, 'profile__birthdate__select profile__birthdate__select--year') }}
|
||||
{{ input_select('birth_year', ['-']|merge(range(null|date('Y'), null|date('Y') - 100)), birthdate_info.year|default(0), '', '', true, 'profile__birthdate__select profile__birthdate__select--year') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
<?php
|
||||
namespace Misuzu;
|
||||
|
||||
use Index\Http\{HttpHeader,HttpHeaders,HttpRequest};
|
||||
use Index\Http\{HttpRequest,HttpUri};
|
||||
use Index\Http\Streams\NullStream;
|
||||
|
||||
require_once __DIR__ . '/../misuzu.php';
|
||||
|
||||
|
@ -76,10 +77,7 @@ handleValue:
|
|||
$hostName ??= 'localhost';
|
||||
|
||||
// this should really not be necessary, mostly done to make sure the url registry is available
|
||||
$msz->createRouting(new HttpRequest('::1', true, 'XX', '1.1', 'GET', '/', [], [], new HttpHeaders([
|
||||
new HttpHeader('Host', $hostName),
|
||||
]), null));
|
||||
|
||||
$msz->createRouting(new HttpRequest('1.1', ['Host' => [$hostName]], NullStream::instance(), [], 'GET', HttpUri::createUri('/'), [], []));
|
||||
$msz->startTemplating(false);
|
||||
|
||||
$ctx = $msz->templating->load(implode(' ', array_slice($argv, $pathIndex)));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue