Browse Source

reg: Delay at unregistration; Display domain history

Miraty 2 years ago
parent
commit
b9af7fee09

+ 7 - 2
db/schema.sql

@@ -10,7 +10,7 @@ INSERT INTO "params"("name", "value") VALUES('secret_key', '0');
 INSERT INTO "params"("name", "value") VALUES('secret_key_last_change', '0');
 INSERT INTO "params"("name", "value") VALUES('username_salt', '00000000000000000000000000000000'); -- Should be unique and secret ; generate one using `openssl rand -hex 16` ; can't be changed without breaking current accounts login
 CREATE TABLE IF NOT EXISTS "users" (
-	"id"                 TEXT    NOT NULL UNIQUE,
+	"id"                 TEXT    NOT NULL UNIQUE CHECK (LENGTH(id) = 64),
 	"username"           TEXT    NOT NULL UNIQUE,
 	"password"           TEXT    NOT NULL,
 	"registration_date"  TEXT    NOT NULL,
@@ -27,10 +27,15 @@ CREATE TABLE IF NOT EXISTS "approval-keys" (
 CREATE TABLE IF NOT EXISTS "registry" (
 	"domain"       TEXT    NOT NULL UNIQUE,
 	"username"     TEXT    NOT NULL,
-	"last_renewal" TEXT    NOT NULL,
+	"creation"     TEXT    NOT NULL,
 	PRIMARY KEY("domain"),
 	FOREIGN KEY("username") REFERENCES "users"("id")
 );
+CREATE TABLE IF NOT EXISTS "registry-history" (
+	"domain"       TEXT    NOT NULL,
+	"creation"     TEXT    NOT NULL,
+	"expiration"   TEXT    NOT NULL
+);
 CREATE TABLE IF NOT EXISTS "zones" (
 	"zone"     TEXT    NOT NULL UNIQUE,
 	"username" TEXT    NOT NULL,

+ 21 - 5
fn/reg.php

@@ -23,11 +23,27 @@ function regDeleteDomain($domain) {
 	if (file_put_contents($path, $content) === false)
 		output(500, 'Failed to write new registry file.');
 
-	// Delete from database
-	query('delete', 'registry', [
-		'domain' => $domain,
-		'username' => $_SESSION['id'],
-	]);
+	try {
+		DB->beginTransaction();
+
+		$conditions = [
+			'domain' => $domain,
+			'username' => $_SESSION['id'],
+		];
+
+		insert('registry-history', [
+			'domain' => $domain,
+			'creation' => query('select', 'registry', $conditions, 'creation')[0],
+			'expiration' => date('Y-m'),
+		]);
+
+		query('delete', 'registry', $conditions);
+
+		DB->commit();
+	} catch (Exception $e) {
+		DB->rollback();
+		output(500, 'Database error.', [$e->getMessage()]);
+	}
 }
 
 function regParseDomain($domain) {

+ 32 - 14
locales/fr/C/LC_MESSAGES/messages.po

@@ -1,5 +1,7 @@
 msgid ""
 msgstr ""
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2023-03-25 16:16+0100\n"
 "Language: fr\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 
@@ -272,15 +274,15 @@ msgstr "Supprimer un accès"
 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"
 
-#: router.php:133 view.php:39
+#: router.php:137 view.php:39
 msgid "This service is currently under maintenance. No action can be taken on it until an administrator finishes repairing it."
 msgstr "Ce service est en cours de maintenance. Aucune action ne peut être effectuée avant qu'ane administrataire termine de le réparer."
 
-#: router.php:144
+#: router.php:148
 msgid "You need to be logged in to do this."
 msgstr "Vous devez être connecté·e à un compte pour faire cela."
 
-#: router.php:146
+#: router.php:150
 msgid "This account doesn't exist anymore. Log out to end this ghost session."
 msgstr "Ce compte n'existe plus. Déconnectez-vous pour terminer cette session fantôme."
 
@@ -485,19 +487,35 @@ msgstr "Zone supprimée."
 msgid "This format of subdomain is not allowed."
 msgstr "Ce format de sous-domaine n'est pas autorisé."
 
-#: pg-act/reg/register.php:27
-msgid "This domain is open to registration!"
-msgstr "Ce domaine est disponible à l'enregistrement."
-
-#: pg-act/reg/register.php:29
+#: pg-act/reg/register.php:21
 msgid "This domain is reserved."
 msgstr "Ce domaine est réservé."
 
-#: pg-act/reg/register.php:31
-msgid "This domain is already registered."
-msgstr "Ce domaine est déjà enregistré."
+#: pg-act/reg/register.php:34
+#, php-format
+msgid "This domain was registered from %s to %s."
+msgstr "Ce domaine était enregistré de %s à %s."
+
+#: pg-act/reg/register.php:38
+#, php-format
+msgid "This blocks it until %s."
+msgstr "Ceci le bloque jusqu'en %s."
+
+#: pg-act/reg/register.php:40
+#, php-format
+msgid "This had blocked it until %s."
+msgstr "Ceci l'avait bloqué jusqu'en %s."
+
+#: pg-act/reg/register.php:48
+#, php-format
+msgid "This domain is already registered, since %s."
+msgstr "Ce domaine est déjà enregistré, depuis %s."
+
+#: pg-act/reg/register.php:54
+msgid "This domain is open to registration!"
+msgstr "Ce domaine est disponible à l'enregistrement."
 
-#: pg-act/reg/register.php:47
+#: pg-act/reg/register.php:71
 msgid "Domain registered."
 msgstr "Domaine enregistré."
 
@@ -1116,8 +1134,8 @@ msgid "Receive the domain"
 msgstr "Recevoir le domaine"
 
 #: pg-view/reg/unregister.php:2
-msgid "This will unregister the domain, making it registerable by anyone again."
-msgstr "Ceci désenregistrera le domaine, ce qui le re-disponibilisera à tout le monde."
+msgid "This will unregister the domain, making it registerable by anyone again (after a delay of 1 year plus half the registration period, with a maximum of 8 years)."
+msgstr "Ceci désenregistrera le domaine, ce qui le re-disponibilisera à tout le monde (après un délai de 1 an + la moitié de la durée d'enregistrement, avec un maximum de 8 ans)."
 
 #: pg-view/reg/unregister.php:16
 msgid "Unregister"

+ 27 - 11
locales/messages.pot

@@ -271,15 +271,15 @@ msgstr ""
 msgid "Delete an existing HTTP access from a subdirectory of the SFTP space"
 msgstr ""
 
-#: router.php:133 view.php:39
+#: router.php:137 view.php:39
 msgid "This service is currently under maintenance. No action can be taken on it until an administrator finishes repairing it."
 msgstr ""
 
-#: router.php:144
+#: router.php:148
 msgid "You need to be logged in to do this."
 msgstr ""
 
-#: router.php:146
+#: router.php:150
 msgid "This account doesn't exist anymore. Log out to end this ghost session."
 msgstr ""
 
@@ -484,19 +484,35 @@ msgstr ""
 msgid "This format of subdomain is not allowed."
 msgstr ""
 
-#: pg-act/reg/register.php:27
-msgid "This domain is open to registration!"
+#: pg-act/reg/register.php:21
+msgid "This domain is reserved."
 msgstr ""
 
-#: pg-act/reg/register.php:29
-msgid "This domain is reserved."
+#: pg-act/reg/register.php:34
+#, php-format
+msgid "This domain was registered from %s to %s."
+msgstr ""
+
+#: pg-act/reg/register.php:38
+#, php-format
+msgid "This blocks it until %s."
 msgstr ""
 
-#: pg-act/reg/register.php:31
-msgid "This domain is already registered."
+#: pg-act/reg/register.php:40
+#, php-format
+msgid "This had blocked it until %s."
+msgstr ""
+
+#: pg-act/reg/register.php:48
+#, php-format
+msgid "This domain is already registered, since %s."
+msgstr ""
+
+#: pg-act/reg/register.php:54
+msgid "This domain is open to registration!"
 msgstr ""
 
-#: pg-act/reg/register.php:47
+#: pg-act/reg/register.php:71
 msgid "Domain registered."
 msgstr ""
 
@@ -1115,7 +1131,7 @@ msgid "Receive the domain"
 msgstr ""
 
 #: pg-view/reg/unregister.php:2
-msgid "This will unregister the domain, making it registerable by anyone again."
+msgid "This will unregister the domain, making it registerable by anyone again (after a delay of 1 year plus half the registration period, with a maximum of 8 years)."
 msgstr ""
 
 #: pg-view/reg/unregister.php:16

+ 1 - 1
pg-act/auth/unregister.php

@@ -6,7 +6,7 @@ if (checkPassword($_SESSION['id'], $_POST['current-password']) !== true)
 if (!isset($_POST['delete']))
 	output(403, _('Account deletion must be confirmed.'));
 
-$user_services = explode(',', query('select', 'users', ['username' => $_SESSION['id']], 'services')[0]);
+$user_services = explode(',', query('select', 'users', ['id' => $_SESSION['id']], 'services')[0]);
 
 foreach (SERVICES_USER as $service)
 	if (in_array($service, $user_services, true) AND CONF['common']['services'][$service] !== 'enabled')

+ 40 - 16
pg-act/reg/register.php

@@ -17,31 +17,55 @@ match (CONF['reg']['suffixes'][$_POST['suffix']]) {
 
 $domain = formatAbsoluteDomain($_POST['subdomain'] . '.' . $_POST['suffix']);
 
-$registered = query('select', 'registry', ['domain' => $domain], 'domain') !== [];
-$reserved = in_array($_POST['subdomain'], explode(LF, file_get_contents(ROOT_PATH . '/pg-act/reg/reserved.txt')));
-
-$message = match ($registered) {
-	false => match ($reserved) {
-		false => match ($_POST['action']) {
-			'register' => NULL,
-			default => '✔️ ' . _('This domain is open to registration!'),
-		},
-		default => '❌ ' . _('This domain is reserved.'),
-	},
-	default => '❌ ' . _('This domain is already registered.'),
-};
-if ($message !== NULL)
+if (in_array($_POST['subdomain'], explode(LF, file_get_contents(ROOT_PATH . '/pg-act/reg/reserved.txt')), true))
+	message('❌ ' . _('This domain is reserved.'));
+
+$message = '<ul>';
+$blocked = false;
+foreach (query('select', 'registry-history', ['domain' => $domain]) as $data) {
+	$creation = DateTimeImmutable::createFromFormat('Y-m', $data['creation']);
+	$expiration = DateTimeImmutable::createFromFormat('Y-m', $data['expiration']);
+
+	$registration_duration = intval(round($creation->diff($expiration)->format('%a') / 30.4375));
+	$expected_expiration_duration = min(intval($registration_duration * 0.5) + 12, 12*8);
+
+	$new_registration_date = $expiration->add(new DateInterval('P' . $expected_expiration_duration . 'M'));
+
+	$message .= '<li>' . sprintf(_('This domain was registered from %s to %s.'), '<time>' . $data['creation'] . '</time>', '<time>' . $data['expiration'] . '</time>') . ' ';
+
+	if (new DateTimeImmutable('now', new DateTimeZone('UTC')) < $new_registration_date) {
+		$blocked = true;
+		$message .= sprintf(_('This blocks it until %s.'), '<time>' . $new_registration_date->format('Y-m') . '</time>') . ' ❌';
+	} else {
+		$message .= sprintf(_('This had blocked it until %s.'), '<time>' . $new_registration_date->format('Y-m') . '</time>');
+	}
+	$message .= '</li>';
+}
+$message .= '</ul>';
+
+$registration_data = query('select', 'registry', ['domain' => $domain]);
+if ($registration_data !== [])
+	$message .= sprintf(_('This domain is already registered, since %s.'), '<time>' . $registration_data[0]['creation'] . '</time>');
+
+if ($blocked OR $registration_data !== [])
+	message($message);
+
+if ($_POST['action'] !== 'register')
+	message($message . ' ✔️ ' . _('This domain is open to registration!'));
+
+function message($message) {
 	output(200, data: [
 		'message' => '<p>' . $message . '</p>',
 		'domain' => htmlspecialchars($_POST['subdomain']),
 	]);
+}
 
 rateLimit();
 
 insert('registry', [
 	'domain' => $domain,
 	'username' => $_SESSION['id'],
-	'last_renewal' => date('Y-m-d H:i:s'),
+	'creation' => date('Y-m'),
 ]);
 
-output(200, _('Domain registered.'));
+output(200, _('Domain registered.'), ['message' => '<p>' . $message . '</p>']);

+ 5 - 1
pg-view/reg/register.php

@@ -25,6 +25,10 @@ foreach (CONF['reg']['suffixes'] as $suffix => $availability) {
 		</div>
 	</fieldset>
 	<button type="submit" name="action" value="check">👁️ <?= _('Check availability') ?></button>
-	<?= $data['message'] ?? '<br>' ?>
+	<br>
 	<button type="submit" name="action" value="register">🔗 <?= _('Register') ?></button>
 </form>
+
+<section>
+	<?= $data['message'] ?? '' ?>
+</section>

+ 1 - 1
pg-view/reg/unregister.php

@@ -1,5 +1,5 @@
 <p>
-	<?= _('This will unregister the domain, making it registerable by anyone again.') ?>
+	<?= _('This will unregister the domain, making it registerable by anyone again (after a delay of 1 year plus half the registration period, with a maximum of 8 years).') ?>
 </p>
 
 <form method="post">

+ 4 - 0
router.php

@@ -3,6 +3,10 @@ const ROOT_PATH = __DIR__;
 define('CONF', parse_ini_file(ROOT_PATH . '/config.ini', true, INI_SCANNER_TYPED));
 
 define('DB', new PDO('sqlite:' . ROOT_PATH . '/db/servnest.db'));
+DB->exec('PRAGMA foreign_keys = ON;');
+DB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+date_default_timezone_set('UTC');
 
 $locale = 'en';
 foreach (explode(',', preg_replace('/[A-Z0-9]|q=|;|-|\./', '', $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '')) as $client_locale) {