import { expect, tap } from '@git.zone/tstest/tapbundle'; import * as plugins from './plugins.js'; import { createTestServer } from '../../helpers/server.loader.js'; import { createSmtpClient } from '../../helpers/smtp.client.js'; tap.test('CRFC-02: should comply with ESMTP extensions (RFC 1869)', async (tools) => { const testId = 'CRFC-02-esmtp-compliance'; console.log(`\n${testId}: Testing ESMTP extension compliance...`); let scenarioCount = 0; // Scenario 1: EHLO vs HELO negotiation await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing EHLO vs HELO negotiation`); const testServer = await createTestServer({ onConnection: async (socket) => { console.log(' [Server] Client connected'); socket.write('220 esmtp.example.com ESMTP Service Ready\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); console.log(` [Server] Received: ${command}`); if (command.startsWith('EHLO')) { // ESMTP response with extensions socket.write('250-esmtp.example.com Hello\r\n'); socket.write('250-SIZE 35882577\r\n'); socket.write('250-8BITMIME\r\n'); socket.write('250-STARTTLS\r\n'); socket.write('250-ENHANCEDSTATUSCODES\r\n'); socket.write('250-PIPELINING\r\n'); socket.write('250-CHUNKING\r\n'); socket.write('250-SMTPUTF8\r\n'); socket.write('250 DSN\r\n'); } else if (command.startsWith('HELO')) { // Basic SMTP response socket.write('250 esmtp.example.com\r\n'); } else if (command.startsWith('MAIL FROM:')) { // Check for ESMTP parameters if (command.includes('SIZE=')) { console.log(' [Server] SIZE parameter detected'); } if (command.includes('BODY=')) { console.log(' [Server] BODY parameter detected'); } socket.write('250 2.1.0 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { // Check for DSN parameters if (command.includes('NOTIFY=')) { console.log(' [Server] NOTIFY parameter detected'); } if (command.includes('ORCPT=')) { console.log(' [Server] ORCPT parameter detected'); } socket.write('250 2.1.5 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write('250 2.0.0 OK: Message accepted\r\n'); } else if (command === 'QUIT') { socket.write('221 2.0.0 Bye\r\n'); socket.end(); } }); } }); // Test EHLO with ESMTP client const esmtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, name: 'client.example.com' }); const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'ESMTP test', text: 'Testing ESMTP negotiation' }); const result = await esmtpClient.sendMail(email); console.log(' EHLO negotiation successful'); expect(result).toBeDefined(); expect(result.messageId).toBeDefined(); // Test fallback to HELO const basicClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, name: 'basic.example.com', disableESMTP: true // Force HELO }); const heloResult = await basicClient.sendMail(email); console.log(' HELO fallback successful'); expect(heloResult).toBeDefined(); await testServer.server.close(); })(); // Scenario 2: SIZE extension compliance await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing SIZE extension compliance`); const maxSize = 5242880; // 5MB const testServer = await createTestServer({ onConnection: async (socket) => { console.log(' [Server] Client connected'); socket.write('220 size.example.com ESMTP\r\n'); socket.on('data', (data) => { const command = data.toString().trim(); console.log(` [Server] Received: ${command}`); if (command.startsWith('EHLO')) { socket.write('250-size.example.com\r\n'); socket.write(`250-SIZE ${maxSize}\r\n`); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { // Extract SIZE parameter const sizeMatch = command.match(/SIZE=(\d+)/i); if (sizeMatch) { const declaredSize = parseInt(sizeMatch[1]); console.log(` [Server] Client declared size: ${declaredSize}`); if (declaredSize > maxSize) { socket.write(`552 5.3.4 Message size exceeds fixed maximum message size (${maxSize})\r\n`); } else { socket.write('250 OK\r\n'); } } else { socket.write('250 OK\r\n'); } } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write('250 OK\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); // Test 1: Small message const smallEmail = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'Small message', text: 'This is a small message' }); const smallResult = await smtpClient.sendMail(smallEmail); console.log(' Small message accepted'); expect(smallResult).toBeDefined(); // Test 2: Large message const largeEmail = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'Large message', text: 'X'.repeat(maxSize + 1000) // Exceed limit }); try { await smtpClient.sendMail(largeEmail); console.log(' Unexpected: Large message accepted'); } catch (error) { console.log(' Large message rejected as expected'); expect(error.message).toContain('size exceeds'); } await testServer.server.close(); })(); // Scenario 3: 8BITMIME extension await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing 8BITMIME extension`); const testServer = await createTestServer({ onConnection: async (socket) => { console.log(' [Server] Client connected'); socket.write('220 8bit.example.com ESMTP\r\n'); let bodyType = '7BIT'; socket.on('data', (data) => { const command = data.toString().trim(); console.log(` [Server] Received: ${command}`); if (command.startsWith('EHLO')) { socket.write('250-8bit.example.com\r\n'); socket.write('250-8BITMIME\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { // Check BODY parameter const bodyMatch = command.match(/BODY=(\w+)/i); if (bodyMatch) { bodyType = bodyMatch[1].toUpperCase(); console.log(` [Server] BODY type: ${bodyType}`); if (bodyType === '8BITMIME' || bodyType === '7BIT') { socket.write('250 OK\r\n'); } else { socket.write('555 5.5.4 Unsupported BODY type\r\n'); } } else { socket.write('250 OK\r\n'); } } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write(`250 OK: Message accepted (BODY=${bodyType})\r\n`); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); // Test with 8-bit content const email8bit = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'Testing 8BITMIME with UTF-8: café, naïve, 你好', text: 'Message with 8-bit characters: émojis 🎉, spéçiål çhåracters, 日本語', encoding: '8bit' }); const result = await smtpClient.sendMail(email8bit); console.log(' 8BITMIME message accepted'); expect(result).toBeDefined(); expect(result.response).toContain('BODY=8BITMIME'); await testServer.server.close(); })(); // Scenario 4: PIPELINING extension await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing PIPELINING extension`); const testServer = await createTestServer({ onConnection: async (socket) => { console.log(' [Server] Client connected'); socket.write('220 pipeline.example.com ESMTP\r\n'); let commandBuffer: string[] = []; socket.on('data', (data) => { const commands = data.toString().split('\r\n').filter(cmd => cmd.length > 0); // With pipelining, multiple commands can arrive at once if (commands.length > 1) { console.log(` [Server] Received ${commands.length} pipelined commands`); } commands.forEach(command => { console.log(` [Server] Processing: ${command}`); commandBuffer.push(command); }); // Process buffered commands while (commandBuffer.length > 0) { const command = commandBuffer.shift()!; if (command.startsWith('EHLO')) { socket.write('250-pipeline.example.com\r\n'); socket.write('250-PIPELINING\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { socket.write('250 OK: Pipelined message accepted\r\n'); } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } } }); } }); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false, pipelining: true }); // Send email with multiple recipients (tests pipelining) const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient1@example.com', 'recipient2@example.com', 'recipient3@example.com'], subject: 'Pipelining test', text: 'Testing SMTP command pipelining' }); const result = await smtpClient.sendMail(email); console.log(' Pipelined commands successful'); expect(result).toBeDefined(); expect(result.response).toContain('Pipelined'); await testServer.server.close(); })(); // Scenario 5: DSN (Delivery Status Notification) extension await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing DSN extension`); const testServer = await createTestServer({ onConnection: async (socket) => { console.log(' [Server] Client connected'); socket.write('220 dsn.example.com ESMTP\r\n'); let envid = ''; const recipients: Array<{ address: string; notify: string; orcpt: string }> = []; socket.on('data', (data) => { const command = data.toString().trim(); console.log(` [Server] Received: ${command}`); if (command.startsWith('EHLO')) { socket.write('250-dsn.example.com\r\n'); socket.write('250-DSN\r\n'); socket.write('250 OK\r\n'); } else if (command.startsWith('MAIL FROM:')) { // Check for ENVID parameter const envidMatch = command.match(/ENVID=([^\s]+)/i); if (envidMatch) { envid = envidMatch[1]; console.log(` [Server] ENVID: ${envid}`); } // Check for RET parameter const retMatch = command.match(/RET=(FULL|HDRS)/i); if (retMatch) { console.log(` [Server] RET: ${retMatch[1]}`); } socket.write('250 OK\r\n'); } else if (command.startsWith('RCPT TO:')) { const address = command.match(/<(.+)>/)?.[1] || ''; let notify = 'NEVER'; let orcpt = ''; // Check NOTIFY parameter const notifyMatch = command.match(/NOTIFY=([^\s]+)/i); if (notifyMatch) { notify = notifyMatch[1]; console.log(` [Server] NOTIFY for ${address}: ${notify}`); } // Check ORCPT parameter const orcptMatch = command.match(/ORCPT=([^\s]+)/i); if (orcptMatch) { orcpt = orcptMatch[1]; console.log(` [Server] ORCPT: ${orcpt}`); } recipients.push({ address, notify, orcpt }); socket.write('250 OK\r\n'); } else if (command === 'DATA') { socket.write('354 Start mail input\r\n'); } else if (command === '.') { const dsnInfo = envid ? ` ENVID=${envid}` : ''; socket.write(`250 OK: Message accepted with DSN${dsnInfo}\r\n`); // Reset for next message envid = ''; recipients.length = 0; } else if (command === 'QUIT') { socket.write('221 Bye\r\n'); socket.end(); } }); } }); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); // Test with DSN options const email = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['recipient@example.com'], subject: 'DSN test', text: 'Testing Delivery Status Notifications', dsn: { notify: ['SUCCESS', 'FAILURE', 'DELAY'], envid: 'unique-message-id-12345', ret: 'FULL' } }); const result = await smtpClient.sendMail(email); console.log(' DSN parameters accepted'); expect(result).toBeDefined(); expect(result.response).toContain('DSN'); await testServer.server.close(); })(); // Scenario 6: ENHANCEDSTATUSCODES extension await (async () => { scenarioCount++; console.log(`\nScenario ${scenarioCount}: Testing ENHANCEDSTATUSCODES extension`); const testServer = await createTestServer({ onConnection: async (socket) => { console.log(' [Server] Client connected'); socket.write('220 enhanced.example.com ESMTP\r\n'); let useEnhancedCodes = false; socket.on('data', (data) => { const command = data.toString().trim(); console.log(` [Server] Received: ${command}`); if (command.startsWith('EHLO')) { socket.write('250-enhanced.example.com\r\n'); socket.write('250-ENHANCEDSTATUSCODES\r\n'); socket.write('250 2.0.0 OK\r\n'); useEnhancedCodes = true; } else if (command.startsWith('HELO')) { socket.write('250 enhanced.example.com\r\n'); useEnhancedCodes = false; } else if (command.startsWith('MAIL FROM:')) { if (useEnhancedCodes) { socket.write('250 2.1.0 Sender OK\r\n'); } else { socket.write('250 Sender OK\r\n'); } } else if (command.startsWith('RCPT TO:')) { const address = command.match(/<(.+)>/)?.[1] || ''; if (address.includes('unknown')) { if (useEnhancedCodes) { socket.write('550 5.1.1 User unknown\r\n'); } else { socket.write('550 User unknown\r\n'); } } else { if (useEnhancedCodes) { socket.write('250 2.1.5 Recipient OK\r\n'); } else { socket.write('250 Recipient OK\r\n'); } } } else if (command === 'DATA') { if (useEnhancedCodes) { socket.write('354 2.0.0 Start mail input\r\n'); } else { socket.write('354 Start mail input\r\n'); } } else if (command === '.') { if (useEnhancedCodes) { socket.write('250 2.0.0 Message accepted for delivery\r\n'); } else { socket.write('250 Message accepted for delivery\r\n'); } } else if (command === 'QUIT') { if (useEnhancedCodes) { socket.write('221 2.0.0 Service closing transmission channel\r\n'); } else { socket.write('221 Service closing transmission channel\r\n'); } socket.end(); } }); } }); const smtpClient = createSmtpClient({ host: testServer.hostname, port: testServer.port, secure: false }); // Test with valid recipient const validEmail = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['valid@example.com'], subject: 'Enhanced status codes test', text: 'Testing enhanced SMTP status codes' }); const result = await smtpClient.sendMail(validEmail); console.log(' Enhanced status codes received'); expect(result).toBeDefined(); expect(result.response).toMatch(/2\.\d\.\d/); // Enhanced code format // Test with invalid recipient const invalidEmail = new plugins.smartmail.Email({ from: 'sender@example.com', to: ['unknown@example.com'], subject: 'Invalid recipient test', text: 'Testing enhanced error codes' }); try { await smtpClient.sendMail(invalidEmail); console.log(' Unexpected: Invalid recipient accepted'); } catch (error) { console.log(' Enhanced error code received'); expect(error.responseCode).toBe(550); expect(error.response).toContain('5.1.1'); } await testServer.server.close(); })(); console.log(`\n${testId}: All ${scenarioCount} ESMTP compliance scenarios tested ✓`); });