misuzu/public/settings.php

427 lines
16 KiB
PHP
Raw Normal View History

<?php
2018-05-16 02:58:21 +00:00
use Misuzu\Database;
2018-03-24 04:31:42 +00:00
use Misuzu\IO\File;
require_once __DIR__ . '/../misuzu.php';
2018-05-16 02:58:21 +00:00
$db = Database::connection();
$templating = $app->getTemplating();
2018-05-27 00:20:35 +00:00
$queryOffset = (int)($_GET['o'] ?? 0);
$queryTake = 15;
2018-05-16 02:58:21 +00:00
if (!$app->hasActiveSession()) {
echo render_error(403);
return;
}
2018-05-27 00:20:35 +00:00
$csrfErrorString = "Couldn't verify you, please refresh the page and retry.";
2018-05-27 00:20:35 +00:00
$settingsModes = [
'account' => 'Account',
'avatar' => 'Avatar',
'sessions' => 'Sessions',
'login-history' => 'Login History',
];
2018-05-27 00:20:35 +00:00
$settingsMode = $_GET['m'] ?? key($settingsModes);
2018-05-27 00:20:35 +00:00
$templating->vars([
'settings_mode' => $settingsMode,
'settings_modes' => $settingsModes,
]);
2018-05-27 00:20:35 +00:00
if (!array_key_exists($settingsMode, $settingsModes)) {
2018-03-25 22:00:42 +00:00
http_response_code(404);
$templating->var('settings_title', 'Not Found');
echo $templating->render('settings.notfound');
return;
}
2018-05-27 00:20:35 +00:00
$settingsErrors = [];
2018-05-27 00:20:35 +00:00
$disableAccountOptions = $app->getConfig()->get('Auth', 'prevent_registration', 'bool', false);
$avatarFileName = "{$app->getUserId()}.msz";
$avatarWidthMax = $app->getConfig()->get('Avatar', 'max_width', 'int', 4000);
$avatarHeightMax = $app->getConfig()->get('Avatar', 'max_height', 'int', 4000);
$avatarFileSizeMax = $app->getConfig()->get('Avatar', 'max_filesize', 'int', 1000000);
2018-03-24 04:31:42 +00:00
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
2018-05-27 00:20:35 +00:00
switch ($settingsMode) {
case 'account':
if (!tmp_csrf_verify($_POST['csrf'] ?? '')) {
2018-05-27 00:20:35 +00:00
$settingsErrors[] = $csrfErrorString;
break;
}
if (isset($_POST['profile']) && is_array($_POST['profile'])) {
2018-05-27 00:20:35 +00:00
$setUserFieldErrors = user_profile_fields_set($app->getUserId(), $_POST['profile']);
if (count($setUserFieldErrors) > 0) {
foreach ($setUserFieldErrors as $name => $error) {
switch ($error) {
case MSZ_USER_PROFILE_INVALID_FIELD:
$settingsErrors[] = sprintf("Field '%s' does not exist!", $name);
break;
2018-05-27 00:20:35 +00:00
case MSZ_USER_PROFILE_FILTER_FAILED:
$settingsErrors[] = sprintf(
'%s field was invalid!',
user_profile_field_get_display_name($name)
);
break;
2018-05-27 00:20:35 +00:00
case MSZ_USER_PROFILE_UPDATE_FAILED:
$settingsErrors[] = 'Failed to update values, contact an administator.';
break;
default:
$settingsErrors[] = 'An unexpected error occurred, contact an administator.';
break;
}
}
}
}
2018-05-27 00:20:35 +00:00
if (!$disableAccountOptions) {
if (!empty($_POST['current_password'])
2018-03-23 00:27:46 +00:00
|| (
2018-05-27 00:20:35 +00:00
(isset($_POST['password']) || isset($_POST['email']))
2018-03-23 00:27:46 +00:00
&& (!empty($_POST['password']['new']) || !empty($_POST['email']['new']))
)
) {
2018-05-27 00:20:35 +00:00
$updateAccountFields = [];
2018-05-16 02:58:21 +00:00
$fetchPassword = $db->prepare('
SELECT `password`
FROM `msz_users`
WHERE `user_id` = :user_id
');
$fetchPassword->bindValue('user_id', $app->getUserId());
$currentPassword = $fetchPassword->execute() ? $fetchPassword->fetchColumn() : null;
if (empty($currentPassword)) {
2018-05-27 00:20:35 +00:00
$settingsErrors[] = 'Something went horribly wrong.';
2018-05-16 02:58:21 +00:00
break;
}
if (!password_verify($_POST['current_password'], $currentPassword)) {
2018-05-27 00:20:35 +00:00
$settingsErrors[] = 'Your current password was incorrect.';
break;
}
if (!empty($_POST['email']['new'])) {
2018-05-16 02:58:21 +00:00
if (empty($_POST['email']['confirm'])
|| $_POST['email']['new'] !== $_POST['email']['confirm']) {
2018-05-27 00:20:35 +00:00
$settingsErrors[] = 'The given e-mail addresses did not match.';
break;
}
2018-05-16 02:58:21 +00:00
$checkIfAlreadySet = $db->prepare('
SELECT COUNT(`user_id`)
FROM `msz_users`
WHERE LOWER(:email) = LOWER(:email)
');
$checkIfAlreadySet->bindValue('email', $_POST['email']['new']);
$isAlreadySet = $checkIfAlreadySet->execute()
? $checkIfAlreadySet->fetchColumn() > 0
: false;
if ($isAlreadySet) {
2018-05-27 00:20:35 +00:00
$settingsErrors[] = 'This is your e-mail address already!';
break;
}
2018-05-16 02:58:21 +00:00
$email_validate = user_validate_email($_POST['email']['new'], true);
if ($email_validate !== '') {
switch ($email_validate) {
case 'dns':
2018-05-27 00:20:35 +00:00
$settingsErrors[] = 'No valid MX record exists for this domain.';
break;
case 'format':
2018-05-27 00:20:35 +00:00
$settingsErrors[] = 'The given e-mail address was incorrectly formatted.';
break;
case 'in-use':
2018-05-27 00:20:35 +00:00
$settingsErrors[] = 'This e-mail address has already been used by another user.';
break;
default:
2018-05-27 00:20:35 +00:00
$settingsErrors[] = 'Unknown e-mail validation error.';
}
break;
}
2018-05-27 00:20:35 +00:00
$updateAccountFields['email'] = strtolower($_POST['email']['new']);
}
if (!empty($_POST['password']['new'])) {
if (empty($_POST['password']['confirm'])
|| $_POST['password']['new'] !== $_POST['password']['confirm']) {
2018-05-27 00:20:35 +00:00
$settingsErrors[] = "The given passwords did not match.";
break;
}
2018-05-16 02:58:21 +00:00
$password_validate = user_validate_password($_POST['password']['new']);
if ($password_validate !== '') {
2018-05-27 00:20:35 +00:00
$settingsErrors[] = "The given passwords was too weak.";
break;
}
2018-05-27 00:20:35 +00:00
$updateAccountFields['password'] = user_password_hash($_POST['password']['new']);
}
2018-05-27 00:20:35 +00:00
if (count($updateAccountFields) > 0) {
$updateUser = $db->prepare('
UPDATE `msz_users`
SET ' . pdo_prepare_array_update($updateAccountFields, true) . '
WHERE `user_id` = :user_id
');
$updateAccountFields['user_id'] = $app->getUserId();
$updateUser->execute($updateAccountFields);
}
}
}
break;
2018-03-24 04:31:42 +00:00
case 'avatar':
if (isset($_POST['delete'])) {
if (!tmp_csrf_verify($_POST['delete'])) {
2018-05-27 00:20:35 +00:00
$settingsErrors[] = $csrfErrorString;
2018-03-24 04:31:42 +00:00
break;
}
2018-03-26 00:13:26 +00:00
$delete_this = [
2018-05-27 00:20:35 +00:00
$app->getStore('avatars/original')->filename($avatarFileName),
$app->getStore('avatars/200x200')->filename($avatarFileName),
2018-03-26 00:13:26 +00:00
];
foreach ($delete_this as $delete_avatar) {
if (File::exists($delete_avatar)) {
File::delete($delete_avatar);
}
}
2018-03-24 04:31:42 +00:00
break;
}
if (isset($_POST['upload'])) {
if (!tmp_csrf_verify($_POST['upload'])) {
2018-05-27 00:20:35 +00:00
$settingsErrors[] = $csrfErrorString;
2018-03-24 04:31:42 +00:00
break;
}
switch ($_FILES['avatar']['error']) {
case UPLOAD_ERR_OK:
break;
2018-03-25 21:40:01 +00:00
case UPLOAD_ERR_NO_FILE:
2018-05-27 00:20:35 +00:00
$settingsErrors[] = 'Select a file before hitting upload!';
2018-03-25 21:40:01 +00:00
break;
2018-03-24 04:31:42 +00:00
case UPLOAD_ERR_PARTIAL:
2018-05-27 00:20:35 +00:00
$settingsErrors[] = 'The upload was interrupted, please try again!';
2018-03-24 04:31:42 +00:00
break;
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
2018-05-27 00:20:35 +00:00
$settingsErrors[] = sprintf(
'Your avatar is not allowed to be larger in filesize than %s',
byte_symbol($avatarFileSizeMax, true)
);
2018-03-24 04:31:42 +00:00
break;
case UPLOAD_ERR_NO_TMP_DIR:
case UPLOAD_ERR_CANT_WRITE:
2018-05-27 00:20:35 +00:00
$settingsErrors[] = 'Unable to save your avatar, contact an administator!';
2018-03-24 04:31:42 +00:00
break;
case UPLOAD_ERR_EXTENSION:
default:
2018-05-27 00:20:35 +00:00
$settingsErrors[] = 'Something happened?';
2018-03-24 04:31:42 +00:00
break;
}
2018-05-27 00:20:35 +00:00
if (count($settingsErrors) > 0) {
2018-03-24 04:31:42 +00:00
break;
}
$upload_path = $_FILES['avatar']['tmp_name'];
$upload_meta = getimagesize($upload_path);
if (!$upload_meta
|| !in_array($upload_meta[2], [IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG], true)
|| $upload_meta[0] < 1
|| $upload_meta[1] < 1) {
2018-05-27 00:20:35 +00:00
$settingsErrors[] = 'Please provide a valid image.';
2018-03-24 04:31:42 +00:00
break;
}
2018-05-27 00:20:35 +00:00
if ($upload_meta[0] > $avatarWidthMax || $upload_meta[1] > $avatarHeightMax) {
$settingsErrors[] = sprintf(
"Your avatar can't be larger than %dx%d, yours was %dx%d",
$avatarWidthMax,
$avatarHeightMax,
$upload_meta[0],
$upload_meta[1]
);
2018-03-24 04:31:42 +00:00
break;
}
2018-05-27 00:20:35 +00:00
if (filesize($upload_path) > $avatarFileSizeMax) {
$settingsErrors[] = sprintf(
'Your avatar is not allowed to be larger in filesize than %s!',
byte_symbol($avatarFileSizeMax, true)
);
2018-03-24 04:31:42 +00:00
break;
}
2018-05-27 00:20:35 +00:00
$avatar_path = $app->getStore('avatars/original')->filename($avatarFileName);
2018-03-24 04:31:42 +00:00
move_uploaded_file($upload_path, $avatar_path);
2018-05-27 00:20:35 +00:00
$crop_path = $app->getStore('avatars/200x200')->filename($avatarFileName);
2018-03-24 04:31:42 +00:00
if (File::exists($crop_path)) {
File::delete($crop_path);
}
break;
}
2018-05-27 00:20:35 +00:00
$settingsErrors[] = "You shouldn't have done that.";
2018-03-24 04:31:42 +00:00
break;
case 'sessions':
if (!tmp_csrf_verify($_POST['csrf'] ?? '')) {
2018-05-27 00:20:35 +00:00
$settingsErrors[] = $csrfErrorString;
break;
}
$session_id = (int)($_POST['session'] ?? 0);
if ($session_id < 1) {
2018-05-27 00:20:35 +00:00
$settingsErrors[] = 'Invalid session.';
break;
}
2018-05-16 02:58:21 +00:00
$findSession = $db->prepare('
SELECT `session_id`, `user_id`
FROM `msz_sessions`
WHERE `session_id` = :session_id
');
$findSession->bindValue('session_id', $session_id);
$session = $findSession->execute() ? $findSession->fetch() : null;
2018-05-16 02:58:21 +00:00
if (!$session || (int)$session['user_id'] !== $app->getUserId()) {
2018-05-27 00:20:35 +00:00
$settingsErrors[] = 'You may only end your own sessions.';
break;
}
2018-05-16 02:58:21 +00:00
if ((int)$session['session_id'] === $app->getSessionId()) {
header('Location: /auth.php?m=logout&s=' . tmp_csrf_token());
return;
}
2018-05-27 00:20:35 +00:00
user_session_delete($session['session_id']);
break;
}
}
2018-05-27 00:20:35 +00:00
$templating->var('settings_title', $settingsModes[$settingsMode]);
$templating->var('settings_errors', $settingsErrors);
2018-05-27 00:20:35 +00:00
switch ($settingsMode) {
case 'account':
2018-05-27 00:20:35 +00:00
$profileFields = user_profile_fields_get();
2018-05-16 02:58:21 +00:00
$getUserFields = $db->prepare('
2018-05-27 00:20:35 +00:00
SELECT ' . pdo_prepare_array($profileFields, true, '`user_%s`') . '
2018-05-16 02:58:21 +00:00
FROM `msz_users`
WHERE `user_id` = :user_id
');
$getUserFields->bindValue('user_id', $app->getUserId());
$userFields = $getUserFields->execute() ? $getUserFields->fetch() : [];
2018-05-27 00:20:35 +00:00
$templating->vars([
'settings_profile_fields' => $profileFields,
'settings_profile_values' => $userFields,
'settings_disable_account_options' => $disableAccountOptions,
]);
break;
2018-03-24 04:31:42 +00:00
case 'avatar':
2018-05-27 00:20:35 +00:00
$userHasAvatar = File::exists($app->getStore('avatars/original')->filename($avatarFileName));
$templating->vars([
'avatar_user_id' => $app->getUserId(),
'avatar_max_width' => $avatarWidthMax,
'avatar_max_height' => $avatarHeightMax,
'avatar_max_filesize' => $avatarFileSizeMax,
'user_has_avatar' => $userHasAvatar,
]);
2018-03-24 04:31:42 +00:00
break;
case 'sessions':
2018-05-16 20:48:33 +00:00
$getSessionCount = $db->prepare('
SELECT COUNT(`session_id`)
FROM `msz_sessions`
WHERE `user_id` = :user_id
');
$getSessionCount->bindValue('user_id', $app->getUserId());
$sessionCount = $getSessionCount->execute() ? $getSessionCount->fetchColumn() : 0;
2018-05-16 02:58:21 +00:00
$getSessions = $db->prepare('
SELECT
`session_id`, `session_country`, `user_agent`, `created_at`, `expires_on`,
INET6_NTOA(`session_ip`) as `session_ip_decoded`
FROM `msz_sessions`
WHERE `user_id` = :user_id
ORDER BY `session_id` DESC
2018-05-16 20:48:33 +00:00
LIMIT :offset, :take
2018-05-16 02:58:21 +00:00
');
2018-05-27 00:20:35 +00:00
$getSessions->bindValue('offset', $queryOffset);
$getSessions->bindValue('take', $queryTake);
2018-05-16 02:58:21 +00:00
$getSessions->bindValue('user_id', $app->getUserId());
$sessions = $getSessions->execute() ? $getSessions->fetchAll() : [];
2018-05-16 20:48:33 +00:00
$templating->vars([
'active_session_id' => $app->getSessionId(),
'user_sessions' => $sessions,
2018-05-27 00:20:35 +00:00
'sessions_offset' => $queryOffset,
'sessions_take' => $queryTake,
2018-05-16 20:48:33 +00:00
'sessions_count' => $sessionCount,
]);
break;
case 'login-history':
2018-05-16 20:48:33 +00:00
$getLoginAttemptsCount = $db->prepare('
SELECT COUNT(`attempt_id`)
FROM `msz_login_attempts`
WHERE `user_id` = :user_id
');
$getLoginAttemptsCount->bindValue('user_id', $app->getUserId());
$loginAttemptsCount = $getLoginAttemptsCount->execute() ? $getLoginAttemptsCount->fetchColumn() : 0;
2018-05-16 02:58:21 +00:00
$getLoginAttempts = $db->prepare('
SELECT
`attempt_id`, `attempt_country`, `was_successful`, `user_agent`, `created_at`,
INET6_NTOA(`attempt_ip`) as `attempt_ip_decoded`
FROM `msz_login_attempts`
WHERE `user_id` = :user_id
ORDER BY `attempt_id` DESC
2018-05-16 20:48:33 +00:00
LIMIT :offset, :take
2018-05-16 02:58:21 +00:00
');
2018-05-27 00:20:35 +00:00
$getLoginAttempts->bindValue('offset', $queryOffset);
$getLoginAttempts->bindValue('take', $queryTake);
2018-05-16 02:58:21 +00:00
$getLoginAttempts->bindValue('user_id', $app->getUserId());
$loginAttempts = $getLoginAttempts->execute() ? $getLoginAttempts->fetchAll() : [];
2018-05-16 20:48:33 +00:00
$templating->vars([
'user_login_attempts' => $loginAttempts,
2018-05-27 00:20:35 +00:00
'login_attempts_offset' => $queryOffset,
'login_attempts_take' => $queryTake,
2018-05-16 20:48:33 +00:00
'login_attempts_count' => $loginAttemptsCount,
]);
break;
}
2018-05-27 00:20:35 +00:00
echo $templating->render("settings.{$settingsMode}");