Add type in functions signatures

This commit is contained in:
Miraty 2023-06-20 00:36:58 +02:00
parent 7f7bcadb58
commit 973a129079
11 changed files with 59 additions and 59 deletions

View file

@ -14,47 +14,47 @@ const OPTIONS_PASSWORD = [
'threads' => 64,
];
function checkUsernameFormat($username) {
function checkUsernameFormat(string $username): void {
if (preg_match('/' . USERNAME_REGEX . '/Du', $username) !== 1)
output(403, 'Username malformed.');
}
function checkPasswordFormat($password) {
function checkPasswordFormat(string $password): void {
if (preg_match('/' . PASSWORD_REGEX . '/Du', $password) !== 1)
output(403, 'Password malformed.');
}
function hashUsername($username) {
function hashUsername(string $username): string {
return base64_encode(sodium_crypto_pwhash(32, $username, hex2bin(query('select', 'params', ['name' => 'username_salt'], 'value')[0]), 2**10, 2**14, SODIUM_CRYPTO_PWHASH_ALG_ARGON2ID13));
}
function hashPassword($password) {
function hashPassword(string $password): string {
return password_hash($password, ALGO_PASSWORD, OPTIONS_PASSWORD);
}
function usernameExists($username) {
function usernameExists(string $username): bool {
return isset(query('select', 'users', ['username' => $username], 'id')[0]);
}
function checkPassword($id, $password) {
function checkPassword(string $id, string $password): bool {
return password_verify($password, query('select', 'users', ['id' => $id], 'password')[0]);
}
function outdatedPasswordHash($id) {
function outdatedPasswordHash(string $id): bool {
return password_needs_rehash(query('select', 'users', ['id' => $id], 'password')[0], ALGO_PASSWORD, OPTIONS_PASSWORD);
}
function changePassword($id, $password) {
function changePassword(string $id, string $password): void {
DB->prepare('UPDATE users SET password = :password WHERE id = :id')
->execute([':password' => hashPassword($password), ':id' => $id]);
}
function stopSession() {
function stopSession(): void {
if (session_status() === PHP_SESSION_ACTIVE)
session_destroy();
}
function logout() {
function logout(): never {
stopSession();
header('Clear-Site-Data: "*"');
@ -62,7 +62,7 @@ function logout() {
redir();
}
function setupDisplayUsername($display_username) {
function setupDisplayUsername(string $display_username): void {
$nonce = random_bytes(24);
$key = sodium_crypto_aead_xchacha20poly1305_ietf_keygen();
$cyphertext = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt(
@ -87,7 +87,7 @@ function setupDisplayUsername($display_username) {
$_SESSION['display-username-cyphertext'] = $cyphertext;
}
function authDeleteUser($user_id) {
function authDeleteUser(string $user_id): void {
$user_services = explode(',', query('select', 'users', ['id' => $user_id], 'services')[0]);
foreach (SERVICES_USER as $service)
@ -138,7 +138,7 @@ function authDeleteUser($user_id) {
query('delete', 'users', ['id' => $user_id]);
}
function rateLimit() {
function rateLimit(): void {
if (PAGE_METADATA['tokens_account_cost'] ?? 0 > 0)
rateLimitAccount(PAGE_METADATA['tokens_account_cost']);
@ -147,7 +147,7 @@ function rateLimit() {
}
const MAX_ACCOUNT_TOKENS = 86400;
function rateLimitAccount($requestedTokens) {
function rateLimitAccount(int $requestedTokens): int {
// Get
$userData = query('select', 'users', ['id' => $_SESSION['id']]);
$tokens = $userData[0]['bucket_tokens'];
@ -174,7 +174,7 @@ function rateLimitAccount($requestedTokens) {
return $tokens;
}
function rateLimitInstance($requestedTokens) {
function rateLimitInstance(int $requestedTokens): void {
// Get
$tokens = query('select', 'params', ['name' => 'instance_bucket_tokens'], 'value')[0];
$bucketLastUpdate = query('select', 'params', ['name' => 'instance_bucket_last_update'], 'value')[0];

View file

@ -1,6 +1,6 @@
<?php
function output($code, $msg = '', $logs = [''], $data = []) {
function output(int $code, string $msg = '', array $logs = [''], array $data = []): never {
http_response_code($code);
$shortCode = intval($code / 100);
if ($shortCode === 5)
@ -21,7 +21,7 @@ function exescape(array $args, array &$output = NULL, int &$result_code = NULL):
return $result_code;
}
function insert($table, $values) {
function insert(string $table, array $values): void {
$query = 'INSERT INTO "' . $table . '"(';
foreach ($values as $key => $val) {
@ -44,7 +44,7 @@ function insert($table, $values) {
->execute($values);
}
function query($action, $table, $conditions = [], $column = NULL) {
function query(string $action, string $table, array $conditions = [], string $column = NULL): array {
$query = match ($action) {
'select' => 'SELECT *',
@ -66,7 +66,7 @@ function query($action, $table, $conditions = [], $column = NULL) {
return array_column($stmt->fetchAll(PDO::FETCH_ASSOC), $column);
}
function displayIndex() { ?>
function displayIndex(): void { ?>
<nav>
<dl>
<?php foreach (PAGES[SERVICE] as $pageId => $page) {
@ -82,11 +82,11 @@ function displayIndex() { ?>
<?php
}
function redirUrl($pageId) {
function redirUrl(string $pageId): string {
return CONF['common']['prefix'] . '/' . $pageId . '?redir=' . PAGE_URL;
}
function redir($redir_to = NULL) {
function redir(string $redir_to = NULL): never {
$redir_to ??= $_GET['redir'] ?? NULL;
if ($redir_to === NULL) {
@ -100,7 +100,7 @@ function redir($redir_to = NULL) {
}
// PHP rmdir() only works on empty directories
function removeDirectory($dir) {
function removeDirectory(string $dir): void {
$dirObj = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
$files = new RecursiveIteratorIterator($dirObj, RecursiveIteratorIterator::CHILD_FIRST);
foreach ($files as $file)
@ -109,7 +109,7 @@ function removeDirectory($dir) {
output(500, 'Unable to remove directory.');
}
function equalArrays($a, $b) {
function equalArrays(array $a, array $b): bool {
return array_diff($a, $b) === [] AND array_diff($b, $a) === [];
}
@ -126,12 +126,12 @@ if (time() - query('select', 'params', ['name' => 'secret_key_last_change'], 'va
->execute([':last_change' => time()]);
}
define('SECRET_KEY', hex2bin(query('select', 'params', ['name' => 'secret_key'], 'value')[0]));
function getAuthToken() {
function getAuthToken(): string {
$salt = bin2hex(random_bytes(4));
$hash = hash_hmac('sha256', $salt . ($_SESSION['id'] ?? ''), SECRET_KEY);
return $salt . '-' . substr($hash, 0, 32);
}
function checkAuthToken($salt, $hash) {
function checkAuthToken(string $salt, string $hash): void {
$correctProof = substr(hash_hmac('sha256', $salt . $_SESSION['id'], SECRET_KEY), 0, 32);
if (hash_equals($correctProof, $hash) !== true)
output(403, _('Wrong proof.'));

View file

@ -1,6 +1,6 @@
<?php
function parseZoneFile($zone_content, $types, $filter_domain = false) {
function parseZoneFile(string $zone_content, array $types, bool|string $filter_domain = false): array {
$parsed_zone_content = [];
foreach (explode(LF, $zone_content) as $zone_line) {
if ($zone_line === '' OR str_starts_with($zone_line, ';'))
@ -46,7 +46,7 @@ function knotcConfExec(array $cmds): void {
}
}
function knotcZoneExec(string $zone, array $cmd, string $action = NULL) {
function knotcZoneExec(string $zone, array $cmd, string $action = NULL): void {
$action = checkAction($action ?? $_POST['action']);
knotc(['zone-begin', $zone], $output['begin'], $code['begin']);
@ -66,7 +66,7 @@ function knotcZoneExec(string $zone, array $cmd, string $action = NULL) {
}
}
function checkIpFormat($ip) {
function checkIpFormat(string $ip): string {
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4))
return 'A';
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))
@ -74,24 +74,24 @@ function checkIpFormat($ip) {
output(403, _('IP address malformed.'));
}
function checkAbsoluteDomainFormat($domain) { // If the domain must end with a dot
function checkAbsoluteDomainFormat(string $domain): void { // If the domain must end with a dot
if (!filter_var($domain, FILTER_VALIDATE_DOMAIN) OR preg_match('/^(?=^.{1,254}$)([a-z0-9_-]{1,63}\.){2,127}$/D', $domain) !== 1)
output(403, _('Domain malformed.'));
}
function formatEndWithDot($str) {
function formatEndWithDot(string $str): string {
if (!str_ends_with($str, '.'))
$str .= '.';
return $str;
}
function formatAbsoluteDomain($domain) {
function formatAbsoluteDomain(string $domain): string {
$domain = formatEndWithDot(strtolower($domain));
checkAbsoluteDomainFormat($domain);
return $domain;
}
function checkAction($action) {
function checkAction(string $action): string {
return match ($action) {
'add' => '',
'delete' => 'un',

View file

@ -3,7 +3,7 @@
const SUBPATH_REGEX = '^[a-z0-9-]{4,63}$';
const ED25519_PUBKEY_REGEX = '^[a-zA-Z0-9/+]{68}$';
function htSetupUserFs($id) {
function htSetupUserFs(string $id): void {
// Setup SFTP directory
if (mkdir(CONF['ht']['ht_path'] . '/fs/' . $id, 0000) !== true)
output(500, 'Can\'t create user directory.');
@ -42,19 +42,19 @@ function htSetupUserFs($id) {
output(500, 'Can\'t create Tor keys directory.');
}
function checkDomainFormat($domain) {
function checkDomainFormat(string $domain): void {
// If the domain must end without a dot
if (!filter_var($domain, FILTER_VALIDATE_DOMAIN) OR !preg_match('/^(?=^.{1,254}$)([a-z0-9_-]{1,63}\.){1,126}[a-z0-9]{1,63}$/D', $domain))
output(403, _('Domain malformed.'));
}
function formatDomain($domain) {
function formatDomain(string $domain): string {
$domain = rtrim(strtolower($domain), '.');
checkDomainFormat($domain);
return $domain;
}
function listFsDirs($username) {
function listFsDirs(string $username): array {
if ($username === '')
return [];
$absoluteDirs = glob(CONF['ht']['ht_path'] . '/fs/' . $username . '/*/', GLOB_ONLYDIR);
@ -65,7 +65,7 @@ function listFsDirs($username) {
return $dirs;
}
function addSite($username, $siteDir, $address, $type) {
function addSite(string $username, string $siteDir, string $address, string $type): void {
insert('sites', [
'username' => $username,
'site_dir' => $siteDir,
@ -75,7 +75,7 @@ function addSite($username, $siteDir, $address, $type) {
]);
}
function dirsStatuses($type) {
function dirsStatuses(string $type): array {
if (isset($_SESSION['id']) !== true)
return [];
$dbDirs = query('select', 'sites', [
@ -88,7 +88,7 @@ function dirsStatuses($type) {
return $dirs;
}
function htRelativeSymlink($target, $name) {
function htRelativeSymlink(string $target, string $name): void {
chdir(pathinfo($name)['dirname']);
$symlink = symlink($target, pathinfo($name)['basename']);
chdir(ROOT_PATH);
@ -96,7 +96,7 @@ function htRelativeSymlink($target, $name) {
output(500, 'Unable to create symlink.');
}
function htDeleteSite($address, $type, $user_id) {
function htDeleteSite(string $address, string $type, string $user_id): void {
if ($type === 'onion') {
$dir = query('select', 'sites', [

View file

@ -17,7 +17,7 @@ const ALLOWED_TYPES = ['AAAA', 'A', 'TXT', 'SRV', 'MX', 'SVCB', 'HTTPS', 'NS', '
const ZONE_MAX_CHARACTERS = 10000;
function nsParseCommonRequirements() {
function nsParseCommonRequirements(): array {
nsCheckZonePossession($_POST['zone']);
if (($_POST['subdomain'] === '') OR ($_POST['subdomain'] === '@'))
@ -35,20 +35,20 @@ function nsParseCommonRequirements() {
return $values;
}
function nsListUserZones() {
function nsListUserZones(): array {
if (isset($_SESSION['id']))
return query('select', 'zones', ['username' => $_SESSION['id']], 'zone');
return [];
}
function nsCheckZonePossession($zone) {
function nsCheckZonePossession(string $zone): void {
checkAbsoluteDomainFormat($zone);
if (!in_array($zone, nsListUserZones(), true))
output(403, 'You don\'t own this zone on the name server.');
}
function nsDeleteZone($zone, $user_id) {
function nsDeleteZone(string $zone, string $user_id): void {
// Remove from Knot configuration
knotcConfExec([['conf-unset', 'zone[' . $zone . ']']]);

View file

@ -2,18 +2,18 @@
const SUBDOMAIN_REGEX = '^(?!\-)(?!..\-\-)[a-z0-9-]{4,63}(?<!\-)$';
function regListUserDomains() {
function regListUserDomains(): array {
if (isset($_SESSION['id']))
return query('select', 'registry', ['username' => $_SESSION['id']], 'domain');
return [];
}
function regCheckDomainPossession($domain) {
function regCheckDomainPossession(string $domain): void {
if (in_array($domain, regListUserDomains(), true) !== true)
output(403, 'You don\'t own this domain on the registry.');
}
function regDeleteDomain($domain, $user_id) {
function regDeleteDomain(string $domain, string $user_id): void {
// Delete domain from registry file
$path = CONF['reg']['suffixes_path'] . '/' . regParseDomain($domain)['suffix'] . 'zone';
$content = file_get_contents($path);
@ -46,7 +46,7 @@ function regDeleteDomain($domain, $user_id) {
}
}
function regParseDomain($domain) {
function regParseDomain(string $domain): array {
$parts = explode('.', $domain, 2);
$subdomain = $parts[0];
$suffix = $parts[1];

View file

@ -5,7 +5,7 @@ set_error_handler(function ($level, $message, $file = '', $line = 0) {
throw new ErrorException($message, 0, $level, $file, $line);
});
set_exception_handler(function ($e) {
error_log($e);
error_log($e->getMessage());
http_response_code(500);
echo '<h1>Error</h1>An error occured.';
});

View file

@ -18,7 +18,7 @@ if (preg_match('/^;; Flags: qr rd ra ad;/Dm', implode("\n", $output)) !== 1)
define('COOKIE_FILE', sys_get_temp_dir() . '/cookie-' . bin2hex(random_bytes(16)) . '.txt');
function curlTest($address, $post = [], $tor = false) {
function curlTest(string $address, array $post = [], bool $tor = false): string {
$req = curl_init();
curl_setopt($req, CURLOPT_RETURNTRANSFER, true);
@ -85,7 +85,7 @@ curlTest('/auth/username', [
echo 'Created account with username "' . $username . '" and password "' . $password . '".' . LF;
function testReg() {
function testReg(): string {
$subdomain = bin2hex(random_bytes(16));
curlTest('/reg/register', [
@ -119,7 +119,7 @@ function testReg() {
return $domain;
}
function testNs($domain) {
function testNs(string $domain): void {
foreach (CONF['ns']['servers'] as $ns)
curlTest('/reg/ns', [
'action' => 'add',
@ -178,7 +178,7 @@ function testNs($domain) {
exit('Error: /ns/edit: AAAA record not set' . LF);
}
function testHt($username, $password) {
function testHt(string $username, string $password): void {
define('TEST_CONTENT', 'test-' . bin2hex(random_bytes(16)));
file_put_contents(sys_get_temp_dir() . '/index.html', TEST_CONTENT);

View file

@ -53,7 +53,7 @@ if ($blocked OR $registration_data !== [])
if ($_POST['action'] !== 'register')
message($message . ' ✔️ ' . _('This domain is open to registration!'));
function message($message) {
function message(string $message): never {
output(200, data: [
'message' => '<p>' . $message . '</p>',
'domain' => htmlspecialchars($_POST['subdomain']),

View file

@ -11,7 +11,7 @@ define('PAGE_ADDRESS', $pageAddress . ((substr($pageAddress, -1) === '/' OR $pag
define('PAGE_LINEAGE', explode('/', PAGE_ADDRESS));
define('SERVICE', dirname(PAGE_ADDRESS));
function getPageInformations($pages, $pageElements) {
function getPageInformations(array $pages, array $pageElements): array {
if (!isset($pages['index']) OR $pageElements[0] === 'index')
return [
'titles_lineage' => [$pages[$pageElements[0]]['title'] ?? false],
@ -44,7 +44,7 @@ if (in_array($_SERVER['SERVER_NAME'], CONF['common']['public_domains'], true) !=
define('SERVER_NAME', $_SERVER['SERVER_NAME']);
const SESSION_COOKIE_NAME = 'servnest-session-key';
function startSession() {
function startSession(): void {
session_start([
'name' => SESSION_COOKIE_NAME,
'sid_length' => 64,
@ -94,7 +94,7 @@ if (isset($_SESSION['id'])) {
}
}
function displayFinalMessage($data) {
function displayFinalMessage(?array $data): void {
if (isset($data['final_message'])) {
echo $data['final_message'];
unset($data['final_message']);
@ -118,7 +118,7 @@ if ($_POST !== []) {
require ROOT_PATH . '/pg-act/' . PAGE_ADDRESS . '.php';
}
function displayPage($data) {
function displayPage(?array $data): never {
require ROOT_PATH . '/view.php';
exit();
}

View file

@ -5,7 +5,7 @@ const DEBUG = false;
require 'init.php';
function deny($reason) {
function deny(string $reason): never {
!DEBUG or file_put_contents(ROOT_PATH . '/db/debug.txt', ob_get_contents() . $reason . LF);
http_response_code(403);
exit();