/**
 * SMTP TLS Handler
 * Responsible for handling TLS-related SMTP functionality
 */
import * as plugins from '../../../plugins.js';
import { SmtpResponseCode, SecurityEventType, SecurityLogLevel } from './constants.js';
import { SmtpLogger } from './utils/logging.js';
import { getSocketDetails, getTlsDetails } from './utils/helpers.js';
import { loadCertificatesFromString, generateSelfSignedCertificates, createTlsOptions } from './certificate-utils.js';
import { SmtpState } from '../interfaces.js';
/**
 * Handles TLS functionality for SMTP server
 */
export class TlsHandler {
    /**
     * Reference to the SMTP server instance
     */
    smtpServer;
    /**
     * Certificate data
     */
    certificates;
    /**
     * TLS options
     */
    options;
    /**
     * Creates a new TLS handler
     * @param smtpServer - SMTP server instance
     */
    constructor(smtpServer) {
        this.smtpServer = smtpServer;
        // Initialize certificates
        const serverOptions = this.smtpServer.getOptions();
        try {
            // Try to load certificates from provided options
            this.certificates = loadCertificatesFromString({
                key: serverOptions.key,
                cert: serverOptions.cert,
                ca: serverOptions.ca
            });
            SmtpLogger.info('Successfully loaded TLS certificates');
        }
        catch (error) {
            SmtpLogger.warn(`Failed to load certificates from options, using self-signed: ${error instanceof Error ? error.message : String(error)}`);
            // Fall back to self-signed certificates for testing
            this.certificates = generateSelfSignedCertificates();
        }
        // Initialize TLS options
        this.options = createTlsOptions(this.certificates);
    }
    /**
     * Handle STARTTLS command
     * @param socket - Client socket
     */
    async handleStartTls(socket, session) {
        // Check if already using TLS
        if (session.useTLS) {
            this.sendResponse(socket, `${SmtpResponseCode.BAD_SEQUENCE} TLS already active`);
            return null;
        }
        // Check if we have the necessary TLS certificates
        if (!this.isTlsEnabled()) {
            this.sendResponse(socket, `${SmtpResponseCode.TLS_UNAVAILABLE_TEMP} TLS not available`);
            return null;
        }
        // Send ready for TLS response
        this.sendResponse(socket, `${SmtpResponseCode.SERVICE_READY} Ready to start TLS`);
        // Upgrade the connection to TLS
        try {
            const tlsSocket = await this.startTLS(socket);
            return tlsSocket;
        }
        catch (error) {
            SmtpLogger.error(`STARTTLS negotiation failed: ${error instanceof Error ? error.message : String(error)}`, {
                sessionId: session.id,
                remoteAddress: session.remoteAddress,
                error: error instanceof Error ? error : new Error(String(error))
            });
            // Log security event
            SmtpLogger.logSecurityEvent(SecurityLogLevel.ERROR, SecurityEventType.TLS_NEGOTIATION, 'STARTTLS negotiation failed', { error: error instanceof Error ? error.message : String(error) }, session.remoteAddress);
            return null;
        }
    }
    /**
     * Upgrade a connection to TLS
     * @param socket - Client socket
     */
    async startTLS(socket) {
        // Get the session for this socket
        const session = this.smtpServer.getSessionManager().getSession(socket);
        try {
            // Import the enhanced STARTTLS handler
            // This uses a more robust approach to TLS upgrades
            const { performStartTLS } = await import('./starttls-handler.js');
            SmtpLogger.info('Using enhanced STARTTLS implementation');
            // Use the enhanced STARTTLS handler with better error handling and socket management
            const serverOptions = this.smtpServer.getOptions();
            const tlsSocket = await performStartTLS(socket, {
                key: serverOptions.key,
                cert: serverOptions.cert,
                ca: serverOptions.ca,
                session: session,
                sessionManager: this.smtpServer.getSessionManager(),
                connectionManager: this.smtpServer.getConnectionManager(),
                // Callback for successful upgrade
                onSuccess: (secureSocket) => {
                    SmtpLogger.info('TLS connection successfully established via enhanced STARTTLS', {
                        remoteAddress: secureSocket.remoteAddress,
                        remotePort: secureSocket.remotePort,
                        protocol: secureSocket.getProtocol() || 'unknown',
                        cipher: secureSocket.getCipher()?.name || 'unknown'
                    });
                    // Log security event
                    SmtpLogger.logSecurityEvent(SecurityLogLevel.INFO, SecurityEventType.TLS_NEGOTIATION, 'STARTTLS successful with enhanced implementation', {
                        protocol: secureSocket.getProtocol(),
                        cipher: secureSocket.getCipher()?.name
                    }, secureSocket.remoteAddress, undefined, true);
                },
                // Callback for failed upgrade
                onFailure: (error) => {
                    SmtpLogger.error(`Enhanced STARTTLS failed: ${error.message}`, {
                        sessionId: session?.id,
                        remoteAddress: socket.remoteAddress,
                        error
                    });
                    // Log security event
                    SmtpLogger.logSecurityEvent(SecurityLogLevel.ERROR, SecurityEventType.TLS_NEGOTIATION, 'Enhanced STARTTLS failed', { error: error.message }, socket.remoteAddress, undefined, false);
                },
                // Function to update session state
                updateSessionState: this.smtpServer.getSessionManager().updateSessionState?.bind(this.smtpServer.getSessionManager())
            });
            // If STARTTLS failed with the enhanced implementation, log the error
            if (!tlsSocket) {
                SmtpLogger.warn('Enhanced STARTTLS implementation failed to create TLS socket', {
                    sessionId: session?.id,
                    remoteAddress: socket.remoteAddress
                });
                throw new Error('Failed to create TLS socket');
            }
            return tlsSocket;
        }
        catch (error) {
            // Log STARTTLS failure
            SmtpLogger.error(`Failed to upgrade connection to TLS: ${error instanceof Error ? error.message : String(error)}`, {
                remoteAddress: socket.remoteAddress,
                remotePort: socket.remotePort,
                error: error instanceof Error ? error : new Error(String(error)),
                stack: error instanceof Error ? error.stack : 'No stack trace available'
            });
            // Log security event
            SmtpLogger.logSecurityEvent(SecurityLogLevel.ERROR, SecurityEventType.TLS_NEGOTIATION, 'Failed to upgrade connection to TLS', {
                error: error instanceof Error ? error.message : String(error),
                stack: error instanceof Error ? error.stack : 'No stack trace available'
            }, socket.remoteAddress, undefined, false);
            // Destroy the socket on error
            socket.destroy();
            throw error;
        }
    }
    /**
     * Create a secure server
     * @returns TLS server instance or undefined if TLS is not enabled
     */
    createSecureServer() {
        if (!this.isTlsEnabled()) {
            return undefined;
        }
        try {
            SmtpLogger.info('Creating secure TLS server');
            // Log certificate info
            SmtpLogger.debug('Using certificates for secure server', {
                keyLength: this.certificates.key.length,
                certLength: this.certificates.cert.length,
                caLength: this.certificates.ca ? this.certificates.ca.length : 0
            });
            // Create TLS options using our certificate utilities
            // This ensures proper PEM format handling and protocol negotiation
            const tlsOptions = createTlsOptions(this.certificates, true); // Use server options
            SmtpLogger.info('Creating TLS server with options', {
                minVersion: tlsOptions.minVersion,
                maxVersion: tlsOptions.maxVersion,
                handshakeTimeout: tlsOptions.handshakeTimeout
            });
            // Create a server with wider TLS compatibility
            const server = new plugins.tls.Server(tlsOptions);
            // Add error handling
            server.on('error', (err) => {
                SmtpLogger.error(`TLS server error: ${err.message}`, {
                    error: err,
                    stack: err.stack
                });
            });
            // Log TLS details for each connection
            server.on('secureConnection', (socket) => {
                SmtpLogger.info('New secure connection established', {
                    protocol: socket.getProtocol(),
                    cipher: socket.getCipher()?.name,
                    remoteAddress: socket.remoteAddress,
                    remotePort: socket.remotePort
                });
            });
            return server;
        }
        catch (error) {
            SmtpLogger.error(`Failed to create 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'
            });
            return undefined;
        }
    }
    /**
     * Check if TLS is enabled
     * @returns Whether TLS is enabled
     */
    isTlsEnabled() {
        const options = this.smtpServer.getOptions();
        return !!(options.key && options.cert);
    }
    /**
     * Send a response to the client
     * @param socket - Client socket
     * @param response - Response message
     */
    sendResponse(socket, response) {
        // Check if socket is still writable before attempting to write
        if (socket.destroyed || socket.readyState !== 'open' || !socket.writable) {
            SmtpLogger.debug(`Skipping response to closed/destroyed socket: ${response}`, {
                remoteAddress: socket.remoteAddress,
                remotePort: socket.remotePort,
                destroyed: socket.destroyed,
                readyState: socket.readyState,
                writable: socket.writable
            });
            return;
        }
        try {
            socket.write(`${response}\r\n`);
            SmtpLogger.logResponse(response, socket);
        }
        catch (error) {
            SmtpLogger.error(`Error sending response: ${error instanceof Error ? error.message : String(error)}`, {
                response,
                remoteAddress: socket.remoteAddress,
                remotePort: socket.remotePort,
                error: error instanceof Error ? error : new Error(String(error))
            });
            socket.destroy();
        }
    }
    /**
     * Check if TLS is available (interface requirement)
     */
    isTlsAvailable() {
        return this.isTlsEnabled();
    }
    /**
     * Get TLS options (interface requirement)
     */
    getTlsOptions() {
        return this.options;
    }
    /**
     * Clean up resources
     */
    destroy() {
        // Clear any cached certificates or TLS contexts
        // TlsHandler doesn't have timers but may have cached resources
        SmtpLogger.debug('TlsHandler destroyed');
    }
}
//# sourceMappingURL=data:application/json;base64, 