import * as plugins from '../../plugins.js'; import * as paths from '../../paths.js'; import { EventEmitter } from 'events'; import { logger } from '../../logger.js'; import { SecurityLogger, SecurityLogLevel, SecurityEventType } from '../../security/index.js'; import { DKIMCreator } from '../security/classes.dkimcreator.js'; import { IPReputationChecker } from '../../security/classes.ipreputationchecker.js'; import { RustSecurityBridge } from '../../security/classes.rustsecuritybridge.js'; import { EmailRouter } from './classes.email.router.js'; import { Email } from '../core/classes.email.js'; import { DomainRegistry } from './classes.domain.registry.js'; import { DnsManager } from './classes.dns.manager.js'; import { BounceManager, BounceType, BounceCategory } from '../core/classes.bouncemanager.js'; import { createPooledSmtpClient } from '../delivery/smtpclient/create-client.js'; import { MultiModeDeliverySystem } from '../delivery/classes.delivery.system.js'; import { UnifiedDeliveryQueue } from '../delivery/classes.delivery.queue.js'; import { UnifiedRateLimiter } from '../delivery/classes.unified.rate.limiter.js'; import { SmtpState } from '../delivery/interfaces.js'; /** * Unified email server that handles all email traffic with pattern-based routing */ export class UnifiedEmailServer extends EventEmitter { dcRouter; options; emailRouter; domainRegistry; servers = []; stats; // Add components needed for sending and securing emails dkimCreator; rustBridge; ipReputationChecker; bounceManager; ipWarmupManager; senderReputationMonitor; deliveryQueue; deliverySystem; rateLimiter; // TODO: Implement rate limiting in SMTP server handlers dkimKeys = new Map(); // domain -> private key smtpClients = new Map(); // host:port -> client constructor(dcRouter, options) { super(); this.dcRouter = dcRouter; // Set default options this.options = { ...options, banner: options.banner || `${options.hostname} ESMTP UnifiedEmailServer`, maxMessageSize: options.maxMessageSize || 10 * 1024 * 1024, // 10MB maxClients: options.maxClients || 100, maxConnections: options.maxConnections || 1000, connectionTimeout: options.connectionTimeout || 60000, // 1 minute socketTimeout: options.socketTimeout || 60000 // 1 minute }; // Initialize Rust security bridge (singleton) this.rustBridge = RustSecurityBridge.getInstance(); // Initialize DKIM creator with storage manager this.dkimCreator = new DKIMCreator(paths.keysDir, dcRouter.storageManager); // Initialize IP reputation checker with storage manager this.ipReputationChecker = IPReputationChecker.getInstance({ enableLocalCache: true, enableDNSBL: true, enableIPInfo: true }, dcRouter.storageManager); // Initialize bounce manager with storage manager this.bounceManager = new BounceManager({ maxCacheSize: 10000, cacheTTL: 30 * 24 * 60 * 60 * 1000, // 30 days storageManager: dcRouter.storageManager }); // IP warmup manager and sender reputation monitor are optional // They will be initialized when the deliverability module is available this.ipWarmupManager = null; this.senderReputationMonitor = null; // Initialize domain registry this.domainRegistry = new DomainRegistry(options.domains, options.defaults); // Initialize email router with routes and storage manager this.emailRouter = new EmailRouter(options.routes || [], { storageManager: dcRouter.storageManager, persistChanges: true }); // Initialize rate limiter this.rateLimiter = new UnifiedRateLimiter(options.rateLimits || { global: { maxConnectionsPerIP: 10, maxMessagesPerMinute: 100, maxRecipientsPerMessage: 50, maxErrorsPerIP: 10, maxAuthFailuresPerIP: 5, blockDuration: 300000 // 5 minutes } }); // Initialize delivery components const queueOptions = { storageType: 'memory', // Default to memory storage maxRetries: 3, baseRetryDelay: 300000, // 5 minutes maxRetryDelay: 3600000 // 1 hour }; this.deliveryQueue = new UnifiedDeliveryQueue(queueOptions); const deliveryOptions = { globalRateLimit: 100, // Default to 100 emails per minute concurrentDeliveries: 10, processBounces: true, bounceHandler: { processSmtpFailure: this.processSmtpFailure.bind(this) }, onDeliverySuccess: async (item, _result) => { // Record delivery success event for reputation monitoring const email = item.processingResult; const senderDomain = email.from.split('@')[1]; if (senderDomain) { this.recordReputationEvent(senderDomain, { type: 'delivered', count: email.to.length }); } } }; this.deliverySystem = new MultiModeDeliverySystem(this.deliveryQueue, deliveryOptions, this); // Initialize statistics this.stats = { startTime: new Date(), connections: { current: 0, total: 0 }, messages: { processed: 0, delivered: 0, failed: 0 }, processingTime: { avg: 0, max: 0, min: 0 } }; // We'll create the SMTP servers during the start() method } /** * Get or create an SMTP client for the given host and port * Uses connection pooling for efficiency */ getSmtpClient(host, port = 25) { const clientKey = `${host}:${port}`; // Check if we already have a client for this destination let client = this.smtpClients.get(clientKey); if (!client) { // Create a new pooled SMTP client client = createPooledSmtpClient({ host, port, secure: port === 465, connectionTimeout: this.options.outbound?.connectionTimeout || 30000, socketTimeout: this.options.outbound?.socketTimeout || 120000, maxConnections: this.options.outbound?.maxConnections || 10, maxMessages: 1000, // Messages per connection before reconnect pool: true, debug: false }); this.smtpClients.set(clientKey, client); logger.log('info', `Created new SMTP client pool for ${clientKey}`); } return client; } /** * Start the unified email server */ async start() { logger.log('info', `Starting UnifiedEmailServer on ports: ${this.options.ports.join(', ')}`); try { // Initialize the delivery queue await this.deliveryQueue.initialize(); logger.log('info', 'Email delivery queue initialized'); // Start the delivery system await this.deliverySystem.start(); logger.log('info', 'Email delivery system started'); // Start Rust security bridge — required for all security operations const bridgeOk = await this.rustBridge.start(); if (!bridgeOk) { throw new Error('Rust security bridge failed to start. The mailer-bin binary is required. Run "pnpm build" to compile it.'); } logger.log('info', 'Rust security bridge started — Rust is the primary security backend'); // Set up DKIM for all domains await this.setupDkimForDomains(); logger.log('info', 'DKIM configuration completed for all domains'); // Create DNS manager and ensure all DNS records are created const dnsManager = new DnsManager(this.dcRouter); await dnsManager.ensureDnsRecords(this.domainRegistry.getAllConfigs(), this.dkimCreator); logger.log('info', 'DNS records ensured for all configured domains'); // Apply per-domain rate limits this.applyDomainRateLimits(); logger.log('info', 'Per-domain rate limits configured'); // Check and rotate DKIM keys if needed await this.checkAndRotateDkimKeys(); logger.log('info', 'DKIM key rotation check completed'); // Ensure we have the necessary TLS options const hasTlsConfig = this.options.tls?.keyPath && this.options.tls?.certPath; // Prepare the certificate and key if available let tlsCertPem; let tlsKeyPem; if (hasTlsConfig) { try { tlsKeyPem = plugins.fs.readFileSync(this.options.tls.keyPath, 'utf8'); tlsCertPem = plugins.fs.readFileSync(this.options.tls.certPath, 'utf8'); logger.log('info', 'TLS certificates loaded successfully'); } catch (error) { logger.log('warn', `Failed to load TLS certificates: ${error.message}`); } } // --- Start Rust SMTP server --- // Register event handlers for email reception and auth this.rustBridge.onEmailReceived(async (data) => { try { await this.handleRustEmailReceived(data); } catch (err) { logger.log('error', `Error handling email from Rust SMTP: ${err.message}`); // Send rejection back to Rust await this.rustBridge.sendEmailProcessingResult({ correlationId: data.correlationId, accepted: false, smtpCode: 451, smtpMessage: 'Internal processing error', }); } }); this.rustBridge.onAuthRequest(async (data) => { try { await this.handleRustAuthRequest(data); } catch (err) { logger.log('error', `Error handling auth from Rust SMTP: ${err.message}`); await this.rustBridge.sendAuthResult({ correlationId: data.correlationId, success: false, message: 'Internal auth error', }); } }); // Determine which ports need STARTTLS and which need implicit TLS const smtpPorts = this.options.ports.filter(p => p !== 465); const securePort = this.options.ports.find(p => p === 465); const started = await this.rustBridge.startSmtpServer({ hostname: this.options.hostname, ports: smtpPorts, securePort: securePort, tlsCertPem, tlsKeyPem, maxMessageSize: this.options.maxMessageSize || 10 * 1024 * 1024, maxConnections: this.options.maxConnections || this.options.maxClients || 100, maxRecipients: 100, connectionTimeoutSecs: this.options.connectionTimeout ? Math.floor(this.options.connectionTimeout / 1000) : 30, dataTimeoutSecs: 60, authEnabled: !!this.options.auth?.required || !!(this.options.auth?.users?.length), maxAuthFailures: 3, socketTimeoutSecs: this.options.socketTimeout ? Math.floor(this.options.socketTimeout / 1000) : 300, processingTimeoutSecs: 30, rateLimits: this.options.rateLimits ? { maxConnectionsPerIp: this.options.rateLimits.global?.maxConnectionsPerIP || 50, maxMessagesPerSender: this.options.rateLimits.global?.maxMessagesPerMinute || 100, maxAuthFailuresPerIp: this.options.rateLimits.global?.maxAuthFailuresPerIP || 5, windowSecs: 60, } : undefined, }); if (!started) { throw new Error('Failed to start Rust SMTP server'); } logger.log('info', `Rust SMTP server listening on ports: ${smtpPorts.join(', ')}${securePort ? ` + ${securePort} (TLS)` : ''}`); logger.log('info', 'UnifiedEmailServer started successfully'); this.emit('started'); } catch (error) { logger.log('error', `Failed to start UnifiedEmailServer: ${error.message}`); throw error; } } /** * Stop the unified email server */ async stop() { logger.log('info', 'Stopping UnifiedEmailServer'); try { // Stop the Rust SMTP server first try { await this.rustBridge.stopSmtpServer(); logger.log('info', 'Rust SMTP server stopped'); } catch (err) { logger.log('warn', `Error stopping Rust SMTP server: ${err.message}`); } // Clear the servers array - servers will be garbage collected this.servers = []; // Stop Rust security bridge await this.rustBridge.stop(); // Stop the delivery system if (this.deliverySystem) { await this.deliverySystem.stop(); logger.log('info', 'Email delivery system stopped'); } // Shut down the delivery queue if (this.deliveryQueue) { await this.deliveryQueue.shutdown(); logger.log('info', 'Email delivery queue shut down'); } // Close all SMTP client connections for (const [clientKey, client] of this.smtpClients) { try { await client.close(); logger.log('info', `Closed SMTP client pool for ${clientKey}`); } catch (error) { logger.log('warn', `Error closing SMTP client for ${clientKey}: ${error.message}`); } } this.smtpClients.clear(); logger.log('info', 'UnifiedEmailServer stopped successfully'); this.emit('stopped'); } catch (error) { logger.log('error', `Error stopping UnifiedEmailServer: ${error.message}`); throw error; } } // ----------------------------------------------------------------------- // Rust SMTP server event handlers // ----------------------------------------------------------------------- /** * Handle an emailReceived event from the Rust SMTP server. * Decodes the email data, processes it through the routing system, * and sends back the result via the correlation-ID callback. */ async handleRustEmailReceived(data) { const { correlationId, mailFrom, rcptTo, remoteAddr, clientHostname, secure, authenticatedUser } = data; logger.log('info', `Rust SMTP received email from=${mailFrom} to=${rcptTo.join(',')} remote=${remoteAddr}`); try { // Decode the email data let rawMessageBuffer; if (data.data.type === 'inline' && data.data.base64) { rawMessageBuffer = Buffer.from(data.data.base64, 'base64'); } else if (data.data.type === 'file' && data.data.path) { rawMessageBuffer = plugins.fs.readFileSync(data.data.path); // Clean up temp file try { plugins.fs.unlinkSync(data.data.path); } catch { // Ignore cleanup errors } } else { throw new Error('Invalid email data transport'); } // Build a session-like object for processEmailByMode const session = { id: data.sessionId || 'rust-' + Math.random().toString(36).substring(2), state: SmtpState.FINISHED, mailFrom: mailFrom, rcptTo: rcptTo, emailData: rawMessageBuffer.toString('utf8'), useTLS: secure, connectionEnded: false, remoteAddress: remoteAddr, clientHostname: clientHostname || '', secure: secure, authenticated: !!authenticatedUser, envelope: { mailFrom: { address: mailFrom, args: {} }, rcptTo: rcptTo.map(addr => ({ address: addr, args: {} })), }, }; if (authenticatedUser) { session.user = { username: authenticatedUser }; } // Attach pre-computed security results from Rust in-process pipeline if (data.securityResults) { session._precomputedSecurityResults = data.securityResults; } // Process the email through the routing system await this.processEmailByMode(rawMessageBuffer, session); // Send acceptance back to Rust await this.rustBridge.sendEmailProcessingResult({ correlationId, accepted: true, smtpCode: 250, smtpMessage: '2.0.0 Message accepted for delivery', }); } catch (err) { logger.log('error', `Failed to process email from Rust SMTP: ${err.message}`); await this.rustBridge.sendEmailProcessingResult({ correlationId, accepted: false, smtpCode: 550, smtpMessage: `5.0.0 Processing failed: ${err.message}`, }); } } /** * Handle an authRequest event from the Rust SMTP server. * Validates credentials and sends back the result. */ async handleRustAuthRequest(data) { const { correlationId, username, password, remoteAddr } = data; logger.log('info', `Rust SMTP auth request for user=${username} from=${remoteAddr}`); // Check against configured users const users = this.options.auth?.users || []; const matched = users.find(u => u.username === username && u.password === password); if (matched) { await this.rustBridge.sendAuthResult({ correlationId, success: true, }); } else { logger.log('warn', `Auth failed for user=${username} from=${remoteAddr}`); await this.rustBridge.sendAuthResult({ correlationId, success: false, message: 'Invalid credentials', }); } } /** * Verify inbound email security (DKIM/SPF/DMARC) using pre-computed Rust results * or falling back to IPC call if no pre-computed results are available. */ async verifyInboundSecurity(email, session) { try { // Check for pre-computed results from Rust in-process security pipeline const precomputed = session._precomputedSecurityResults; let result; if (precomputed) { logger.log('info', 'Using pre-computed security results from Rust in-process pipeline'); result = precomputed; } else { // Fallback: IPC round-trip to Rust (for backward compat / handleSocket mode) const rawMessage = session.emailData || email.toRFC822String(); result = await this.rustBridge.verifyEmail({ rawMessage, ip: session.remoteAddress, heloDomain: session.clientHostname || '', hostname: this.options.hostname, mailFrom: session.envelope?.mailFrom?.address || session.mailFrom || '', }); } // Apply DKIM result headers if (result.dkim && result.dkim.length > 0) { const dkimSummary = result.dkim .map((d) => `${d.status}${d.domain ? ` (${d.domain})` : ''}`) .join(', '); email.addHeader('X-DKIM-Result', dkimSummary); } // Apply SPF result header if (result.spf) { email.addHeader('Received-SPF', `${result.spf.result} (domain: ${result.spf.domain}, ip: ${result.spf.ip})`); // Mark as spam on SPF hard fail if (result.spf.result === 'fail') { email.mightBeSpam = true; logger.log('warn', `SPF fail for ${session.remoteAddress} — marking as potential spam`); } } // Apply DMARC result header and policy if (result.dmarc) { email.addHeader('X-DMARC-Result', `${result.dmarc.action} (policy=${result.dmarc.policy}, dkim=${result.dmarc.dkim_result}, spf=${result.dmarc.spf_result})`); if (result.dmarc.action === 'reject') { email.mightBeSpam = true; logger.log('warn', `DMARC reject for domain ${result.dmarc.domain} — marking as spam`); } else if (result.dmarc.action === 'quarantine') { email.mightBeSpam = true; logger.log('info', `DMARC quarantine for domain ${result.dmarc.domain} — marking as potential spam`); } } // Apply content scan results (from pre-computed pipeline) if (result.contentScan) { const scan = result.contentScan; if (scan.threatScore > 0) { email.addHeader('X-Spam-Score', String(scan.threatScore)); if (scan.threatType) { email.addHeader('X-Spam-Type', scan.threatType); } if (scan.threatScore >= 50) { email.mightBeSpam = true; logger.log('warn', `Content scan threat score ${scan.threatScore} (${scan.threatType}) — marking as potential spam`); } } } // Apply IP reputation results (from pre-computed pipeline) if (result.ipReputation) { const rep = result.ipReputation; email.addHeader('X-IP-Reputation-Score', String(rep.score)); if (rep.is_spam) { email.mightBeSpam = true; logger.log('warn', `IP ${rep.ip} flagged by reputation check (score=${rep.score}) — marking as potential spam`); } } logger.log('info', `Inbound security verified for email from ${session.remoteAddress}: DKIM=${result.dkim?.[0]?.status ?? 'none'}, SPF=${result.spf?.result ?? 'none'}, DMARC=${result.dmarc?.action ?? 'none'}`); } catch (err) { logger.log('warn', `Inbound security verification failed: ${err.message} — accepting email`); } } /** * Process email based on routing rules */ async processEmailByMode(emailData, session) { // Convert Buffer to Email if needed let email; if (Buffer.isBuffer(emailData)) { // Parse the email data buffer into an Email object try { const parsed = await plugins.mailparser.simpleParser(emailData); email = new Email({ from: parsed.from?.value[0]?.address || session.envelope.mailFrom.address, to: session.envelope.rcptTo[0]?.address || '', subject: parsed.subject || '', text: parsed.text || '', html: parsed.html || undefined, attachments: parsed.attachments?.map(att => ({ filename: att.filename || '', content: att.content, contentType: att.contentType })) || [] }); } catch (error) { logger.log('error', `Error parsing email data: ${error.message}`); throw new Error(`Error parsing email data: ${error.message}`); } } else { email = emailData; } // Run inbound security verification (DKIM/SPF/DMARC) via Rust bridge if (session.remoteAddress && session.remoteAddress !== '127.0.0.1') { await this.verifyInboundSecurity(email, session); } // First check if this is a bounce notification email // Look for common bounce notification subject patterns const subject = email.subject || ''; const isBounceLike = /mail delivery|delivery (failed|status|notification)|failure notice|returned mail|undeliverable|delivery problem/i.test(subject); if (isBounceLike) { logger.log('info', `Email subject matches bounce notification pattern: "${subject}"`); // Try to process as a bounce const isBounce = await this.processBounceNotification(email); if (isBounce) { logger.log('info', 'Successfully processed as bounce notification, skipping regular processing'); return email; } logger.log('info', 'Not a valid bounce notification, continuing with regular processing'); } // Find matching route const context = { email, session }; const route = await this.emailRouter.evaluateRoutes(context); if (!route) { // No matching route - reject throw new Error('No matching route for email'); } // Store matched route in session session.matchedRoute = route; // Execute action based on route await this.executeAction(route.action, email, context); // Return the processed email return email; } /** * Execute action based on route configuration */ async executeAction(action, email, context) { switch (action.type) { case 'forward': await this.handleForwardAction(action, email, context); break; case 'process': await this.handleProcessAction(action, email, context); break; case 'deliver': await this.handleDeliverAction(action, email, context); break; case 'reject': await this.handleRejectAction(action, email, context); break; default: throw new Error(`Unknown action type: ${action.type}`); } } /** * Handle forward action */ async handleForwardAction(_action, email, context) { if (!_action.forward) { throw new Error('Forward action requires forward configuration'); } const { host, port = 25, auth, addHeaders } = _action.forward; logger.log('info', `Forwarding email to ${host}:${port}`); // Add forwarding headers if (addHeaders) { for (const [key, value] of Object.entries(addHeaders)) { email.headers[key] = value; } } // Add standard forwarding headers email.headers['X-Forwarded-For'] = context.session.remoteAddress || 'unknown'; email.headers['X-Forwarded-To'] = email.to.join(', '); email.headers['X-Forwarded-Date'] = new Date().toISOString(); // Get SMTP client const client = this.getSmtpClient(host, port); try { // Send email await client.sendMail(email); logger.log('info', `Successfully forwarded email to ${host}:${port}`); SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.INFO, type: SecurityEventType.EMAIL_FORWARDING, message: 'Email forwarded successfully', ipAddress: context.session.remoteAddress, details: { sessionId: context.session.id, routeName: context.session.matchedRoute?.name, targetHost: host, targetPort: port, recipients: email.to }, success: true }); } catch (error) { logger.log('error', `Failed to forward email: ${error.message}`); SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.ERROR, type: SecurityEventType.EMAIL_FORWARDING, message: 'Email forwarding failed', ipAddress: context.session.remoteAddress, details: { sessionId: context.session.id, routeName: context.session.matchedRoute?.name, targetHost: host, targetPort: port, error: error.message }, success: false }); // Handle as bounce for (const recipient of email.getAllRecipients()) { await this.bounceManager.processSmtpFailure(recipient, error.message, { sender: email.from, originalEmailId: email.headers['Message-ID'] }); } throw error; } } /** * Handle process action */ async handleProcessAction(action, email, context) { logger.log('info', `Processing email with action options`); // Apply scanning if requested if (action.process?.scan) { // Use existing content scanner // Note: ContentScanner integration would go here logger.log('info', 'Content scanning requested'); } // Note: DKIM signing will be applied at delivery time to ensure signature validity // Queue for delivery const queue = action.process?.queue || 'normal'; await this.deliveryQueue.enqueue(email, 'process', context.session.matchedRoute); logger.log('info', `Email queued for delivery in ${queue} queue`); } /** * Handle deliver action */ async handleDeliverAction(_action, email, context) { logger.log('info', `Delivering email locally`); // Queue for local delivery await this.deliveryQueue.enqueue(email, 'mta', context.session.matchedRoute); logger.log('info', 'Email queued for local delivery'); } /** * Handle reject action */ async handleRejectAction(action, email, context) { const code = action.reject?.code || 550; const message = action.reject?.message || 'Message rejected'; logger.log('info', `Rejecting email with code ${code}: ${message}`); SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.WARN, type: SecurityEventType.EMAIL_PROCESSING, message: 'Email rejected by routing rule', ipAddress: context.session.remoteAddress, details: { sessionId: context.session.id, routeName: context.session.matchedRoute?.name, rejectCode: code, rejectMessage: message, from: email.from, to: email.to }, success: false }); // Throw error with SMTP code and message const error = new Error(message); error.responseCode = code; throw error; } /** * Handle email in MTA mode (programmatic processing) */ async _handleMtaMode(email, session) { logger.log('info', `Handling email in MTA mode for session ${session.id}`); try { // Apply MTA rule options if provided if (session.matchedRoute?.action.options?.mtaOptions) { const options = session.matchedRoute.action.options.mtaOptions; // Apply DKIM signing if enabled if (options.dkimSign && options.dkimOptions) { const dkimDomain = options.dkimOptions.domainName; const dkimSelector = options.dkimOptions.keySelector || 'mta'; logger.log('info', `Signing email with DKIM for domain ${dkimDomain}`); await this.handleDkimSigning(email, dkimDomain, dkimSelector); } } // Get email content for logging/processing const subject = email.subject; const recipients = email.getAllRecipients().join(', '); logger.log('info', `Email processed by MTA: ${subject} to ${recipients}`); SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.INFO, type: SecurityEventType.EMAIL_PROCESSING, message: 'Email processed by MTA', ipAddress: session.remoteAddress, details: { sessionId: session.id, ruleName: session.matchedRoute?.name || 'default', subject, recipients }, success: true }); } catch (error) { logger.log('error', `Failed to process email in MTA mode: ${error.message}`); SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.ERROR, type: SecurityEventType.EMAIL_PROCESSING, message: 'MTA processing failed', ipAddress: session.remoteAddress, details: { sessionId: session.id, ruleName: session.matchedRoute?.name || 'default', error: error.message }, success: false }); throw error; } } /** * Handle email in process mode (store-and-forward with scanning) */ async _handleProcessMode(email, session) { logger.log('info', `Handling email in process mode for session ${session.id}`); try { const route = session.matchedRoute; // Apply content scanning if enabled if (route?.action.options?.contentScanning && route.action.options.scanners && route.action.options.scanners.length > 0) { logger.log('info', 'Performing content scanning'); // Apply each scanner for (const scanner of route.action.options.scanners) { switch (scanner.type) { case 'spam': logger.log('info', 'Scanning for spam content'); // Implement spam scanning break; case 'virus': logger.log('info', 'Scanning for virus content'); // Implement virus scanning break; case 'attachment': logger.log('info', 'Scanning attachments'); // Check for blocked extensions if (scanner.blockedExtensions && scanner.blockedExtensions.length > 0) { for (const attachment of email.attachments) { const ext = this.getFileExtension(attachment.filename); if (scanner.blockedExtensions.includes(ext)) { if (scanner.action === 'reject') { throw new Error(`Blocked attachment type: ${ext}`); } else { // tag email.addHeader('X-Attachment-Warning', `Potentially unsafe attachment: ${attachment.filename}`); } } } } break; } } } // Apply transformations if defined if (route?.action.options?.transformations && route.action.options.transformations.length > 0) { logger.log('info', 'Applying email transformations'); for (const transform of route.action.options.transformations) { switch (transform.type) { case 'addHeader': if (transform.header && transform.value) { email.addHeader(transform.header, transform.value); } break; } } } logger.log('info', `Email successfully processed in store-and-forward mode`); SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.INFO, type: SecurityEventType.EMAIL_PROCESSING, message: 'Email processed and queued', ipAddress: session.remoteAddress, details: { sessionId: session.id, ruleName: route?.name || 'default', contentScanning: route?.action.options?.contentScanning || false, subject: email.subject }, success: true }); } catch (error) { logger.log('error', `Failed to process email: ${error.message}`); SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.ERROR, type: SecurityEventType.EMAIL_PROCESSING, message: 'Email processing failed', ipAddress: session.remoteAddress, details: { sessionId: session.id, ruleName: session.matchedRoute?.name || 'default', error: error.message }, success: false }); throw error; } } /** * Get file extension from filename */ getFileExtension(filename) { return filename.substring(filename.lastIndexOf('.')).toLowerCase(); } /** * Set up DKIM configuration for all domains */ async setupDkimForDomains() { const domainConfigs = this.domainRegistry.getAllConfigs(); if (domainConfigs.length === 0) { logger.log('warn', 'No domains configured for DKIM'); return; } for (const domainConfig of domainConfigs) { const domain = domainConfig.domain; const selector = domainConfig.dkim?.selector || 'default'; try { // Check if DKIM keys already exist for this domain let keyPair; try { // Try to read existing keys keyPair = await this.dkimCreator.readDKIMKeys(domain); logger.log('info', `Using existing DKIM keys for domain: ${domain}`); } catch (error) { // Generate new keys if they don't exist keyPair = await this.dkimCreator.createDKIMKeys(); // Store them for future use await this.dkimCreator.createAndStoreDKIMKeys(domain); logger.log('info', `Generated new DKIM keys for domain: ${domain}`); } // Store the private key for signing this.dkimKeys.set(domain, keyPair.privateKey); // DNS record creation is now handled by DnsManager logger.log('info', `DKIM keys loaded for domain: ${domain} with selector: ${selector}`); } catch (error) { logger.log('error', `Failed to set up DKIM for domain ${domain}: ${error.message}`); } } } /** * Apply per-domain rate limits from domain configurations */ applyDomainRateLimits() { const domainConfigs = this.domainRegistry.getAllConfigs(); for (const domainConfig of domainConfigs) { if (domainConfig.rateLimits) { const domain = domainConfig.domain; const rateLimitConfig = {}; // Convert domain-specific rate limits to the format expected by UnifiedRateLimiter if (domainConfig.rateLimits.outbound) { if (domainConfig.rateLimits.outbound.messagesPerMinute) { rateLimitConfig.maxMessagesPerMinute = domainConfig.rateLimits.outbound.messagesPerMinute; } // Note: messagesPerHour and messagesPerDay would need additional implementation in rate limiter } if (domainConfig.rateLimits.inbound) { if (domainConfig.rateLimits.inbound.messagesPerMinute) { rateLimitConfig.maxMessagesPerMinute = domainConfig.rateLimits.inbound.messagesPerMinute; } if (domainConfig.rateLimits.inbound.connectionsPerIp) { rateLimitConfig.maxConnectionsPerIP = domainConfig.rateLimits.inbound.connectionsPerIp; } if (domainConfig.rateLimits.inbound.recipientsPerMessage) { rateLimitConfig.maxRecipientsPerMessage = domainConfig.rateLimits.inbound.recipientsPerMessage; } } // Apply the rate limits if we have any if (Object.keys(rateLimitConfig).length > 0) { this.rateLimiter.applyDomainLimits(domain, rateLimitConfig); logger.log('info', `Applied rate limits for domain ${domain}:`, rateLimitConfig); } } } } /** * Check and rotate DKIM keys if needed */ async checkAndRotateDkimKeys() { const domainConfigs = this.domainRegistry.getAllConfigs(); for (const domainConfig of domainConfigs) { const domain = domainConfig.domain; const selector = domainConfig.dkim?.selector || 'default'; const rotateKeys = domainConfig.dkim?.rotateKeys || false; const rotationInterval = domainConfig.dkim?.rotationInterval || 90; const keySize = domainConfig.dkim?.keySize || 2048; if (!rotateKeys) { logger.log('debug', `DKIM key rotation disabled for ${domain}`); continue; } try { // Check if keys need rotation const needsRotation = await this.dkimCreator.needsRotation(domain, selector, rotationInterval); if (needsRotation) { logger.log('info', `DKIM keys need rotation for ${domain} (selector: ${selector})`); // Rotate the keys const newSelector = await this.dkimCreator.rotateDkimKeys(domain, selector, keySize); // Update the domain config with new selector domainConfig.dkim = { ...domainConfig.dkim, selector: newSelector }; // Re-register DNS handler for new selector if internal-dns mode if (domainConfig.dnsMode === 'internal-dns' && this.dcRouter.dnsServer) { // Get new public key const keyPair = await this.dkimCreator.readDKIMKeysForSelector(domain, newSelector); const publicKeyBase64 = keyPair.publicKey .replace(/-----BEGIN PUBLIC KEY-----/g, '') .replace(/-----END PUBLIC KEY-----/g, '') .replace(/\s/g, ''); const ttl = domainConfig.dns?.internal?.ttl || 3600; // Register new selector this.dcRouter.dnsServer.registerHandler(`${newSelector}._domainkey.${domain}`, ['TXT'], () => ({ name: `${newSelector}._domainkey.${domain}`, type: 'TXT', class: 'IN', ttl: ttl, data: `v=DKIM1; k=rsa; p=${publicKeyBase64}` })); logger.log('info', `DKIM DNS handler registered for new selector: ${newSelector}._domainkey.${domain}`); // Store the updated public key in storage await this.dcRouter.storageManager.set(`/email/dkim/${domain}/public.key`, keyPair.publicKey); } // Clean up old keys after grace period (async, don't wait) this.dkimCreator.cleanupOldKeys(domain, 30).catch(error => { logger.log('warn', `Failed to cleanup old DKIM keys for ${domain}: ${error.message}`); }); } else { logger.log('debug', `DKIM keys for ${domain} are up to date`); } } catch (error) { logger.log('error', `Failed to check/rotate DKIM keys for ${domain}: ${error.message}`); } } } /** * Generate SmartProxy routes for email ports */ generateProxyRoutes(portMapping) { const routes = []; const defaultPortMapping = { 25: 10025, 587: 10587, 465: 10465 }; const actualPortMapping = portMapping || defaultPortMapping; // Generate routes for each configured port for (const externalPort of this.options.ports) { const internalPort = actualPortMapping[externalPort] || externalPort + 10000; let routeName = 'email-route'; let tlsMode = 'passthrough'; // Configure based on port switch (externalPort) { case 25: routeName = 'smtp-route'; tlsMode = 'passthrough'; // STARTTLS break; case 587: routeName = 'submission-route'; tlsMode = 'passthrough'; // STARTTLS break; case 465: routeName = 'smtps-route'; tlsMode = 'terminate'; // Implicit TLS break; default: routeName = `email-port-${externalPort}-route`; } routes.push({ name: routeName, match: { ports: [externalPort] }, action: { type: 'forward', target: { host: 'localhost', port: internalPort }, tls: { mode: tlsMode } } }); } return routes; } /** * Update server configuration */ updateOptions(options) { // Stop the server if changing ports const portsChanged = options.ports && (!this.options.ports || JSON.stringify(options.ports) !== JSON.stringify(this.options.ports)); if (portsChanged) { this.stop().then(() => { this.options = { ...this.options, ...options }; this.start(); }); } else { // Update options without restart this.options = { ...this.options, ...options }; // Update domain registry if domains changed if (options.domains) { this.domainRegistry = new DomainRegistry(options.domains, options.defaults || this.options.defaults); } // Update email router if routes changed if (options.routes) { this.emailRouter.updateRoutes(options.routes); } } } /** * Update email routes */ updateEmailRoutes(routes) { this.options.routes = routes; this.emailRouter.updateRoutes(routes); } /** * Get server statistics */ getStats() { return { ...this.stats }; } /** * Get domain registry */ getDomainRegistry() { return this.domainRegistry; } /** * Update email routes dynamically */ updateRoutes(routes) { this.emailRouter.setRoutes(routes); logger.log('info', `Updated email routes with ${routes.length} routes`); } /** * Send an email through the delivery system * @param email The email to send * @param mode The processing mode to use * @param rule Optional rule to apply * @param options Optional sending options * @returns The ID of the queued email */ async sendEmail(email, mode = 'mta', route, options) { logger.log('info', `Sending email: ${email.subject} to ${email.to.join(', ')}`); try { // Validate the email if (!email.from) { throw new Error('Email must have a sender address'); } if (!email.to || email.to.length === 0) { throw new Error('Email must have at least one recipient'); } // Check if any recipients are on the suppression list (unless explicitly skipped) if (!options?.skipSuppressionCheck) { const suppressedRecipients = email.to.filter(recipient => this.isEmailSuppressed(recipient)); if (suppressedRecipients.length > 0) { // Filter out suppressed recipients const originalCount = email.to.length; const suppressed = suppressedRecipients.map(recipient => { const info = this.getSuppressionInfo(recipient); return { email: recipient, reason: info?.reason || 'Unknown', until: info?.expiresAt ? new Date(info.expiresAt).toISOString() : 'permanent' }; }); logger.log('warn', `Filtering out ${suppressedRecipients.length} suppressed recipient(s)`, { suppressed }); // If all recipients are suppressed, throw an error if (suppressedRecipients.length === originalCount) { throw new Error('All recipients are on the suppression list'); } // Filter the recipients list to only include non-suppressed addresses email.to = email.to.filter(recipient => !this.isEmailSuppressed(recipient)); } } // IP warmup handling let ipAddress = options?.ipAddress; // If no specific IP was provided, use IP warmup manager to find the best IP if (!ipAddress) { const domain = email.from.split('@')[1]; ipAddress = this.getBestIPForSending({ from: email.from, to: email.to, domain, isTransactional: options?.isTransactional }); if (ipAddress) { logger.log('info', `Selected IP ${ipAddress} for sending based on warmup status`); } } // If an IP is provided or selected by warmup manager, check its capacity if (ipAddress) { // Check if the IP can send more today if (!this.canIPSendMoreToday(ipAddress)) { logger.log('warn', `IP ${ipAddress} has reached its daily sending limit, email will be queued for later delivery`); } // Check if the IP can send more this hour if (!this.canIPSendMoreThisHour(ipAddress)) { logger.log('warn', `IP ${ipAddress} has reached its hourly sending limit, email will be queued for later delivery`); } // Record the send for IP warmup tracking this.recordIPSend(ipAddress); // Add IP header to the email email.addHeader('X-Sending-IP', ipAddress); } // Check if the sender domain has DKIM keys and sign the email if needed if (mode === 'mta' && route?.action.options?.mtaOptions?.dkimSign) { const domain = email.from.split('@')[1]; await this.handleDkimSigning(email, domain, route.action.options.mtaOptions.dkimOptions?.keySelector || 'mta'); } // Generate a unique ID for this email const id = plugins.uuid.v4(); // Queue the email for delivery await this.deliveryQueue.enqueue(email, mode, route); // Record 'sent' event for domain reputation monitoring const senderDomain = email.from.split('@')[1]; if (senderDomain) { this.recordReputationEvent(senderDomain, { type: 'sent', count: email.to.length }); } logger.log('info', `Email queued with ID: ${id}`); return id; } catch (error) { logger.log('error', `Failed to send email: ${error.message}`); throw error; } } /** * Handle DKIM signing for an email * @param email The email to sign * @param domain The domain to sign with * @param selector The DKIM selector */ async handleDkimSigning(email, domain, selector) { try { // Ensure we have DKIM keys for this domain await this.dkimCreator.handleDKIMKeysForDomain(domain); // Get the private key const { privateKey } = await this.dkimCreator.readDKIMKeys(domain); // Convert Email to raw format for signing const rawEmail = email.toRFC822String(); // Sign the email via Rust bridge const signResult = await this.rustBridge.signDkim({ rawMessage: rawEmail, domain, selector, privateKey, }); if (signResult.header) { email.addHeader('DKIM-Signature', signResult.header); logger.log('info', `Successfully added DKIM signature for ${domain}`); } } catch (error) { logger.log('error', `Failed to sign email with DKIM: ${error.message}`); // Continue without DKIM rather than failing the send } } /** * Process a bounce notification email * @param bounceEmail The email containing bounce notification information * @returns Processed bounce record or null if not a bounce */ async processBounceNotification(bounceEmail) { logger.log('info', 'Processing potential bounce notification email'); try { // Process as a bounce notification (no conversion needed anymore) const bounceRecord = await this.bounceManager.processBounceEmail(bounceEmail); if (bounceRecord) { logger.log('info', `Successfully processed bounce notification for ${bounceRecord.recipient}`, { bounceType: bounceRecord.bounceType, bounceCategory: bounceRecord.bounceCategory }); // Notify any registered listeners about the bounce this.emit('bounceProcessed', bounceRecord); // Record bounce event for domain reputation tracking if (bounceRecord.domain) { this.recordReputationEvent(bounceRecord.domain, { type: 'bounce', hardBounce: bounceRecord.bounceCategory === BounceCategory.HARD, receivingDomain: bounceRecord.recipient.split('@')[1] }); } // Log security event SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.INFO, type: SecurityEventType.EMAIL_VALIDATION, message: `Bounce notification processed for recipient`, domain: bounceRecord.domain, details: { recipient: bounceRecord.recipient, bounceType: bounceRecord.bounceType, bounceCategory: bounceRecord.bounceCategory }, success: true }); return true; } else { logger.log('info', 'Email not recognized as a bounce notification'); return false; } } catch (error) { logger.log('error', `Error processing bounce notification: ${error.message}`); SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.ERROR, type: SecurityEventType.EMAIL_VALIDATION, message: 'Failed to process bounce notification', details: { error: error.message, subject: bounceEmail.subject }, success: false }); return false; } } /** * Process an SMTP failure as a bounce * @param recipient Recipient email that failed * @param smtpResponse SMTP error response * @param options Additional options for bounce processing * @returns Processed bounce record */ async processSmtpFailure(recipient, smtpResponse, options = {}) { logger.log('info', `Processing SMTP failure for ${recipient}: ${smtpResponse}`); try { // Process the SMTP failure through the bounce manager const bounceRecord = await this.bounceManager.processSmtpFailure(recipient, smtpResponse, options); logger.log('info', `Successfully processed SMTP failure for ${recipient} as ${bounceRecord.bounceCategory} bounce`, { bounceType: bounceRecord.bounceType }); // Notify any registered listeners about the bounce this.emit('bounceProcessed', bounceRecord); // Record bounce event for domain reputation tracking if (bounceRecord.domain) { this.recordReputationEvent(bounceRecord.domain, { type: 'bounce', hardBounce: bounceRecord.bounceCategory === BounceCategory.HARD, receivingDomain: bounceRecord.recipient.split('@')[1] }); } // Log security event SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.INFO, type: SecurityEventType.EMAIL_VALIDATION, message: `SMTP failure processed for recipient`, domain: bounceRecord.domain, details: { recipient: bounceRecord.recipient, bounceType: bounceRecord.bounceType, bounceCategory: bounceRecord.bounceCategory, smtpResponse }, success: true }); return true; } catch (error) { logger.log('error', `Error processing SMTP failure: ${error.message}`); SecurityLogger.getInstance().logEvent({ level: SecurityLogLevel.ERROR, type: SecurityEventType.EMAIL_VALIDATION, message: 'Failed to process SMTP failure', details: { recipient, smtpResponse, error: error.message }, success: false }); return false; } } /** * Check if an email address is suppressed (has bounced previously) * @param email Email address to check * @returns Whether the email is suppressed */ isEmailSuppressed(email) { return this.bounceManager.isEmailSuppressed(email); } /** * Get suppression information for an email * @param email Email address to check * @returns Suppression information or null if not suppressed */ getSuppressionInfo(email) { return this.bounceManager.getSuppressionInfo(email); } /** * Get bounce history information for an email * @param email Email address to check * @returns Bounce history or null if no bounces */ getBounceHistory(email) { return this.bounceManager.getBounceInfo(email); } /** * Get all suppressed email addresses * @returns Array of suppressed email addresses */ getSuppressionList() { return this.bounceManager.getSuppressionList(); } /** * Get all hard bounced email addresses * @returns Array of hard bounced email addresses */ getHardBouncedAddresses() { return this.bounceManager.getHardBouncedAddresses(); } /** * Add an email to the suppression list * @param email Email address to suppress * @param reason Reason for suppression * @param expiresAt Optional expiration time (undefined for permanent) */ addToSuppressionList(email, reason, expiresAt) { this.bounceManager.addToSuppressionList(email, reason, expiresAt); logger.log('info', `Added ${email} to suppression list: ${reason}`); } /** * Remove an email from the suppression list * @param email Email address to remove from suppression */ removeFromSuppressionList(email) { this.bounceManager.removeFromSuppressionList(email); logger.log('info', `Removed ${email} from suppression list`); } /** * Get the status of IP warmup process * @param ipAddress Optional specific IP to check * @returns Status of IP warmup */ getIPWarmupStatus(ipAddress) { return this.ipWarmupManager.getWarmupStatus(ipAddress); } /** * Add a new IP address to the warmup process * @param ipAddress IP address to add */ addIPToWarmup(ipAddress) { this.ipWarmupManager.addIPToWarmup(ipAddress); } /** * Remove an IP address from the warmup process * @param ipAddress IP address to remove */ removeIPFromWarmup(ipAddress) { this.ipWarmupManager.removeIPFromWarmup(ipAddress); } /** * Update metrics for an IP in the warmup process * @param ipAddress IP address * @param metrics Metrics to update */ updateIPWarmupMetrics(ipAddress, metrics) { this.ipWarmupManager.updateMetrics(ipAddress, metrics); } /** * Check if an IP can send more emails today * @param ipAddress IP address to check * @returns Whether the IP can send more today */ canIPSendMoreToday(ipAddress) { return this.ipWarmupManager.canSendMoreToday(ipAddress); } /** * Check if an IP can send more emails in the current hour * @param ipAddress IP address to check * @returns Whether the IP can send more this hour */ canIPSendMoreThisHour(ipAddress) { return this.ipWarmupManager.canSendMoreThisHour(ipAddress); } /** * Get the best IP to use for sending an email based on warmup status * @param emailInfo Information about the email being sent * @returns Best IP to use or null */ getBestIPForSending(emailInfo) { return this.ipWarmupManager.getBestIPForSending(emailInfo); } /** * Set the active IP allocation policy for warmup * @param policyName Name of the policy to set */ setIPAllocationPolicy(policyName) { this.ipWarmupManager.setActiveAllocationPolicy(policyName); } /** * Record that an email was sent using a specific IP * @param ipAddress IP address used for sending */ recordIPSend(ipAddress) { this.ipWarmupManager.recordSend(ipAddress); } /** * Get reputation data for a domain * @param domain Domain to get reputation for * @returns Domain reputation metrics */ getDomainReputationData(domain) { return this.senderReputationMonitor.getReputationData(domain); } /** * Get summary reputation data for all monitored domains * @returns Summary data for all domains */ getReputationSummary() { return this.senderReputationMonitor.getReputationSummary(); } /** * Add a domain to the reputation monitoring system * @param domain Domain to add */ addDomainToMonitoring(domain) { this.senderReputationMonitor.addDomain(domain); } /** * Remove a domain from the reputation monitoring system * @param domain Domain to remove */ removeDomainFromMonitoring(domain) { this.senderReputationMonitor.removeDomain(domain); } /** * Record an email event for domain reputation tracking * @param domain Domain sending the email * @param event Event details */ recordReputationEvent(domain, event) { this.senderReputationMonitor.recordSendEvent(domain, event); } /** * Check if DKIM key exists for a domain * @param domain Domain to check */ hasDkimKey(domain) { return this.dkimKeys.has(domain); } /** * Record successful email delivery * @param domain Sending domain */ recordDelivery(domain) { this.recordReputationEvent(domain, { type: 'delivered', count: 1 }); } /** * Record email bounce * @param domain Sending domain * @param receivingDomain Receiving domain that bounced * @param bounceType Type of bounce (hard/soft) * @param reason Bounce reason */ recordBounce(domain, receivingDomain, bounceType, reason) { // Record bounce in bounce manager const bounceRecord = { id: `bounce_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`, recipient: `user@${receivingDomain}`, sender: `user@${domain}`, domain: domain, bounceType: bounceType === 'hard' ? BounceType.INVALID_RECIPIENT : BounceType.TEMPORARY_FAILURE, bounceCategory: bounceType === 'hard' ? BounceCategory.HARD : BounceCategory.SOFT, timestamp: Date.now(), smtpResponse: reason, diagnosticCode: reason, statusCode: bounceType === 'hard' ? '550' : '450', processed: false }; // Process the bounce this.bounceManager.processBounce(bounceRecord); // Record reputation event this.recordReputationEvent(domain, { type: 'bounce', count: 1, hardBounce: bounceType === 'hard', receivingDomain }); } /** * Get the rate limiter instance * @returns The unified rate limiter */ getRateLimiter() { return this.rateLimiter; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy51bmlmaWVkLmVtYWlsLnNlcnZlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL21haWwvcm91dGluZy9jbGFzc2VzLnVuaWZpZWQuZW1haWwuc2VydmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxLQUFLLEtBQUssTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sUUFBUSxDQUFDO0FBQ3RDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUN6QyxPQUFPLEVBQ0wsY0FBYyxFQUNkLGdCQUFnQixFQUNoQixpQkFBaUIsRUFDbEIsTUFBTSx5QkFBeUIsQ0FBQztBQUNqQyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFDakUsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sK0NBQStDLENBQUM7QUFDcEYsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sOENBQThDLENBQUM7QUErQmxGLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUV4RCxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDakQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQzlELE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUN0RCxPQUFPLEVBQUUsYUFBYSxFQUFFLFVBQVUsRUFBRSxjQUFjLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUM3RixPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQztBQUVqRixPQUFPLEVBQUUsdUJBQXVCLEVBQWtDLE1BQU0sd0NBQXdDLENBQUM7QUFDakgsT0FBTyxFQUFFLG9CQUFvQixFQUFzQixNQUFNLHVDQUF1QyxDQUFDO0FB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