From 973a129079fec1d28509fe0f1bc3575d8b7deae1 Mon Sep 17 00:00:00 2001
From: Miraty
Date: Tue, 20 Jun 2023 00:36:58 +0200
Subject: [PATCH 01/13] Add type in functions signatures
---
fn/auth.php | 30 +++++++++++++++---------------
fn/common.php | 20 ++++++++++----------
fn/dns.php | 14 +++++++-------
fn/ht.php | 16 ++++++++--------
fn/ns.php | 8 ++++----
fn/reg.php | 8 ++++----
init.php | 2 +-
jobs/check.php | 8 ++++----
pg-act/reg/register.php | 2 +-
router.php | 8 ++++----
sftpgo-auth.php | 2 +-
11 files changed, 59 insertions(+), 59 deletions(-)
diff --git a/fn/auth.php b/fn/auth.php
index 4f321e2..d96d1cb 100644
--- a/fn/auth.php
+++ b/fn/auth.php
@@ -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];
diff --git a/fn/common.php b/fn/common.php
index 0002ca1..33815e9 100644
--- a/fn/common.php
+++ b/fn/common.php
@@ -1,6 +1,6 @@
$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 { ?>
$page) {
@@ -82,11 +82,11 @@ function displayIndex() { ?>
'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.'));
diff --git a/fn/dns.php b/fn/dns.php
index ef4f803..6c4b171 100644
--- a/fn/dns.php
+++ b/fn/dns.php
@@ -1,6 +1,6 @@
'',
'delete' => 'un',
diff --git a/fn/ht.php b/fn/ht.php
index 8b9e3d5..75d03a3 100644
--- a/fn/ht.php
+++ b/fn/ht.php
@@ -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', [
diff --git a/fn/ns.php b/fn/ns.php
index a93e82c..a659385 100644
--- a/fn/ns.php
+++ b/fn/ns.php
@@ -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 . ']']]);
diff --git a/fn/reg.php b/fn/reg.php
index 0a5f981..c49e6c4 100644
--- a/fn/reg.php
+++ b/fn/reg.php
@@ -2,18 +2,18 @@
const SUBDOMAIN_REGEX = '^(?!\-)(?!..\-\-)[a-z0-9-]{4,63}(? $_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];
diff --git a/init.php b/init.php
index 703cd93..c85e75c 100644
--- a/init.php
+++ b/init.php
@@ -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 'Error An error occured.';
});
diff --git a/jobs/check.php b/jobs/check.php
index 733447b..a6dc279 100644
--- a/jobs/check.php
+++ b/jobs/check.php
@@ -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);
diff --git a/pg-act/reg/register.php b/pg-act/reg/register.php
index f516c45..ad590f7 100644
--- a/pg-act/reg/register.php
+++ b/pg-act/reg/register.php
@@ -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' => '' . $message . '
',
'domain' => htmlspecialchars($_POST['subdomain']),
diff --git a/router.php b/router.php
index a977203..0c0a024 100644
--- a/router.php
+++ b/router.php
@@ -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();
}
diff --git a/sftpgo-auth.php b/sftpgo-auth.php
index fa474a0..6ec3ef8 100644
--- a/sftpgo-auth.php
+++ b/sftpgo-auth.php
@@ -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();
From edcad22a841f64ce3659466aaa8ed555f60bfb56 Mon Sep 17 00:00:00 2001
From: Miraty
Date: Tue, 20 Jun 2023 00:41:30 +0200
Subject: [PATCH 02/13] Fix missing string internationalization
---
locales/fr/C/LC_MESSAGES/messages.po | 31 ++++++++++++++++------------
locales/messages.pot | 31 ++++++++++++++++------------
pg-view/ht/del.php | 2 +-
3 files changed, 37 insertions(+), 27 deletions(-)
diff --git a/locales/fr/C/LC_MESSAGES/messages.po b/locales/fr/C/LC_MESSAGES/messages.po
index 1e0cf70..ac61d4f 100644
--- a/locales/fr/C/LC_MESSAGES/messages.po
+++ b/locales/fr/C/LC_MESSAGES/messages.po
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-06-15 01:33+0200\n"
+"POT-Creation-Date: 2023-06-20 00:38+0200\n"
"Language: fr\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -321,11 +321,11 @@ msgstr "%sCode source%s disponible sous %s."
msgid "Your account can't be deleted because the %s service is currently unavailable."
msgstr "Votre compte ne peut pas être supprimé car le service %s est actuellement indisponible."
-#: fn/auth.php:143
+#: fn/auth.php:161
msgid "Account rate limit reached, try again later."
msgstr "Limite de taux pour ce compte atteinte, réessayez plus tard."
-#: fn/auth.php:168
+#: fn/auth.php:186
msgid "Global rate limit reached, try again later."
msgstr "Limite de taux globale atteinte, réessayez plus tard."
@@ -341,15 +341,15 @@ msgstr "Erreur de l'utilisataire : "
msgid "Server error : "
msgstr "Erreur du serveur : "
-#: fn/common.php:132
+#: fn/common.php:137
msgid "Wrong proof."
msgstr "Preuve incorrecte."
-#: fn/dns.php:64
+#: fn/dns.php:74
msgid "IP address malformed."
msgstr "Adresse IP malformée."
-#: fn/dns.php:69 fn/ht.php:31
+#: fn/dns.php:79 fn/ht.php:48
msgid "Domain malformed."
msgstr "Domaine malformé."
@@ -434,13 +434,13 @@ msgstr "Ce domaine doit avoir %2$s pour unique enregistrement %1$s."
msgid "No TXT record with the expected format has been found."
msgstr "Aucun enregistrement TXT avec le format attendu n'a été trouvé."
-#: pg-act/ht/add-dns.php:41 pg-act/ht/add-onion.php:37
+#: pg-act/ht/add-dns.php:48 pg-act/ht/add-onion.php:50
#: pg-act/ht/add-subdomain.php:19 pg-act/ht/add-subpath.php:19
#, php-format
msgid "%s added on this directory."
msgstr "%s ajouté sur ce dossier."
-#: pg-act/ht/add-onion.php:37
+#: pg-act/ht/add-onion.php:50
#, php-format
msgid "Its address is: %s"
msgstr "Son adresse est : %s"
@@ -476,7 +476,7 @@ msgstr "Clés SSH mises à jour."
#: pg-act/ns/caa.php:25 pg-act/ns/cname.php:16 pg-act/ns/dname.php:16
#: pg-act/ns/ip.php:16 pg-act/ns/loc.php:72 pg-act/ns/mx.php:20
#: pg-act/ns/ns.php:16 pg-act/ns/srv.php:28 pg-act/ns/sshfp.php:25
-#: pg-act/ns/tlsa.php:29 pg-act/ns/txt.php:17 pg-act/reg/ds.php:32
+#: pg-act/ns/tlsa.php:29 pg-act/ns/txt.php:17 pg-act/reg/ds.php:30
#: pg-act/reg/glue.php:14 pg-act/reg/ns.php:14
msgid "Modification done."
msgstr "Modification effectuée."
@@ -503,15 +503,15 @@ msgstr "Le contenu de zone envoyé n'est pas correct (selon kzonecheck\n"
"Language-Team: LANGUAGE \n"
@@ -333,11 +333,11 @@ msgstr ""
msgid "Your account can't be deleted because the %s service is currently unavailable."
msgstr ""
-#: fn/auth.php:143
+#: fn/auth.php:161
msgid "Account rate limit reached, try again later."
msgstr ""
-#: fn/auth.php:168
+#: fn/auth.php:186
msgid "Global rate limit reached, try again later."
msgstr ""
@@ -353,15 +353,15 @@ msgstr ""
msgid "Server error : "
msgstr ""
-#: fn/common.php:132
+#: fn/common.php:137
msgid "Wrong proof."
msgstr ""
-#: fn/dns.php:64
+#: fn/dns.php:74
msgid "IP address malformed."
msgstr ""
-#: fn/dns.php:69 fn/ht.php:31
+#: fn/dns.php:79 fn/ht.php:48
msgid "Domain malformed."
msgstr ""
@@ -446,13 +446,13 @@ msgstr ""
msgid "No TXT record with the expected format has been found."
msgstr ""
-#: pg-act/ht/add-dns.php:41 pg-act/ht/add-onion.php:37
+#: pg-act/ht/add-dns.php:48 pg-act/ht/add-onion.php:50
#: pg-act/ht/add-subdomain.php:19 pg-act/ht/add-subpath.php:19
#, php-format
msgid "%s added on this directory."
msgstr ""
-#: pg-act/ht/add-onion.php:37
+#: pg-act/ht/add-onion.php:50
#, php-format
msgid "Its address is: %s"
msgstr ""
@@ -488,7 +488,7 @@ msgstr ""
#: pg-act/ns/caa.php:25 pg-act/ns/cname.php:16 pg-act/ns/dname.php:16
#: pg-act/ns/ip.php:16 pg-act/ns/loc.php:72 pg-act/ns/mx.php:20
#: pg-act/ns/ns.php:16 pg-act/ns/srv.php:28 pg-act/ns/sshfp.php:25
-#: pg-act/ns/tlsa.php:29 pg-act/ns/txt.php:17 pg-act/reg/ds.php:32
+#: pg-act/ns/tlsa.php:29 pg-act/ns/txt.php:17 pg-act/reg/ds.php:30
#: pg-act/reg/glue.php:14 pg-act/reg/ns.php:14
msgid "Modification done."
msgstr ""
@@ -515,15 +515,15 @@ msgstr ""
msgid "This zone already exists on the service."
msgstr ""
-#: pg-act/ns/zone-add.php:12
+#: pg-act/ns/zone-add.php:18
msgid "Parent zone's name servers not found."
msgstr ""
-#: pg-act/ns/zone-add.php:18 pg-act/reg/transfer.php:18
+#: pg-act/ns/zone-add.php:30 pg-act/reg/transfer.php:24
msgid "NS authentication record not found."
msgstr ""
-#: pg-act/ns/zone-add.php:54
+#: pg-act/ns/zone-add.php:66
msgid "Zone created."
msgstr ""
@@ -571,7 +571,7 @@ msgstr ""
msgid "The current account already owns this domain."
msgstr ""
-#: pg-act/reg/transfer.php:33
+#: pg-act/reg/transfer.php:39
msgid "The domain has been transferred to the current account ; the NS authentication record has been automatically deleted."
msgstr ""
@@ -743,6 +743,11 @@ msgstr ""
msgid "Access to delete"
msgstr ""
+#: pg-view/ht/del.php:13
+#, php-format
+msgid "%1$s to %2$s"
+msgstr ""
+
#: pg-view/ht/index.php:2
msgid "This service allows you to send files on the server using SFTP, and to make them publicly available with HTTP."
msgstr ""
diff --git a/pg-view/ht/del.php b/pg-view/ht/del.php
index 57acb40..d74adb8 100644
--- a/pg-view/ht/del.php
+++ b/pg-view/ht/del.php
@@ -10,7 +10,7 @@ foreach (query('select', 'sites', ['username' => $_SESSION['id'] ?? '']) as $sit
'onion' => 'http://' . $site['address'] . '/',
'dns' => 'https://' . $site['address'] . '/',
};
- echo ' ' . $url . ' vers /' . $site['site_dir'] . ' ' . LF;
+ echo ' ' . ' ' . sprintf(_('%1$s to %2$s'), $url, '/' . $site['site_dir']) . ' ' . LF;
}
?>
From d8b4ee90bbe22bafa2553d3b5387b12ffa1eee05 Mon Sep 17 00:00:00 2001
From: Miraty
Date: Tue, 20 Jun 2023 02:32:36 +0200
Subject: [PATCH 03/13] check.php: test domain transfers
---
init.php | 2 +-
jobs/check.php | 55 +++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 47 insertions(+), 10 deletions(-)
diff --git a/init.php b/init.php
index c85e75c..edc6694 100644
--- a/init.php
+++ b/init.php
@@ -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->getMessage());
+ error_log((string) $e);
http_response_code(500);
echo 'Error An error occured.';
});
diff --git a/jobs/check.php b/jobs/check.php
index a6dc279..7dff255 100644
--- a/jobs/check.php
+++ b/jobs/check.php
@@ -47,7 +47,7 @@ function curlTest(string $address, array $post = [], bool $tor = false): string
if ($status_code >= 400 OR $result === false) {
var_dump($result);
var_dump(curl_error($req));
- exit($address . ' test failed with status code ' . $status_code . LF);
+ throw new Exception($address . ' test failed with status code ' . $status_code);
}
return $result;
}
@@ -60,13 +60,6 @@ curlTest('/auth/register', [
'password' => $password,
]);
-curlTest('/auth/logout', []);
-
-curlTest('/auth/login', [
- 'username' => $username,
- 'password' => $password,
-]);
-
$new_password = bin2hex(random_bytes(16));
curlTest('/auth/password', [
'current-password' => $password,
@@ -74,9 +67,20 @@ curlTest('/auth/password', [
]);
$password = $new_password;
+curlTest('/auth/register', [
+ 'username' => $username . '2',
+ 'password' => $password,
+]);
+curlTest('/auth/logout', []);
+
+curlTest('/auth/login', [
+ 'username' => $username,
+ 'password' => $password,
+]);
+
curlTest('/auth/username', [
'current-password' => $password,
- 'new-username' => $username . '2',
+ 'new-username' => $username . '3',
]);
curlTest('/auth/username', [
'current-password' => $password,
@@ -86,6 +90,8 @@ curlTest('/auth/username', [
echo 'Created account with username "' . $username . '" and password "' . $password . '".' . LF;
function testReg(): string {
+ global $username, $password;
+
$subdomain = bin2hex(random_bytes(16));
curlTest('/reg/register', [
@@ -116,6 +122,37 @@ function testReg(): string {
'ns' => 'ns1.servnest.invalid.',
]);
+ { // Domain transfer
+ curlTest('/auth/logout', []);
+ curlTest('/auth/login', [
+ 'username' => $username . '2',
+ 'password' => $password,
+ ]);
+ preg_match('#\(?[0-9a-z-]{16,128}\._transfer-verification\.' . preg_quote(CORE_DOMAIN, '#') . '\.)\
#', curlTest('/reg/transfer', []), $matches);
+
+ curlTest('/auth/logout', []);
+ curlTest('/auth/login', [
+ 'username' => $username,
+ 'password' => $password,
+ ]);
+ curlTest('/reg/ns', [
+ 'action' => 'add',
+ 'domain' => $domain,
+ 'ns' => $matches['token'],
+ ]);
+
+ curlTest('/auth/logout', []);
+ curlTest('/auth/login', [
+ 'username' => $username . '2',
+ 'password' => $password,
+ ]);
+ curlTest('/reg/transfer', [
+ 'subdomain' => $subdomain,
+ 'suffix' => SUFFIX,
+ 'ns' => $matches['token'],
+ ]);
+ }
+
return $domain;
}
From b2fdc0c92575365a52a89020d504a1d1295db4c0 Mon Sep 17 00:00:00 2001
From: Miraty
Date: Wed, 21 Jun 2023 01:23:19 +0200
Subject: [PATCH 04/13] Fix jobs/check.php
---
jobs/check.php | 2 ++
1 file changed, 2 insertions(+)
diff --git a/jobs/check.php b/jobs/check.php
index 7dff255..ac97876 100644
--- a/jobs/check.php
+++ b/jobs/check.php
@@ -151,6 +151,8 @@ function testReg(): string {
'suffix' => SUFFIX,
'ns' => $matches['token'],
]);
+
+ $username = $username . '2';
}
return $domain;
From ccd17b7ffa5cde99c2ab946251b725f2759b153a Mon Sep 17 00:00:00 2001
From: Miraty
Date: Wed, 21 Jun 2023 22:08:57 +0200
Subject: [PATCH 05/13] Clear entries in ssh-keys when deleting account
---
fn/auth.php | 2 ++
1 file changed, 2 insertions(+)
diff --git a/fn/auth.php b/fn/auth.php
index d96d1cb..fd3d48f 100644
--- a/fn/auth.php
+++ b/fn/auth.php
@@ -133,6 +133,8 @@ function authDeleteUser(string $user_id): void {
], result_code: $code);
if ($code !== 0)
output(500, 'Can\'t remove user\'s directory.');
+
+ query('delete', 'ssh-keys', ['username' => $user_id]);
}
query('delete', 'users', ['id' => $user_id]);
From 858d6e8d02bab59c16e01d9bf817a8da6f376814 Mon Sep 17 00:00:00 2001
From: Miraty
Date: Sat, 24 Jun 2023 16:54:36 +0200
Subject: [PATCH 06/13] Add ns/sync and jobs/ns-syncs
---
db/migrations/006-create-ns-syncs-table.sql | 12 ++++++
db/schema.sql | 8 ++++
fn/auth.php | 4 +-
fn/common.php | 18 +++++++++
fn/dns.php | 9 +++--
fn/ns.php | 2 +
init.php | 2 +-
jobs/ns-sync.php | 42 +++++++++++++++++++++
pages.php | 5 +++
pg-act/ns/sync.php | 37 ++++++++++++++++++
pg-view/ht/keys.php | 8 ++--
pg-view/ns/form.ns.php | 2 +-
pg-view/ns/print.php | 2 +-
pg-view/ns/sync.php | 38 +++++++++++++++++++
14 files changed, 178 insertions(+), 11 deletions(-)
create mode 100644 db/migrations/006-create-ns-syncs-table.sql
create mode 100644 jobs/ns-sync.php
create mode 100644 pg-act/ns/sync.php
create mode 100644 pg-view/ns/sync.php
diff --git a/db/migrations/006-create-ns-syncs-table.sql b/db/migrations/006-create-ns-syncs-table.sql
new file mode 100644
index 0000000..baca87f
--- /dev/null
+++ b/db/migrations/006-create-ns-syncs-table.sql
@@ -0,0 +1,12 @@
+BEGIN TRANSACTION;
+
+CREATE TABLE IF NOT EXISTS "ns-syncs" (
+ "username" TEXT NOT NULL,
+ "source" TEXT NOT NULL,
+ "destination" TEXT NOT NULL UNIQUE,
+ UNIQUE("username", "source", "destination"),
+ FOREIGN KEY("username") REFERENCES "users"("id"),
+ FOREIGN KEY("destination") REFERENCES "zones"("zone")
+);
+
+COMMIT;
diff --git a/db/schema.sql b/db/schema.sql
index 8233bbe..a0a664c 100644
--- a/db/schema.sql
+++ b/db/schema.sql
@@ -42,6 +42,14 @@ CREATE TABLE IF NOT EXISTS "zones" (
PRIMARY KEY("zone"),
FOREIGN KEY("username") REFERENCES "users"("id")
);
+CREATE TABLE IF NOT EXISTS "ns-syncs" (
+ "username" TEXT NOT NULL,
+ "source" TEXT NOT NULL,
+ "destination" TEXT NOT NULL UNIQUE,
+ UNIQUE("username", "source", "destination"),
+ FOREIGN KEY("username") REFERENCES "users"("id"),
+ FOREIGN KEY("destination") REFERENCES "zones"("zone")
+);
CREATE TABLE IF NOT EXISTS "sites" (
"username" TEXT NOT NULL,
"site_dir" TEXT NOT NULL,
diff --git a/fn/auth.php b/fn/auth.php
index fd3d48f..023bb41 100644
--- a/fn/auth.php
+++ b/fn/auth.php
@@ -98,9 +98,11 @@ function authDeleteUser(string $user_id): void {
foreach (query('select', 'registry', ['username' => $user_id], 'domain') as $domain)
regDeleteDomain($domain, $user_id);
- if (in_array('ns', $user_services, true))
+ if (in_array('ns', $user_services, true)) {
+ query('delete', 'ns-syncs', ['username' => $user_id]);
foreach (query('select', 'zones', ['username' => $user_id], 'zone') as $zone)
nsDeleteZone($zone, $user_id);
+ }
if (in_array('ht', $user_services, true)) {
foreach (query('select', 'sites', ['username' => $user_id]) as $site)
diff --git a/fn/common.php b/fn/common.php
index 33815e9..2ce54ca 100644
--- a/fn/common.php
+++ b/fn/common.php
@@ -21,6 +21,24 @@ function exescape(array $args, array &$output = NULL, int &$result_code = NULL):
return $result_code;
}
+class KdigException extends Exception {};
+function kdig(string $name, string $type, string $server = NULL): array {
+ exescape([
+ CONF['dns']['kdig_path'],
+ '+json',
+ '+timeout=5',
+ '+retry=0',
+ '-q',
+ $name,
+ '-t',
+ $type,
+ ...(isset($server) ? ['@' . $server] : []),
+ ], $output, $code);
+ if ($code !== 0)
+ throw new KdigException();
+ return json_decode(implode(LF, $output), true, flags: JSON_THROW_ON_ERROR);
+}
+
function insert(string $table, array $values): void {
$query = 'INSERT INTO "' . $table . '"(';
diff --git a/fn/dns.php b/fn/dns.php
index 6c4b171..33597b6 100644
--- a/fn/dns.php
+++ b/fn/dns.php
@@ -1,16 +1,19 @@
!str_ends_with($elements[0], $filter_domain),
+ false => $elements[0] !== $filter_domain,
+ })
continue; // Ignore records for other domains
if (!in_array($elements[2], $types, true))
continue; // Ignore records generated by Knot
- array_push($parsed_zone_content, array_map('htmlspecialchars', $elements));
+ array_push($parsed_zone_content, $elements);
}
return $parsed_zone_content;
}
diff --git a/fn/ns.php b/fn/ns.php
index a659385..128fca1 100644
--- a/fn/ns.php
+++ b/fn/ns.php
@@ -17,6 +17,8 @@ const ALLOWED_TYPES = ['AAAA', 'A', 'TXT', 'SRV', 'MX', 'SVCB', 'HTTPS', 'NS', '
const ZONE_MAX_CHARACTERS = 10000;
+const SYNC_TTL = 3600;
+
function nsParseCommonRequirements(): array {
nsCheckZonePossession($_POST['zone']);
diff --git a/init.php b/init.php
index edc6694..7ac3acb 100644
--- a/init.php
+++ b/init.php
@@ -7,7 +7,7 @@ set_error_handler(function ($level, $message, $file = '', $line = 0) {
set_exception_handler(function ($e) {
error_log((string) $e);
http_response_code(500);
- echo 'Error An error occured.';
+ echo 'Error An error occured.
';
});
register_shutdown_function(function () { // Also catch fatal errors
if (($error = error_get_last()) !== NULL)
diff --git a/jobs/ns-sync.php b/jobs/ns-sync.php
new file mode 100644
index 0000000..934e32c
--- /dev/null
+++ b/jobs/ns-sync.php
@@ -0,0 +1,42 @@
+ _('Store geographic coordinates'),
'tokens_account_cost' => 120,
],
+ 'sync' => [
+ 'title' => sprintf(_('Synchronized records')),
+ 'description' => _('Regularly fetch distant record value and update it to a local zone'),
+ 'tokens_account_cost' => 900,
+ ],
],
'ht' => [
'index' => [
diff --git a/pg-act/ns/sync.php b/pg-act/ns/sync.php
new file mode 100644
index 0000000..9275678
--- /dev/null
+++ b/pg-act/ns/sync.php
@@ -0,0 +1,37 @@
+ 8)
+ output(403, 'Wrong elements number.');
+
+foreach ($_POST['syncs'] as $i => &$sync) {
+ if (($sync['source'] ?? '') === '') {
+ unset($_POST['syncs'][$i]);
+ continue;
+ }
+ $sync['source'] = formatAbsoluteDomain($sync['source']);
+ nsCheckZonePossession($sync['destination']);
+}
+$syncs = array_values($_POST['syncs']);
+
+rateLimit();
+
+try {
+ DB->beginTransaction();
+
+ query('delete', 'ns-syncs', ['username' => $_SESSION['id']]);
+
+ foreach ($syncs as $sync)
+ insert('ns-syncs', [
+ 'username' => $_SESSION['id'],
+ 'source' => $sync['source'],
+ 'destination' => $sync['destination'],
+ ]);
+
+ DB->commit();
+} catch (Exception $e) {
+ DB->rollback();
+ output(500, 'Database error.', [$e->getMessage()]);
+}
+
+output(200, _('Synchronized records updated.'));
diff --git a/pg-view/ht/keys.php b/pg-view/ht/keys.php
index e99f28c..3f613ef 100755
--- a/pg-view/ht/keys.php
+++ b/pg-view/ht/keys.php
@@ -16,12 +16,12 @@ foreach (array_slice(array_merge(query('select', 'ssh-keys', ['username' => $_SE
= ($ssh_key['key'] === '') ? _('Add new SSH key access') : _('SSH key access') ?>
- = _('Public key') ?>
- ssh-ed15519
+ = _('Public key') ?>
+ ssh-ed15519
- = _('Allowed directory') ?>
-
+ = _('Allowed directory') ?>
+
= _('Zone') ?>
- -
+ —
" . $zone . "";
diff --git a/pg-view/ns/print.php b/pg-view/ns/print.php
index a7dbbdf..3492b1d 100644
--- a/pg-view/ns/print.php
+++ b/pg-view/ns/print.php
@@ -38,7 +38,7 @@ if (isset($data['zone-table'])) { ?>
foreach ($data['zone-table'] as $zone_line) {
echo ' ' . LF;
foreach ($zone_line as $element)
- echo ' ' . $element . '
' . LF;
+ echo ' ' . htmlspecialchars($element) . '
' . LF;
echo ' ' . LF;
}
}
diff --git a/pg-view/ns/sync.php b/pg-view/ns/sync.php
new file mode 100644
index 0000000..740ba10
--- /dev/null
+++ b/pg-view/ns/sync.php
@@ -0,0 +1,38 @@
+
+ = sprintf(_('AAAA, A and CAA records are regularly copied from the source domain to the target domain. Their TTLs are set to %s seconds.'), SYNC_TTL) ?>
+
+
+ = _('Source domains that are not signed with DNSSEC are not synchronized. Synchronizations that remain broken may be deleted.') ?>
+
+
+ = _('This is meant to be used for apex domains, where a CNAME record is not allowed. For non-apex domains, CNAME records should be used instead.') ?>
+
+
+
From 1d856e1e2e77e7f4601805ac02c13708012f4039 Mon Sep 17 00:00:00 2001
From: Miraty
Date: Mon, 26 Jun 2023 04:13:52 +0200
Subject: [PATCH 07/13] ns/sync: translations and bugfixes
---
db/schema.sql | 1 -
fn/auth.php | 4 +-
fn/ns.php | 4 +-
jobs/check.php | 2 +-
jobs/delete-old-testing.php | 2 +-
jobs/ns-sync.php | 8 +-
locales/fr/C/LC_MESSAGES/messages.po | 90 ++++++++++++++++------
locales/messages.pot | 110 +++++++++++++++++++--------
pages.php | 2 +-
pg-act/ns/sync.php | 6 +-
pg-view/ns/sync.php | 6 +-
11 files changed, 165 insertions(+), 70 deletions(-)
diff --git a/db/schema.sql b/db/schema.sql
index a0a664c..4d253f2 100644
--- a/db/schema.sql
+++ b/db/schema.sql
@@ -46,7 +46,6 @@ CREATE TABLE IF NOT EXISTS "ns-syncs" (
"username" TEXT NOT NULL,
"source" TEXT NOT NULL,
"destination" TEXT NOT NULL UNIQUE,
- UNIQUE("username", "source", "destination"),
FOREIGN KEY("username") REFERENCES "users"("id"),
FOREIGN KEY("destination") REFERENCES "zones"("zone")
);
diff --git a/fn/auth.php b/fn/auth.php
index 023bb41..fd3d48f 100644
--- a/fn/auth.php
+++ b/fn/auth.php
@@ -98,11 +98,9 @@ function authDeleteUser(string $user_id): void {
foreach (query('select', 'registry', ['username' => $user_id], 'domain') as $domain)
regDeleteDomain($domain, $user_id);
- if (in_array('ns', $user_services, true)) {
- query('delete', 'ns-syncs', ['username' => $user_id]);
+ if (in_array('ns', $user_services, true))
foreach (query('select', 'zones', ['username' => $user_id], 'zone') as $zone)
nsDeleteZone($zone, $user_id);
- }
if (in_array('ht', $user_services, true)) {
foreach (query('select', 'sites', ['username' => $user_id]) as $site)
diff --git a/fn/ns.php b/fn/ns.php
index 128fca1..f84b553 100644
--- a/fn/ns.php
+++ b/fn/ns.php
@@ -17,7 +17,7 @@ const ALLOWED_TYPES = ['AAAA', 'A', 'TXT', 'SRV', 'MX', 'SVCB', 'HTTPS', 'NS', '
const ZONE_MAX_CHARACTERS = 10000;
-const SYNC_TTL = 3600;
+const SYNC_TTL = 10800;
function nsParseCommonRequirements(): array {
nsCheckZonePossession($_POST['zone']);
@@ -73,6 +73,8 @@ function nsDeleteZone(string $zone, string $user_id): void {
if ($code !== 0)
output(500, 'Failed to purge zone data.');
+ query('delete', 'ns-syncs', ['destination' => $zone]);
+
// Remove from database
query('delete', 'zones', [
'zone' => $zone,
diff --git a/jobs/check.php b/jobs/check.php
index ac97876..fb28394 100644
--- a/jobs/check.php
+++ b/jobs/check.php
@@ -1,6 +1,6 @@
SFTP space"
msgstr "Téléverser un site statique dans un espace SFTP "
-#: pages.php:173
+#: pages.php:178
#, php-format
msgid "%s subpath access"
msgstr "Accès par sous-chemin de %s"
-#: pages.php:174 pages.php:179 pages.php:184
+#: pages.php:179 pages.php:184 pages.php:189
#, php-format
msgid "Its URL will look like %s"
msgstr "Son URL ressemblera à %s"
-#: pages.php:174 pages.php:179 pages.php:184
+#: pages.php:179 pages.php:184 pages.php:189
msgid "mysite"
msgstr "monsite"
-#: pages.php:178
+#: pages.php:183
#, php-format
msgid "%s subdomain access"
msgstr "Accès par sous-domaine de %s"
-#: pages.php:183
+#: pages.php:188
msgid "Dedicated domain with Let's Encrypt certificate access"
msgstr "Accès par domaine dédié avec certificat Let's Encrypt"
-#: pages.php:188
+#: pages.php:193
msgid "Onion service access"
msgstr "Accès par service Onion"
-#: pages.php:189
+#: pages.php:194
#, php-format
msgid "Its URL will look like %s, and work only through the Tor network"
msgstr "Son URL ressemblera à %s, et ne fonctionnera que par le réseau Tor"
-#: pages.php:193 pg-view/ht/del.php:18
+#: pages.php:198 pg-view/ht/del.php:18
msgid "Delete access"
msgstr "Supprimer un accès"
-#: pages.php:194
+#: pages.php:199
msgid "Delete an existing HTTP access from a subdirectory of the SFTP space"
msgstr "Retirer un accès HTTP existant d'un sous-dossier de l'espace SFTP"
-#: pages.php:197
+#: pages.php:202
msgid "Manage SSH keys"
msgstr "Gérer les clés SSH"
-#: pages.php:198
+#: pages.php:203
msgid "Choose what SSH key can edit what directory"
msgstr "Choisir quelle clé SSH peut modifier quel dossier"
@@ -321,11 +330,11 @@ msgstr "%sCode source%s disponible sous %s."
msgid "Your account can't be deleted because the %s service is currently unavailable."
msgstr "Votre compte ne peut pas être supprimé car le service %s est actuellement indisponible."
-#: fn/auth.php:161
+#: fn/auth.php:163
msgid "Account rate limit reached, try again later."
msgstr "Limite de taux pour ce compte atteinte, réessayez plus tard."
-#: fn/auth.php:186
+#: fn/auth.php:188
msgid "Global rate limit reached, try again later."
msgstr "Limite de taux globale atteinte, réessayez plus tard."
@@ -341,24 +350,24 @@ msgstr "Erreur de l'utilisataire : "
msgid "Server error : "
msgstr "Erreur du serveur : "
-#: fn/common.php:137
+#: fn/common.php:155
msgid "Wrong proof."
msgstr "Preuve incorrecte."
-#: fn/dns.php:74
+#: fn/dns.php:77
msgid "IP address malformed."
msgstr "Adresse IP malformée."
-#: fn/dns.php:79 fn/ht.php:48
+#: fn/dns.php:82 fn/ht.php:48
msgid "Domain malformed."
msgstr "Domaine malformé."
-#: fn/ns.php:31 pg-act/ns/edit.php:25
+#: fn/ns.php:33 pg-act/ns/edit.php:25
#, php-format
msgid "TTLs shorter than %s seconds are forbidden."
msgstr "Les TTLs plus courts que %s secondes sont interdits."
-#: fn/ns.php:33 pg-act/ns/edit.php:27
+#: fn/ns.php:35 pg-act/ns/edit.php:27
#, php-format
msgid "TTLs longer than %s seconds are forbidden."
msgstr "Les TTLs plus longs que %s secondes sont interdits."
@@ -499,6 +508,14 @@ msgstr "Le type %s n'est pas autorisé."
msgid "Sent zone content is not correct (according to kzonecheck
)."
msgstr "Le contenu de zone envoyé n'est pas correct (selon kzonecheck
)."
+#: pg-act/ns/sync.php:19
+msgid "Multiple source domains can't be applied to the same target domain."
+msgstr "Plusieurs domaines sources ne peuvent pas être appliqués sur un même domaine cible."
+
+#: pg-act/ns/sync.php:41
+msgid "Synchronized records updated."
+msgstr "Enregistrements synchronisés mis à jour."
+
#: pg-act/ns/zone-add.php:6
msgid "This zone already exists on the service."
msgstr "Cette zone existe déjà sur ce service."
@@ -858,7 +875,7 @@ msgstr "Clé publique"
msgid "Allowed directory"
msgstr "Dossier autorisé"
-#: pg-view/ht/keys.php:30
+#: pg-view/ht/keys.php:30 pg-view/ns/sync.php:37
msgid "Update"
msgstr "Mettre à jour"
@@ -1109,6 +1126,35 @@ msgstr "Type de hash"
msgid "Fingerprint"
msgstr "Empreinte"
+#: pg-view/ns/sync.php:2
+#, php-format
+msgid "AAAA, A and CAA records are regularly copied from the source domain to the target domain. Their TTLs are set to %s seconds."
+msgstr "Les enregistrements AAAA, A et CAA sont régulièrement copiés du domain source vers le domain cible. Leurs TTLs sont définis à %s secondes."
+
+#: pg-view/ns/sync.php:5
+msgid "Source domains that are not signed with DNSSEC are not synchronized. Synchronizations that remain broken may be deleted."
+msgstr "Les domains sources qui ne sont pas signés avec DNSSEC ne sont pas synchronisés. Les synchronisations qui restent cassées peuvent être supprimées."
+
+#: pg-view/ns/sync.php:8
+msgid "This is meant to be used for apex domains, where CNAME records are not allowed. For non-apex domains, CNAME records should be used instead."
+msgstr "Ceci est destiné à être utilisé sur des domaines apex, où les enregistrements CNAME ne sont pas autorisés. Pour des domaines non-apex, les enregistrements CNAME devraient être utilisés à la place."
+
+#: pg-view/ns/sync.php:16
+msgid "Add new domain records to be synchronized"
+msgstr "Ajouter de nouveaux enregistrements de domain à synchroniser"
+
+#: pg-view/ns/sync.php:16
+msgid "Synchronized domain"
+msgstr "Domaine synchronisé"
+
+#: pg-view/ns/sync.php:18
+msgid "Source domain"
+msgstr "Domaine source"
+
+#: pg-view/ns/sync.php:22
+msgid "Target domain"
+msgstr "Domaine cible"
+
#: pg-view/ns/tlsa.php:4
msgid "Use"
msgstr "Utilisation"
diff --git a/locales/messages.pot b/locales/messages.pot
index 8cf3904..d65ec2c 100644
--- a/locales/messages.pot
+++ b/locales/messages.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-06-20 00:38+0200\n"
+"POT-Creation-Date: 2023-06-26 02:14+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
@@ -238,59 +238,68 @@ msgstr ""
msgid "Store geographic coordinates"
msgstr ""
-#: pages.php:169
+#: pages.php:167
+#, php-format
+msgid "Synchronized records"
+msgstr ""
+
+#: pages.php:168
+msgid "Regularly fetch distant records and update them to a local zone"
+msgstr ""
+
+#: pages.php:174
msgid "Web"
msgstr ""
-#: pages.php:170
+#: pages.php:175
msgid "Upload a static website into an SFTP space"
msgstr ""
-#: pages.php:173
-#, php-format
-msgid "%s subpath access"
-msgstr ""
-
-#: pages.php:174 pages.php:179 pages.php:184
-#, php-format
-msgid "Its URL will look like %s"
-msgstr ""
-
-#: pages.php:174 pages.php:179 pages.php:184
-msgid "mysite"
-msgstr ""
-
#: pages.php:178
#, php-format
-msgid "%s subdomain access"
+msgid "%s subpath access"
+msgstr ""
+
+#: pages.php:179 pages.php:184 pages.php:189
+#, php-format
+msgid "Its URL will look like %s"
+msgstr ""
+
+#: pages.php:179 pages.php:184 pages.php:189
+msgid "mysite"
msgstr ""
#: pages.php:183
-msgid "Dedicated domain with Let's Encrypt certificate access"
+#, php-format
+msgid "%s subdomain access"
msgstr ""
#: pages.php:188
+msgid "Dedicated domain with Let's Encrypt certificate access"
+msgstr ""
+
+#: pages.php:193
msgid "Onion service access"
msgstr ""
-#: pages.php:189
+#: pages.php:194
#, php-format
msgid "Its URL will look like %s, and work only through the Tor network"
msgstr ""
-#: pages.php:193 pg-view/ht/del.php:18
+#: pages.php:198 pg-view/ht/del.php:18
msgid "Delete access"
msgstr ""
-#: pages.php:194
+#: pages.php:199
msgid "Delete an existing HTTP access from a subdirectory of the SFTP space"
msgstr ""
-#: pages.php:197
+#: pages.php:202
msgid "Manage SSH keys"
msgstr ""
-#: pages.php:198
+#: pages.php:203
msgid "Choose what SSH key can edit what directory"
msgstr ""
@@ -333,11 +342,11 @@ msgstr ""
msgid "Your account can't be deleted because the %s service is currently unavailable."
msgstr ""
-#: fn/auth.php:161
+#: fn/auth.php:163
msgid "Account rate limit reached, try again later."
msgstr ""
-#: fn/auth.php:186
+#: fn/auth.php:188
msgid "Global rate limit reached, try again later."
msgstr ""
@@ -353,24 +362,24 @@ msgstr ""
msgid "Server error : "
msgstr ""
-#: fn/common.php:137
+#: fn/common.php:155
msgid "Wrong proof."
msgstr ""
-#: fn/dns.php:74
+#: fn/dns.php:77
msgid "IP address malformed."
msgstr ""
-#: fn/dns.php:79 fn/ht.php:48
+#: fn/dns.php:82 fn/ht.php:48
msgid "Domain malformed."
msgstr ""
-#: fn/ns.php:31 pg-act/ns/edit.php:25
+#: fn/ns.php:33 pg-act/ns/edit.php:25
#, php-format
msgid "TTLs shorter than %s seconds are forbidden."
msgstr ""
-#: fn/ns.php:33 pg-act/ns/edit.php:27
+#: fn/ns.php:35 pg-act/ns/edit.php:27
#, php-format
msgid "TTLs longer than %s seconds are forbidden."
msgstr ""
@@ -511,6 +520,14 @@ msgstr ""
msgid "Sent zone content is not correct (according to kzonecheck
)."
msgstr ""
+#: pg-act/ns/sync.php:19
+msgid "Multiple source domains can't be applied to the same target domain."
+msgstr ""
+
+#: pg-act/ns/sync.php:41
+msgid "Synchronized records updated."
+msgstr ""
+
#: pg-act/ns/zone-add.php:6
msgid "This zone already exists on the service."
msgstr ""
@@ -870,7 +887,7 @@ msgstr ""
msgid "Allowed directory"
msgstr ""
-#: pg-view/ht/keys.php:30
+#: pg-view/ht/keys.php:30 pg-view/ns/sync.php:37
msgid "Update"
msgstr ""
@@ -1121,6 +1138,35 @@ msgstr ""
msgid "Fingerprint"
msgstr ""
+#: pg-view/ns/sync.php:2
+#, php-format
+msgid "AAAA, A and CAA records are regularly copied from the source domain to the target domain. Their TTLs are set to %s seconds."
+msgstr ""
+
+#: pg-view/ns/sync.php:5
+msgid "Source domains that are not signed with DNSSEC are not synchronized. Synchronizations that remain broken may be deleted."
+msgstr ""
+
+#: pg-view/ns/sync.php:8
+msgid "This is meant to be used for apex domains, where CNAME records are not allowed. For non-apex domains, CNAME records should be used instead."
+msgstr ""
+
+#: pg-view/ns/sync.php:16
+msgid "Add new domain records to be synchronized"
+msgstr ""
+
+#: pg-view/ns/sync.php:16
+msgid "Synchronized domain"
+msgstr ""
+
+#: pg-view/ns/sync.php:18
+msgid "Source domain"
+msgstr ""
+
+#: pg-view/ns/sync.php:22
+msgid "Target domain"
+msgstr ""
+
#: pg-view/ns/tlsa.php:4
msgid "Use"
msgstr ""
diff --git a/pages.php b/pages.php
index ebbc4ac..fadb435 100644
--- a/pages.php
+++ b/pages.php
@@ -165,7 +165,7 @@ define('PAGES', [
],
'sync' => [
'title' => sprintf(_('Synchronized records')),
- 'description' => _('Regularly fetch distant record value and update it to a local zone'),
+ 'description' => _('Regularly fetch distant records and update them to a local zone'),
'tokens_account_cost' => 900,
],
],
diff --git a/pg-act/ns/sync.php b/pg-act/ns/sync.php
index 9275678..9160350 100644
--- a/pg-act/ns/sync.php
+++ b/pg-act/ns/sync.php
@@ -4,7 +4,7 @@ $el_nb = count($_POST['syncs']);
if ($el_nb < 1 OR $el_nb > 8)
output(403, 'Wrong elements number.');
-foreach ($_POST['syncs'] as $i => &$sync) {
+foreach ($_POST['syncs'] as $i => $sync) {
if (($sync['source'] ?? '') === '') {
unset($_POST['syncs'][$i]);
continue;
@@ -14,6 +14,10 @@ foreach ($_POST['syncs'] as $i => &$sync) {
}
$syncs = array_values($_POST['syncs']);
+$destinations = array_column($syncs, 'destination');
+if (count($destinations) !== count(array_unique($destinations)))
+ output(403, _('Multiple source domains can\'t be applied to the same target domain.'));
+
rateLimit();
try {
diff --git a/pg-view/ns/sync.php b/pg-view/ns/sync.php
index 740ba10..2e52645 100644
--- a/pg-view/ns/sync.php
+++ b/pg-view/ns/sync.php
@@ -5,7 +5,7 @@
= _('Source domains that are not signed with DNSSEC are not synchronized. Synchronizations that remain broken may be deleted.') ?>
- = _('This is meant to be used for apex domains, where a CNAME record is not allowed. For non-apex domains, CNAME records should be used instead.') ?>
+ = _('This is meant to be used for apex domains, where CNAME records are not allowed. For non-apex domains, CNAME records should be used instead.') ?>