misuzu/src/url.php

279 lines
14 KiB
PHP
Raw Normal View History

2019-01-24 21:54:24 +01:00
<?php
// URL FORMATTING
// [0] => Path part of URL.
// [1] => Query part of URL.
// [2] => Fragment part of URL.
//
// text surrounded by < and > will be replaced accordingly to an array of variables supplied to the format function
// text surrounded by [ and ] will be replaced by the constant/define of that name
// text surrounded by { and } will be replaced by a CSRF token with the given text as its realm, this will have no effect in a sessionless environment
define('MSZ_URLS', [
'index' => ['/'],
'info' => ['/info.php/<title>'],
'media-proxy' => ['/proxy.php/<hash>/<url>'],
2019-01-24 21:54:24 +01:00
2019-04-13 18:44:41 +02:00
'search-index' => ['/search.php'],
'search-query' => ['/search.php', ['q' => '<query>']],
2019-04-13 18:44:41 +02:00
'auth-login' => ['/auth/login.php', ['username' => '<username>', 'redirect' => '<redirect>']],
'auth-login-welcome' => ['/auth/login.php', ['welcome' => '1', 'username' => '<username>']],
2019-03-08 01:35:53 +01:00
'auth-register' => ['/auth/register.php'],
'auth-forgot' => ['/auth/password.php'],
'auth-reset' => ['/auth/password.php', ['user' => '<user>']],
2019-06-10 17:21:53 +02:00
'auth-logout' => ['/auth/logout.php', ['csrf' => '{csrf}']],
'auth-resolve-user' => ['/auth/login.php', ['resolve_user' => '<username>']],
'auth-two-factor' => ['/auth/twofactor.php', ['token' => '<token>']],
2019-01-24 21:54:24 +01:00
'changelog-index' => ['/changelog.php'],
'changelog-change' => ['/changelog.php', ['c' => '<change>']],
'changelog-date' => ['/changelog.php', ['d' => '<date>']],
'changelog-tag' => ['/changelog.php', ['t' => '<tag>']],
'news-index' => ['/news', ['page' => '<page>']],
'news-post' => ['/news/post.php', ['p' => '<post>']],
'news-post-comments' => ['/news/post.php', ['p' => '<post>'], 'comments'],
'news-category' => ['/news/category.php', ['c' => '<category>', 'p' => '<page>']],
'news-feed-rss' => ['/news/feed.php/rss'],
'news-category-feed-rss' => ['/news/feed.php/rss', ['c' => '<category>']],
'news-feed-atom' => ['/news/feed.php/atom'],
'news-category-feed-atom' => ['/news/feed.php/atom', ['c' => '<category>']],
2019-01-24 21:54:24 +01:00
'forum-index' => ['/forum'],
'forum-leaderboard' => ['/forum/leaderboard.php', ['id' => '<id>', 'mode' => '<mode>']],
2019-06-10 17:21:53 +02:00
'forum-mark-global' => ['/forum/index.php', ['m' => 'mark', 'csrf' => '{csrf}']],
'forum-mark-single' => ['/forum/index.php', ['m' => 'mark', 'csrf' => '{csrf}', 'f' => '<forum>']],
'forum-topic-new' => ['/forum/posting.php', ['f' => '<forum>']],
'forum-reply-new' => ['/forum/posting.php', ['t' => '<topic>']],
'forum-category' => ['/forum/forum.php', ['f' => '<forum>', 'p' => '<page>']],
'forum-topic' => ['/forum/topic.php', ['t' => '<topic>', 'page' => '<page>']],
'forum-topic-create' => ['/forum/posting.php', ['f' => '<forum>']],
2019-06-10 17:21:53 +02:00
'forum-topic-bump' => ['/forum/topic.php', ['t' => '<topic>', 'm' => 'bump', 'csrf' => '{csrf}']],
'forum-topic-lock' => ['/forum/topic.php', ['t' => '<topic>', 'm' => 'lock', 'csrf' => '{csrf}']],
'forum-topic-unlock' => ['/forum/topic.php', ['t' => '<topic>', 'm' => 'unlock', 'csrf' => '{csrf}']],
'forum-topic-delete' => ['/forum/topic.php', ['t' => '<topic>', 'm' => 'delete', 'csrf' => '{csrf}']],
'forum-topic-restore' => ['/forum/topic.php', ['t' => '<topic>', 'm' => 'restore', 'csrf' => '{csrf}']],
'forum-topic-nuke' => ['/forum/topic.php', ['t' => '<topic>', 'm' => 'nuke', 'csrf' => '{csrf}']],
'forum-topic-priority' => ['/forum/topic-priority.php', ['t' => '<topic>', 'b' => '<bump>']],
'forum-post' => ['/forum/topic.php', ['p' => '<post>'], '<post_fragment>'],
'forum-post-create' => ['/forum/posting.php', ['t' => '<topic>']],
'forum-post-delete' => ['/forum/post.php', ['p' => '<post>', 'm' => 'delete']],
'forum-post-restore' => ['/forum/post.php', ['p' => '<post>', 'm' => 'restore']],
'forum-post-nuke' => ['/forum/post.php', ['p' => '<post>', 'm' => 'nuke']],
'forum-post-quote' => ['/forum/posting.php', ['q' => '<post>']],
'forum-post-edit' => ['/forum/posting.php', ['p' => '<post>', 'm' => 'edit']],
2019-04-18 01:59:33 +02:00
'forum-poll-vote' => ['/forum/poll.php'],
2019-01-24 21:54:24 +01:00
'user-list' => ['/members.php', ['r' => '<role>', 'ss' => '<sort>', 'sd' => '<direction>', 'p' => '<page>']],
'user-profile' => ['/profile.php', ['u' => '<user>']],
'user-profile-following' => ['/profile.php', ['u' => '<user>', 'm' => 'following']],
'user-profile-followers' => ['/profile.php', ['u' => '<user>', 'm' => 'followers']],
'user-profile-forum-topics' => ['/profile.php', ['u' => '<user>', 'm' => 'forum-topics']],
'user-profile-forum-posts' => ['/profile.php', ['u' => '<user>', 'm' => 'forum-posts']],
'user-profile-edit' => ['/profile.php', ['u' => '<user>', 'edit' => '1']],
'user-account-standing' => ['/profile.php', ['u' => '<user>'], 'account-standing'],
'user-avatar' => ['/user-assets.php', ['u' => '<user>', 'm' => 'avatar', 'r' => '<res>']],
'user-background' => ['/user-assets.php', ['u' => '<user>', 'm' => 'background']],
2019-01-24 21:54:24 +01:00
2019-06-10 17:21:53 +02:00
'user-relation-create' => ['/relations.php', ['u' => '<user>', 'm' => '<type>', 'csrf' => '{csrf}']],
'user-relation-none' => ['/relations.php', ['u' => '<user>', 'm' => '[MSZ_USER_RELATION_NONE]', 'csrf' => '{csrf}']],
'user-relation-follow' => ['/relations.php', ['u' => '<user>', 'm' => '[MSZ_USER_RELATION_FOLLOW]', 'csrf' => '{csrf}']],
2019-01-24 21:54:24 +01:00
2019-06-06 22:09:27 +02:00
'settings-index' => ['/settings'],
'settings-account' => ['/settings/account.php'],
'settings-sessions' => ['/settings/sessions.php'],
'settings-logs' => ['/settings/logs.php'],
'settings-data' => ['/settings/data.php'],
2019-01-24 21:54:24 +01:00
'comment-create' => ['/comments.php', ['m' => 'create']],
2019-06-10 17:21:53 +02:00
'comment-vote' => ['/comments.php', ['c' => '<comment>', 'csrf' => '{csrf}', 'm' => 'vote', 'v' => '<vote>']],
'comment-delete' => ['/comments.php', ['c' => '<comment>', 'csrf' => '{csrf}', 'm' => 'delete']],
'comment-restore' => ['/comments.php', ['c' => '<comment>', 'csrf' => '{csrf}', 'm' => 'restore']],
'comment-pin' => ['/comments.php', ['c' => '<comment>', 'csrf' => '{csrf}', 'm' => 'pin']],
'comment-unpin' => ['/comments.php', ['c' => '<comment>', 'csrf' => '{csrf}', 'm' => 'unpin']],
'manage-index' => ['/manage'],
2019-06-10 00:10:59 +02:00
'manage-general-overview' => ['/manage/general'],
'manage-general-logs' => ['/manage/general/logs.php'],
'manage-general-emoticons' => ['/manage/general/emoticons.php'],
2019-07-04 19:27:21 +02:00
'manage-general-emoticon' => ['/manage/general/emoticon.php', ['e' => '<emote>']],
2019-07-05 02:14:05 +02:00
'manage-general-emoticon-order-up' => ['/manage/general/emoticons.php', ['emote' => '<emote>', 'order' => 'd', 'csrf' => '{token}']],
'manage-general-emoticon-order-down'=> ['/manage/general/emoticons.php', ['emote' => '<emote>', 'order' => 'i', 'csrf' => '{token}']],
'manage-general-emoticon-delete' => ['/manage/general/emoticons.php', ['emote' => '<emote>', 'delete' => '1', 'csrf' => '{token}']],
'manage-general-emoticon-alias' => ['/manage/general/emoticons.php', ['emote' => '<emote>', 'alias' => '<string>', 'csrf' => '{token}']],
'manage-general-settings' => ['/manage/general/settings.php'],
'manage-general-blacklist' => ['/manage/general/blacklist.php'],
'manage-forum-categories' => ['/manage/forum/index.php'],
'manage-forum-category' => ['/manage/forum/category.php', ['f' => '<forum>']],
2019-06-10 00:10:59 +02:00
'manage-changelog-changes' => ['/manage/changelog'],
'manage-changelog-change' => ['/manage/changelog/change.php', ['c' => '<change>']],
'manage-changelog-tags' => ['/manage/changelog/tags.php'],
'manage-changelog-tag' => ['/manage/changelog/tag.php', ['t' => '<tag>']],
2019-06-10 00:10:59 +02:00
'manage-news-categories' => ['/manage/news/categories.php'],
'manage-news-category' => ['/manage/news/category.php', ['c' => '<category>']],
'manage-news-posts' => ['/manage/news/posts.php'],
'manage-news-post' => ['/manage/news/post.php', ['p' => '<post>']],
2019-06-10 00:10:59 +02:00
'manage-users' => ['/manage/users'],
'manage-user' => ['/manage/users/user.php', ['u' => '<user>']],
'manage-users-reports' => ['/manage/users/reports.php', ['u' => '<user>']],
'manage-users-report' => ['/manage/users/report.php', ['r' => '<report>']],
'manage-users-warnings' => ['/manage/users/warnings.php', ['u' => '<user>']],
2019-06-10 17:21:53 +02:00
'manage-users-warning-delete' => ['/manage/users/warnings.php', ['w' => '<warning>', 'delete' => '1', 'csrf' => '{csrf}']],
2019-06-10 00:10:59 +02:00
'manage-roles' => ['/manage/users/roles.php'],
'manage-role' => ['/manage/users/role.php', ['r' => '<role>']],
2019-01-24 21:54:24 +01:00
]);
2019-06-10 19:04:53 +02:00
function url(string $name, array $variables = []): string {
if(!array_key_exists($name, MSZ_URLS)) {
2019-01-24 21:54:24 +01:00
return '';
}
$info = MSZ_URLS[$name];
2019-06-10 19:04:53 +02:00
if(!is_string($info[0] ?? null)) {
return '';
}
$splitUrl = explode('/', $info[0]);
2019-06-10 19:04:53 +02:00
for($i = 0; $i < count($splitUrl); $i++) {
$splitUrl[$i] = url_variable($splitUrl[$i], $variables);
}
$url = implode('/', $splitUrl);
2019-01-24 21:54:24 +01:00
2019-06-10 19:04:53 +02:00
if(!empty($info[1]) && is_array($info[1])) {
2019-01-24 21:54:24 +01:00
$url .= '?';
2019-06-10 19:04:53 +02:00
foreach($info[1] as $key => $value) {
2019-01-24 21:54:24 +01:00
$value = url_variable($value, $variables);
2019-06-10 19:04:53 +02:00
if(empty($value) || ($key === 'page' && $value < 2)) {
2019-01-24 21:54:24 +01:00
continue;
}
$url .= sprintf('%s=%s&', $key, $value);
}
$url = trim($url, '?&');
}
2019-06-10 19:04:53 +02:00
if(!empty($info[2]) && is_string($info[2])) {
2019-01-24 21:54:24 +01:00
$url .= rtrim(sprintf('#%s', url_variable($info[2], $variables)), '#');
}
return $url;
}
2019-06-10 19:04:53 +02:00
function redirect(string $url): void {
2019-06-10 00:10:59 +02:00
header('Location: ' . $url);
}
2019-06-10 19:04:53 +02:00
function url_redirect(string $name, array $variables = []): void {
redirect(url($name, $variables));
}
2019-06-10 19:04:53 +02:00
function url_variable(string $value, array $variables): string {
if(starts_with($value, '<') && ends_with($value, '>')) {
2019-01-24 21:54:24 +01:00
return $variables[trim($value, '<>')] ?? '';
}
2019-06-10 19:04:53 +02:00
if(starts_with($value, '[') && ends_with($value, ']')) {
2019-01-24 21:54:24 +01:00
return constant(trim($value, '[]'));
}
2019-06-10 19:04:53 +02:00
if(starts_with($value, '{') && ends_with($value, '}') && csrf_is_ready()) {
2019-06-10 17:21:53 +02:00
return csrf_token();
2019-01-24 21:54:24 +01:00
}
return $value;
}
2019-06-10 19:04:53 +02:00
function url_list(): array {
global $hasManageAccess;
2019-01-24 21:54:24 +01:00
$collection = [];
2019-06-10 19:04:53 +02:00
foreach(MSZ_URLS as $name => $urlInfo) {
if(empty($hasManageAccess) && starts_with($name, 'manage-'))
continue;
2019-01-24 21:54:24 +01:00
$item = [
'name' => $name,
'path' => $urlInfo[0],
'query' => [],
'fragment' => $urlInfo[2] ?? '',
];
2019-06-10 19:04:53 +02:00
if(!empty($urlInfo[1]) && is_array($urlInfo[1])) {
foreach($urlInfo[1] as $name => $value) {
2019-01-24 21:54:24 +01:00
$item['query'][] = [
'name' => $name,
'value' => $value,
];
}
}
$collection[] = $item;
}
return $collection;
}
2019-06-10 19:04:53 +02:00
function url_construct(string $url, array $query = [], ?string $fragment = null): string {
if(count($query)) {
2019-01-24 21:54:24 +01:00
$url .= mb_strpos($url, '?') !== false ? '&' : '?';
2019-06-10 19:04:53 +02:00
foreach($query as $key => $value) {
if($value) {
2019-01-24 21:54:24 +01:00
$url .= rawurlencode($key) . '=' . rawurlencode($value) . '&';
}
}
$url = mb_substr($url, 0, -1);
}
2019-06-10 19:04:53 +02:00
if(!empty($fragment)) {
2019-01-24 21:54:24 +01:00
$url .= "#{$fragment}";
}
return $url;
}
2019-06-10 19:04:53 +02:00
function url_proxy_media(?string $url): ?string {
if(empty($url) || !\Misuzu\Config::get('media_proxy.enable', \Misuzu\Config::TYPE_BOOL) || is_local_url($url)) {
return $url;
}
$secret = \Misuzu\Config::get('media_proxy.secret', \Misuzu\Config::TYPE_STR, 'insecure');
2019-12-03 03:02:32 +01:00
$url = \Misuzu\Base64::encode($url, true);
$hash = hash_hmac('sha256', $url, $secret);
return url('media-proxy', compact('hash', 'url'));
}
2019-03-31 18:49:16 +02:00
2019-06-10 19:04:53 +02:00
function url_prefix(bool $trailingSlash = true): string {
2019-03-31 18:49:16 +02:00
return 'http' . (empty($_SERVER['HTTPS']) ? '' : 's') . '://' . $_SERVER['HTTP_HOST'] . ($trailingSlash ? '/' : '');
}
2019-06-10 19:04:53 +02:00
function is_local_url(string $url): bool {
2019-03-31 18:49:16 +02:00
$length = mb_strlen($url);
2019-06-10 19:04:53 +02:00
if($length < 1) {
2019-03-31 18:49:16 +02:00
return false;
}
2019-06-10 19:04:53 +02:00
if($url[0] === '/' && ($length > 1 ? $url[1] !== '/' : true)) {
2019-03-31 18:49:16 +02:00
return true;
}
return starts_with($url, url_prefix());
}