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 destroy_user_sessions(int $userId): void { global $pdo; $delete = $pdo->prepare('DELETE FROM `fmf_sessions` WHERE `user_id` = :user'); $delete->bindValue('user', $userId); $delete->execute(); } function destroy_current_user_sessions(): void { destroy_user_sessions(current_user_id()); } 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'; }