240 lines
7.4 KiB
PHP
240 lines
7.4 KiB
PHP
|
<?php
|
||
|
include_once '_utils.php';
|
||
|
|
||
|
define('FMF_UF_SCROLLBEYOND', 1);
|
||
|
|
||
|
function get_user_id(string $username, string $email): int {
|
||
|
global $pdo;
|
||
|
|
||
|
$checkUser = $pdo->prepare('SELECT `user_id` FROM `fmf_users` WHERE LOWER(`user_login`) = LOWER(:login) OR LOWER(`user_email`) = LOWER(:email)');
|
||
|
$checkUser->bindValue('login', $username);
|
||
|
$checkUser->bindValue('email', $email);
|
||
|
$checkUser->execute();
|
||
|
|
||
|
return (int)$checkUser->fetchColumn();
|
||
|
}
|
||
|
|
||
|
function get_user_for_login(string $nameOrMail): array {
|
||
|
global $pdo;
|
||
|
|
||
|
$getUser = $pdo->prepare('SELECT `user_id`, `user_login`, `user_password`, `user_email_verification` FROM `fmf_users` WHERE LOWER(`user_login`) = LOWER(:login) OR LOWER(`user_email`) = LOWER(:email)');
|
||
|
$getUser->bindValue('login', $nameOrMail);
|
||
|
$getUser->bindValue('email', $nameOrMail);
|
||
|
$user = $getUser->execute() ? $getUser->fetch(PDO::FETCH_ASSOC) : false;
|
||
|
|
||
|
return $user ? $user : [];
|
||
|
}
|
||
|
|
||
|
function user_info(?int $user, bool $fresh = false): array {
|
||
|
global $pdo;
|
||
|
static $cache = [];
|
||
|
|
||
|
if($user < 1)
|
||
|
return [];
|
||
|
if(!$fresh && !empty($cache[$user]))
|
||
|
return $cache[$user];
|
||
|
|
||
|
$getUserInfo = $pdo->prepare('SELECT *, UNIX_TIMESTAMP(`user_created`) AS `user_created`, UNIX_TIMESTAMP(`user_banned`) AS `user_banned`, MD5(LOWER(TRIM(`user_email`))) AS `gravatar_hash` FROM `fmf_users` WHERE `user_id` = :user');
|
||
|
$getUserInfo->bindValue('user', $user);
|
||
|
$userInfo = $getUserInfo->execute() ? $getUserInfo->fetch(PDO::FETCH_ASSOC) : false;
|
||
|
|
||
|
return $cache[$user] = ($userInfo ? $userInfo : []);
|
||
|
}
|
||
|
|
||
|
function user_has_flag(int $user, int $flag, bool $strict = false): bool {
|
||
|
$userInfo = user_info($user);
|
||
|
|
||
|
if(empty($userInfo))
|
||
|
return false;
|
||
|
|
||
|
$flags = ($userInfo['user_flags'] & $flag);
|
||
|
|
||
|
return $strict ? ($flags === $flag) : ($flags > 0);
|
||
|
}
|
||
|
|
||
|
function create_user(string $username, string $email, string $password, string $ipAddr, bool $verified = false): array {
|
||
|
global $pdo;
|
||
|
|
||
|
$verification = $verified ? null : bin2hex(random_bytes(16));
|
||
|
|
||
|
$createUser = $pdo->prepare('INSERT INTO `fmf_users` (`user_login`, `user_password`, `user_email`, `user_email_verification`, `user_ip_created`) VALUES (:login, :password, :email, :verification, INET6_ATON(:ip))');
|
||
|
$createUser->bindValue('login', $username);
|
||
|
$createUser->bindValue('password', password_hash($password, PASSWORD_DEFAULT));
|
||
|
$createUser->bindValue('email', $email);
|
||
|
$createUser->bindValue('verification', $verification);
|
||
|
$createUser->bindValue('ip', $ipAddr);
|
||
|
$createUser->execute();
|
||
|
|
||
|
return [
|
||
|
'user_id' => (int)$pdo->lastInsertId(),
|
||
|
'verification' => $verification,
|
||
|
];
|
||
|
}
|
||
|
|
||
|
function validate_username(string $username): ?string {
|
||
|
if($username !== trim($username))
|
||
|
return 'Your username may not start or end with spaces.';
|
||
|
|
||
|
$usernameLength = strlen($username);
|
||
|
|
||
|
if($usernameLength < 3)
|
||
|
return 'Your username must be longer than 3 characters.';
|
||
|
|
||
|
if($usernameLength > 16)
|
||
|
return 'Your username may not be longer than 16 characters.';
|
||
|
|
||
|
if(!preg_match('#^[A-Za-z0-9-_]+$#u', $username))
|
||
|
return 'Your username may only contains alphanumeric characters, dashes and underscores (A-Z, a-z, 0-9, -, _).';
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
function validate_email(string $email): ?string {
|
||
|
if(filter_var($email, FILTER_VALIDATE_EMAIL) === false)
|
||
|
return 'Your e-mail address is not correctly formatted.';
|
||
|
|
||
|
$domain = mb_substr(mb_strstr($email, '@'), 1);
|
||
|
|
||
|
if(!checkdnsrr($domain, 'MX') && !checkdnsrr($domain, 'A'))
|
||
|
return 'Your e-mail address domain does not have valid DNS records.';
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
function validate_password(string $password): ?string {
|
||
|
if(unique_chars($password) < 10)
|
||
|
return 'Your password is too weak.';
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
function activate_user(string $code): void {
|
||
|
global $pdo;
|
||
|
|
||
|
if(strlen($code) !== 32)
|
||
|
return;
|
||
|
|
||
|
$verify = $pdo->prepare('UPDATE `fmf_users` SET `user_email_verification` = NULL WHERE `user_email_verification` = :code');
|
||
|
$verify->bindValue('code', $code);
|
||
|
$verify->execute();
|
||
|
}
|
||
|
|
||
|
function create_session(int $userId): string {
|
||
|
global $pdo;
|
||
|
|
||
|
if($userId < 1)
|
||
|
return '';
|
||
|
|
||
|
$sessionKey = bin2hex(random_bytes(32));
|
||
|
|
||
|
$createSession = $pdo->prepare('INSERT INTO `fmf_sessions` (`user_id`, `sess_key`) VALUES (:user, :key)');
|
||
|
$createSession->bindValue('user', $userId);
|
||
|
$createSession->bindValue('key', $sessionKey);
|
||
|
$createSession->execute();
|
||
|
|
||
|
return $sessionKey;
|
||
|
}
|
||
|
|
||
|
function purge_old_sessions(): void {
|
||
|
global $pdo;
|
||
|
$pdo->exec('DELETE FROM `fmf_sessions` WHERE `sess_created` + INTERVAL 1 MONTH <= NOW()');
|
||
|
}
|
||
|
|
||
|
function session_activate(?string $key): void {
|
||
|
global $pdo;
|
||
|
|
||
|
if(empty($key) || strlen($key) !== 64)
|
||
|
return;
|
||
|
|
||
|
$verify = $pdo->prepare('SELECT `user_id` FROM `fmf_sessions` WHERE `sess_key` = :key AND `sess_created` + INTERVAL 1 MONTH > NOW()');
|
||
|
$verify->bindValue('key', $key);
|
||
|
$userId = $verify->execute() ? $verify->fetchColumn() : 0;
|
||
|
|
||
|
if($userId < 1)
|
||
|
return;
|
||
|
|
||
|
$GLOBALS['fmf_user_id'] = (int)$userId;
|
||
|
}
|
||
|
|
||
|
function session_active(): bool {
|
||
|
return !empty($GLOBALS['fmf_user_id']) && is_int($GLOBALS['fmf_user_id']) && $GLOBALS['fmf_user_id'] > 0;
|
||
|
}
|
||
|
|
||
|
function logout_token(): string {
|
||
|
$sessionKey = $_COOKIE['fmfauth'] ?? '';
|
||
|
|
||
|
if(strlen($sessionKey) !== 64 || !ctype_xdigit($sessionKey))
|
||
|
return bin2hex(random_bytes(4));
|
||
|
|
||
|
$offset = hexdec($sessionKey[0]) * 2;
|
||
|
$offset = hexdec($sessionKey[$offset]) * 2;
|
||
|
$offset = hexdec($sessionKey[$offset]) * 2;
|
||
|
|
||
|
return substr($sessionKey, $offset, 8);
|
||
|
}
|
||
|
|
||
|
function destroy_session(string $token): void {
|
||
|
global $pdo;
|
||
|
|
||
|
$delete = $pdo->prepare('DELETE FROM `fmf_sessions` WHERE `sess_key` = :key');
|
||
|
$delete->bindValue('key', $token);
|
||
|
$delete->execute();
|
||
|
}
|
||
|
|
||
|
function current_user_id(): int {
|
||
|
return session_active() ? $GLOBALS['fmf_user_id'] : 0;
|
||
|
}
|
||
|
|
||
|
function verify_password(string $pass, ?int $user = null): bool {
|
||
|
global $pdo;
|
||
|
$user = $user ?? current_user_id();
|
||
|
|
||
|
if($user < 1)
|
||
|
return false;
|
||
|
|
||
|
$getHash = $pdo->prepare('SELECT `user_password` FROM `fmf_users` WHERE `user_id` = :user');
|
||
|
$getHash->bindValue('user', $user);
|
||
|
$hash = $getHash->execute() ? $getHash->fetchColumn() : '';
|
||
|
|
||
|
if(empty($hash))
|
||
|
return false;
|
||
|
|
||
|
return password_verify($pass, $hash);
|
||
|
}
|
||
|
|
||
|
function user_set_password(int $user, string $password): void {
|
||
|
global $pdo;
|
||
|
|
||
|
if($user < 1)
|
||
|
return;
|
||
|
|
||
|
$password = password_hash($password, PASSWORD_DEFAULT);
|
||
|
|
||
|
$update = $pdo->prepare('UPDATE `fmf_users` SET `user_password` = :pass WHERE `user_id` = :user');
|
||
|
$update->bindValue('pass', $password);
|
||
|
$update->bindValue('user', $user);
|
||
|
$update->execute();
|
||
|
}
|
||
|
|
||
|
function user_set_email(int $user, string $email, bool $verified = false): ?string {
|
||
|
global $pdo;
|
||
|
|
||
|
if($user < 1)
|
||
|
return null;
|
||
|
|
||
|
$verification = $verified ? null : bin2hex(random_bytes(16));
|
||
|
|
||
|
$update = $pdo->prepare('UPDATE `fmf_users` SET `user_email` = LOWER(:mail), `user_email_verification` = :verf WHERE `user_id` = :user');
|
||
|
$update->bindValue('mail', $email);
|
||
|
$update->bindValue('verf', $verification);
|
||
|
$update->bindValue('user', $user);
|
||
|
$update->execute();
|
||
|
|
||
|
return $verification;
|
||
|
}
|
||
|
|
||
|
function user_gravatar(?int $user, int $res = 80): string {
|
||
|
$authorInfo = user_info($user);
|
||
|
return '//www.gravatar.com/avatar/'. ($authorInfo['gravatar_hash'] ?? str_repeat('0', 32)) .'?s='. $res .'&r=g&d=identicon';
|
||
|
}
|