diff --git a/misuzu.php b/misuzu.php index 2733c280..25f14cee 100644 --- a/misuzu.php +++ b/misuzu.php @@ -371,6 +371,12 @@ MIG; // we're running this again because ob_clean breaks gzip otherwise ob_start(); + if (!mb_check_encoding()) { + http_response_code(415); + echo 'Invalid request encoding.'; + exit; + } + if (!is_readable(MSZ_STORAGE) || !is_writable(MSZ_STORAGE)) { echo 'Cannot access storage directory.'; exit; diff --git a/public/auth.php b/public/auth.php index a1288516..467dcfcf 100644 --- a/public/auth.php +++ b/public/auth.php @@ -1,9 +1,10 @@ select('m')->value()) { case 'logout': echo tpl_render('auth.logout'); break; diff --git a/public/changelog.php b/public/changelog.php index 321908e8..96d479a7 100644 --- a/public/changelog.php +++ b/public/changelog.php @@ -1,10 +1,12 @@ select('c')->value('int', 0); +$changelogDate = RequestVar::get()->select('d')->value('string', ''); +$changelogUser = RequestVar::get()->select('u')->value('int', 0); +$changelogTags = RequestVar::get()->select('t')->value('string', ''); tpl_var('comments_perms', $commentPerms = comments_get_perms(user_session_current('user_id', 0))); diff --git a/public/comments.php b/public/comments.php index 424e78d6..314de7d5 100644 --- a/public/comments.php +++ b/public/comments.php @@ -1,4 +1,6 @@ 0) { header(csrf_http_header('comments')); $commentPerms = comments_get_perms($currentUserId); -switch ($_GET['m'] ?? null) { +$commentId = RequestVar::get()->select('c')->value('int', 0); +$commentMode = RequestVar::get()->select('m')->value(); + +switch ($commentMode) { case 'pin': case 'unpin': if (!$commentPerms['can_pin']) { @@ -45,8 +50,7 @@ switch ($_GET['m'] ?? null) { break; } - $comment = (int)($_GET['c'] ?? 0); - $commentInfo = comments_post_get($comment, false); + $commentInfo = comments_post_get($commentId, false); if (!$commentInfo || $commentInfo['comment_deleted'] !== null) { echo render_info_or_json($isXHR, "This comment doesn't exist!", 400); @@ -58,7 +62,7 @@ switch ($_GET['m'] ?? null) { break; } - $isPinning = $_GET['m'] === 'pin'; + $isPinning = $commentMode === 'pin'; if ($isPinning && !empty($commentInfo['comment_pinned'])) { echo render_info_or_json($isXHR, 'This comment is already pinned.', 400); @@ -87,15 +91,14 @@ switch ($_GET['m'] ?? null) { break; } - $vote = (int)($_GET['v'] ?? MSZ_COMMENTS_VOTE_INDIFFERENT); + $vote = RequestVar::get()->select('v')->value('int', MSZ_COMMENTS_VOTE_INDIFFERENT); if (!comments_vote_type_valid($vote)) { echo render_info_or_json($isXHR, 'Invalid vote action.', 400); break; } - $comment = (int)($_GET['c'] ?? 0); - $commentInfo = comments_post_get($comment, false); + $commentInfo = comments_post_get($commentId, false); if (!$commentInfo || $commentInfo['comment_deleted'] !== null) { echo render_info_or_json($isXHR, "This comment doesn't exist!", 400); @@ -103,17 +106,17 @@ switch ($_GET['m'] ?? null) { } $voteResult = comments_vote_add( - $comment, + $commentInfo['comment_id'], user_session_current('user_id', 0), $vote ); if (!$isXHR) { - header('Location: ' . $redirect . '#comment-' . $comment); + header('Location: ' . $redirect . '#comment-' . $commentInfo['comment_id']); break; } - echo json_encode(comments_votes_get($comment)); + echo json_encode(comments_votes_get($commentInfo['comment_id'])); break; case 'delete': @@ -122,8 +125,7 @@ switch ($_GET['m'] ?? null) { break; } - $comment = (int)($_GET['c'] ?? 0); - $commentInfo = comments_post_get($comment, false); + $commentInfo = comments_post_get($commentId, false); if (!$commentInfo) { echo render_info_or_json($isXHR, "This comment doesn't exist.", 400); @@ -147,19 +149,19 @@ switch ($_GET['m'] ?? null) { break; } - if (!comments_post_delete($comment)) { + if (!comments_post_delete($commentInfo['comment_id'])) { echo render_info_or_json($isXHR, 'Failed to delete comment.', 500); break; } if ($isModAction) { audit_log(MSZ_AUDIT_COMMENT_ENTRY_DELETE_MOD, $currentUserId, [ - $comment, + $commentInfo['comment_id'], (int)($commentInfo['user_id'] ?? 0), $commentInfo['username'] ?? '(Deleted User)', ]); } else { - audit_log(MSZ_AUDIT_COMMENT_ENTRY_DELETE, $currentUserId, [$comment]); + audit_log(MSZ_AUDIT_COMMENT_ENTRY_DELETE, $currentUserId, [$commentInfo['comment_id']]); } if ($redirect) { @@ -168,7 +170,7 @@ switch ($_GET['m'] ?? null) { } echo json_encode([ - 'id' => $comment, + 'id' => $commentInfo['comment_id'], ]); break; @@ -178,8 +180,7 @@ switch ($_GET['m'] ?? null) { break; } - $comment = (int)($_GET['c'] ?? 0); - $commentInfo = comments_post_get($comment, false); + $commentInfo = comments_post_get($commentId, false); if (!$commentInfo) { echo render_info_or_json($isXHR, "This comment doesn't exist.", 400); @@ -197,18 +198,18 @@ switch ($_GET['m'] ?? null) { } audit_log(MSZ_AUDIT_COMMENT_ENTRY_RESTORE, $currentUserId, [ - $comment, + $commentInfo['comment_id'], (int)($commentInfo['user_id'] ?? 0), $commentInfo['username'] ?? '(Deleted User)', ]); if ($redirect) { - header('Location: ' . $redirect . '#comment-' . $comment); + header('Location: ' . $redirect . '#comment-' . $commentInfo['comment_id']); break; } echo json_encode([ - 'id' => $comment, + 'id' => $commentInfo['comment_id'], ]); break; diff --git a/public/forum/index.php b/public/forum/index.php index 47c0be44..5b3993ab 100644 --- a/public/forum/index.php +++ b/public/forum/index.php @@ -1,9 +1,11 @@ select('m')->string()) { case 'mark': - $forumId = (int)($_GET['f'] ?? null); + $forumId = RequestVar::get()->select('f')->int(); $markEntireForum = $forumId === 0; if (user_session_active() && csrf_verify('forum_mark', $_GET['c'] ?? '')) { diff --git a/public/forum/post.php b/public/forum/post.php index 097fc65d..4226e6ee 100644 --- a/public/forum/post.php +++ b/public/forum/post.php @@ -1,8 +1,10 @@ select('p')->int(); +$postMode = RequestVar::get()->select('m')->string(); // basing whether or not this is an xhr request on whether a referrer header is present // this page is never directy accessed, under normal circumstances diff --git a/public/forum/posting.php b/public/forum/posting.php index a7cf66c2..db28fe7e 100644 --- a/public/forum/posting.php +++ b/public/forum/posting.php @@ -1,4 +1,6 @@ post->mode->string('create'); + $postId = max(0, RequestVar::post()->post->id->int()); + $topicId = max(0, RequestVar::post()->post->topic->int()); + $forumId = max(0, RequestVar::post()->post->forum->int()); } else { - $mode = $_GET['m'] ?? 'create'; - $postId = max(0, (int)($_GET['p'] ?? 0)); - $topicId = max(0, (int)($_GET['t'] ?? 0)); - $forumId = max(0, (int)($_GET['f'] ?? 0)); + $mode = RequestVar::get()->select('m')->string('create'); + $postId = max(0, RequestVar::get()->select('p')->int()); + $topicId = max(0, RequestVar::get()->select('t')->int()); + $forumId = max(0, RequestVar::get()->select('f')->int()); } if (!in_array($mode, $forumPostingModes, true)) { diff --git a/public/forum/topic.php b/public/forum/topic.php index 4fb30e5a..ec585168 100644 --- a/public/forum/topic.php +++ b/public/forum/topic.php @@ -1,8 +1,10 @@ select('p')->int(); +$topicId = RequestVar::get()->select('t')->int(); $topicUserId = user_session_current('user_id', 0); @@ -53,7 +55,7 @@ $canDelete = !$topicIsDeleted && ( ) ); -$moderationMode = (string)($_GET['m'] ?? ''); +$moderationMode = RequestVar::get()->select('m')->string(); $validModerationModes = [ 'delete', 'restore', 'nuke', 'bump', 'lock', 'unlock', @@ -91,7 +93,7 @@ if (in_array($moderationMode, $validModerationModes, true)) { return; } - switch ($_GET['m'] ?? '') { + switch ($moderationMode) { case 'delete': $canDeleteCode = forum_topic_can_delete($topic, $topicUserId); $canDeleteMsg = ''; diff --git a/public/manage/changelog.php b/public/manage/changelog.php index 5e8d90b8..31ab1a71 100644 --- a/public/manage/changelog.php +++ b/public/manage/changelog.php @@ -1,9 +1,11 @@ select('v')->string()) { default: case 'changes': if (!perms_check($changelogPerms, MSZ_PERM_CHANGELOG_MANAGE_CHANGES)) { @@ -74,7 +76,7 @@ switch ($_GET['v'] ?? null) { break; } - $changeId = (int)($_GET['c'] ?? 0); + $changeId = RequestVar::get()->select('c')->int(); if ($_SERVER['REQUEST_METHOD'] === 'POST' && csrf_verify('changelog_add', $_POST['csrf'] ?? '')) { if (!empty($_POST['change']) && is_array($_POST['change'])) { @@ -261,7 +263,7 @@ switch ($_GET['v'] ?? null) { break; } - $tagId = (int)($_GET['t'] ?? 0); + $tagId = RequestVar::get()->select('t')->int(); if ($_SERVER['REQUEST_METHOD'] === 'POST' && csrf_verify('changelog_tag', $_POST['csrf'] ?? '')) { if (!empty($_POST['tag']) && is_array($_POST['tag'])) { @@ -326,7 +328,7 @@ switch ($_GET['v'] ?? null) { break; } - $actionId = (int)($_GET['a'] ?? 0); + $actionId = RequestVar::get()->select('a')->int(); if ($_SERVER['REQUEST_METHOD'] === 'POST' && csrf_verify('changelog_action', $_POST['csrf'] ?? '')) { if (!empty($_POST['action']) && is_array($_POST['action'])) { diff --git a/public/manage/forum.php b/public/manage/forum.php index e2bb8065..5ad82c27 100644 --- a/public/manage/forum.php +++ b/public/manage/forum.php @@ -1,7 +1,9 @@ select('v')->string()) { case 'listing': $forums = db_query('SELECT * FROM `msz_forum_categories`'); $rawPerms = forum_perms_create(); @@ -22,7 +24,7 @@ switch ($_GET['v'] ?? null) { FROM `msz_forum_categories` WHERE `forum_id` = :forum_id '); - $getForum->bindValue('forum_id', (int)($_GET['f'] ?? 0)); + $getForum->bindValue('forum_id', RequestVar::get()->select('f')->int()); $forum = db_fetch($getForum); if (!$forum) { diff --git a/public/manage/index.php b/public/manage/index.php index 83cf9052..24cbd4af 100644 --- a/public/manage/index.php +++ b/public/manage/index.php @@ -1,9 +1,11 @@ select('v')->string()) { default: case 'overview': echo tpl_render('manage.general.overview'); diff --git a/public/manage/news.php b/public/manage/news.php index 34e08599..703d4c89 100644 --- a/public/manage/news.php +++ b/public/manage/news.php @@ -1,9 +1,11 @@ select('v')->string()) { default: case 'posts': if (!perms_check($newsPerms, MSZ_PERM_NEWS_MANAGE_POSTS)) { @@ -51,7 +53,7 @@ switch ($_GET['v'] ?? null) { case 'category': $category = []; - $categoryId = (int)($_GET['c'] ?? null); + $categoryId = RequestVar::get()->select('c')->int(); if (!empty($_POST['category']) && csrf_verify('news_category', $_POST['csrf'] ?? '')) { $originalCategoryId = (int)($_POST['category']['id'] ?? null); @@ -80,7 +82,7 @@ switch ($_GET['v'] ?? null) { case 'post': $post = []; - $postId = (int)($_GET['p'] ?? null); + $postId = RequestVar::get()->select('p')->int(); $categories = news_categories_get(0, 0, false, false, true); if (!empty($_POST['post']) && csrf_verify('news_post', $_POST['csrf'] ?? '')) { diff --git a/public/manage/users.php b/public/manage/users.php index 25721a44..4fb017ea 100644 --- a/public/manage/users.php +++ b/public/manage/users.php @@ -1,4 +1,6 @@ $canManageWarnings = perms_check($userPerms, MSZ_PERM_USER_MANAGE_WARNINGS), ]); -switch ($_GET['v'] ?? null) { +switch (RequestVar::get()->select('v')->string()) { default: case 'listing': if (!$canManageUsers && !$canManagePerms) { @@ -63,7 +65,7 @@ switch ($_GET['v'] ?? null) { break; } - $userId = (int)($_GET['u'] ?? 0); + $userId = RequestVar::get()->select('u')->int(); if ($userId < 1) { echo render_error(404); @@ -302,7 +304,7 @@ switch ($_GET['v'] ?? null) { break; } - $roleId = $_GET['r'] ?? null; + $roleId = RequestVar::get()->select('r')->int(); if ($canManagePerms) { tpl_var('permissions', $permissions = manage_perms_list(perms_get_role_raw($roleId ?? 0))); @@ -587,13 +589,15 @@ switch ($_GET['v'] ?? null) { user_warning_remove($warningId); break; } - header('Location: ' . ($_SERVER['HTTP_REFERER'] ?? '?m=warnings' . (empty($_GET['u']) ? '' : '&u=' . (int)($_GET['u'])))); + header('Location: ' . ($_SERVER['HTTP_REFERER'] ?? url('manage-user-warnings', [ + 'user' => RequestVar::get()->select('u')->int(), + ]))); return; } } if (empty($warningsUser)) { - $warningsUser = max(0, (int)($_GET['u'] ?? 0)); + $warningsUser = max(0, RequestVar::get()->select('u')->int()); } $warningsPagination = pagination_create(user_warning_global_count($warningsUser), 50); diff --git a/public/members.php b/public/members.php index dc2f8db6..a04d9198 100644 --- a/public/members.php +++ b/public/members.php @@ -1,9 +1,11 @@ select('r')->value('int', MSZ_ROLE_MAIN); +$orderBy = RequestVar::get()->select('ss')->value(); +$orderDir = RequestVar::get()->select('sd')->value(); $orderDirs = [ 'asc' => 'Ascending', diff --git a/public/news.php b/public/news.php index e32bf610..a7c2df8c 100644 --- a/public/news.php +++ b/public/news.php @@ -1,10 +1,20 @@ isset('n')) { + header(sprintf('Location: %s', url('news-post', [ + 'post' => RequestVar::get()->select('n')->value('int'), + ]))); + http_response_code(301); + return; +} -if ($postId !== null) { +$categoryId = RequestVar::get()->select('c')->value('int'); +$postId = RequestVar::get()->select('p')->value('int'); + +if ($postId > 0) { $post = news_post_get($postId); if (!$post) { @@ -35,7 +45,7 @@ if ($postId !== null) { return; } -if ($categoryId !== null) { +if ($categoryId > 0) { $category = news_category_get($categoryId, true); if (empty($category)) { diff --git a/public/profile.php b/public/profile.php index ccc8c58c..ff0da47f 100644 --- a/public/profile.php +++ b/public/profile.php @@ -1,7 +1,9 @@ select('u')->value()); if ($userId < 1) { http_response_code(404); @@ -9,8 +11,8 @@ if ($userId < 1) { return; } -$mode = (string)($_GET['m'] ?? null); -$isEditing = !empty($_GET['edit']) || !empty($_POST); +$mode = RequestVar::get()->select('m')->string(); +$isEditing = RequestVar::get()->edit->bool() || !empty($_POST); $notices = []; $currentUserId = user_session_current('user_id', 0); diff --git a/public/relations.php b/public/relations.php index f223266c..f90896cd 100644 --- a/public/relations.php +++ b/public/relations.php @@ -1,4 +1,6 @@ 0) { return; } -$subjectId = (int)($_GET['u'] ?? 0); -$relationType = (int)($_GET['m'] ?? -1); +$subjectId = RequestVar::get()->select('u')->int(); +$relationType = RequestVar::get()->select('m')->int(-1); if (!user_relation_is_valid_type($relationType)) { echo render_info_or_json($isXHR, 'Invalid relation type.', 400); diff --git a/public/user-assets.php b/public/user-assets.php index e406eae0..cced1a3f 100644 --- a/public/user-assets.php +++ b/public/user-assets.php @@ -1,10 +1,13 @@ select('u')->int(); $userExists = user_exists($userId); $canViewImages = !$userExists diff --git a/src/Request/RequestVar.php b/src/Request/RequestVar.php index 524abd7f..a30ecd14 100644 --- a/src/Request/RequestVar.php +++ b/src/Request/RequestVar.php @@ -1,5 +1,5 @@ value); } + public function raw() + { + return $this->value; + } + public function select(string $name): RequestVar { switch ($this->type) { case 'array': - return new static($this->value[$name] ?? []); + return new static($this->value[$name] ?? null); case 'object': - return new static($this->value->{$name} ?? new \stdClass); + return new static($this->value->{$name} ?? null); default: return new static(null); } } + public function string(?string $default = null): ?string + { + return mb_scrub(preg_replace('/[\x00-\x09\x0B-\x0C\x0D-\x1F\x7F]/u', '', (string)$this->value)); + } + + public function int(?int $default = null): ?int + { + return (int)$this->value == $this->value ? (int)$this->value : $default; + } + + public function bool(?bool $default = null): bool + { + return (bool)$this->value == $this->value ? (bool)$this->value : $default; + } + + public function float(?float $default = null): float + { + return (float)$this->value == $this->value ? (float)$this->value : $default; + } + + // avoid using when possible public function value(string $type = 'string', $default = null) { if (!is_null($this->valueCasted)) { - $this->valueCasted; + return $this->valueCasted; } if ($this->type === 'NULL' || (($type === 'object' || $type === 'array') && $this->type !== $type)) { - return $default; + return $this->valueCasted = $default; } - if ($type !== 'string' && $this->type === 'string') { + if ($type === 'string') { + // Remove undesired control characters, can be circumvented by using ->raw() + $value = $this->string($default); + } elseif ($type !== 'string' && $this->type === 'string') { switch ($type) { case 'boolean': case 'bool': - return (bool)$this->value; + $value = $this->bool($default); + break; case 'integer': case 'int': - return (int)$this->value; + $value = $this->int($default); + break; case 'double': case 'float': - return (float)$this->value; + $value = $this->float($default); + break; } } elseif ($type !== $this->type) { - return $default; + $value = $default; } return $this->valueCasted = $this->value; diff --git a/src/url.php b/src/url.php index 901b21fa..b40a690b 100644 --- a/src/url.php +++ b/src/url.php @@ -96,6 +96,7 @@ define('MSZ_URLS', [ 'manage-user-index' => ['/manage/users.php', ['v' => 'listing']], 'manage-user-edit' => ['/manage/users.php', ['v' => 'view', 'u' => '']], + 'manage-user-warnings' => ['/manage/users.php', ['v' => 'warnings', 'u' => '']], 'manage-role-index' => ['/manage/users.php', ['v' => 'roles']], 'manage-role-create' => ['/manage/users.php', ['v' => 'role']],