import * as plugins from '../../plugins.js'; import * as paths from '../../paths.js'; import { Email } from '../core/classes.email.js'; import type { UnifiedEmailServer } from '../routing/classes.unified.email.server.js'; import { logger } from '../../logger.js'; import { SecurityLogger, SecurityLogLevel, SecurityEventType, IPReputationChecker, ReputationThreshold } from '../../security/index.js'; import type { ISmtpServerOptions, ISmtpSession, EmailProcessingMode } from './interfaces.js'; import { SmtpState } from './interfaces.js'; // Import refactored SMTP server components import { SmtpServer, createSmtpServer, type ISmtpServer } from './smtpserver/index.js'; /** * Legacy SMTP Server implementation that uses the refactored modular version * Maintains the original API for backward compatibility */ export class SMTPServer { // Public properties used by existing code public emailServerRef: UnifiedEmailServer; // Protected properties for test access protected server: plugins.net.Server; protected secureServer?: plugins.tls.Server; // Original properties maintained for compatibility private smtpServerOptions: ISmtpServerOptions; private sessions: Map; private sessionTimeouts: Map; private hostname: string; private sessionIdCounter: number = 0; private connectionCount: number = 0; private maxConnections: number = 100; private cleanupInterval?: NodeJS.Timeout; // New refactored server implementation private smtpServerImpl: ISmtpServer; constructor(emailServerRefArg: UnifiedEmailServer, optionsArg: ISmtpServerOptions) { console.log('SMTPServer instance is being created (using refactored implementation)...'); // Store original arguments and properties for backward compatibility this.emailServerRef = emailServerRefArg; this.smtpServerOptions = optionsArg; this.sessions = new Map(); this.sessionTimeouts = new Map(); this.hostname = optionsArg.hostname || 'mail.lossless.one'; this.maxConnections = optionsArg.maxConnections || 100; // Log enhanced server configuration const socketTimeout = optionsArg.socketTimeout || 300000; const connectionTimeout = optionsArg.connectionTimeout || 30000; const cleanupFrequency = optionsArg.cleanupInterval || 5000; logger.log('info', 'SMTP server configuration', { hostname: this.hostname, maxConnections: this.maxConnections, socketTimeout: socketTimeout, connectionTimeout: connectionTimeout, cleanupInterval: cleanupFrequency, tlsEnabled: !!(optionsArg.key && optionsArg.cert), starttlsEnabled: !!(optionsArg.key && optionsArg.cert), securePort: optionsArg.securePort }); // Create the refactored SMTP server implementation this.smtpServerImpl = createSmtpServer(emailServerRefArg, optionsArg); // Initialize server properties to support existing test code // These will be properly set during the listen() call this.server = new plugins.net.Server(); if (optionsArg.key && optionsArg.cert) { this.secureServer = new plugins.tls.Server({ key: optionsArg.key, cert: optionsArg.cert, ca: optionsArg.ca }); } // Set up session events to maintain legacy behavior const sessionManager = this.smtpServerImpl.getSessionManager(); // Track sessions for backward compatibility sessionManager.on('created', (session, socket) => { this.sessions.set(socket, session); this.connectionCount++; }); sessionManager.on('completed', (session, socket) => { this.sessions.delete(socket); this.connectionCount--; }); } /** * Start the SMTP server and listen on the specified port * @returns A promise that resolves when the server is listening */ public listen(): Promise { return new Promise((resolve, reject) => { this.smtpServerImpl.listen() .then(() => { // Get created servers for test compatibility // Get the actual server instances for backward compatibility const netServer = (this.smtpServerImpl as any).server; if (netServer) { this.server = netServer; } const tlsServer = (this.smtpServerImpl as any).secureServer; if (tlsServer) { this.secureServer = tlsServer; } resolve(); }) .catch(err => { logger.log('error', `Failed to start SMTP server: ${err.message}`, { stack: err.stack }); reject(err); }); }); } /** * Stop the SMTP server * @returns A promise that resolves when the server has stopped */ public close(): Promise { return new Promise((resolve, reject) => { this.smtpServerImpl.close() .then(() => { // Clean up legacy resources this.sessions.clear(); for (const timeoutId of this.sessionTimeouts.values()) { clearTimeout(timeoutId); } this.sessionTimeouts.clear(); if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = undefined; } resolve(); }) .catch(err => { logger.log('error', `Failed to stop SMTP server: ${err.message}`, { stack: err.stack }); reject(err); }); }); } /** * @deprecated Use the refactored implementation directly * Maintained for backward compatibility */ private handleNewConnection(socket: plugins.net.Socket): void { logger.log('warn', 'Using deprecated handleNewConnection method'); } /** * @deprecated Use the refactored implementation directly * Maintained for backward compatibility */ private handleNewSecureConnection(socket: plugins.tls.TLSSocket): void { logger.log('warn', 'Using deprecated handleNewSecureConnection method'); } /** * @deprecated Use the refactored implementation directly * Maintained for backward compatibility */ private cleanupIdleSessions(): void { // This is now handled by the session manager in the refactored implementation } /** * @deprecated Use the refactored implementation directly * Maintained for backward compatibility */ private generateSessionId(): string { return `${Date.now()}-${++this.sessionIdCounter}`; } /** * @deprecated Use the refactored implementation directly * Maintained for backward compatibility */ private removeSession(socket: plugins.net.Socket | plugins.tls.TLSSocket): void { // This is now handled by the session manager in the refactored implementation } /** * @deprecated Use the refactored implementation directly * Maintained for backward compatibility */ private processCommand(socket: plugins.net.Socket | plugins.tls.TLSSocket, line: string): void { // This is now handled by the command handler in the refactored implementation } /** * @deprecated Use the refactored implementation directly * Maintained for backward compatibility */ private handleDataChunk(socket: plugins.net.Socket | plugins.tls.TLSSocket, chunk: string): void { // This is now handled by the data handler in the refactored implementation } /** * @deprecated Use the refactored implementation directly * Maintained for backward compatibility */ private sendResponse(socket: plugins.net.Socket | plugins.tls.TLSSocket, response: string): void { try { socket.write(`${response}\r\n`); } catch (error) { logger.log('error', `Error sending response: ${error instanceof Error ? error.message : 'Unknown error'}`, { response, remoteAddress: socket.remoteAddress, remotePort: socket.remotePort }); } } /** * Get the current active connection count * @returns Number of active connections */ public getConnectionCount(): number { return this.connectionCount; } /** * Get the refactored SMTP server implementation * This provides access to the new implementation for future use */ public getSmtpServerImpl(): ISmtpServer { return this.smtpServerImpl; } }