summaryrefslogtreecommitdiffstats
path: root/identicon.php
diff options
context:
space:
mode:
authorGab <24553253+gabrix73@users.noreply.github.com>2026-06-29 00:13:23 +0200
committerGab <24553253+gabrix73@users.noreply.github.com>2026-06-29 00:13:23 +0200
commit1d1d06c025be8b16a3535ecceffc26fe303977db (patch)
treecc814b265bcaa2711f092f53349cc66068a6256a /identicon.php
parent322a17dba0d44200f6c241b18a58761220050aba (diff)
downloadm2usenet-and-mail2news-1d1d06c025be8b16a3535ecceffc26fe303977db.tar.gz
m2usenet-and-mail2news-1d1d06c025be8b16a3535ecceffc26fe303977db.tar.xz
m2usenet-and-mail2news-1d1d06c025be8b16a3535ecceffc26fe303977db.zip
Update m2usenet UI and backend; add identicon renderer and nacl libs
- index.php, send.php: updated client UI - m2usenet.go: backend changes - identicon.php: new identicon renderer - nacl.min.js, nacl-util.min.js: vendored client crypto libs
Diffstat (limited to 'identicon.php')
-rw-r--r--identicon.php59
1 files changed, 59 insertions, 0 deletions
diff --git a/identicon.php b/identicon.php
new file mode 100644
index 0000000..742cdf0
--- /dev/null
+++ b/identicon.php
@@ -0,0 +1,59 @@
+<?php
+// /var/www/m2usenet/identicon.php
+// VFACE identicon generator for m2usenet — wraps the ORIGINAL identicons-cli
+// engine (Ch1ffr3punk algorithm), the same binary used by identicons.virebent.art.
+// Input is PUBLIC only (username|email|ed25519-pubkey); no secret key involved.
+// No logging (privacy).
+
+ini_set('display_errors', 0);
+error_reporting(0);
+header('Content-Type: application/json');
+header('Referrer-Policy: no-referrer');
+header('X-Content-Type-Options: nosniff');
+
+define('IDENTICONS_CLI', '/usr/local/bin/identicons-cli');
+
+function fail(string $msg, int $code = 400): void {
+ http_response_code($code);
+ echo json_encode(['error' => $msg]);
+ exit;
+}
+
+if ($_SERVER['REQUEST_METHOD'] !== 'POST') fail('POST only', 405);
+if (!is_executable(IDENTICONS_CLI)) fail('engine unavailable', 503);
+
+$username = trim($_POST['username'] ?? '');
+$email = trim($_POST['email'] ?? '');
+$pubkey = trim($_POST['pubkey'] ?? '');
+
+// Validation: public fields only. No '|' (identity-string separator) allowed inside.
+if ($username === '' || mb_strlen($username) > 64 || strpos($username, '|') !== false) fail('bad username');
+if ($email === '' || mb_strlen($email) > 128 || strpos($email, '|') !== false) fail('bad email');
+if (!preg_match('#^[A-Za-z0-9+/=]{1,128}$#', $pubkey)) fail('bad pubkey');
+
+// Identity string = exact format consumed by identicons-cli / identicons.virebent.art
+$input = $username . '|' . $email . '|' . $pubkey;
+
+function identiconB64(string $input, int $size): ?string {
+ $cmd = IDENTICONS_CLI . ' -input ' . escapeshellarg($input) .
+ ' -size ' . intval($size) . ' -transparent -format base64 2>/dev/null';
+ $out = trim(shell_exec($cmd) ?? '');
+ return ($out !== '' && preg_match('#^[A-Za-z0-9+/=]+$#', $out)) ? $out : null;
+}
+
+// Single-line Face: header (no folding) so the base64 stays clean in the
+// saved identity JSON and after downstream RFC unfolding. ~222 octets < 998.
+function foldFaceHeader(string $base64): string {
+ return 'Face: ' . $base64;
+}
+
+$face48 = identiconB64($input, 48);
+$face256 = identiconB64($input, 256);
+if ($face48 === null || $face256 === null) fail('generation failed', 500);
+
+echo json_encode([
+ 'hash' => hash('sha256', $input),
+ 'face48' => $face48, // raw base64 PNG (48x48) -> Face: header
+ 'faceHeader' => foldFaceHeader($face48), // ready-to-paste folded Face: header
+ 'preview' => 'data:image/png;base64,' . $face256, // 256x256 dataurl for display
+]);