summaryrefslogtreecommitdiff
path: root/src/index.php
diff options
context:
space:
mode:
Diffstat (limited to 'src/index.php')
-rw-r--r--src/index.php286
1 files changed, 215 insertions, 71 deletions
diff --git a/src/index.php b/src/index.php
index de46204..a42aa19 100644
--- a/src/index.php
+++ b/src/index.php
@@ -1,5 +1,8 @@
<?php
+use FFMpeg\Coordinate\TimeCode;
+use FFMpeg\FFMpeg;
+use FFMpeg\FFProbe;
use Gregwar\Captcha\CaptchaBuilder;
use mystic\forum\Database;
use mystic\forum\exceptions\DatabaseConnectionException;
@@ -23,11 +26,6 @@ if (($_SERVER["HTTP_USER_AGENT"] ?? "") === "") {
exit;
}
-// function exception_error_handler($errno, $errstr, $errfile, $errline ) {
-// throw new ErrorException(html_entity_decode($errstr), $errno, 0, $errfile, $errline);
-// }
-// set_error_handler("exception_error_handler");
-
define("REGISTRATION_ENABLED", isTrue(env("REGISTRATION_ENABLED") ?? ""));
session_name("fsid");
@@ -44,6 +42,26 @@ $_action = $_GET["_action"] ?? null;
$GLOBALS["action"] = $_action;
+function msg_error(string $err, bool $skipLoginCheck = false): void {
+ _view("template_start", ["_title" => __("Error")]);
+ _view("template_navigation_start");
+ if (!$skipLoginCheck)
+ _view("template_navigation", ["user" => RequestUtils::getAuthorizedUser($GLOBALS["db"])]);
+ _view("template_navigation_end");
+ _view("alert_error", ["message" => $err]);
+ _view("template_end");
+}
+
+function msg_info(string $msg, bool $skipLoginCheck = false): void {
+ _view("template_start", ["_title" => __("Information")]);
+ _view("template_navigation_start");
+ if (!$skipLoginCheck)
+ _view("template_navigation", ["user" => RequestUtils::getAuthorizedUser($GLOBALS["db"])]);
+ _view("template_navigation_end");
+ _view("alert_info", ["message" => $msg]);
+ _view("template_end");
+}
+
function generateCaptchaText(): string {
$phrase = "";
for ($i = 0; $i < CAPTCHA_PHRASE_LENGTH; ++$i)
@@ -124,18 +142,35 @@ function env(string $key): ?string {
}
require_once __DIR__ . "/vendor/autoload.php";
-
require_once __DIR__ . "/application/i18n.php";
-i18n_locale("de");
+
+if ($_SERVER["REQUEST_METHOD"] === "GET" && isset($_GET["lang"])) {
+ parse_str($_SERVER["QUERY_STRING"], $query);
+ if (empty($query["lang"])) {
+ setcookie("lang", "", 100);
+ } else {
+ setcookie("lang", $query["lang"], time()+60*60*24*30);
+ }
+ unset($query["lang"]);
+ $path = strtok($_SERVER["REQUEST_URI"], "?");
+ header("Location: $path?" . http_build_query($query));
+ exit;
+}
+
+$user_locale = env("LOCALE") ?? $_COOKIE["lang"] ?? locale_accept_from_http($_SERVER["HTTP_ACCEPT_LANGUAGE"] ?? "");
+$chosen_locale = locale_lookup(i18n_get_available_locales(), $user_locale, true, "en");
+i18n_locale($user_locale);
$db = null;
try {
$db = new Database(Database::getConnectionString("db", getenv("POSTGRES_USER"), getenv("POSTGRES_PASSWORD"), getenv("POSTGRES_DBNAME")));
} catch (DatabaseConnectionException $ex) {
- Messaging::error([
- Messaging::bold(__("Failed to connect to database!")),
- Messaging::italic($ex->getMessage()),
- ]);
+ msg_error(
+ __("Failed to connect to database:\n%details%", [
+ "details" => $ex->getMessage(),
+ ]),
+ true
+ );
exit;
}
@@ -217,7 +252,7 @@ if ($_action === "auth") {
if (!REGISTRATION_ENABLED) {
http_response_code(403);
- Messaging::error(__("Public registration disabled"));
+ msg_error(__("Public registration disabled"));
exit;
}
@@ -308,9 +343,7 @@ if ($_action === "auth") {
$db->insert($user);
- Messaging::info(
- Messaging::html(nl2br(htmlentities(__("Your account has been created!\nPlease check your emails for an activation link!"), true)))
- );
+ msg_info(__("Your account has been created!\nPlease check your emails for an activation link!"));
} else {
_view("template_start", ["_title" => __("Register")]);
_view("template_navigation_start");
@@ -330,7 +363,7 @@ if ($_action === "auth") {
if (!$db->fetchWhere($user, [ "activated", "activation_token" ])) {
http_response_code(400);
- Messaging::error(__("Invalid token"));
+ msg_error(__("Invalid token"));
exit;
}
@@ -338,7 +371,7 @@ if ($_action === "auth") {
if ($expectedSignature !== $sig) {
http_response_code(400);
- Messaging::error(__("Invalid signature."));
+ msg_error(__("Invalid signature."));
exit;
}
@@ -347,19 +380,17 @@ if ($_action === "auth") {
if (!$db->update($user)) {
http_response_code(400);
- Messaging::error(__("Failed to update user"));
+ msg_error(__("Failed to update user"));
exit;
}
- Messaging::info([
- Messaging::html(nl2br(__(
- "Your account has been activated!\nPlease click %link%here%/link% to log in!",
- [
- "link" => '<a href="?_action=auth">',
- "/link" => '</a>',
- ]
- )), true),
- ]);
+ msg_info("?!HTML::" . __(
+ "Your account has been activated!\nPlease click %link%here%/link% to log in!",
+ [
+ "link" => '<a href="?_action=auth">',
+ "/link" => '</a>',
+ ]
+ ));
} elseif ($_action === "logout") {
RequestUtils::unsetAuthorizedUser();
header("Location: " . ($_GET["next"] ?? "."));
@@ -369,14 +400,14 @@ if ($_action === "auth") {
$topic->id = $topicId;
if (!$db->fetch($topic)) {
http_response_code(404);
- Messaging::error("No topic exists with this id");
+ msg_error("No topic exists with this id");
exit;
}
if (RequestUtils::isRequestMethod("POST")) {
if (!$currentUser) {
http_response_code(403);
- Messaging::error("You need to be logged in to add new posts!");
+ msg_error("You need to be logged in to add new posts!");
exit;
}
@@ -447,8 +478,8 @@ if ($_action === "auth") {
/** @var Post $post */
foreach ($posts as $post) {
- /** @var ?User $topicAuthor */
- $topicAuthor = null;
+ /** @var ?User $postAuthor */
+ $postAuthor = null;
if ($post->authorId !== null && !isset($userCache[$post->authorId])) {
$usr = new User();
$usr->id = $post->authorId;
@@ -456,13 +487,14 @@ if ($_action === "auth") {
$userCache[$post->authorId] = &$usr;
}
if (isset($userCache[$post->authorId]))
- $topicAuthor = &$userCache[$post->authorId];
+ $postAuthor = &$userCache[$post->authorId];
$attachments = $db->fetchCustom(Attachment::class, 'WHERE post_id = $1', [ $post->id ]);
_view("view_post", [
"post" => $post,
- "postAuthor" => $topicAuthor,
+ "postAuthor" => $postAuthor,
+ "topicAuthor" => $topicAuthor,
"attachments" => $attachments,
]);
}
@@ -479,7 +511,7 @@ if ($_action === "auth") {
} elseif ($_action === "newtopic") {
if (!$currentUser) {
http_response_code(403);
- Messaging::error("You need to be logged in to create new topics!");
+ msg_error("You need to be logged in to create new topics!");
exit;
}
@@ -560,7 +592,7 @@ if ($_action === "auth") {
if (!$db->fetchWhere($user, "name")) {
http_response_code(404);
- Messaging::error(__("No user with name @%user_handle%", [ "user_handle" => $userHandle ]));
+ msg_error(__("No user with name @%user_handle%", [ "user_handle" => $userHandle ]));
exit;
}
@@ -571,7 +603,7 @@ if ($_action === "auth") {
$user->id = $userId;
if (!$db->fetch($user)) {
http_response_code(404);
- Messaging::error(__("No user exists with this id"));
+ msg_error(__("No user exists with this id"));
exit;
}
@@ -646,14 +678,16 @@ if ($_action === "auth") {
} else {
$posts = $db->fetchCustom(Post::class, 'WHERE author_id = $1 ORDER BY post_date DESC', [ $userId ]);
$topics = [];
+ $attachments = [];
foreach ($posts as $post) {
- if (isset($topics[$post->topicId]))
- continue;
- $topic = new Topic();
- $topic->id = $post->topicId;
- if (!$db->fetch($topic))
- continue;
- $topics[$post->topicId] = $topic;
+ if (!isset($topics[$post->topicId])) {
+ $topic = new Topic();
+ $topic->id = $post->topicId;
+ if ($db->fetch($topic))
+ $topics[$post->topicId] = $topic;
+ }
+ $attachs = $db->fetchCustom(Attachment::class, 'WHERE post_id = $1', [ $post->id ]);
+ $attachments[$post->id] = $attachs;
}
_view("template_start", ["_title" => $user->displayName]);
_view("template_navigation_start");
@@ -663,6 +697,7 @@ if ($_action === "auth") {
"user" => $user,
"posts" => $posts,
"topics" => $topics,
+ "attachments" => $attachments,
"lastNameChangeTooRecent" => $lastNameChangeTooRecent,
]);
_view("template_end");
@@ -670,7 +705,7 @@ if ($_action === "auth") {
} elseif ($_action === "attachment") {
if (!$currentUser) {
http_response_code(403);
- Messaging::error(__("You must be logged in to view attachments"));
+ msg_error(__("You must be logged in to view attachments"));
exit;
}
@@ -679,7 +714,7 @@ if ($_action === "auth") {
$attachment->id = $attId;
if (!$db->fetch($attachment)) {
http_response_code(404);
- Messaging::error(__("No attachment exists with this id"));
+ msg_error(__("No attachment exists with this id"));
exit;
}
@@ -710,7 +745,7 @@ if ($_action === "auth") {
$user->id = $userId;
if (!$db->fetch($user)) {
http_response_code(404);
- Messaging::error(__("No user exists with this id"));
+ msg_error(__("No user exists with this id"));
exit;
}
@@ -747,13 +782,16 @@ if ($_action === "auth") {
$attachment->id = $attId;
if (!$db->fetch($attachment)) {
http_response_code(404);
- Messaging::error(__("No attachment exists with this id"));
+ msg_error(__("No attachment exists with this id"));
exit;
}
- if (!str_starts_with($attachment->mimeType, "image/")) {
+ $isImage = str_starts_with($attachment->mimeType, "image/");
+ $isVideo = str_starts_with($attachment->mimeType, "video/");
+
+ if (!$isImage && !$isVideo) {
http_response_code(400);
- Messaging::error(__("Attachment is not an image"));
+ msg_error(__("Attachment is neither an image nor a video"));
exit;
}
@@ -772,13 +810,41 @@ if ($_action === "auth") {
if ($info->contentHash === $contentHash) {
header("Content-Type: image/jpeg");
header("Cache-Control: max-age=86400");
- header("X-Debug-Content: $cacheFileData");
+ //header("X-Debug-Content: $cacheFileData");
readfile($cacheFileData);
exit;
}
}
- $im = imagecreatefromstring($attachment->contents);
+ if ($isVideo) {
+ $suffix = (microtime(true) * 1000) . "-" . random_int(0, 99999);
+ $tempVid = sys_get_temp_dir() . "/video_" . $suffix;
+ file_put_contents($tempVid, $attachment->contents);
+ $tempImg = sys_get_temp_dir() . "/image_" . $suffix . ".jpg";
+
+ try {
+ $ffprobe = FFProbe::create();
+ /** @var string $duration */
+ $duration = $ffprobe
+ ->format($tempVid)
+ ->get("duration", "0");
+
+ $screenshotFramePoint = TimeCode::fromSeconds(floatval($duration) / 2.0);
+
+ $ffmpeg = FFMpeg::create();
+ $video = $ffmpeg->open($tempVid);
+ $screenshot = $video
+ ->frame($screenshotFramePoint)
+ ->save($tempImg);
+ $im = imagecreatefromjpeg($tempImg);
+ } finally {
+ if (is_file($tempVid)) unlink($tempVid);
+ if (is_file($tempImg)) unlink($tempImg);
+ }
+ } elseif ($isImage) {
+ $im = imagecreatefromstring($attachment->contents);
+ }
+
$w = imagesx($im);
$h = imagesy($im);
$r = $w / floatval($h);
@@ -810,7 +876,7 @@ if ($_action === "auth") {
if (!$currentUser) {
http_response_code(403);
- Messaging::error("You need to be logged in to delete posts!");
+ msg_error("You need to be logged in to delete posts!");
exit;
}
@@ -821,7 +887,7 @@ if ($_action === "auth") {
if (!$db->fetch($post) || $post->deleted) {
http_response_code(404);
- Messaging::error("No post exists with this id");
+ msg_error("No post exists with this id");
exit;
}
@@ -836,7 +902,7 @@ if ($_action === "auth") {
if (!$canEdit) {
http_response_code(403);
- Messaging::error("You don't have permission to delete this post");
+ msg_error("You don't have permission to delete this post");
exit;
}
@@ -847,7 +913,7 @@ if ($_action === "auth") {
$expectedConfirm = base64_encode(hash("sha256", "confirm" . $post->id, true));
if ($confirm !== $expectedConfirm) {
http_response_code(400);
- Messaging::error("Invalid confirmation");
+ msg_error("Invalid confirmation");
exit;
}
@@ -856,14 +922,14 @@ if ($_action === "auth") {
if (!$db->update($post)) {
http_response_code(500);
- Messaging::error("Failed to delete post");
+ msg_error("Failed to delete post");
exit;
}
foreach ($attachments as $attachment) {
if (!$db->delete($attachment)) {
http_response_code(500);
- Messaging::error("Failed to delete attachment");
+ msg_error("Failed to delete attachment");
exit;
}
}
@@ -886,7 +952,7 @@ if ($_action === "auth") {
if (!$currentUser) {
http_response_code(403);
- Messaging::error("You need to be logged in to update posts!");
+ msg_error(__("You need to be logged in to update posts!"));
exit;
}
@@ -898,7 +964,7 @@ if ($_action === "auth") {
if (!$db->fetch($post) || $post->deleted) {
http_response_code(404);
- Messaging::error("No post exists with this id");
+ msg_error(__("No post exists with this id"));
exit;
}
@@ -913,7 +979,7 @@ if ($_action === "auth") {
if (!$canEdit) {
http_response_code(403);
- Messaging::error("You don't have permission to edit this post");
+ msg_error(__("You don't have permission to edit this post"));
exit;
}
@@ -924,7 +990,7 @@ if ($_action === "auth") {
if (!$db->update($post)) {
http_response_code(500);
- Messaging::error("Failed to update post");
+ msg_error(__("Failed to update post"));
exit;
}
@@ -934,7 +1000,7 @@ if ($_action === "auth") {
if (!$currentUser) {
http_response_code(403);
- Messaging::error("You need to be logged in to delete topics!");
+ msg_error(__("You need to be logged in to delete topics!"));
exit;
}
@@ -945,7 +1011,7 @@ if ($_action === "auth") {
if (!$db->fetch($topic)) {
http_response_code(404);
- Messaging::error("No topic exists with this id");
+ msg_error(__("No topic exists with this id"));
exit;
}
@@ -960,7 +1026,7 @@ if ($_action === "auth") {
if (!$canEdit) {
http_response_code(403);
- Messaging::error("You don't have permission to delete this topic");
+ msg_error(__("You don't have permission to delete this topic"));
exit;
}
@@ -969,13 +1035,13 @@ if ($_action === "auth") {
$expectedConfirm = base64_encode(hash("sha256", "confirm" . $topic->id, true));
if ($confirm !== $expectedConfirm) {
http_response_code(400);
- Messaging::error("Invalid confirmation");
+ msg_error(__("Invalid confirmation"));
exit;
}
if (!$db->delete($topic)) {
http_response_code(500);
- Messaging::error("Failed to delete topic");
+ msg_error(__("Failed to delete topic"));
exit;
}
@@ -996,7 +1062,7 @@ if ($_action === "auth") {
if (!$currentUser) {
http_response_code(403);
- Messaging::error("You need to be logged in to update topics!");
+ msg_error(__("You need to be logged in to update topics!"));
exit;
}
@@ -1008,7 +1074,7 @@ if ($_action === "auth") {
if (!$db->fetch($topic)) {
http_response_code(404);
- Messaging::error("No topic exists with this id");
+ msg_error(__("No topic exists with this id"));
exit;
}
@@ -1023,7 +1089,7 @@ if ($_action === "auth") {
if (!$canEdit) {
http_response_code(403);
- Messaging::error("You don't have permission to update this topic");
+ msg_error(__("You don't have permission to update this topic"));
exit;
}
@@ -1031,7 +1097,7 @@ if ($_action === "auth") {
if (!$db->update($topic)) {
http_response_code(500);
- Messaging::error("Failed to update topic");
+ msg_error(__("Failed to update topic"));
exit;
}
@@ -1045,6 +1111,84 @@ if ($_action === "auth") {
header("Pragma: no-cache");
header("Cache-Control: no-cache");
$builder->save(null, 40);
+} elseif ($_action === "ji18n") {
+ header("Content-Type: application/javascript; charset=UTF-8");
+ echo 'var I18N_MESSAGES = ' . json_encode(i18n_get_message_store(i18n_get_current_locale()), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . ";\n";
+} elseif ($_action === "ctheme") {
+ // options
+ $enableLogging = true;
+ $etag_strip_gzip_suffix = true;
+
+ $cssFatal = function(string $msg): never {
+ if (!headers_sent())
+ http_response_code(500);
+ echo "/*!FATAL $msg */\n";
+ exit;
+ };
+ $cssError = function(string &$buffer, string $msg) use($enableLogging): void {
+ if ($enableLogging)
+ $buffer .= "/*!ERROR $msg */\n";
+ };
+ $cssWarning = function(string &$buffer, string $msg) use ($enableLogging): void {
+ if ($enableLogging)
+ $buffer .= "/*!WARN $msg */\n";
+ };
+
+ $buffer = "";
+
+ header("Content-Type: text/css; charset=UTF-8");
+ header("Cache-Control: no-cache");
+ // Disable Apache's gzip filter, as it interferes with our ETag
+ // (Apache adds '-gzip' as a ETag suffix)
+ if (!$etag_strip_gzip_suffix)
+ apache_setenv('no-gzip', '1');
+
+ $themeName = $_GET["theme"] ?? $_COOKIE["theme"] ?? env("MYSTIC_FORUM_THEME") ?? "default";
+ if (!preg_match('/^[a-z0-9_-]+$/i', $themeName)) {
+ $cssWarning($buffer, "Invalid theme '" . str_replace('*/', '*\\/', $themeName) . "'");
+ $cssWarning($buffer, "Loading default theme");
+ $themeName = "default";
+ }
+ $themePath = __DIR__ . '/themes/' . $themeName . '/theme.json';
+ $themeDefaultPath = __DIR__ . '/themes/default/theme.json';
+ if (!is_file($themePath) && is_file($themeDefaultPath)) {
+ $cssWarning($buffer, "Invalid theme '" . str_replace('*/', '*\\/', $themeName) . "'");
+ $cssWarning($buffer, "Loading default theme");
+ $themePath = $themeDefaultPath;
+ } elseif (!is_file($themePath) && !is_file($themeDefaultPath)) {
+ $cssFatal("Failed to load default theme");
+ }
+ $themeDir = dirname($themePath);
+ $theme = json_decode(file_get_contents($themePath));
+ if ($theme->{'$format'} !== 1)
+ $cssFatal("Invalid theme format");
+ foreach ($theme->files as $file) {
+ if (is_array($file)) {
+ if ($enableLogging) $buffer .= "/*!INLINE start */\n";
+ $buffer .= implode("\n", $file);
+ if ($enableLogging) $buffer .= "/*!INLINE end */\n";
+ } elseif (is_file($filePath = $themeDir . "/" . $file)) {
+ if ($enableLogging) $buffer .= "/*!INCLUDE " . basename($file) . " */\n";
+ $buffer .= file_get_contents($filePath);
+ if ($enableLogging) $buffer .= "/*!INCLUDE end */\n";
+ } else
+ $cssError($buffer, "Could not include file $file");
+ }
+ $etag = md5($buffer);
+
+ $ifNoneMatch = $_SERVER["HTTP_IF_NONE_MATCH"] ?? null;
+ if ($ifNoneMatch !== null) {
+ $ifNoneMatch = trim($ifNoneMatch, '"');
+ if ($etag_strip_gzip_suffix)
+ $ifNoneMatch = preg_replace('/-gzip$/', '', $ifNoneMatch);
+ }
+
+ header("ETag: \"$etag\"");
+ if ($ifNoneMatch === $etag)
+ http_response_code(304);
+ else
+ echo $buffer;
+
} elseif ($_action === null) {
_view("template_start");
_view("template_navigation_start");
@@ -1054,5 +1198,5 @@ if ($_action === "auth") {
_view("template_end");
} else {
http_response_code(404);
- Messaging::error("Invalid or unknown action $_action");
+ msg_error(__("Invalid or unknown action $_action"));
}