diff options
| author | Gab <24553253+gabrix73@users.noreply.github.com> | 2025-11-13 17:10:29 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-11-13 17:10:29 +0100 |
| commit | c7d183d4ae08dc1b0a15a5b8af76a32e620347fe (patch) | |
| tree | 88438001e6b68d0302ef1f63c485735ddbdc857b | |
| parent | 2f91561aa80e559a22608a314374463b6115adc9 (diff) | |
| download | m2usenet-and-mail2news-c7d183d4ae08dc1b0a15a5b8af76a32e620347fe.tar.gz m2usenet-and-mail2news-c7d183d4ae08dc1b0a15a5b8af76a32e620347fe.tar.xz m2usenet-and-mail2news-c7d183d4ae08dc1b0a15a5b8af76a32e620347fe.zip | |
Update print statement from 'Hello' to 'Goodbye'
| -rw-r--r-- | index.php | 593 |
1 files changed, 218 insertions, 375 deletions
@@ -1,6 +1,6 @@ <?php // /var/www/m2usenet/index.php -// m2usenet v2.0.0 - Hardened Gateway Interface +// m2usenet v2.1.0 - Hardened Gateway Interface // Start session for CSRF token if (session_status() === PHP_SESSION_NONE) { @@ -21,7 +21,7 @@ $csrfToken = $_SESSION['csrf_token']; <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <title>m2usenet Gateway v2.0</title> + <title>m2usenet Gateway v2.1</title> <style> :root { --background: #f9f9f9; @@ -115,7 +115,6 @@ $csrfToken = $_SESSION['csrf_token']; display: none; } - /* Notification System */ .notification { position: fixed; top: 20px; @@ -131,51 +130,19 @@ $csrfToken = $_SESSION['csrf_token']; gap: 10px; } - .notification.success { - background: var(--success); - color: white; - } - - .notification.error { - background: var(--error); - color: white; - } - - .notification.warning { - background: var(--warning); - color: #333; - } - - .notification.info { - background: #007bff; - color: white; - } + .notification.success { background: var(--success); color: white; } + .notification.error { background: var(--error); color: white; } + .notification.warning { background: var(--warning); color: #333; } + .notification.info { background: #007bff; color: white; } @keyframes slideIn { - from { - transform: translateX(400px); - opacity: 0; - } - to { - transform: translateX(0); - opacity: 1; - } + from { transform: translateX(400px); opacity: 0; } + to { transform: translateX(0); opacity: 1; } } - .notification-icon { - font-size: 1.5em; - } - - .notification-close { - margin-left: auto; - cursor: pointer; - font-size: 1.2em; - opacity: 0.8; - } - - .notification-close:hover { - opacity: 1; - } + .notification-icon { font-size: 1.5em; } + .notification-close { margin-left: auto; cursor: pointer; font-size: 1.2em; opacity: 0.8; } + .notification-close:hover { opacity: 1; } .section-info { background: var(--card-bg); @@ -209,14 +176,8 @@ $csrfToken = $_SESSION['csrf_token']; position: relative; } - .tabs button:hover { - background: var(--primary); - color: white; - } - - .tabs button:active { - transform: scale(0.98); - } + .tabs button:hover { background: var(--primary); color: white; } + .tabs button:active { transform: scale(0.98); } .tabs button.completed::after { content: 'โ'; @@ -272,9 +233,7 @@ $csrfToken = $_SESSION['csrf_token']; box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2); } - input.error, textarea.error { - border-color: var(--error); - } + input.error, textarea.error { border-color: var(--error); } input[readonly], textarea[readonly] { background: var(--input-readonly); @@ -295,14 +254,8 @@ $csrfToken = $_SESSION['csrf_token']; font-family: inherit; } - button:hover:not(:disabled) { - background: var(--primary-hover); - } - - button:disabled { - opacity: 0.6; - cursor: not-allowed; - } + button:hover:not(:disabled) { background: var(--primary-hover); } + button:disabled { opacity: 0.6; cursor: not-allowed; } .progress-bar { width: 100%; @@ -336,10 +289,7 @@ $csrfToken = $_SESSION['csrf_token']; font-size: 0.9em; } - .output-field.empty { - color: #999; - font-style: italic; - } + .output-field.empty { color: #999; font-style: italic; } .gateway-info { background: var(--card-bg); @@ -357,9 +307,7 @@ $csrfToken = $_SESSION['csrf_token']; border-bottom: 1px solid var(--border); } - .gateway-item:last-child { - border-bottom: none; - } + .gateway-item:last-child { border-bottom: none; } .gateway-priority { background: #007bff; @@ -372,14 +320,8 @@ $csrfToken = $_SESSION['csrf_token']; text-align: center; } - .gateway-priority.secondary { - background: #6c757d; - } - - .gateway-priority.fallback { - background: #ffc107; - color: #333; - } + .gateway-priority.secondary { background: #6c757d; } + .gateway-priority.fallback { background: #ffc107; color: #333; } .gateway-address { font-family: monospace; @@ -417,46 +359,22 @@ $csrfToken = $_SESSION['csrf_token']; text-decoration: underline; } - .step-indicator { - font-size: 0.85em; - color: #666; - margin-bottom: 10px; - } - @media (max-width: 600px) { - .container { - padding: 10px; - } - .tab-content { - padding: 15px 10px; - } - .footer-links { - flex-direction: column; - gap: 10px; - } - .gateway-item { - flex-direction: column; - align-items: flex-start; - gap: 5px; - } - .gateway-address { - width: 100%; - } - .notification { - right: 10px; - left: 10px; - max-width: none; - } + .container { padding: 10px; } + .tab-content { padding: 15px 10px; } + .footer-links { flex-direction: column; gap: 10px; } + .gateway-item { flex-direction: column; align-items: flex-start; gap: 5px; } + .gateway-address { width: 100%; } + .notification { right: 10px; left: 10px; max-width: none; } } </style> - <!-- Include both TweetNaCl libraries --> <script src="https://cdn.jsdelivr.net/npm/tweetnacl@1.0.3/nacl.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/tweetnacl-util@0.15.1/nacl-util.min.js"></script> </head> <body> <div class="container"> <header> - <h1>m2usenet Gateway v2.0</h1> + <h1>m2usenet Gateway v2.1</h1> <div class="theme-toggle"> <span>๐</span> <input type="checkbox" id="themeToggle"> @@ -479,13 +397,11 @@ $csrfToken = $_SESSION['csrf_token']; <div id="pow" class="tab-content active"> <h2>Proof-of-Work Token</h2> - <div class="section-info"> <p><strong>About m2usenet:</strong> This application is a privacy-focused gateway that sends your messages to Usenet newsgroups via our mail2news gateways on the onion network. No access logs are collected, and your messages are routed through secure gateways.</p> <p><strong>What is this?</strong> This step generates a "proof-of-work" token (hashcash) that prevents spam by requiring your computer to perform some calculations. This is similar to how cryptocurrencies work - you need to "mine" a valid token before sending a message.</p> <p><strong>How to use:</strong> Enter your email address, select the difficulty level (higher bits = longer processing time), and click "Generate Token". Your browser will mine a valid token that will be required in the next steps.</p> </div> - <label>Email (resource): <input type="email" id="hcEmail" placeholder="your@email.com" required></label> <label>Difficulty (bits): <select id="hcBits"> @@ -503,19 +419,16 @@ $csrfToken = $_SESSION['csrf_token']; <div id="sign" class="tab-content"> <h2>Ed25519 Digital Signature</h2> - <div class="section-info"> <p><strong>What is this?</strong> This step creates a digital signature for your message using the Ed25519 cryptographic algorithm. This signature helps verify that the message was sent by you and hasn't been tampered with.</p> <p><strong>How to use:</strong> First, generate a key pair (this creates a public and private key). Then, write your message and click "Sign Message". This will create a digital signature that will be attached to your post.</p> </div> - <label>Email used for PoW:</label> <input type="text" id="readonlyEmailSign" readonly> <label>Message to Sign:</label> <textarea id="messageToSign" rows="6" placeholder="Write your message here..." required></textarea> <button id="genKeyBtn">Generate Key Pair</button> <button id="signMsgBtn" disabled>Sign Message</button> - <label>Generated Public Key:</label> <div id="pubKeyOutput" class="output-field empty">Public key will appear here</div> <label>Generated Signature:</label> @@ -524,32 +437,25 @@ $csrfToken = $_SESSION['csrf_token']; <div id="send" class="tab-content"> <h2>Send Message</h2> - <div class="section-info"> - <p><strong>What is this?</strong> This final step sends your signed message to the Usenet network via mail2news gateways. m2usenet v2.0.0 uses Tor for all connections with automatic fallback.</p> - + <p><strong>What is this?</strong> This final step sends your signed message to the Usenet network via mail2news gateways. m2usenet v2.1.0 uses Tor for all connections with automatic fallback.</p> <div class="gateway-info"> <h4>๐ Full Onion Network Path</h4> - <p style="margin-bottom: 15px; font-size: 0.95em;">Your message travels entirely within the Tor network through these three components:</p> - - <div class="info-section" style="background: #f8f9fa; padding: 15px; border-radius: 8px; margin: 20px 0;"> - <strong>SMTP Relay: <a href="https://github.com/gabrix73/fog" target="_blank" style="color: #007bff; text-decoration: none;">Fog Sphinx Mixnet (v1.3.3)</a></strong> - <p style="font-size: 0.85em; margin: 8px 0 0 0; color: #666; line-height: 1.5;"> - Random selection from 4-node mixnet operating exclusively over Tor. - Each message routes through 3-6 hops with AES-256-GCM encryption, forward secrecy, - and automatic failover for maximum reliability and anonymity. - </p> - <details style="margin-top: 8px; font-size: 0.8em;"> - <summary style="cursor: pointer; color: #007bff;">Show relay nodes</summary> - <ul style="margin: 8px 0; padding-left: 20px; font-family: monospace; color: #555;"> - <li>dgayvmsxvvofpdxsas22fo7eu5tous6aavzjs6eun6jnouluwqflz7ad.onion:2525</li> - <li>iycr4wfrdzieogdfeo7uxrj77w2vjlrhlrv3jg2ve62oe5aceqsqu7ad.onion:2525</li> - <li>ztavxfthfr2fgovxzfg3sudl2ajtbo6db4iw5cx37nzr5jc6q7ma6ryd.onion:2525</li> - <li>hqahdugpxz7jrmsfxqav5nfl452cvc5rsdhcpyixvcbcpfsopwnznlqd.onion:2525</li> - </ul> - </details> -</div> - + <p style="margin-bottom: 15px; font-size: 0.95em;">Your message travels entirely within the Tor network:</p> + <div class="gateway-item"> + <span class="gateway-priority">Step 1</span> + <div style="flex: 1;"> + <strong>SMTP Relays:</strong> + <p style="font-size: 0.85em; margin: 5px 0 0 0; color: #666;">Specialized SMTP relays operating over Tor hidden services</p> + </div> + </div> + <details style="margin-top: 8px; font-size: 0.8em;"> + <summary style="cursor: pointer; color: #007bff;">Show relay nodes</summary> + <ul style="margin: 8px 0; padding-left: 20px; font-family: monospace; color: #555;"> + <li>4uwpi53u524xdphjw2dv5kywsxmyjxtk4facb76jgl3sc3nda3sz4fqd.onion:25</li> + <li>xilb7y4kj6u6qfo45o3yk2kilfv54ffukzei3puonuqlncy7cn2afwyd.onion:25</li> + </ul> + </details> <div class="gateway-item"> <span class="gateway-priority">Step 2</span> <div style="flex: 1;"> @@ -558,7 +464,6 @@ $csrfToken = $_SESSION['csrf_token']; <p style="font-size: 0.85em; margin: 5px 0 0 0; color: #666;">Converts email to Usenet posts while maintaining full anonymity within the onion network.</p> </div> </div> - <div class="gateway-item"> <span class="gateway-priority">Step 3</span> <div style="flex: 1;"> @@ -567,18 +472,14 @@ $csrfToken = $_SESSION['csrf_token']; <p style="font-size: 0.85em; margin: 5px 0 0 0; color: #666;">Final destination for Usenet Tor hidden service.</p> </div> </div> - <p style="margin-top: 15px; padding: 10px; background: rgba(0,123,255,0.1); border-radius: 4px; font-size: 0.9em;"> <strong>๐ Privacy Guarantee:</strong> Your message never leaves the Tor network until it reaches the NNTP server. All three components (Pluto2 โ Mail2News โ NNTP) operate as Tor hidden services, providing end-to-end anonymity with protection against traffic analysis, timing attacks, and metadata correlation. </p> </div> - <p><strong>How to use:</strong> Complete the form below with your name, newsgroups (max 3), subject, and verify that your message and authentication details are correct. Then click "Send" to post your message via the gateway system.</p> </div> - <label>Email used for PoW:</label> <input type="text" id="readonlyEmailSend" readonly> - <form id="sendForm" method="POST" action="send.php"> <input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($csrfToken); ?>"> <label>From (Name): <input type="text" id="fromName" required placeholder="Your Name"></label> @@ -598,7 +499,7 @@ $csrfToken = $_SESSION['csrf_token']; </div> <footer> - <div>m2usenet Gateway v2.0.0 ยฉ 2025 - Privacy-focused Usenet posting tool</div> + <div>m2usenet Gateway v2.1.0 ยฉ 2025 - Privacy-focused Usenet posting tool</div> <div class="footer-links"> <a href="https://yamn.virebent.art">Home</a> <a href="mailto:%69%6E%66%6F%40%76%69%72%65%62%65%6E%74%2E%61%72%74">Contact</a> @@ -607,17 +508,20 @@ $csrfToken = $_SESSION['csrf_token']; </footer> </div> -<!-- SCRIPT 1: Core Functions (Theme, Notifications, POW, Signing) --> <script> -// Notification system -function showNotification(message, type = 'info', duration = 5000) { - const icons = { - success: 'โ', - error: 'โ', - warning: 'โ ', - info: 'โน' - }; +// ============================================================================ +// DEFINIZIONI FUNZIONI GLOBALI - DEVONO ESSERE PRIMA DI TUTTO +// ============================================================================ + +function showTab(id) { + document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('active')); + document.getElementById(id).classList.add('active'); +} +function showNotification(message, type, duration) { + type = type || 'info'; + duration = duration === undefined ? 5000 : duration; + const icons = { success: 'โ', error: 'โ', warning: 'โ ', info: 'โน' }; const notification = document.createElement('div'); notification.className = `notification ${type}`; notification.innerHTML = ` @@ -625,19 +529,16 @@ function showNotification(message, type = 'info', duration = 5000) { <span class="notification-message">${message}</span> <span class="notification-close" onclick="this.parentElement.remove()">ร</span> `; - document.body.appendChild(notification); - if (duration > 0) { - setTimeout(() => { - if (notification.parentElement) { - notification.remove(); - } - }, duration); + setTimeout(() => { if (notification.parentElement) notification.remove(); }, duration); } } -// State management +// ============================================================================ +// STATE MANAGEMENT +// ============================================================================ + const appState = { step1Complete: false, step2Complete: false, @@ -645,89 +546,134 @@ const appState = { }; function updateTabIndicators() { - if (appState.step1Complete) { - document.getElementById('tabBtn1').classList.add('completed'); - } - if (appState.step2Complete) { - document.getElementById('tabBtn2').classList.add('completed'); - } - if (appState.step3Complete) { - document.getElementById('tabBtn3').classList.add('completed'); + if (appState.step1Complete) document.getElementById('tabBtn1').classList.add('completed'); + if (appState.step2Complete) document.getElementById('tabBtn2').classList.add('completed'); + if (appState.step3Complete) document.getElementById('tabBtn3').classList.add('completed'); +} + +let keyPair = null; +let workersSupported = true; + +try { + if (typeof Worker === 'undefined') { + workersSupported = false; + console.warn('[WARN] Web Workers not supported'); } +} catch (e) { + workersSupported = false; + console.warn('[WARN] Web Workers check failed:', e); } -// Theme toggle functionality +// ============================================================================ +// THEME TOGGLE +// ============================================================================ + document.getElementById('themeToggle').addEventListener('change', function() { document.body.classList.toggle('dark-theme', this.checked); localStorage.setItem('darkTheme', this.checked); }); -// Check for saved theme preference +// ============================================================================ +// INITIALIZATION +// ============================================================================ + document.addEventListener('DOMContentLoaded', function() { const darkTheme = localStorage.getItem('darkTheme') === 'true'; document.getElementById('themeToggle').checked = darkTheme; document.body.classList.toggle('dark-theme', darkTheme); - // Check if nacl is properly loaded if (typeof nacl === 'undefined') { showNotification('Cryptography library failed to load. Please refresh the page.', 'error', 0); return; } - // Initialize nacl.util if it doesn't exist if (typeof nacl.util === 'undefined') { nacl.util = { - decodeUTF8: function(str) { - return new TextEncoder().encode(str); - }, - encodeUTF8: function(arr) { - return new TextDecoder().decode(arr); - }, + decodeUTF8: function(str) { return new TextEncoder().encode(str); }, + encodeUTF8: function(arr) { return new TextDecoder().decode(arr); }, encodeBase64: function(arr) { - return btoa(Array.from(new Uint8Array(arr)) - .map(byte => String.fromCharCode(byte)) - .join('')); + return btoa(Array.from(new Uint8Array(arr)).map(byte => String.fromCharCode(byte)).join('')); }, decodeBase64: function(b64) { const bin = atob(b64); const arr = new Uint8Array(bin.length); - for (let i = 0; i < bin.length; i++) { - arr[i] = bin.charCodeAt(i); - } + for (let i = 0; i < bin.length; i++) arr[i] = bin.charCodeAt(i); return arr; } }; } - showNotification('m2usenet Gateway v2.0 ready', 'success', 3000); + showNotification('m2usenet Gateway v2.1 ready', 'success', 3000); + + // CRITICAL: Assicura che solo il primo tab sia visibile + showTab('pow'); }); -function showTab(id) { - document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('active')); - document.getElementById(id).classList.add('active'); -} +// ============================================================================ +// FORM SUBMISSION PROTECTION +// ============================================================================ -// Step 1: Generate Hashcash Token -let keyPair = null; -let workersSupported = true; - -// Check if Web Workers are supported (may be blocked in Tor Browser) -try { - if (typeof Worker === 'undefined') { - workersSupported = false; - console.warn('[WARN] Web Workers not supported in this browser'); +document.addEventListener('DOMContentLoaded', function() { + const form = document.querySelector('form'); + const submitButton = form.querySelector('button[type="submit"], input[type="submit"]'); + + if (!submitButton) { + console.error('Submit button not found'); + return; } -} catch (e) { - workersSupported = false; - console.warn('[WARN] Web Workers check failed:', e); -} + + let isSubmitting = false; + + form.addEventListener('submit', function(e) { + if (isSubmitting) { + e.preventDefault(); + console.log('Form already submitting, preventing duplicate'); + return false; + } + + isSubmitting = true; + submitButton.disabled = true; + const originalText = submitButton.textContent || submitButton.value; + submitButton.textContent = 'Sending...'; + submitButton.value = 'Sending...'; + submitButton.style.opacity = '0.6'; + submitButton.style.cursor = 'not-allowed'; + + const progressDiv = document.createElement('div'); + progressDiv.id = 'sending-progress'; + progressDiv.style.cssText = 'margin-top: 10px; padding: 10px; background: #fff3cd; border-left: 4px solid #ffc107; color: #856404;'; + progressDiv.innerHTML = '<strong>โณ Sending message...</strong><br>This may take 30-60 seconds. Please wait.'; + submitButton.parentNode.insertBefore(progressDiv, submitButton.nextSibling); + + return true; + }); + + form.addEventListener('keypress', function(e) { + if (e.key === 'Enter' && isSubmitting) { + e.preventDefault(); + return false; + } + }); + + window.addEventListener('pageshow', function(event) { + if (event.persisted) { + isSubmitting = false; + submitButton.disabled = false; + submitButton.textContent = originalText; + submitButton.style.opacity = '1'; + submitButton.style.cursor = 'pointer'; + const progressDiv = document.getElementById('sending-progress'); + if (progressDiv) progressDiv.remove(); + } + }); +}); -// SHA-1 implementation for fallback mode -function sha1(str) { - function rotate_left(n, s) { - return (n << s) | (n >>> (32 - s)); - } +// ============================================================================ +// SHA-1 IMPLEMENTATION +// ============================================================================ +function sha1(str) { + function rotate_left(n, s) { return (n << s) | (n >>> (32 - s)); } function cvt_hex(val) { let str = ''; for (let i = 7; i >= 0; i--) { @@ -736,50 +682,31 @@ function sha1(str) { } return str; } + function utf8Encode(str) { return unescape(encodeURIComponent(str)); } - function utf8Encode(str) { - return unescape(encodeURIComponent(str)); - } - - let blockstart; - let i, j; + let blockstart, i, j; const W = new Array(80); - let H0 = 0x67452301; - let H1 = 0xEFCDAB89; - let H2 = 0x98BADCFE; - let H3 = 0x10325476; - let H4 = 0xC3D2E1F0; - let A, B, C, D, E; - let temp; + let H0 = 0x67452301, H1 = 0xEFCDAB89, H2 = 0x98BADCFE, H3 = 0x10325476, H4 = 0xC3D2E1F0; + let A, B, C, D, E, temp; str = utf8Encode(str); const str_len = str.length; - const word_array = []; + for (i = 0; i < str_len - 3; i += 4) { j = str.charCodeAt(i) << 24 | str.charCodeAt(i + 1) << 16 | str.charCodeAt(i + 2) << 8 | str.charCodeAt(i + 3); word_array.push(j); } switch (str_len % 4) { - case 0: - i = 0x080000000; - break; - case 1: - i = str.charCodeAt(str_len - 1) << 24 | 0x0800000; - break; - case 2: - i = str.charCodeAt(str_len - 2) << 24 | str.charCodeAt(str_len - 1) << 16 | 0x08000; - break; - case 3: - i = str.charCodeAt(str_len - 3) << 24 | str.charCodeAt(str_len - 2) << 16 | str.charCodeAt(str_len - 1) << 8 | 0x80; - break; + case 0: i = 0x080000000; break; + case 1: i = str.charCodeAt(str_len - 1) << 24 | 0x0800000; break; + case 2: i = str.charCodeAt(str_len - 2) << 24 | str.charCodeAt(str_len - 1) << 16 | 0x08000; break; + case 3: i = str.charCodeAt(str_len - 3) << 24 | str.charCodeAt(str_len - 2) << 16 | str.charCodeAt(str_len - 1) << 8 | 0x80; break; } word_array.push(i); - while ((word_array.length % 16) != 14) word_array.push(0); - word_array.push(str_len >>> 29); word_array.push((str_len << 3) & 0x0ffffffff); @@ -787,46 +714,23 @@ function sha1(str) { for (i = 0; i < 16; i++) W[i] = word_array[blockstart + i]; for (i = 16; i <= 79; i++) W[i] = rotate_left(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1); - A = H0; - B = H1; - C = H2; - D = H3; - E = H4; + A = H0; B = H1; C = H2; D = H3; E = H4; for (i = 0; i <= 19; i++) { temp = (rotate_left(A, 5) + ((B & C) | (~B & D)) + E + W[i] + 0x5A827999) & 0x0ffffffff; - E = D; - D = C; - C = rotate_left(B, 30); - B = A; - A = temp; + E = D; D = C; C = rotate_left(B, 30); B = A; A = temp; } - for (i = 20; i <= 39; i++) { temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff; - E = D; - D = C; - C = rotate_left(B, 30); - B = A; - A = temp; + E = D; D = C; C = rotate_left(B, 30); B = A; A = temp; } - for (i = 40; i <= 59; i++) { temp = (rotate_left(A, 5) + ((B & C) | (B & D) | (C & D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff; - E = D; - D = C; - C = rotate_left(B, 30); - B = A; - A = temp; + E = D; D = C; C = rotate_left(B, 30); B = A; A = temp; } - for (i = 60; i <= 79; i++) { temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff; - E = D; - D = C; - C = rotate_left(B, 30); - B = A; - A = temp; + E = D; D = C; C = rotate_left(B, 30); B = A; A = temp; } H0 = (H0 + A) & 0x0ffffffff; @@ -839,45 +743,38 @@ function sha1(str) { return cvt_hex(H0) + cvt_hex(H1) + cvt_hex(H2) + cvt_hex(H3) + cvt_hex(H4); } -// Single-threaded mining fallback function mineSingleThread(prefix, targetZeros, progressCallback, foundCallback) { - console.log('[INFO] Using single-threaded mining (Web Workers not available)'); + console.log('[INFO] Using single-threaded mining'); const target = '0'.repeat(targetZeros); - let nonce = 0; - let checked = 0; - const batchSize = 100; // Check 100 hashes before updating UI + let nonce = 0, checked = 0; + const batchSize = 100; function mineNextBatch() { const endNonce = nonce + batchSize; - while (nonce < endNonce) { const token = prefix + nonce; const hash = sha1(token); checked++; - if (hash.startsWith(target)) { foundCallback(nonce, checked); return; } - nonce++; } - - // Update progress progressCallback(checked); - - // Continue mining in next tick to avoid blocking UI setTimeout(mineNextBatch, 0); } - mineNextBatch(); } +// ============================================================================ +// STEP 1: HASHCASH TOKEN GENERATION +// ============================================================================ + document.getElementById('genTokenBtn').onclick = () => { const email = document.getElementById('hcEmail').value.trim(); const bits = parseInt(document.getElementById('hcBits').value); - // Validation if (!email) { showNotification('โ Please enter your email address first!', 'warning'); document.getElementById('hcEmail').classList.add('error'); @@ -885,7 +782,6 @@ document.getElementById('genTokenBtn').onclick = () => { return; } - // Email format validation const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { showNotification('โ Please enter a valid email address!', 'warning'); @@ -895,11 +791,8 @@ document.getElementById('genTokenBtn').onclick = () => { } document.getElementById('hcEmail').classList.remove('error'); + showNotification(`๐จ Starting token generation (${bits} bits)...`, 'info'); - // Start generation - showNotification(`๐จ Starting token generation (${bits} bits)... This may take a while.`, 'info'); - - // Update UI to show processing const btn = document.getElementById('genTokenBtn'); btn.disabled = true; btn.textContent = 'Mining Token...'; @@ -933,8 +826,6 @@ document.getElementById('genTokenBtn').onclick = () => { function onFound(nonce, totalHashes) { const token = prefix + nonce; - - // Update UI const outputField = document.getElementById('tokenOutput'); outputField.innerText = token; outputField.classList.remove('empty'); @@ -945,34 +836,26 @@ document.getElementById('genTokenBtn').onclick = () => { document.getElementById('readonlyEmailSign').value = email; document.getElementById('readonlyEmailSend').value = email; - // Restore button btn.disabled = false; btn.textContent = 'Generate Token'; progBar.style.width = '100%'; progBar.innerText = `Complete! (${totalHashes} hashes)`; - // Mark step as complete appState.step1Complete = true; updateTabIndicators(); const elapsed = ((Date.now() - startTime) / 1000).toFixed(1); - showNotification(`โ Token generated in ${elapsed}s! You can now proceed to sign your message.`, 'success', 8000); - + showNotification(`โ Token generated in ${elapsed}s!`, 'success', 8000); console.log(`[INFO] Token mined: ${totalHashes} hashes in ${elapsed}s`); - // Auto-switch to next tab after a delay - setTimeout(() => { - showTab('sign'); - }, 2000); + setTimeout(() => showTab('sign'), 2000); } - // Try Web Workers first, fallback to single-thread if (workersSupported) { - console.log('[INFO] Attempting multi-threaded mining with Web Workers'); - + console.log('[INFO] Attempting multi-threaded mining'); try { const cores = navigator.hardwareConcurrency || 2; - console.log(`[INFO] Using ${cores} worker threads`); + console.log(`[INFO] Using ${cores} workers`); let found = false; let coreChecked = Array(cores).fill(0); @@ -986,18 +869,15 @@ document.getElementById('genTokenBtn').onclick = () => { for (let i = 0; i < cores; i++) { try { const w = new Worker('powWorker.js'); - w.postMessage({ prefix, targetZeros: zeros, startNonce: i, step: cores }); w.onmessage = e => { if (found) return; - if (e.data.type === 'found') { found = true; workers.forEach(x => x.terminate()); onFound(e.data.nonce, e.data.checked); } - if (e.data.type === 'progress') { coreChecked[i] = e.data.checked; updateCombinedProgress(); @@ -1007,8 +887,7 @@ document.getElementById('genTokenBtn').onclick = () => { w.onerror = (err) => { console.error(`[ERROR] Worker ${i} error:`, err); if (i === 0) { - // First worker failed, fallback to single-thread - console.warn('[WARN] Worker failed, falling back to single-thread'); + console.warn('[WARN] Falling back to single-thread'); workers.forEach(x => x.terminate()); mineSingleThread(prefix, zeros, updateProgress, onFound); } @@ -1018,42 +897,40 @@ document.getElementById('genTokenBtn').onclick = () => { } catch (err) { console.error(`[ERROR] Failed to create worker ${i}:`, err); if (i === 0) { - // Fallback to single-thread mineSingleThread(prefix, zeros, updateProgress, onFound); return; } } } - // Timeout safety (10 minutes for 24 bits) setTimeout(() => { if (!found) { - console.warn('[WARN] Mining timeout, this is taking too long'); - showNotification('โ Mining is taking longer than expected. Consider using a lower difficulty.', 'warning', 10000); + console.warn('[WARN] Mining timeout'); + showNotification('โ Mining is taking longer than expected.', 'warning', 10000); } }, 600000); } catch (err) { console.error('[ERROR] Worker mining failed:', err); - showNotification('โ Multi-threaded mining failed, using fallback mode', 'warning'); + showNotification('โ Multi-threaded mining failed, using fallback', 'warning'); mineSingleThread(prefix, zeros, updateProgress, onFound); } } else { - // Single-threaded fallback mineSingleThread(prefix, zeros, updateProgress, onFound); } }; -// Step 2: Generate Key Pair +// ============================================================================ +// STEP 2: ED25519 SIGNATURE +// ============================================================================ + document.getElementById('genKeyBtn').onclick = function() { try { - // Make sure nacl is loaded if (typeof nacl === 'undefined' || typeof nacl.sign === 'undefined') { throw new Error("Cryptography library not loaded"); } showNotification('๐ Generating Ed25519 key pair...', 'info'); - keyPair = nacl.sign.keyPair(); const pubKey = keyPair.publicKey; @@ -1065,18 +942,15 @@ document.getElementById('genKeyBtn').onclick = function() { pubOutput.innerText = pubB64; pubOutput.classList.remove('empty'); document.getElementById('x-ed25519-pub').value = pubB64; - - // Enable sign button document.getElementById('signMsgBtn').disabled = false; - showNotification('โ Key pair generated successfully! Now write your message and click "Sign Message".', 'success', 6000); + showNotification('โ Key pair generated!', 'success', 6000); } catch (error) { console.error("Error generating key pair:", error); - showNotification('โ Error generating key pair: ' + error.message, 'error'); + showNotification('โ Error: ' + error.message, 'error'); } }; -// Step 2: Sign Message document.getElementById('signMsgBtn').onclick = function() { try { if (!keyPair) { @@ -1086,19 +960,18 @@ document.getElementById('signMsgBtn').onclick = function() { const msg = document.getElementById('messageToSign').value.trim(); if (!msg) { - showNotification('โ Please write a message to sign!', 'warning'); + showNotification('โ Please write a message!', 'warning'); document.getElementById('messageToSign').classList.add('error'); document.getElementById('messageToSign').focus(); return; } if (msg.length < 10) { - showNotification('โ Message is too short. Please write at least 10 characters.', 'warning'); + showNotification('โ Message too short (min 10 characters)', 'warning'); return; } document.getElementById('messageToSign').classList.remove('error'); - showNotification('โ Signing message...', 'info'); const msgBytes = typeof nacl.util.decodeUTF8 === 'function' @@ -1116,31 +989,24 @@ document.getElementById('signMsgBtn').onclick = function() { sigOutput.classList.remove('empty'); document.getElementById('x-ed25519-sig').value = sigB64; - // EXACTLY like v1.0: message + "\n\n--- Digital Signature ---\n" + signature - // Public key goes ONLY in X-Ed25519-Pub header, NOT in message body document.getElementById('messageContent').value = msg + "\n\n--- Digital Signature ---\n" + sigB64; - console.log('[DEBUG] Message signed like v1.0, total length:', (msg + "\n\n--- Digital Signature ---\n" + sigB64).length); - - // Mark step as complete appState.step2Complete = true; updateTabIndicators(); - showNotification('โ Message signed successfully! You can now send your message to Usenet.', 'success', 8000); - - // Auto-switch to send tab - setTimeout(() => { - showTab('send'); - }, 2000); + showNotification('โ Message signed!', 'success', 8000); + setTimeout(() => showTab('send'), 2000); } catch (error) { - console.error("Error signing message:", error); - showNotification('โ Error signing message: ' + error.message, 'error'); + console.error("Error signing:", error); + showNotification('โ Error: ' + error.message, 'error'); } }; -// Step 3: Send Form +// ============================================================================ +// STEP 3: SEND FORM +// ============================================================================ + document.getElementById('sendForm').addEventListener('submit', function(e) { - // Form validation const requiredFields = [ {id: 'fromName', name: 'Name'}, {id: 'newsgroups', name: 'Newsgroups'}, @@ -1164,78 +1030,62 @@ document.getElementById('sendForm').addEventListener('submit', function(e) { if (missing.length > 0) { e.preventDefault(); - showNotification(`โ Missing required fields: ${missing.join(', ')}. Please complete all previous steps.`, 'warning', 8000); + showNotification(`โ Missing: ${missing.join(', ')}`, 'warning', 8000); return false; } - // Debug: log message length const messageLen = document.getElementById('messageContent').value.length; - console.log('[DEBUG] Final message length being sent:', messageLen); - console.log('[DEBUG] Message does NOT include signature in body - signature is in headers only'); - if (messageLen < 10) { e.preventDefault(); - showNotification('โ Message is too short (minimum 10 characters)', 'warning'); + showNotification('โ Message too short', 'warning'); return false; } - // Validate newsgroups const newsgroups = document.getElementById('newsgroups').value.split(',').map(g => g.trim()).filter(g => g); if (newsgroups.length === 0) { e.preventDefault(); - showNotification('โ Please enter at least one newsgroup!', 'warning'); + showNotification('โ Enter at least one newsgroup!', 'warning'); return false; } if (newsgroups.length > 3) { e.preventDefault(); - showNotification('โ Maximum 3 newsgroups allowed!', 'warning'); + showNotification('โ Max 3 newsgroups!', 'warning'); return false; } - // Confirm submission - const confirmMsg = `Are you ready to send your message to Usenet?\n\nNewsgroups: ${newsgroups.join(', ')}\nSubject: ${document.getElementById('subject').value}\nMessage length: ${messageLen} chars\n\nThis action cannot be undone.`; + const confirmMsg = `Send to Usenet?\n\nNewsgroups: ${newsgroups.join(', ')}\nSubject: ${document.getElementById('subject').value}\nLength: ${messageLen} chars\n\nThis cannot be undone.`; if (!confirm(confirmMsg)) { e.preventDefault(); return false; } - // Log for debugging - console.log('[DEBUG] Submitting message:'); - console.log('[DEBUG] From:', document.getElementById('fromFull').value); - console.log('[DEBUG] Newsgroups:', document.getElementById('newsgroups').value); - console.log('[DEBUG] Subject:', document.getElementById('subject').value); - console.log('[DEBUG] Message length:', messageLen); - console.log('[DEBUG] Hashcash:', document.getElementById('hcToken').value.substring(0, 50) + '...'); - console.log('[DEBUG] Ed25519-Pub:', document.getElementById('x-ed25519-pub').value.substring(0, 30) + '...'); - console.log('[DEBUG] Ed25519-Sig:', document.getElementById('x-ed25519-sig').value.substring(0, 30) + '...'); - - // Show sending notification - showNotification('๐ค Sending your message via m2usenet gateway...', 'info', 0); + showNotification('๐ค Sending...', 'info', 0); document.getElementById('sendBtn').disabled = true; document.getElementById('sendBtn').textContent = 'Sending...'; return true; }); -// Handle form field updates for "from" field document.getElementById('fromName').addEventListener('input', function() { const email = document.getElementById('hcEmail').value; if (email) { document.getElementById('fromFull').value = `${this.value} <${email}>`; } }); -</script> -<!-- SCRIPT 2: Newsreader Integration (Autofill from URL parameters) --> -<script> +// ============================================================================ +// NEWSREADER INTEGRATION +// ============================================================================ + (function() { const u = new URLSearchParams(window.location.search); const a = u.get('action'); if (!a) return; - function fill(id, v, readonly = false) { + function fill(id, v, readonly) { + readonly = readonly || false; const f = document.getElementById(id); if (f && v) { f.value = decodeURIComponent(v); @@ -1248,50 +1098,43 @@ document.getElementById('fromName').addEventListener('input', function() { } } - // NEW POST: campo newsgroups immutabile if (a === 'new') { const ng = u.get('newsgroups'); if (ng) { - fill('newsgroups', ng, true); // READONLY - non puรฒ essere modificato + fill('newsgroups', ng, true); if (typeof showNotification === 'function') { - showNotification('๐ NEW POST: Posting to ' + decodeURIComponent(ng), 'success'); + showNotification('๐ NEW POST: ' + decodeURIComponent(ng), 'success'); } } setTimeout(() => showTab('pow'), 500); } - // REPLY TO POST: 4 campi immutabili (references, newsgroups, subject) + body editabile con quote if (a === 'reply') { const ng = u.get('newsgroups'); const subj = u.get('subject'); const refs = u.get('references'); const qbody = u.get('quoted-body'); - // Riempi i campi IMMUTABILI (readonly) - if (ng) fill('newsgroups', ng, true); // READONLY - if (subj) fill('subject', subj, true); // READONLY (con Re: prefix) - if (refs) fill('references', refs, true); // READONLY + if (ng) fill('newsgroups', ng, true); + if (subj) fill('subject', subj, true); + if (refs) fill('references', refs, true); - // Riempi il body con quote Usenet (EDITABILE) if (qbody) { const origFrom = u.get('original-from'); const origDate = u.get('original-date'); - // Formato quote tipico Usenet let quotedText = ''; if (origFrom && origDate) { quotedText = 'On ' + decodeURIComponent(origDate) + ', ' + decodeURIComponent(origFrom) + ' wrote:\n\n'; } - // Aggiungi '> ' davanti ad ogni linea del messaggio originale const bodyLines = decodeURIComponent(qbody).split('\n'); for (let i = 0; i < bodyLines.length; i++) { quotedText += '> ' + bodyLines[i] + '\n'; } - quotedText += '\n\n'; // Spazio per la risposta + quotedText += '\n\n'; - // Riempi il campo messageToSign (questo รจ EDITABILE) const msgField = document.getElementById('messageToSign'); if (msgField) { msgField.value = quotedText; @@ -1299,7 +1142,7 @@ document.getElementById('fromName').addEventListener('input', function() { } if (typeof showNotification === 'function') { - showNotification('๐ฌ REPLY: References and metadata are locked', 'info'); + showNotification('๐ฌ REPLY: Metadata locked', 'info'); } setTimeout(() => showTab('pow'), 500); |
