698 lines
55 KiB
JavaScript
698 lines
55 KiB
JavaScript
/**
|
|
* SMTP Server
|
|
* Core implementation for the refactored SMTP server
|
|
*/
|
|
import * as plugins from '../../../plugins.js';
|
|
import { SmtpState } from './interfaces.js';
|
|
import { SessionManager } from './session-manager.js';
|
|
import { ConnectionManager } from './connection-manager.js';
|
|
import { CommandHandler } from './command-handler.js';
|
|
import { DataHandler } from './data-handler.js';
|
|
import { TlsHandler } from './tls-handler.js';
|
|
import { SecurityHandler } from './security-handler.js';
|
|
import { SMTP_DEFAULTS } from './constants.js';
|
|
import { mergeWithDefaults } from './utils/helpers.js';
|
|
import { SmtpLogger } from './utils/logging.js';
|
|
import { adaptiveLogger } from './utils/adaptive-logging.js';
|
|
import { UnifiedEmailServer } from '../../routing/classes.unified.email.server.js';
|
|
/**
|
|
* SMTP Server implementation
|
|
* The main server class that coordinates all components
|
|
*/
|
|
export class SmtpServer {
|
|
/**
|
|
* Email server reference
|
|
*/
|
|
emailServer;
|
|
/**
|
|
* Session manager
|
|
*/
|
|
sessionManager;
|
|
/**
|
|
* Connection manager
|
|
*/
|
|
connectionManager;
|
|
/**
|
|
* Command handler
|
|
*/
|
|
commandHandler;
|
|
/**
|
|
* Data handler
|
|
*/
|
|
dataHandler;
|
|
/**
|
|
* TLS handler
|
|
*/
|
|
tlsHandler;
|
|
/**
|
|
* Security handler
|
|
*/
|
|
securityHandler;
|
|
/**
|
|
* SMTP server options
|
|
*/
|
|
options;
|
|
/**
|
|
* Net server instance
|
|
*/
|
|
server = null;
|
|
/**
|
|
* Secure server instance
|
|
*/
|
|
secureServer = null;
|
|
/**
|
|
* Whether the server is running
|
|
*/
|
|
running = false;
|
|
/**
|
|
* Server recovery state
|
|
*/
|
|
recoveryState = {
|
|
/**
|
|
* Whether recovery is in progress
|
|
*/
|
|
recovering: false,
|
|
/**
|
|
* Number of consecutive connection failures
|
|
*/
|
|
connectionFailures: 0,
|
|
/**
|
|
* Last recovery attempt timestamp
|
|
*/
|
|
lastRecoveryAttempt: 0,
|
|
/**
|
|
* Recovery cooldown in milliseconds
|
|
*/
|
|
recoveryCooldown: 5000,
|
|
/**
|
|
* Maximum recovery attempts before giving up
|
|
*/
|
|
maxRecoveryAttempts: 3,
|
|
/**
|
|
* Current recovery attempt
|
|
*/
|
|
currentRecoveryAttempt: 0
|
|
};
|
|
/**
|
|
* Creates a new SMTP server
|
|
* @param config - Server configuration
|
|
*/
|
|
constructor(config) {
|
|
this.emailServer = config.emailServer;
|
|
this.options = mergeWithDefaults(config.options);
|
|
// Create components - all components now receive the SMTP server instance
|
|
this.sessionManager = config.sessionManager || new SessionManager({
|
|
socketTimeout: this.options.socketTimeout,
|
|
connectionTimeout: this.options.connectionTimeout,
|
|
cleanupInterval: this.options.cleanupInterval
|
|
});
|
|
this.securityHandler = config.securityHandler || new SecurityHandler(this);
|
|
this.tlsHandler = config.tlsHandler || new TlsHandler(this);
|
|
this.dataHandler = config.dataHandler || new DataHandler(this);
|
|
this.commandHandler = config.commandHandler || new CommandHandler(this);
|
|
this.connectionManager = config.connectionManager || new ConnectionManager(this);
|
|
}
|
|
/**
|
|
* Start the SMTP server
|
|
* @returns Promise that resolves when server is started
|
|
*/
|
|
async listen() {
|
|
if (this.running) {
|
|
throw new Error('SMTP server is already running');
|
|
}
|
|
try {
|
|
// Create the server
|
|
this.server = plugins.net.createServer((socket) => {
|
|
// Check IP reputation before handling connection
|
|
this.securityHandler.checkIpReputation(socket)
|
|
.then(allowed => {
|
|
if (allowed) {
|
|
this.connectionManager.handleNewConnection(socket);
|
|
}
|
|
else {
|
|
// Close connection if IP is not allowed
|
|
socket.destroy();
|
|
}
|
|
})
|
|
.catch(error => {
|
|
SmtpLogger.error(`IP reputation check error: ${error instanceof Error ? error.message : String(error)}`, {
|
|
remoteAddress: socket.remoteAddress,
|
|
error: error instanceof Error ? error : new Error(String(error))
|
|
});
|
|
// Allow connection on error (fail open)
|
|
this.connectionManager.handleNewConnection(socket);
|
|
});
|
|
});
|
|
// Set up error handling with recovery
|
|
this.server.on('error', (err) => {
|
|
SmtpLogger.error(`SMTP server error: ${err.message}`, { error: err });
|
|
// Try to recover from specific errors
|
|
if (this.shouldAttemptRecovery(err)) {
|
|
this.attemptServerRecovery('standard', err);
|
|
}
|
|
});
|
|
// Start listening
|
|
await new Promise((resolve, reject) => {
|
|
if (!this.server) {
|
|
reject(new Error('Server not initialized'));
|
|
return;
|
|
}
|
|
this.server.listen(this.options.port, this.options.host, () => {
|
|
SmtpLogger.info(`SMTP server listening on ${this.options.host || '0.0.0.0'}:${this.options.port}`);
|
|
resolve();
|
|
});
|
|
this.server.on('error', reject);
|
|
});
|
|
// Start secure server if configured
|
|
if (this.options.securePort && this.tlsHandler.isTlsEnabled()) {
|
|
try {
|
|
// Import the secure server creation utility from our new module
|
|
// This gives us better certificate handling and error resilience
|
|
const { createSecureTlsServer } = await import('./secure-server.js');
|
|
// Create secure server with the certificates
|
|
// This uses a more robust approach to certificate loading and validation
|
|
this.secureServer = createSecureTlsServer({
|
|
key: this.options.key,
|
|
cert: this.options.cert,
|
|
ca: this.options.ca
|
|
});
|
|
SmtpLogger.info(`Created secure TLS server for port ${this.options.securePort}`);
|
|
if (this.secureServer) {
|
|
// Use explicit error handling for secure connections
|
|
this.secureServer.on('tlsClientError', (err, tlsSocket) => {
|
|
SmtpLogger.error(`TLS client error: ${err.message}`, {
|
|
error: err,
|
|
remoteAddress: tlsSocket.remoteAddress,
|
|
remotePort: tlsSocket.remotePort,
|
|
stack: err.stack
|
|
});
|
|
// No need to destroy, the error event will handle that
|
|
});
|
|
// Register the secure connection handler
|
|
this.secureServer.on('secureConnection', (socket) => {
|
|
SmtpLogger.info(`New secure connection from ${socket.remoteAddress}:${socket.remotePort}`, {
|
|
protocol: socket.getProtocol(),
|
|
cipher: socket.getCipher()?.name
|
|
});
|
|
// Check IP reputation before handling connection
|
|
this.securityHandler.checkIpReputation(socket)
|
|
.then(allowed => {
|
|
if (allowed) {
|
|
// Pass the connection to the connection manager
|
|
this.connectionManager.handleNewSecureConnection(socket);
|
|
}
|
|
else {
|
|
// Close connection if IP is not allowed
|
|
socket.destroy();
|
|
}
|
|
})
|
|
.catch(error => {
|
|
SmtpLogger.error(`IP reputation check error: ${error instanceof Error ? error.message : String(error)}`, {
|
|
remoteAddress: socket.remoteAddress,
|
|
error: error instanceof Error ? error : new Error(String(error)),
|
|
stack: error instanceof Error ? error.stack : 'No stack trace available'
|
|
});
|
|
// Allow connection on error (fail open)
|
|
this.connectionManager.handleNewSecureConnection(socket);
|
|
});
|
|
});
|
|
// Global error handler for the secure server with recovery
|
|
this.secureServer.on('error', (err) => {
|
|
SmtpLogger.error(`SMTP secure server error: ${err.message}`, {
|
|
error: err,
|
|
stack: err.stack
|
|
});
|
|
// Try to recover from specific errors
|
|
if (this.shouldAttemptRecovery(err)) {
|
|
this.attemptServerRecovery('secure', err);
|
|
}
|
|
});
|
|
// Start listening on secure port
|
|
await new Promise((resolve, reject) => {
|
|
if (!this.secureServer) {
|
|
reject(new Error('Secure server not initialized'));
|
|
return;
|
|
}
|
|
this.secureServer.listen(this.options.securePort, this.options.host, () => {
|
|
SmtpLogger.info(`SMTP secure server listening on ${this.options.host || '0.0.0.0'}:${this.options.securePort}`);
|
|
resolve();
|
|
});
|
|
// Only use error event for startup issues
|
|
this.secureServer.once('error', reject);
|
|
});
|
|
}
|
|
else {
|
|
SmtpLogger.warn('Failed to create secure server, TLS may not be properly configured');
|
|
}
|
|
}
|
|
catch (error) {
|
|
SmtpLogger.error(`Error setting up secure server: ${error instanceof Error ? error.message : String(error)}`, {
|
|
error: error instanceof Error ? error : new Error(String(error)),
|
|
stack: error instanceof Error ? error.stack : 'No stack trace available'
|
|
});
|
|
}
|
|
}
|
|
this.running = true;
|
|
}
|
|
catch (error) {
|
|
SmtpLogger.error(`Failed to start SMTP server: ${error instanceof Error ? error.message : String(error)}`, {
|
|
error: error instanceof Error ? error : new Error(String(error))
|
|
});
|
|
// Clean up on error
|
|
this.close();
|
|
throw error;
|
|
}
|
|
}
|
|
/**
|
|
* Stop the SMTP server
|
|
* @returns Promise that resolves when server is stopped
|
|
*/
|
|
async close() {
|
|
if (!this.running) {
|
|
return;
|
|
}
|
|
SmtpLogger.info('Stopping SMTP server');
|
|
try {
|
|
// Close all active connections
|
|
this.connectionManager.closeAllConnections();
|
|
// Clear all sessions
|
|
this.sessionManager.clearAllSessions();
|
|
// Clean up adaptive logger to prevent hanging timers
|
|
adaptiveLogger.destroy();
|
|
// Destroy all components to clean up their resources
|
|
await this.destroy();
|
|
// Close servers
|
|
const closePromises = [];
|
|
if (this.server) {
|
|
closePromises.push(new Promise((resolve, reject) => {
|
|
if (!this.server) {
|
|
resolve();
|
|
return;
|
|
}
|
|
this.server.close((err) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
else {
|
|
resolve();
|
|
}
|
|
});
|
|
}));
|
|
}
|
|
if (this.secureServer) {
|
|
closePromises.push(new Promise((resolve, reject) => {
|
|
if (!this.secureServer) {
|
|
resolve();
|
|
return;
|
|
}
|
|
this.secureServer.close((err) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
else {
|
|
resolve();
|
|
}
|
|
});
|
|
}));
|
|
}
|
|
// Add timeout to prevent hanging on close
|
|
await Promise.race([
|
|
Promise.all(closePromises),
|
|
new Promise((resolve) => {
|
|
setTimeout(() => {
|
|
SmtpLogger.warn('Server close timed out after 3 seconds, forcing shutdown');
|
|
resolve();
|
|
}, 3000);
|
|
})
|
|
]);
|
|
this.server = null;
|
|
this.secureServer = null;
|
|
this.running = false;
|
|
SmtpLogger.info('SMTP server stopped');
|
|
}
|
|
catch (error) {
|
|
SmtpLogger.error(`Error stopping SMTP server: ${error instanceof Error ? error.message : String(error)}`, {
|
|
error: error instanceof Error ? error : new Error(String(error))
|
|
});
|
|
throw error;
|
|
}
|
|
}
|
|
/**
|
|
* Get the session manager
|
|
* @returns Session manager instance
|
|
*/
|
|
getSessionManager() {
|
|
return this.sessionManager;
|
|
}
|
|
/**
|
|
* Get the connection manager
|
|
* @returns Connection manager instance
|
|
*/
|
|
getConnectionManager() {
|
|
return this.connectionManager;
|
|
}
|
|
/**
|
|
* Get the command handler
|
|
* @returns Command handler instance
|
|
*/
|
|
getCommandHandler() {
|
|
return this.commandHandler;
|
|
}
|
|
/**
|
|
* Get the data handler
|
|
* @returns Data handler instance
|
|
*/
|
|
getDataHandler() {
|
|
return this.dataHandler;
|
|
}
|
|
/**
|
|
* Get the TLS handler
|
|
* @returns TLS handler instance
|
|
*/
|
|
getTlsHandler() {
|
|
return this.tlsHandler;
|
|
}
|
|
/**
|
|
* Get the security handler
|
|
* @returns Security handler instance
|
|
*/
|
|
getSecurityHandler() {
|
|
return this.securityHandler;
|
|
}
|
|
/**
|
|
* Get the server options
|
|
* @returns SMTP server options
|
|
*/
|
|
getOptions() {
|
|
return this.options;
|
|
}
|
|
/**
|
|
* Get the email server reference
|
|
* @returns Email server instance
|
|
*/
|
|
getEmailServer() {
|
|
return this.emailServer;
|
|
}
|
|
/**
|
|
* Check if the server is running
|
|
* @returns Whether the server is running
|
|
*/
|
|
isRunning() {
|
|
return this.running;
|
|
}
|
|
/**
|
|
* Check if we should attempt to recover from an error
|
|
* @param error - The error that occurred
|
|
* @returns Whether recovery should be attempted
|
|
*/
|
|
shouldAttemptRecovery(error) {
|
|
// Skip recovery if we're already in recovery mode
|
|
if (this.recoveryState.recovering) {
|
|
return false;
|
|
}
|
|
// Check if we've reached the maximum number of recovery attempts
|
|
if (this.recoveryState.currentRecoveryAttempt >= this.recoveryState.maxRecoveryAttempts) {
|
|
SmtpLogger.warn('Maximum recovery attempts reached, not attempting further recovery');
|
|
return false;
|
|
}
|
|
// Check if enough time has passed since the last recovery attempt
|
|
const now = Date.now();
|
|
if (now - this.recoveryState.lastRecoveryAttempt < this.recoveryState.recoveryCooldown) {
|
|
SmtpLogger.warn('Recovery cooldown period not elapsed, skipping recovery attempt');
|
|
return false;
|
|
}
|
|
// Recoverable errors include:
|
|
// - EADDRINUSE: Address already in use (port conflict)
|
|
// - ECONNRESET: Connection reset by peer
|
|
// - EPIPE: Broken pipe
|
|
// - ETIMEDOUT: Connection timed out
|
|
const recoverableErrors = [
|
|
'EADDRINUSE',
|
|
'ECONNRESET',
|
|
'EPIPE',
|
|
'ETIMEDOUT',
|
|
'ECONNABORTED',
|
|
'EPROTO',
|
|
'EMFILE' // Too many open files
|
|
];
|
|
// Check if this is a recoverable error
|
|
const errorCode = error.code;
|
|
return recoverableErrors.includes(errorCode);
|
|
}
|
|
/**
|
|
* Attempt to recover the server after a critical error
|
|
* @param serverType - The type of server to recover ('standard' or 'secure')
|
|
* @param error - The error that triggered recovery
|
|
*/
|
|
async attemptServerRecovery(serverType, error) {
|
|
// Set recovery flag to prevent multiple simultaneous recovery attempts
|
|
if (this.recoveryState.recovering) {
|
|
SmtpLogger.warn('Recovery already in progress, skipping new recovery attempt');
|
|
return;
|
|
}
|
|
this.recoveryState.recovering = true;
|
|
this.recoveryState.lastRecoveryAttempt = Date.now();
|
|
this.recoveryState.currentRecoveryAttempt++;
|
|
SmtpLogger.info(`Attempting server recovery for ${serverType} server after error: ${error.message}`, {
|
|
attempt: this.recoveryState.currentRecoveryAttempt,
|
|
maxAttempts: this.recoveryState.maxRecoveryAttempts,
|
|
errorCode: error.code
|
|
});
|
|
try {
|
|
// Determine which server to restart
|
|
const isStandardServer = serverType === 'standard';
|
|
// Close the affected server
|
|
if (isStandardServer && this.server) {
|
|
await new Promise((resolve) => {
|
|
if (!this.server) {
|
|
resolve();
|
|
return;
|
|
}
|
|
// First try a clean shutdown
|
|
this.server.close((err) => {
|
|
if (err) {
|
|
SmtpLogger.warn(`Error during server close in recovery: ${err.message}`);
|
|
}
|
|
resolve();
|
|
});
|
|
// Set a timeout to force close
|
|
setTimeout(() => {
|
|
resolve();
|
|
}, 3000);
|
|
});
|
|
this.server = null;
|
|
}
|
|
else if (!isStandardServer && this.secureServer) {
|
|
await new Promise((resolve) => {
|
|
if (!this.secureServer) {
|
|
resolve();
|
|
return;
|
|
}
|
|
// First try a clean shutdown
|
|
this.secureServer.close((err) => {
|
|
if (err) {
|
|
SmtpLogger.warn(`Error during secure server close in recovery: ${err.message}`);
|
|
}
|
|
resolve();
|
|
});
|
|
// Set a timeout to force close
|
|
setTimeout(() => {
|
|
resolve();
|
|
}, 3000);
|
|
});
|
|
this.secureServer = null;
|
|
}
|
|
// Short delay before restarting
|
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
// Clean up any lingering connections
|
|
this.connectionManager.closeAllConnections();
|
|
this.sessionManager.clearAllSessions();
|
|
// Restart the affected server
|
|
if (isStandardServer) {
|
|
// Create and start the standard server
|
|
this.server = plugins.net.createServer((socket) => {
|
|
// Check IP reputation before handling connection
|
|
this.securityHandler.checkIpReputation(socket)
|
|
.then(allowed => {
|
|
if (allowed) {
|
|
this.connectionManager.handleNewConnection(socket);
|
|
}
|
|
else {
|
|
// Close connection if IP is not allowed
|
|
socket.destroy();
|
|
}
|
|
})
|
|
.catch(error => {
|
|
SmtpLogger.error(`IP reputation check error: ${error instanceof Error ? error.message : String(error)}`, {
|
|
remoteAddress: socket.remoteAddress,
|
|
error: error instanceof Error ? error : new Error(String(error))
|
|
});
|
|
// Allow connection on error (fail open)
|
|
this.connectionManager.handleNewConnection(socket);
|
|
});
|
|
});
|
|
// Set up error handling with recovery
|
|
this.server.on('error', (err) => {
|
|
SmtpLogger.error(`SMTP server error after recovery: ${err.message}`, { error: err });
|
|
// Try to recover again if needed
|
|
if (this.shouldAttemptRecovery(err)) {
|
|
this.attemptServerRecovery('standard', err);
|
|
}
|
|
});
|
|
// Start listening again
|
|
await new Promise((resolve, reject) => {
|
|
if (!this.server) {
|
|
reject(new Error('Server not initialized during recovery'));
|
|
return;
|
|
}
|
|
this.server.listen(this.options.port, this.options.host, () => {
|
|
SmtpLogger.info(`SMTP server recovered and listening on ${this.options.host || '0.0.0.0'}:${this.options.port}`);
|
|
resolve();
|
|
});
|
|
// Only use error event for startup issues during recovery
|
|
this.server.once('error', (err) => {
|
|
SmtpLogger.error(`Failed to restart server during recovery: ${err.message}`);
|
|
reject(err);
|
|
});
|
|
});
|
|
}
|
|
else if (this.options.securePort && this.tlsHandler.isTlsEnabled()) {
|
|
// Try to recreate the secure server
|
|
try {
|
|
// Import the secure server creation utility
|
|
const { createSecureTlsServer } = await import('./secure-server.js');
|
|
// Create secure server with the certificates
|
|
this.secureServer = createSecureTlsServer({
|
|
key: this.options.key,
|
|
cert: this.options.cert,
|
|
ca: this.options.ca
|
|
});
|
|
if (this.secureServer) {
|
|
SmtpLogger.info(`Created secure TLS server for port ${this.options.securePort} during recovery`);
|
|
// Use explicit error handling for secure connections
|
|
this.secureServer.on('tlsClientError', (err, tlsSocket) => {
|
|
SmtpLogger.error(`TLS client error after recovery: ${err.message}`, {
|
|
error: err,
|
|
remoteAddress: tlsSocket.remoteAddress,
|
|
remotePort: tlsSocket.remotePort,
|
|
stack: err.stack
|
|
});
|
|
});
|
|
// Register the secure connection handler
|
|
this.secureServer.on('secureConnection', (socket) => {
|
|
// Check IP reputation before handling connection
|
|
this.securityHandler.checkIpReputation(socket)
|
|
.then(allowed => {
|
|
if (allowed) {
|
|
// Pass the connection to the connection manager
|
|
this.connectionManager.handleNewSecureConnection(socket);
|
|
}
|
|
else {
|
|
// Close connection if IP is not allowed
|
|
socket.destroy();
|
|
}
|
|
})
|
|
.catch(error => {
|
|
SmtpLogger.error(`IP reputation check error after recovery: ${error instanceof Error ? error.message : String(error)}`, {
|
|
remoteAddress: socket.remoteAddress,
|
|
error: error instanceof Error ? error : new Error(String(error))
|
|
});
|
|
// Allow connection on error (fail open)
|
|
this.connectionManager.handleNewSecureConnection(socket);
|
|
});
|
|
});
|
|
// Global error handler for the secure server with recovery
|
|
this.secureServer.on('error', (err) => {
|
|
SmtpLogger.error(`SMTP secure server error after recovery: ${err.message}`, {
|
|
error: err,
|
|
stack: err.stack
|
|
});
|
|
// Try to recover again if needed
|
|
if (this.shouldAttemptRecovery(err)) {
|
|
this.attemptServerRecovery('secure', err);
|
|
}
|
|
});
|
|
// Start listening on secure port again
|
|
await new Promise((resolve, reject) => {
|
|
if (!this.secureServer) {
|
|
reject(new Error('Secure server not initialized during recovery'));
|
|
return;
|
|
}
|
|
this.secureServer.listen(this.options.securePort, this.options.host, () => {
|
|
SmtpLogger.info(`SMTP secure server recovered and listening on ${this.options.host || '0.0.0.0'}:${this.options.securePort}`);
|
|
resolve();
|
|
});
|
|
// Only use error event for startup issues during recovery
|
|
this.secureServer.once('error', (err) => {
|
|
SmtpLogger.error(`Failed to restart secure server during recovery: ${err.message}`);
|
|
reject(err);
|
|
});
|
|
});
|
|
}
|
|
else {
|
|
SmtpLogger.warn('Failed to create secure server during recovery');
|
|
}
|
|
}
|
|
catch (error) {
|
|
SmtpLogger.error(`Error setting up secure server during recovery: ${error instanceof Error ? error.message : String(error)}`);
|
|
}
|
|
}
|
|
// Recovery successful
|
|
SmtpLogger.info('Server recovery completed successfully');
|
|
}
|
|
catch (recoveryError) {
|
|
SmtpLogger.error(`Server recovery failed: ${recoveryError instanceof Error ? recoveryError.message : String(recoveryError)}`, {
|
|
error: recoveryError instanceof Error ? recoveryError : new Error(String(recoveryError)),
|
|
attempt: this.recoveryState.currentRecoveryAttempt,
|
|
maxAttempts: this.recoveryState.maxRecoveryAttempts
|
|
});
|
|
}
|
|
finally {
|
|
// Reset recovery flag
|
|
this.recoveryState.recovering = false;
|
|
}
|
|
}
|
|
/**
|
|
* Clean up all component resources
|
|
*/
|
|
async destroy() {
|
|
SmtpLogger.info('Destroying SMTP server components');
|
|
// Destroy all components in parallel
|
|
const destroyPromises = [];
|
|
if (this.sessionManager && typeof this.sessionManager.destroy === 'function') {
|
|
destroyPromises.push(Promise.resolve(this.sessionManager.destroy()));
|
|
}
|
|
if (this.connectionManager && typeof this.connectionManager.destroy === 'function') {
|
|
destroyPromises.push(Promise.resolve(this.connectionManager.destroy()));
|
|
}
|
|
if (this.commandHandler && typeof this.commandHandler.destroy === 'function') {
|
|
destroyPromises.push(Promise.resolve(this.commandHandler.destroy()));
|
|
}
|
|
if (this.dataHandler && typeof this.dataHandler.destroy === 'function') {
|
|
destroyPromises.push(Promise.resolve(this.dataHandler.destroy()));
|
|
}
|
|
if (this.tlsHandler && typeof this.tlsHandler.destroy === 'function') {
|
|
destroyPromises.push(Promise.resolve(this.tlsHandler.destroy()));
|
|
}
|
|
if (this.securityHandler && typeof this.securityHandler.destroy === 'function') {
|
|
destroyPromises.push(Promise.resolve(this.securityHandler.destroy()));
|
|
}
|
|
await Promise.all(destroyPromises);
|
|
// Destroy the adaptive logger singleton to clean up its timer
|
|
const { adaptiveLogger } = await import('./utils/adaptive-logging.js');
|
|
if (adaptiveLogger && typeof adaptiveLogger.destroy === 'function') {
|
|
adaptiveLogger.destroy();
|
|
}
|
|
// Clear recovery state
|
|
this.recoveryState = {
|
|
recovering: false,
|
|
connectionFailures: 0,
|
|
lastRecoveryAttempt: 0,
|
|
recoveryCooldown: 5000,
|
|
maxRecoveryAttempts: 3,
|
|
currentRecoveryAttempt: 0
|
|
};
|
|
SmtpLogger.info('All SMTP server components destroyed');
|
|
}
|
|
}
|
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic210cC1zZXJ2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi90cy9tYWlsL2RlbGl2ZXJ5L3NtdHBzZXJ2ZXIvc210cC1zZXJ2ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsT0FBTyxLQUFLLE9BQU8sTUFBTSxxQkFBcUIsQ0FBQztBQUMvQyxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFHNUMsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ3RELE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQzVELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUN0RCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDaEQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUN4RCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDL0MsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDdkQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLG9CQUFvQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUM3RCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSwrQ0FBK0MsQ0FBQztBQUVuRjs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sVUFBVTtJQUNyQjs7T0FFRztJQUNLLFdBQVcsQ0FBcUI7SUFFeEM7O09BRUc7SUFDSyxjQUFjLENBQWtCO0lBRXhDOztPQUVHO0lBQ0ssaUJBQWlCLENBQXFCO0lBRTlDOztPQUVHO0lBQ0ssY0FBYyxDQUFrQjtJQUV4Qzs7T0FFRztJQUNLLFdBQVcsQ0FBZTtJQUVsQzs7T0FF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
|