summaryrefslogtreecommitdiff
path: root/src/index.php
diff options
context:
space:
mode:
Diffstat (limited to 'src/index.php')
-rw-r--r--src/index.php193
1 files changed, 175 insertions, 18 deletions
diff --git a/src/index.php b/src/index.php
index 109cd2e..1cc0d01 100644
--- a/src/index.php
+++ b/src/index.php
@@ -8,15 +8,16 @@ use mystic\forum\orm\Post;
use mystic\forum\orm\Topic;
use mystic\forum\orm\User;
use mystic\forum\orm\UserPermissions;
+use mystic\forum\utils\FileUtils;
use mystic\forum\utils\RequestUtils;
use mystic\forum\utils\ValidationUtils;
header_remove("X-Powered-By");
-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");
+// 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", true);
@@ -34,6 +35,7 @@ $GLOBALS["action"] = $_action;
function _view(string $name, array $params = []): void {
$___NAME = $name;
+ $___PARAMS = &$params;
extract($params);
echo "<!--{" . htmlentities($name) . "}-->\n";
include __DIR__ . "/application/views/" . $___NAME . ".php";
@@ -425,12 +427,99 @@ if ($_action === "auth") {
Messaging::error("No user exists with this id");
exit;
}
- _view("template_start", ["_title" => "Forum"]);
- _view("template_navigation_start");
- _view("template_navigation", ["user" => RequestUtils::getAuthorizedUser($db)]);
- _view("template_navigation_end");
- _view("view_user", ["user" => $user]);
- _view("template_end");
+
+ $lastNameChangeTooRecent = false;
+ $isOwnProfile = $user->id === $currentUser?->id;
+ if ($isOwnProfile && $user->nameLastChanged !== null) {
+ $diff = $user->nameLastChanged->diff(new DateTime());
+ $diffSeconds = (new DateTime())->setTimestamp(0)->add($diff)->getTimestamp();
+ $diffDays = $diffSeconds / 60.0 / 60.0 / 24.0 / 30.0;
+ $lastNameChangeTooRecent = $diffDays <= 30;
+ }
+
+ if (RequestUtils::isRequestMethod("POST")) {
+ $displayName = RequestUtils::getRequiredField("display_name");
+ $pfpAction = RequestUtils::getRequiredField("pfp_action");
+
+ $userName = $_POST["name"] ?? $user->name;
+
+ $user->displayName = $displayName;
+
+ $userName = strtolower($userName);
+
+ if ($userName !== $user->name) {
+ if ($lastNameChangeTooRecent) {
+ RequestUtils::triggerFormError("You can only change your username every 30 days!");
+ } else {
+ if (!ValidationUtils::isUsernameValid($userName))
+ RequestUtils::triggerFormError("Invalid username!");
+ if (!ValidationUtils::isUsernameAvailable($db, $userName))
+ RequestUtils::triggerFormError("This username is already taken!");
+ $user->name = $userName;
+ $user->nameLastChanged = new DateTimeImmutable();
+ }
+ }
+
+ switch ($pfpAction) {
+ case "keep":
+ // Do nothing
+ break;
+ case "remove":
+ $user->profilePicture = null;
+ break;
+ case "replace": {
+ if (!isset($_FILES["pfp"]) || $_FILES["pfp"]["error"] !== UPLOAD_ERR_OK) {
+ RequestUtils::triggerFormError("Please upload an image to change your profile picture");
+ }
+ $im = @imagecreatefromjpeg($_FILES["pfp"]["tmp_name"]);
+ if ($im === false)
+ $im = @imagecreatefrompng($_FILES["pfp"]["tmp_name"]);
+ if ($im === false)
+ RequestUtils::triggerFormError("Please upload a valid PNG or JPEG file");
+ /** @var \GdImage $im */
+ $thumb = imagecreatetruecolor(64, 64);
+ imagecopyresampled($thumb, $im, 0, 0, 0, 0, 64, 64, imagesx($im), imagesy($im));
+ imagedestroy($im);
+ $stream = fopen("php://memory", "w+");
+ imagejpeg($thumb, $stream, 50);
+ rewind($stream);
+ imagedestroy($thumb);
+ $user->profilePicture = stream_get_contents($stream);
+ fclose($stream);
+ } break;
+ default:
+ RequestUtils::triggerFormError("Invalid value for pfp_action");
+ break;
+ }
+
+ if (!$db->update($user))
+ RequestUtils::triggerFormError("Failed to save changes");
+
+ header("Location: $_SERVER[REQUEST_URI]");
+ } else {
+ $posts = $db->fetchCustom(Post::class, 'WHERE author_id = $1 ORDER BY post_date DESC', [ $userId ]);
+ $topics = [];
+ 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;
+ }
+ _view("template_start", ["_title" => "Forum"]);
+ _view("template_navigation_start");
+ _view("template_navigation", ["user" => $currentUser]);
+ _view("template_navigation_end");
+ _view("view_user", [
+ "user" => $user,
+ "posts" => $posts,
+ "topics" => $topics,
+ "lastNameChangeTooRecent" => $lastNameChangeTooRecent,
+ ]);
+ _view("template_end");
+ }
} elseif ($_action === "attachment") {
if (!$currentUser) {
http_response_code(403);
@@ -447,10 +536,48 @@ if ($_action === "auth") {
exit;
}
- header("Content-Type: " . $attachment->mimeType);
+ $extension = pathinfo($attachment->name, PATHINFO_EXTENSION);
+ header("Content-Type: " . FileUtils::getMimeTypeForExtension($extension));
header("Content-Length: " . strlen($attachment->contents));
header("Cache-Control: no-cache");
+ header("Content-Disposition: inline; filename=\"" . $attachment->name . "\"");
echo $attachment->contents;
+} elseif ($_action === "profilepicture") {
+ $userId = $_GET["user"] ?? throw new Exception("Missing user id");
+ $user = new User();
+ $user->id = $userId;
+ if (!$db->fetch($user)) {
+ http_response_code(404);
+ Messaging::error("No user exists with this id");
+ exit;
+ }
+
+ $ifNoneMatch = $_SERVER["HTTP_IF_NONE_MATCH"] ?? null;
+ if ($ifNoneMatch !== null)
+ $ifNoneMatch = trim($ifNoneMatch, '"');
+
+ if ($user->profilePicture === null) {
+ $fallback = __DIR__ . "/application/assets/user-fallback.jpg";
+ $etag = md5("\0");
+ header("Content-Type: image/jpeg");
+ header("Content-Length: " . filesize($fallback));
+ header("Cache-Control: no-cache");
+ header("ETag: \"" . $etag . "\"");
+ if ($ifNoneMatch === $etag)
+ http_response_code(304);
+ else
+ readfile($fallback);
+ } else {
+ $etag = md5($user->profilePicture);
+ header("Content-Type: image/jpeg");
+ header("Content-Length: " . strlen($user->profilePicture));
+ header("Cache-Control: no-cache");
+ header("ETag: \"" . $etag . "\"");
+ if ($ifNoneMatch === $etag)
+ http_response_code(304);
+ else
+ echo $user->profilePicture;
+ }
} elseif ($_action === "thumb") {
$attId = $_GET["attachment"] ?? throw new Exception("Missing attachment id");
@@ -468,7 +595,27 @@ if ($_action === "auth") {
exit;
}
- // TODO Cache thumbnail
+ $contentHash = hash("sha256", $attachment->contents);
+
+ $cacheId = bin2hex($attachment->id);
+ $cacheDir = sys_get_temp_dir() . "/mystic/forum/0/cache/thumbs/" . substr($cacheId, 0, 2) . "/" . substr($cacheId, 0, 8) . "/";
+ if (!is_dir($cacheDir))
+ mkdir($cacheDir, recursive: true);
+
+ $cacheFileData = $cacheDir . $cacheId . ".data";
+ $cacheFileInfo = $cacheDir . $cacheId . ".info";
+
+ if (is_file($cacheFileData) && is_file($cacheFileInfo)) {
+ $info = json_decode(file_get_contents($cacheFileInfo));
+ if ($info->contentHash === $contentHash) {
+ header("Content-Type: image/jpeg");
+ header("Cache-Control: max-age=86400");
+ header("X-Debug-Content: $cacheFileData");
+ readfile($cacheFileData);
+ exit;
+ }
+ }
+
$im = imagecreatefromstring($attachment->contents);
$w = imagesx($im);
$h = imagesy($im);
@@ -487,9 +634,15 @@ if ($_action === "auth") {
imagedestroy($im);
header("Content-Type: image/jpeg");
- header("Cache-Control: no-cache");
- imagejpeg($thumb, null, 40);
+ header("Cache-Control: max-age=86400");
+ imagejpeg($thumb, $cacheFileData, 40);
imagedestroy($thumb);
+ file_put_contents($cacheFileInfo, json_encode([
+ "format" => 1,
+ "contentHash" => $contentHash,
+ "created" => time(),
+ ], JSON_UNESCAPED_SLASHES));
+ readfile($cacheFileData);
} elseif ($_action === "deletepost") {
RequestUtils::ensureRequestMethod("POST");
@@ -504,7 +657,7 @@ if ($_action === "auth") {
$post = new Post();
$post->id = $postId;
- if (!$db->fetch($post)) {
+ if (!$db->fetch($post) || $post->deleted) {
http_response_code(404);
Messaging::error("No post exists with this id");
exit;
@@ -525,6 +678,8 @@ if ($_action === "auth") {
exit;
}
+ $attachments = $db->fetchCustom(Attachment::class, 'WHERE post_id = $1', [ $post->id ]);
+
$confirm = $_POST["confirm"] ?? null;
if ($confirm !== null) {
$expectedConfirm = base64_encode(hash("sha256", "confirm" . $post->id, true));
@@ -542,8 +697,6 @@ if ($_action === "auth") {
Messaging::error("Failed to delete post");
exit;
}
-
- $attachments = $db->fetchCustom(Attachment::class, 'WHERE post_id = $1', [ $post->id ]);
foreach ($attachments as $attachment) {
if (!$db->delete($attachment)) {
@@ -559,7 +712,11 @@ if ($_action === "auth") {
_view("template_navigation_start");
_view("template_navigation", ["user" => RequestUtils::getAuthorizedUser($db)]);
_view("template_navigation_end");
- _view("form_delete_post_confirm", ["post" => $post]);
+ _view("form_delete_post_confirm", [
+ "post" => $post,
+ "postAuthor" => $postAuthor,
+ "attachments" => $attachments,
+ ]);
_view("template_end");
}
} elseif ($_action === null) {