/** * SMTP Helper Functions * Provides utility functions for SMTP server implementation */ import * as plugins from '../../../../plugins.js'; import { SMTP_DEFAULTS } from '../constants.js'; /** * Formats a multi-line SMTP response according to RFC 5321 * @param code - Response code * @param lines - Response lines * @returns Formatted SMTP response */ export function formatMultilineResponse(code, lines) { if (!lines || lines.length === 0) { return `${code} `; } if (lines.length === 1) { return `${code} ${lines[0]}`; } let response = ''; for (let i = 0; i < lines.length - 1; i++) { response += `${code}-${lines[i]}${SMTP_DEFAULTS.CRLF}`; } response += `${code} ${lines[lines.length - 1]}`; return response; } /** * Generates a unique session ID * @returns Unique session ID */ export function generateSessionId() { return `${Date.now()}-${Math.floor(Math.random() * 10000)}`; } /** * Safely parses an integer from string with a default value * @param value - String value to parse * @param defaultValue - Default value if parsing fails * @returns Parsed integer or default value */ export function safeParseInt(value, defaultValue) { if (!value) { return defaultValue; } const parsed = parseInt(value, 10); return isNaN(parsed) ? defaultValue : parsed; } /** * Safely gets the socket details * @param socket - Socket to get details from * @returns Socket details object */ export function getSocketDetails(socket) { return { remoteAddress: socket.remoteAddress || 'unknown', remotePort: socket.remotePort || 0, remoteFamily: socket.remoteFamily || 'unknown', localAddress: socket.localAddress || 'unknown', localPort: socket.localPort || 0, encrypted: socket instanceof plugins.tls.TLSSocket }; } /** * Gets TLS details if socket is TLS * @param socket - Socket to get TLS details from * @returns TLS details or undefined if not TLS */ export function getTlsDetails(socket) { if (!(socket instanceof plugins.tls.TLSSocket)) { return undefined; } return { protocol: socket.getProtocol(), cipher: socket.getCipher()?.name, authorized: socket.authorized }; } /** * Merges default options with provided options * @param options - User provided options * @returns Merged options with defaults */ export function mergeWithDefaults(options) { return { port: options.port || SMTP_DEFAULTS.SMTP_PORT, key: options.key || '', cert: options.cert || '', hostname: options.hostname || SMTP_DEFAULTS.HOSTNAME, host: options.host, securePort: options.securePort, ca: options.ca, maxSize: options.size || SMTP_DEFAULTS.MAX_MESSAGE_SIZE, maxConnections: options.maxConnections || SMTP_DEFAULTS.MAX_CONNECTIONS, socketTimeout: options.socketTimeout || SMTP_DEFAULTS.SOCKET_TIMEOUT, connectionTimeout: options.connectionTimeout || SMTP_DEFAULTS.CONNECTION_TIMEOUT, cleanupInterval: options.cleanupInterval || SMTP_DEFAULTS.CLEANUP_INTERVAL, maxRecipients: options.maxRecipients || SMTP_DEFAULTS.MAX_RECIPIENTS, size: options.size || SMTP_DEFAULTS.MAX_MESSAGE_SIZE, dataTimeout: options.dataTimeout || SMTP_DEFAULTS.DATA_TIMEOUT, auth: options.auth, }; } /** * Creates a text response formatter for the SMTP server * @param socket - Socket to send responses to * @returns Function to send formatted response */ export function createResponseFormatter(socket) { return (response) => { try { socket.write(`${response}${SMTP_DEFAULTS.CRLF}`); console.log(`→ ${response}`); } catch (error) { console.error(`Error sending response: ${error instanceof Error ? error.message : String(error)}`); socket.destroy(); } }; } /** * Extracts SMTP command name from a command line * @param commandLine - Full command line * @returns Command name in uppercase */ export function extractCommandName(commandLine) { if (!commandLine || typeof commandLine !== 'string') { return ''; } // Handle specific command patterns first const ehloMatch = commandLine.match(/^(EHLO|HELO)\b/i); if (ehloMatch) { return ehloMatch[1].toUpperCase(); } const mailMatch = commandLine.match(/^MAIL\b/i); if (mailMatch) { return 'MAIL'; } const rcptMatch = commandLine.match(/^RCPT\b/i); if (rcptMatch) { return 'RCPT'; } // Default handling const parts = commandLine.trim().split(/\s+/); return (parts[0] || '').toUpperCase(); } /** * Extracts SMTP command arguments from a command line * @param commandLine - Full command line * @returns Arguments string */ export function extractCommandArgs(commandLine) { if (!commandLine || typeof commandLine !== 'string') { return ''; } const command = extractCommandName(commandLine); if (!command) { return commandLine.trim(); } // Special handling for specific commands if (command === 'EHLO' || command === 'HELO') { const match = commandLine.match(/^(?:EHLO|HELO)\s+(.+)$/i); return match ? match[1].trim() : ''; } if (command === 'MAIL') { return commandLine.replace(/^MAIL\s+/i, ''); } if (command === 'RCPT') { return commandLine.replace(/^RCPT\s+/i, ''); } // Default extraction const firstSpace = commandLine.indexOf(' '); if (firstSpace === -1) { return ''; } return commandLine.substring(firstSpace + 1).trim(); } /** * Sanitizes data for logging (hides sensitive info) * @param data - Data to sanitize * @returns Sanitized data */ export function sanitizeForLogging(data) { if (!data) { return data; } if (typeof data !== 'object') { return data; } const result = Array.isArray(data) ? [] : {}; for (const key in data) { if (Object.prototype.hasOwnProperty.call(data, key)) { // Sanitize sensitive fields if (key.toLowerCase().includes('password') || key.toLowerCase().includes('token') || key.toLowerCase().includes('secret') || key.toLowerCase().includes('credential')) { result[key] = '********'; } else if (typeof data[key] === 'object' && data[key] !== null) { result[key] = sanitizeForLogging(data[key]); } else { result[key] = data[key]; } } } return result; } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvc210cHNlcnZlci91dGlscy9oZWxwZXJzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sS0FBSyxPQUFPLE1BQU0sd0JBQXdCLENBQUM7QUFDbEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBR2hEOzs7OztHQUtHO0FBQ0gsTUFBTSxVQUFVLHVCQUF1QixDQUFDLElBQVksRUFBRSxLQUFlO0lBQ25FLElBQUksQ0FBQyxLQUFLLElBQUksS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUNqQyxPQUFPLEdBQUcsSUFBSSxHQUFHLENBQUM7SUFDcEIsQ0FBQztJQUVELElBQUksS0FBSyxDQUFDLE1BQU0sS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN2QixPQUFPLEdBQUcsSUFBSSxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0lBQy9CLENBQUM7SUFFRCxJQUFJLFFBQVEsR0FBRyxFQUFFLENBQUM7SUFDbEIsS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLEtBQUssQ0FBQyxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUM7UUFDMUMsUUFBUSxJQUFJLEdBQUcsSUFBSSxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUMsR0FBRyxhQUFhLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDekQsQ0FBQztJQUNELFFBQVEsSUFBSSxHQUFHLElBQUksSUFBSSxLQUFLLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDO0lBRWpELE9BQU8sUUFBUSxDQUFDO0FBQ2xCLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLFVBQVUsaUJBQWlCO0lBQy9CLE9BQU8sR0FBRyxJQUFJLENBQUMs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