downloadRemailers()) { error_log('Remailer refresh failed; keeping the locally available data.'); } // Reload after a successful refresh, or continue with the local list. $entryRemailers = getRemailers('entry'); $middleRemailers = getRemailers('middle'); $exitRemailers = getRemailers('exit'); } catch (Exception $e) { error_log("Failed to download remailers: " . $e->getMessage()); } } $message = ''; $messageType = ''; // Check for flash messages from previous redirect (PRG pattern) if (isset($_SESSION['flash_message'])) { $message = $_SESSION['flash_message']; $messageType = isset($_SESSION['flash_type']) ? $_SESSION['flash_type'] : 'info'; // Clear flash messages after displaying unset($_SESSION['flash_message']); unset($_SESSION['flash_type']); } // Handle form submission if ($_SERVER['REQUEST_METHOD'] === 'POST') { // CSRF validation - IMPROVED with better error handling if (!isset($_POST['csrf_token'])) { // Save error in session and redirect $_SESSION['flash_message'] = "Security token missing. Please reload the page and try again."; $_SESSION['flash_type'] = 'error'; header('Location: ' . $_SERVER['PHP_SELF']); exit; } elseif ($_POST['csrf_token'] !== $currentCsrfToken) { // Save error in session and redirect $_SESSION['flash_message'] = "Security token mismatch. Please try submitting again."; $_SESSION['flash_type'] = 'error'; header('Location: ' . $_SERVER['PHP_SELF']); exit; } else { try { // Get and sanitize form data with additional validation $entryRemailer = isset($_POST['entry_remailer']) ? filter_var($_POST['entry_remailer'], FILTER_SANITIZE_STRING) : ''; $middleRemailer = isset($_POST['middle_remailer']) ? filter_var($_POST['middle_remailer'], FILTER_SANITIZE_STRING) : ''; $exitRemailer = isset($_POST['exit_remailer']) ? filter_var($_POST['exit_remailer'], FILTER_SANITIZE_STRING) : ''; $from = isset($_POST['from']) ? filter_var($_POST['from'], FILTER_SANITIZE_STRING) : ''; $replyTo = isset($_POST['reply_to']) ? filter_var($_POST['reply_to'], FILTER_SANITIZE_STRING) : ''; $to = isset($_POST['to']) ? filter_var($_POST['to'], FILTER_SANITIZE_EMAIL) : ''; $subject = isset($_POST['subject']) ? filter_var($_POST['subject'], FILTER_SANITIZE_STRING) : ''; $newsgroups = isset($_POST['newsgroups']) ? filter_var($_POST['newsgroups'], FILTER_SANITIZE_STRING) : ''; $references = isset($_POST['references']) ? filter_var($_POST['references'], FILTER_SANITIZE_STRING) : ''; $data = isset($_POST['data']) ? $_POST['data'] : ''; // Keep original formatting $copies = isset($_POST['copies']) ? intval($_POST['copies']) : 1; // Validate copies if ($copies < 1 || $copies > 3) { throw new Exception("Number of copies must be between 1 and 3."); } if (empty($from)) { throw new Exception("Sender (From) is required."); } $messageKind = trim($newsgroups) === '' ? 'email' : 'usenet'; if ($messageKind === 'email' && empty($to)) { throw new Exception("Email recipient is required when Newsgroup is empty."); } if ($messageKind === 'usenet') { $to = yamnConfig('YAMN_USENET_GATEWAY', 'mail2news@mail2news.tcpreset.net'); if (!filter_var($to, FILTER_VALIDATE_EMAIL)) { throw new Exception("Usenet gateway is not configured correctly."); } } // Validate remailer chain if (empty($entryRemailer) || empty($middleRemailer) || empty($exitRemailer)) { throw new Exception("All three remailers must be specified."); } // Resolve asterisks (*) to actual random remailers $resolvedEntry = resolveRemailer($entryRemailer, $entryRemailers); $resolvedMiddle = resolveRemailer($middleRemailer, $middleRemailers); $resolvedExit = resolveRemailer($exitRemailer, $exitRemailers); $encoded = encodeYamnMessage([ 'kind' => $messageKind, 'entry' => $resolvedEntry, 'chain' => [$resolvedEntry, $resolvedMiddle, $resolvedExit], 'from' => $from, 'reply_to' => $replyTo, 'to' => $to, 'subject' => $subject, 'newsgroup' => $newsgroups, 'body' => $data, 'references' => $references, ]); if (!$encoded['success']) { throw new Exception($encoded['error'] ?? 'YAMN encoding failed'); } $responseFinished = false; $onHandoff = null; if (function_exists('fastcgi_finish_request')) { $onHandoff = static function () use (&$responseFinished): void { ignore_user_abort(true); $_SESSION['flash_message'] = 'Message accepted into the local in-memory Nym queue. Delivery continues in the background; YAMN queues may take several hours, and this confirmation does not mean the message has been published or delivered yet.'; $_SESSION['flash_type'] = 'success'; $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); session_write_close(); header('Location: ' . $_SERVER['PHP_SELF']); fastcgi_finish_request(); $responseFinished = true; }; } $result = sendNymEnvelope($encoded['envelope'], 1, $encoded['entry_address'], $onHandoff); if ($responseFinished) { exit; } if (!$result['success']) { throw new Exception($result['error'] ?? 'Nym submission failed'); } $_SESSION['flash_message'] = 'Message accepted into the local in-memory Nym queue. Delivery through the YAMN queues may take several hours; this confirmation does not mean the message has been published or delivered yet.'; $_SESSION['flash_type'] = 'success'; $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); header('Location: ' . $_SERVER['PHP_SELF']); exit; } catch (Exception $e) { // Save error in session and redirect (PRG pattern for errors too) $_SESSION['flash_message'] = "✗ Error: " . $e->getMessage(); $_SESSION['flash_type'] = 'error'; error_log("YAMN submission error: " . $e->getMessage() . "\nStack: " . $e->getTraceAsString()); // Redirect to prevent form resubmission header('Location: ' . $_SERVER['PHP_SELF']); exit; } catch (Error $e) { // Catch PHP 7+ Error class (for fatal errors) $_SESSION['flash_message'] = "✗ Fatal Error: " . $e->getMessage(); $_SESSION['flash_type'] = 'error'; error_log("YAMN fatal error: " . $e->getMessage() . "\nStack: " . $e->getTraceAsString()); // Redirect to prevent form resubmission header('Location: ' . $_SERVER['PHP_SELF']); exit; } catch (Throwable $e) { // Catch everything else $_SESSION['flash_message'] = "✗ Unexpected error: " . $e->getMessage(); $_SESSION['flash_type'] = 'error'; error_log("YAMN unexpected error: " . $e->getMessage()); // Redirect to prevent form resubmission header('Location: ' . $_SERVER['PHP_SELF']); exit; } } } ?>
Remailer statistics and public keys are checked automatically when this page loads. Local data no more than 24 hours old is reused without a network connection.
Missing or older data is downloaded through Tor. If a source is unavailable, the existing data remains usable and another download is not attempted until the next daily check.