/** * SMTP Client Command Handler * SMTP command sending and response parsing */ import { EventEmitter } from 'node:events'; import { SMTP_COMMANDS, SMTP_CODES, LINE_ENDINGS } from './constants.js'; import { parseSmtpResponse, parseEhloResponse, formatCommand, isSuccessCode } from './utils/helpers.js'; import { logCommand, logDebug } from './utils/logging.js'; export class CommandHandler extends EventEmitter { options; responseBuffer = ''; pendingCommand = null; commandTimeout = null; constructor(options) { super(); this.options = options; } /** * Send EHLO command and parse capabilities */ async sendEhlo(connection, domain) { const hostname = domain || this.options.domain || 'localhost'; const command = `${SMTP_COMMANDS.EHLO} ${hostname}`; const response = await this.sendCommand(connection, command); if (!isSuccessCode(response.code)) { throw new Error(`EHLO failed: ${response.message}`); } const capabilities = parseEhloResponse(response.raw); connection.capabilities = capabilities; logDebug('EHLO capabilities parsed', this.options, { capabilities }); return capabilities; } /** * Send MAIL FROM command */ async sendMailFrom(connection, fromAddress) { // Handle empty return path for bounce messages const command = fromAddress === '' ? `${SMTP_COMMANDS.MAIL_FROM}:<>` : `${SMTP_COMMANDS.MAIL_FROM}:<${fromAddress}>`; return this.sendCommand(connection, command); } /** * Send RCPT TO command */ async sendRcptTo(connection, toAddress) { const command = `${SMTP_COMMANDS.RCPT_TO}:<${toAddress}>`; return this.sendCommand(connection, command); } /** * Send DATA command */ async sendData(connection) { return this.sendCommand(connection, SMTP_COMMANDS.DATA); } /** * Send email data content */ async sendDataContent(connection, emailData) { // Normalize line endings to CRLF let data = emailData.replace(/\r\n/g, '\n').replace(/\r/g, '\n').replace(/\n/g, '\r\n'); // Ensure email data ends with CRLF if (!data.endsWith(LINE_ENDINGS.CRLF)) { data += LINE_ENDINGS.CRLF; } // Perform dot stuffing (escape lines starting with a dot) data = data.replace(/\r\n\./g, '\r\n..'); // Add termination sequence data += '.' + LINE_ENDINGS.CRLF; return this.sendRawData(connection, data); } /** * Send RSET command */ async sendRset(connection) { return this.sendCommand(connection, SMTP_COMMANDS.RSET); } /** * Send NOOP command */ async sendNoop(connection) { return this.sendCommand(connection, SMTP_COMMANDS.NOOP); } /** * Send QUIT command */ async sendQuit(connection) { return this.sendCommand(connection, SMTP_COMMANDS.QUIT); } /** * Send STARTTLS command */ async sendStartTls(connection) { return this.sendCommand(connection, SMTP_COMMANDS.STARTTLS); } /** * Send AUTH command */ async sendAuth(connection, method, credentials) { const command = credentials ? `${SMTP_COMMANDS.AUTH} ${method} ${credentials}` : `${SMTP_COMMANDS.AUTH} ${method}`; return this.sendCommand(connection, command); } /** * Send a generic SMTP command */ async sendCommand(connection, command) { return new Promise((resolve, reject) => { if (this.pendingCommand) { reject(new Error('Another command is already pending')); return; } this.pendingCommand = { resolve, reject, command }; // Set command timeout const timeout = 30000; // 30 seconds this.commandTimeout = setTimeout(() => { this.pendingCommand = null; this.commandTimeout = null; reject(new Error(`Command timeout: ${command}`)); }, timeout); // Set up data handler const dataHandler = (data) => { this.handleIncomingData(data.toString()); }; connection.socket.on('data', dataHandler); // Clean up function const cleanup = () => { connection.socket.removeListener('data', dataHandler); if (this.commandTimeout) { clearTimeout(this.commandTimeout); this.commandTimeout = null; } }; // Send command const formattedCommand = command.endsWith(LINE_ENDINGS.CRLF) ? command : formatCommand(command); logCommand(command, undefined, this.options); logDebug(`Sending command: ${command}`, this.options); connection.socket.write(formattedCommand, (error) => { if (error) { cleanup(); this.pendingCommand = null; reject(error); } }); // Override resolve/reject to include cleanup const originalResolve = resolve; const originalReject = reject; this.pendingCommand.resolve = (response) => { cleanup(); this.pendingCommand = null; logCommand(command, response, this.options); originalResolve(response); }; this.pendingCommand.reject = (error) => { cleanup(); this.pendingCommand = null; originalReject(error); }; }); } /** * Send raw data without command formatting */ async sendRawData(connection, data) { return new Promise((resolve, reject) => { if (this.pendingCommand) { reject(new Error('Another command is already pending')); return; } this.pendingCommand = { resolve, reject, command: 'DATA_CONTENT' }; // Set data timeout const timeout = 60000; // 60 seconds for data this.commandTimeout = setTimeout(() => { this.pendingCommand = null; this.commandTimeout = null; reject(new Error('Data transmission timeout')); }, timeout); // Set up data handler const dataHandler = (chunk) => { this.handleIncomingData(chunk.toString()); }; connection.socket.on('data', dataHandler); // Clean up function const cleanup = () => { connection.socket.removeListener('data', dataHandler); if (this.commandTimeout) { clearTimeout(this.commandTimeout); this.commandTimeout = null; } }; // Override resolve/reject to include cleanup const originalResolve = resolve; const originalReject = reject; this.pendingCommand.resolve = (response) => { cleanup(); this.pendingCommand = null; originalResolve(response); }; this.pendingCommand.reject = (error) => { cleanup(); this.pendingCommand = null; originalReject(error); }; // Send data connection.socket.write(data, (error) => { if (error) { cleanup(); this.pendingCommand = null; reject(error); } }); }); } /** * Wait for server greeting */ async waitForGreeting(connection) { return new Promise((resolve, reject) => { const timeout = 30000; // 30 seconds let timeoutHandler; const dataHandler = (data) => { this.responseBuffer += data.toString(); if (this.isCompleteResponse(this.responseBuffer)) { clearTimeout(timeoutHandler); connection.socket.removeListener('data', dataHandler); const response = parseSmtpResponse(this.responseBuffer); this.responseBuffer = ''; if (isSuccessCode(response.code)) { resolve(response); } else { reject(new Error(`Server greeting failed: ${response.message}`)); } } }; timeoutHandler = setTimeout(() => { connection.socket.removeListener('data', dataHandler); reject(new Error('Greeting timeout')); }, timeout); connection.socket.on('data', dataHandler); }); } handleIncomingData(data) { if (!this.pendingCommand) { return; } this.responseBuffer += data; if (this.isCompleteResponse(this.responseBuffer)) { const response = parseSmtpResponse(this.responseBuffer); this.responseBuffer = ''; if (isSuccessCode(response.code) || (response.code >= 300 && response.code < 400) || response.code >= 400) { this.pendingCommand.resolve(response); } else { this.pendingCommand.reject(new Error(`Command failed: ${response.message}`)); } } } isCompleteResponse(buffer) { // Check if we have a complete response const lines = buffer.split(/\r?\n/); if (lines.length < 1) { return false; } // Check the last non-empty line for (let i = lines.length - 1; i >= 0; i--) { const line = lines[i].trim(); if (line.length > 0) { // Response is complete if line starts with "XXX " (space after code) return /^\d{3} /.test(line); } } return false; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tbWFuZC1oYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vdHMvbWFpbC9kZWxpdmVyeS9zbXRwY2xpZW50L2NvbW1hbmQtaGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFFSCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQzNDLE9BQU8sRUFBRSxhQUFhLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBT3pFLE9BQU8sRUFDTCxpQkFBaUIsRUFDakIsaUJBQWlCLEVBQ2pCLGFBQWEsRUFDYixhQUFhLEVBQ2QsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QixPQUFPLEVBQUUsVUFBVSxFQUFFLFFBQVEsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBRTFELE1BQU0sT0FBTyxjQUFlLFNBQVEsWUFBWTtJQUN0QyxPQUFPLENBQXFCO0lBQzVCLGNBQWMsR0FBVyxFQUFFLENBQUM7SUFDNUIsY0FBYyxHQUFvRSxJQUFJLENBQUM7SUFDdkYsY0FBYyxHQUEwQixJQUFJLENBQUM7SUFFckQsWUFBWSxPQUEyQjtRQUNyQyxLQUFLLEVBQUUsQ0FBQztRQUNSLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO0lBQ3pCLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxRQUFRLENBQUMsVUFBMkIsRUFBRSxNQUFlO1FBQ2hFLE1BQU0sUUFBUSxHQUFHLE1BQU0sSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sSUFBSSxXQUFXLENBQUM7UUFDOUQsTUFBTSxPQUFPLEdBQUcsR0FBRyxhQUFhLENBQUMsSUFBSSxJQUFJLFFBQVEsRUFBRSxDQUFDO1FBRXBELE1BQU0sUUFBUSxHQUFHLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxVQUFVLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFFN0QsSUFBSSxDQUFDLGFBQWEsQ0FBQyxRQUFRLENB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