/** * SMTP Client Core Implementation * Main client class with delegation to handlers */ import { EventEmitter } from 'node:events'; import { CONNECTION_STATES, SmtpErrorType } from './constants.js'; import { validateSender, validateRecipients } from './utils/validation.js'; import { logEmailSend, logPerformance, logDebug } from './utils/logging.js'; export class SmtpClient extends EventEmitter { options; connectionManager; commandHandler; authHandler; tlsHandler; errorHandler; isShuttingDown = false; constructor(dependencies) { super(); this.options = dependencies.options; this.connectionManager = dependencies.connectionManager; this.commandHandler = dependencies.commandHandler; this.authHandler = dependencies.authHandler; this.tlsHandler = dependencies.tlsHandler; this.errorHandler = dependencies.errorHandler; this.setupEventForwarding(); } /** * Send an email */ async sendMail(email) { const startTime = Date.now(); // Extract clean email addresses without display names for SMTP operations const fromAddress = email.getFromAddress(); const recipients = email.getToAddresses(); const ccRecipients = email.getCcAddresses(); const bccRecipients = email.getBccAddresses(); // Combine all recipients for SMTP operations const allRecipients = [...recipients, ...ccRecipients, ...bccRecipients]; // Validate email addresses if (!validateSender(fromAddress)) { throw new Error(`Invalid sender address: ${fromAddress}`); } const recipientErrors = validateRecipients(allRecipients); if (recipientErrors.length > 0) { throw new Error(`Invalid recipients: ${recipientErrors.join(', ')}`); } logEmailSend('start', allRecipients, this.options); let connection = null; const result = { success: false, acceptedRecipients: [], rejectedRecipients: [], envelope: { from: fromAddress, to: allRecipients } }; try { // Get connection connection = await this.connectionManager.getConnection(); connection.state = CONNECTION_STATES.BUSY; // Wait for greeting if new connection if (!connection.capabilities) { await this.commandHandler.waitForGreeting(connection); } // Perform EHLO await this.commandHandler.sendEhlo(connection, this.options.domain); // Upgrade to TLS if needed if (this.tlsHandler.shouldUseTLS(connection)) { await this.tlsHandler.upgradeToTLS(connection); // Re-send EHLO after TLS upgrade await this.commandHandler.sendEhlo(connection, this.options.domain); } // Authenticate if needed if (this.options.auth) { await this.authHandler.authenticate(connection); } // Send MAIL FROM const mailFromResponse = await this.commandHandler.sendMailFrom(connection, fromAddress); if (mailFromResponse.code >= 400) { throw new Error(`MAIL FROM failed: ${mailFromResponse.message}`); } // Send RCPT TO for each recipient (includes TO, CC, and BCC) for (const recipient of allRecipients) { try { const rcptResponse = await this.commandHandler.sendRcptTo(connection, recipient); if (rcptResponse.code >= 400) { result.rejectedRecipients.push(recipient); logDebug(`Recipient rejected: ${recipient}`, this.options, { response: rcptResponse }); } else { result.acceptedRecipients.push(recipient); } } catch (error) { result.rejectedRecipients.push(recipient); logDebug(`Recipient error: ${recipient}`, this.options, { error }); } } // Check if we have any accepted recipients if (result.acceptedRecipients.length === 0) { throw new Error('All recipients were rejected'); } // Send DATA command const dataResponse = await this.commandHandler.sendData(connection); if (dataResponse.code !== 354) { throw new Error(`DATA command failed: ${dataResponse.message}`); } // Send email content const emailData = await this.formatEmailData(email); const sendResponse = await this.commandHandler.sendDataContent(connection, emailData); if (sendResponse.code >= 400) { throw new Error(`Email data rejected: ${sendResponse.message}`); } // Success result.success = true; result.messageId = this.extractMessageId(sendResponse.message); result.response = sendResponse.message; connection.messageCount++; logEmailSend('success', recipients, this.options, { messageId: result.messageId, duration: Date.now() - startTime }); } catch (error) { result.success = false; result.error = error instanceof Error ? error : new Error(String(error)); // Classify error and determine if we should retry const errorType = this.errorHandler.classifyError(result.error); result.error = this.errorHandler.createError(result.error.message, errorType, { command: 'SEND_MAIL' }, result.error); logEmailSend('failure', recipients, this.options, { error: result.error, duration: Date.now() - startTime }); } finally { // Release connection if (connection) { connection.state = CONNECTION_STATES.READY; this.connectionManager.updateActivity(connection); this.connectionManager.releaseConnection(connection); } logPerformance('sendMail', Date.now() - startTime, this.options); } return result; } /** * Test connection to SMTP server */ async verify() { let connection = null; try { connection = await this.connectionManager.createConnection(); await this.commandHandler.waitForGreeting(connection); await this.commandHandler.sendEhlo(connection, this.options.domain); if (this.tlsHandler.shouldUseTLS(connection)) { await this.tlsHandler.upgradeToTLS(connection); await this.commandHandler.sendEhlo(connection, this.options.domain); } if (this.options.auth) { await this.authHandler.authenticate(connection); } await this.commandHandler.sendQuit(connection); return true; } catch (error) { logDebug('Connection verification failed', this.options, { error }); return false; } finally { if (connection) { this.connectionManager.closeConnection(connection); } } } /** * Check if client is connected */ isConnected() { const status = this.connectionManager.getPoolStatus(); return status.total > 0; } /** * Get connection pool status */ getPoolStatus() { return this.connectionManager.getPoolStatus(); } /** * Update client options */ updateOptions(newOptions) { this.options = { ...this.options, ...newOptions }; logDebug('Client options updated', this.options); } /** * Close all connections and shutdown client */ async close() { if (this.isShuttingDown) { return; } this.isShuttingDown = true; logDebug('Shutting down SMTP client', this.options); try { this.connectionManager.closeAllConnections(); this.emit('close'); } catch (error) { logDebug('Error during client shutdown', this.options, { error }); } } async formatEmailData(email) { // Convert Email object to raw SMTP data const headers = []; // Required headers headers.push(`From: ${email.from}`); headers.push(`To: ${Array.isArray(email.to) ? email.to.join(', ') : email.to}`); headers.push(`Subject: ${email.subject || ''}`); headers.push(`Date: ${new Date().toUTCString()}`); headers.push(`Message-ID: <${Date.now()}.${Math.random().toString(36)}@${this.options.host}>`); // Optional headers if (email.cc) { const cc = Array.isArray(email.cc) ? email.cc.join(', ') : email.cc; headers.push(`Cc: ${cc}`); } if (email.bcc) { const bcc = Array.isArray(email.bcc) ? email.bcc.join(', ') : email.bcc; headers.push(`Bcc: ${bcc}`); } // Content headers if (email.html && email.text) { // Multipart message const boundary = `boundary_${Date.now()}_${Math.random().toString(36)}`; headers.push(`Content-Type: multipart/alternative; boundary="${boundary}"`); headers.push('MIME-Version: 1.0'); const body = [ `--${boundary}`, 'Content-Type: text/plain; charset=utf-8', 'Content-Transfer-Encoding: quoted-printable', '', email.text, '', `--${boundary}`, 'Content-Type: text/html; charset=utf-8', 'Content-Transfer-Encoding: quoted-printable', '', email.html, '', `--${boundary}--` ].join('\r\n'); return headers.join('\r\n') + '\r\n\r\n' + body; } else if (email.html) { headers.push('Content-Type: text/html; charset=utf-8'); headers.push('MIME-Version: 1.0'); return headers.join('\r\n') + '\r\n\r\n' + email.html; } else { headers.push('Content-Type: text/plain; charset=utf-8'); headers.push('MIME-Version: 1.0'); return headers.join('\r\n') + '\r\n\r\n' + (email.text || ''); } } extractMessageId(response) { // Try to extract message ID from server response const match = response.match(/queued as ([^\s]+)/i) || response.match(/id=([^\s]+)/i) || response.match(/Message-ID: <([^>]+)>/i); return match ? match[1] : undefined; } setupEventForwarding() { // Forward events from connection manager this.connectionManager.on('connection', (connection) => { this.emit('connection', connection); }); this.connectionManager.on('disconnect', (connection) => { this.emit('disconnect', connection); }); this.connectionManager.on('error', (error) => { this.emit('error', error); }); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic210cC1jbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90cy9tYWlsL2RlbGl2ZXJ5L3NtdHBjbGllbnQvc210cC1jbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGFBQWEsQ0FBQztBQVMzQyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsYUFBYSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFNbEUsT0FBTyxFQUFFLGNBQWMsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQzNFLE9BQU8sRUFBRSxZQUFZLEVBQUUsY0FBYyxFQUFFLFFBQVEsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBVzVFLE1BQU0sT0FBTyxVQUFXLFNBQVEsWUFBWTtJQUNsQyxPQUFPLENBQXFCO0lBQzVCLGlCQUFpQixDQUFvQjtJQUNyQyxjQUFjLENBQWlCO0lBQy9CLFdBQVcsQ0FBYztJQUN6QixVQUFVLENBQWE7SUFDdkIsWUFBWSxDQUFtQjtJQUMvQixjQUFjLEdBQVksS0FBSyxDQUFDO0lBRXhDLFlBQVksWUFBcUM7UUFDL0MsS0FBSyxFQUFFLENBQUM7UUFFUixJQUFJLENBQUMsT0FBTyxHQUFHLFlBQVksQ0FBQyxPQUFPLENBQUM7UUFDcEMsSUFBSSxDQUFDLGlCQUFpQixHQUFHLFlBQVksQ0FBQyxpQkFBaUIsQ0FBQztRQUN4RCxJQUFJLENBQUMsY0FBYyxHQUFHLFlBQVksQ0FBQyxjQUFjLENBQUM7UUFDbEQsSUFBSSxDQUFDLFdBQVcsR0FBRyxZQUFZLENBQUMsV0FBVyxDQUFDO1FBQzVDLElBQUksQ0FBQyxVQUFVLEdBQUcsWUFBWSxDQUFDLFVBQVUsQ0FBQztRQUMxQyxJQUFJLENBQUMsWUFBWSxHQUFHLFlBQVksQ0FBQyxZQUFZLENBQUM7UUFFOUMsSUFBSSxDQUFDLG9CQUFvQixFQUFFLENBQUM7SUFDOUIsQ0FBQztJQUVEOztPQUVH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