diff options
| author | Jonas Kohl | 2024-09-18 23:05:35 +0200 | 
|---|---|---|
| committer | Jonas Kohl | 2024-09-18 23:05:35 +0200 | 
| commit | 01454544896827113e49db0d2848b5aab6ce14ae (patch) | |
| tree | 9487c3d45a04d350bca2ed8401a797ed69cd7e8a /src/index.php | |
| parent | a65d424263adfbff9629c7d91a613e4504c84613 (diff) | |
Many changes
Diffstat (limited to 'src/index.php')
| -rw-r--r-- | src/index.php | 261 | 
1 files changed, 171 insertions, 90 deletions
| diff --git a/src/index.php b/src/index.php index f3a59b2..801cc07 100644 --- a/src/index.php +++ b/src/index.php @@ -18,6 +18,7 @@ use mystic\forum\utils\ValidationUtils;  use Symfony\Component\Mailer\Transport;  use Symfony\Component\Mime\Address;  use Symfony\Component\Mime\Email; +use Symfony\Contracts\Service\Attribute\Required;  header_remove("X-Powered-By"); @@ -134,6 +135,9 @@ function reArrayFiles(&$file_post) {  }  function renderPost(string $contents): string { +    $contents = preg_replace('~\R~u', "\n", $contents); +    $contents = trim($contents); +    $contents = preg_replace('/\n{3,}/', "\n\n", $contents);      $contents = htmlentities($contents);      $contents = nl2br($contents, false);      $lines = explode("\n", $contents); @@ -220,8 +224,15 @@ $superuser->id = "SUPERUSER";  if (!$db->fetch($superuser)) {      $superUserPassword = base64_encode(random_bytes(12)); +    $suEmail = env("MYSTIC_FORUM_SUPERUSER_EMAIL"); +    if ($suEmail === null) { +        http_response_code(500); +        Messaging::error("No superuser email defined. Please set the 'MYSTIC_FORUM_SUPERUSER_EMAIL' environment variable accordingly"); +        exit; +    } +      $superuser->name = "superuser"; -    $superuser->email = ""; +    $superuser->email = $suEmail;      $superuser->passwordHash = password_hash($superUserPassword, PASSWORD_DEFAULT);      $superuser->displayName = "SuperUser";      $superuser->created = new \DateTimeImmutable(); @@ -255,17 +266,18 @@ if ($_action === "auth") {      }      if (RequestUtils::isRequestMethod("POST")) { -        $username = RequestUtils::getRequiredField("username"); -        $password = RequestUtils::getRequiredField("password"); +        $formId = "login"; +        $username = RequestUtils::getRequiredField("username", $formId); +        $password = RequestUtils::getRequiredField("password", $formId);          $user = new User();          $user->name = $username;          if (!$db->fetchWhere($user, "name") || !password_verify($password, $user->passwordHash)) { -            RequestUtils::triggerFormError(__("Username or password incorrect!")); +            RequestUtils::triggerFormError(__("Username or password incorrect!"), $formId);          }          if (!$user->activated) { -            RequestUtils::triggerFormError(__("Please activate your user account first!")); +            RequestUtils::triggerFormError(__("Please activate your user account first!"), $formId);          }          RequestUtils::setAuthorizedUser($user); @@ -291,40 +303,41 @@ if ($_action === "auth") {      }      if (RequestUtils::isRequestMethod("POST")) { +        $formId = "register";          $doNotFill = $_POST["username"] ?? null;          if (!empty($doNotFill)) {              sleep(10);              http_response_code(204);              exit;          } -        $username = RequestUtils::getRequiredField("df82a9bc21"); -        $password = RequestUtils::getRequiredField("password"); -        $passwordRetype = RequestUtils::getRequiredField("password_retype"); -        $email = trim(RequestUtils::getRequiredField("email")); -        $displayName = RequestUtils::getRequiredField("display_name"); -        $captcha = RequestUtils::getRequiredField("captcha"); +        $username = RequestUtils::getRequiredField("df82a9bc21", $formId); +        $password = RequestUtils::getRequiredField("password", $formId); +        $passwordRetype = RequestUtils::getRequiredField("password_retype", $formId); +        $email = trim(RequestUtils::getRequiredField("email", $formId)); +        $displayName = RequestUtils::getRequiredField("display_name", $formId); +        $captcha = RequestUtils::getRequiredField("captcha", $formId);          if ($captcha !== ($_SESSION["captchaPhrase"] ?? null)) { -            RequestUtils::triggerFormError(__("Incorrect CAPTCHA text!")); +            RequestUtils::triggerFormError(__("Incorrect CAPTCHA text!"), $formId);          }          // usernames are always lowercase          $username = strtolower($username);          if ($password !== $passwordRetype) { -            RequestUtils::triggerFormError(__("Passwords do not match!")); +            RequestUtils::triggerFormError(__("Passwords do not match!"), $formId);          }          if (strlen($password) < 8) { -            RequestUtils::triggerFormError(__("Password too short! Your password must consist of 8 or more characters")); +            RequestUtils::triggerFormError(__("Password too short! Your password must consist of 8 or more characters"), $formId);          }          if (!ValidationUtils::isUsernameValid($username)) { -            RequestUtils::triggerFormError(__("Username has an invalid format")); +            RequestUtils::triggerFormError(__("Username has an invalid format"), $formId);          }          if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) { -            RequestUtils::triggerFormError(__("Invalid email address")); +            RequestUtils::triggerFormError(__("Invalid email address"), $formId);          }          $user = new User(); @@ -332,11 +345,11 @@ if ($_action === "auth") {          $user->email = $email;          if ($db->fetchWhere($user, "name")) { -            RequestUtils::triggerFormError(__("This username is already taken!")); +            RequestUtils::triggerFormError(__("This username is already taken!"), $formId);          }          if ($db->fetchWhere($user, "email")) { -            RequestUtils::triggerFormError(__("This email address is already in use!")); +            RequestUtils::triggerFormError(__("This email address is already in use!"), $formId);          }          // re-create user so we don't forget to clear properties set by the above queries @@ -429,6 +442,7 @@ if ($_action === "auth") {      RequestUtils::unsetAuthorizedUser();      header("Location: " . ($_GET["next"] ?? "."));  } elseif ($_action === "viewtopic") { +    $formId = "addpost";      $topicId = $_GET["topic"] ?? throw new Exception("Missing topic id");      $topic = new Topic();      $topic->id = $topicId; @@ -448,19 +462,19 @@ if ($_action === "auth") {          $attachments = reArrayFiles($_FILES["files"]);          if (count($attachments) > MAX_ATTACHMENT_COUNT) -            RequestUtils::triggerFormError(__("Too many attachments")); +            RequestUtils::triggerFormError(__("Too many attachments"), $formId);          // check all attachments before saving one          foreach ($attachments as $att) {              if ($att["size"] > MAX_ATTACHMENT_SIZE) { -                RequestUtils::triggerFormError(__("Individual file size exceeded")); +                RequestUtils::triggerFormError(__("Individual file size exceeded"), $formId);              }          } -        $message = trim(RequestUtils::getRequiredField("message")); +        $message = trim(RequestUtils::getRequiredField("message", $formId));          if (strlen($message) < 1 || strlen($message) > 0x8000) { -            RequestUtils::triggerFormError(__("Message too short or too long!")); +            RequestUtils::triggerFormError(__("Message too short or too long!"), $formId);          }          $post = new Post(); @@ -552,27 +566,28 @@ if ($_action === "auth") {      }      if (RequestUtils::isRequestMethod("POST")) { -        $title = trim(RequestUtils::getRequiredField("title")); -        $message = trim(RequestUtils::getRequiredField("message")); +        $formId = "newtopic"; +        $title = trim(RequestUtils::getRequiredField("title", $formId)); +        $message = trim(RequestUtils::getRequiredField("message", $formId));          $attachments = reArrayFiles($_FILES["files"]);          if (count($attachments) > MAX_ATTACHMENT_COUNT) -            RequestUtils::triggerFormError(__("Too many attachments")); +            RequestUtils::triggerFormError(__("Too many attachments"), $formId);          // check all attachments before saving one          foreach ($attachments as $att) {              if ($att["size"] > MAX_ATTACHMENT_SIZE) { -                RequestUtils::triggerFormError(__("Individual file size exceeded")); +                RequestUtils::triggerFormError(__("Individual file size exceeded"), $formId);              }          }          if (strlen($title) < 1 || strlen($title) > 255) { -            RequestUtils::triggerFormError(__("Title too short or too long!")); +            RequestUtils::triggerFormError(__("Title too short or too long!"), $formId);          }          if (strlen($message) < 1 || strlen($message) > 0x8000) { -            RequestUtils::triggerFormError(__("Message too short or too long!")); +            RequestUtils::triggerFormError(__("Message too short or too long!"), $formId);          }          $topic = new Topic(); @@ -653,64 +668,125 @@ if ($_action === "auth") {      }      if (RequestUtils::isRequestMethod("POST")) { -        $displayName = RequestUtils::getRequiredField("display_name"); -        $pfpAction = RequestUtils::getRequiredField("pfp_action"); +        $formId = $_POST["form_id"] ?? null; +        if ($formId === null) { +            http_response_code(400); +            msg_error("Missing form_id"); +            exit; +        } -        $userName = $_POST["name"] ?? $user->name; +        if ($formId === "update_password") { +            if (!$currentUser) { +                http_response_code(403); +                msg_error(__("You must be logged in to update your password")); +                exit; +            } -        $user->displayName = $displayName; +            if (!$isOwnProfile) { +                RequestUtils::triggerFormError(__("You don't have permission to update this user's password"), $formId); +            } -        $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(); +            RequestUtils::ensureRequestMethod("POST"); +            $currentPassword = RequestUtils::getRequiredField("current_password", $formId); +            $newPassword = RequestUtils::getRequiredField("new_password", $formId); +            $retypePassword = RequestUtils::getRequiredField("retype_password", $formId); + +            if (!password_verify($currentPassword, $currentUser->passwordHash)) { +                RequestUtils::triggerFormError(__("Current password is incorrect"), $formId); +            } + +            if ($newPassword !== $retypePassword) { +                RequestUtils::triggerFormError(__("New passwords don't match"), $formId); +            } + +            if (strlen($newPassword) < 8) { +                RequestUtils::triggerFormError(__("Password too short! Your password must consist of 8 or more characters"), $formId);              } -        } -        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")); +            $currentUser->passwordHash = password_hash($newPassword, PASSWORD_DEFAULT); +             +            if (!$db->update($currentUser)) { +                RequestUtils::triggerFormError(__("Failed to update password"), $formId); +            } + +            header("Location: $_SERVER[REQUEST_URI]"); +        } elseif ($formId === "update_profile") { +            if (!$currentUser) { +                http_response_code(403); +                msg_error(__("You must be logged in to update your profile")); +                exit; +            } + +            $canEdit = ($currentUser?->id === $user?->id && $user?->hasPermission(UserPermissions::EDIT_OWN_USER)) +                    || ($currentUser?->hasPermission(UserPermissions::EDIT_OTHER_USER)); + +            if (!$canEdit) { +                http_response_code(403); +                msg_error(__("You don't have permission to update this profile")); +                exit; +            } + +            $displayName = RequestUtils::getRequiredField("display_name", $formId); +            $pfpAction = RequestUtils::getRequiredField("pfp_action", $formId); + +            $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!"), $formId); +                } else { +                    if (!ValidationUtils::isUsernameValid($userName)) +                        RequestUtils::triggerFormError(__("Invalid username!"), $formId); +                    if (!ValidationUtils::isUsernameAvailable($db, $userName)) +                        RequestUtils::triggerFormError(__("This username is already taken!"), $formId); +                    $user->name = $userName; +                    $user->nameLastChanged = new DateTimeImmutable();                  } -                $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", context: "Update profile")); +            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"), $formId); +                    } +                    $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"), $formId); +                    /** @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", $formId); +                    break; +            } + +            if (!$db->update($user)) +                RequestUtils::triggerFormError(__("Failed to save changes", context: "Update profile"), $formId); -        header("Location: $_SERVER[REQUEST_URI]"); +            header("Location: $_SERVER[REQUEST_URI]"); +        } else { +            msg_error("Invalid formId"); +        }      } else {          $posts = $db->fetchCustom(Post::class, 'WHERE author_id = $1 ORDER BY post_date DESC', [ $userId ]);          $topics = []; @@ -727,7 +803,10 @@ if ($_action === "auth") {          }          _view("template_start", ["_title" => $user->displayName]);          _view("template_navigation_start"); -        _view("template_navigation", ["user" => $currentUser]); +        _view("template_navigation", [ +            "user" => $currentUser, +            "isViewingOwnProfile" => $isOwnProfile, +        ]);          _view("template_navigation_end");          _view("view_user", [              "user" => $user, @@ -915,8 +994,8 @@ if ($_action === "auth") {          msg_error("You need to be logged in to delete posts!");          exit;      } - -    $postId = RequestUtils::getRequiredField("post"); +    $formId = "deletepost"; +    $postId = RequestUtils::getRequiredField("post", $formId);      $post = new Post();      $post->id = $postId; @@ -993,8 +1072,9 @@ if ($_action === "auth") {          exit;      } -    $postId = RequestUtils::getRequiredField("post"); -    $message = RequestUtils::getRequiredField("message"); +    $formId = "updatepost"; +    $postId = RequestUtils::getRequiredField("post", $formId); +    $message = RequestUtils::getRequiredField("message", $formId);      $post = new Post();      $post->id = $postId; @@ -1041,7 +1121,8 @@ if ($_action === "auth") {          exit;      } -    $topicId = RequestUtils::getRequiredField("topic"); +    $formId = "deletetopic"; +    $topicId = RequestUtils::getRequiredField("topic", $formId);      $topic = new Topic();      $topic->id = $topicId; @@ -1103,8 +1184,9 @@ if ($_action === "auth") {          exit;      } -    $topicId = RequestUtils::getRequiredField("topic"); -    $title = RequestUtils::getRequiredField("title"); +    $formId = "updatetopic"; +    $topicId = RequestUtils::getRequiredField("topic", $formId); +    $title = RequestUtils::getRequiredField("title", $formId);      $topic = new Topic();      $topic->id = $topicId; @@ -1140,11 +1222,10 @@ if ($_action === "auth") {      header("Location: ./?_action=viewtopic&topic=" . urlencode($topicId));  } elseif ($_action === "search") { -    if (RequestUtils::isRequestMethod("POST")) { -        $query = RequestUtils::getRequiredField("query"); -        /** @var Post[] $posts */ - +    $query = $_GET["query"] ?? null; +    if ($query !== null) {          $start_time = microtime(true); +        /** @var Post[] $posts */          $posts = $db->execCustomQuery(<<<SQL              SELECT posts.* FROM topics, posts              WHERE |