@push.rocks/smartmta 5.1.3 → 5.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/changelog.md +15 -0
  2. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  3. package/dist_ts/00_commitinfo_data.js +9 -0
  4. package/dist_ts/index.d.ts +3 -0
  5. package/dist_ts/index.js +4 -0
  6. package/dist_ts/logger.d.ts +17 -0
  7. package/dist_ts/logger.js +76 -0
  8. package/dist_ts/mail/core/classes.bouncemanager.d.ts +185 -0
  9. package/dist_ts/mail/core/classes.bouncemanager.js +569 -0
  10. package/dist_ts/mail/core/classes.email.d.ts +291 -0
  11. package/dist_ts/mail/core/classes.email.js +802 -0
  12. package/dist_ts/mail/core/classes.emailvalidator.d.ts +61 -0
  13. package/dist_ts/mail/core/classes.emailvalidator.js +184 -0
  14. package/dist_ts/mail/core/classes.templatemanager.d.ts +95 -0
  15. package/dist_ts/mail/core/classes.templatemanager.js +240 -0
  16. package/dist_ts/mail/core/index.d.ts +4 -0
  17. package/dist_ts/mail/core/index.js +6 -0
  18. package/dist_ts/mail/delivery/classes.delivery.queue.d.ts +163 -0
  19. package/dist_ts/mail/delivery/classes.delivery.queue.js +488 -0
  20. package/dist_ts/mail/delivery/classes.delivery.system.d.ts +160 -0
  21. package/dist_ts/mail/delivery/classes.delivery.system.js +630 -0
  22. package/dist_ts/mail/delivery/classes.unified.rate.limiter.d.ts +200 -0
  23. package/dist_ts/mail/delivery/classes.unified.rate.limiter.js +820 -0
  24. package/dist_ts/mail/delivery/index.d.ts +4 -0
  25. package/dist_ts/mail/delivery/index.js +6 -0
  26. package/dist_ts/mail/delivery/interfaces.d.ts +140 -0
  27. package/dist_ts/mail/delivery/interfaces.js +17 -0
  28. package/dist_ts/mail/index.d.ts +7 -0
  29. package/dist_ts/mail/index.js +12 -0
  30. package/dist_ts/mail/routing/classes.dkim.manager.d.ts +25 -0
  31. package/dist_ts/mail/routing/classes.dkim.manager.js +127 -0
  32. package/dist_ts/mail/routing/classes.dns.manager.d.ts +79 -0
  33. package/dist_ts/mail/routing/classes.dns.manager.js +415 -0
  34. package/dist_ts/mail/routing/classes.domain.registry.d.ts +54 -0
  35. package/dist_ts/mail/routing/classes.domain.registry.js +119 -0
  36. package/dist_ts/mail/routing/classes.email.action.executor.d.ts +33 -0
  37. package/dist_ts/mail/routing/classes.email.action.executor.js +137 -0
  38. package/dist_ts/mail/routing/classes.email.router.d.ts +171 -0
  39. package/dist_ts/mail/routing/classes.email.router.js +494 -0
  40. package/dist_ts/mail/routing/classes.unified.email.server.d.ts +241 -0
  41. package/dist_ts/mail/routing/classes.unified.email.server.js +935 -0
  42. package/dist_ts/mail/routing/index.d.ts +7 -0
  43. package/dist_ts/mail/routing/index.js +9 -0
  44. package/dist_ts/mail/routing/interfaces.d.ts +187 -0
  45. package/dist_ts/mail/routing/interfaces.js +2 -0
  46. package/dist_ts/mail/security/classes.dkimcreator.d.ts +72 -0
  47. package/dist_ts/mail/security/classes.dkimcreator.js +360 -0
  48. package/dist_ts/mail/security/classes.spfverifier.d.ts +62 -0
  49. package/dist_ts/mail/security/classes.spfverifier.js +87 -0
  50. package/dist_ts/mail/security/index.d.ts +2 -0
  51. package/dist_ts/mail/security/index.js +4 -0
  52. package/dist_ts/paths.d.ts +14 -0
  53. package/dist_ts/paths.js +39 -0
  54. package/dist_ts/plugins.d.ts +24 -0
  55. package/dist_ts/plugins.js +28 -0
  56. package/dist_ts/security/classes.contentscanner.d.ts +130 -0
  57. package/dist_ts/security/classes.contentscanner.js +338 -0
  58. package/dist_ts/security/classes.ipreputationchecker.d.ts +73 -0
  59. package/dist_ts/security/classes.ipreputationchecker.js +263 -0
  60. package/dist_ts/security/classes.rustsecuritybridge.d.ts +403 -0
  61. package/dist_ts/security/classes.rustsecuritybridge.js +502 -0
  62. package/dist_ts/security/classes.securitylogger.d.ts +140 -0
  63. package/dist_ts/security/classes.securitylogger.js +235 -0
  64. package/dist_ts/security/index.d.ts +4 -0
  65. package/dist_ts/security/index.js +5 -0
  66. package/package.json +6 -1
  67. package/ts/00_commitinfo_data.ts +8 -0
  68. package/ts/index.ts +3 -0
  69. package/ts/logger.ts +91 -0
  70. package/ts/mail/core/classes.bouncemanager.ts +731 -0
  71. package/ts/mail/core/classes.email.ts +942 -0
  72. package/ts/mail/core/classes.emailvalidator.ts +239 -0
  73. package/ts/mail/core/classes.templatemanager.ts +320 -0
  74. package/ts/mail/core/index.ts +5 -0
  75. package/ts/mail/delivery/classes.delivery.queue.ts +645 -0
  76. package/ts/mail/delivery/classes.delivery.system.ts +816 -0
  77. package/ts/mail/delivery/classes.unified.rate.limiter.ts +1053 -0
  78. package/ts/mail/delivery/index.ts +5 -0
  79. package/ts/mail/delivery/interfaces.ts +167 -0
  80. package/ts/mail/index.ts +17 -0
  81. package/ts/mail/routing/classes.dkim.manager.ts +157 -0
  82. package/ts/mail/routing/classes.dns.manager.ts +573 -0
  83. package/ts/mail/routing/classes.domain.registry.ts +139 -0
  84. package/ts/mail/routing/classes.email.action.executor.ts +175 -0
  85. package/ts/mail/routing/classes.email.router.ts +575 -0
  86. package/ts/mail/routing/classes.unified.email.server.ts +1207 -0
  87. package/ts/mail/routing/index.ts +9 -0
  88. package/ts/mail/routing/interfaces.ts +202 -0
  89. package/ts/mail/security/classes.dkimcreator.ts +447 -0
  90. package/ts/mail/security/classes.spfverifier.ts +126 -0
  91. package/ts/mail/security/index.ts +3 -0
  92. package/ts/paths.ts +48 -0
  93. package/ts/plugins.ts +53 -0
  94. package/ts/security/classes.contentscanner.ts +400 -0
  95. package/ts/security/classes.ipreputationchecker.ts +315 -0
  96. package/ts/security/classes.rustsecuritybridge.ts +964 -0
  97. package/ts/security/classes.securitylogger.ts +299 -0
  98. package/ts/security/index.ts +40 -0
@@ -0,0 +1,935 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import * as paths from '../../paths.js';
3
+ import { EventEmitter } from 'events';
4
+ import { logger } from '../../logger.js';
5
+ import { SecurityLogger, SecurityLogLevel, SecurityEventType } from '../../security/index.js';
6
+ import { DKIMCreator } from '../security/classes.dkimcreator.js';
7
+ import { RustSecurityBridge } from '../../security/classes.rustsecuritybridge.js';
8
+ import { EmailRouter } from './classes.email.router.js';
9
+ import { Email } from '../core/classes.email.js';
10
+ import { DomainRegistry } from './classes.domain.registry.js';
11
+ import { DnsManager } from './classes.dns.manager.js';
12
+ import { BounceManager, BounceType, BounceCategory } from '../core/classes.bouncemanager.js';
13
+ import { MultiModeDeliverySystem } from '../delivery/classes.delivery.system.js';
14
+ import { UnifiedDeliveryQueue } from '../delivery/classes.delivery.queue.js';
15
+ import { UnifiedRateLimiter } from '../delivery/classes.unified.rate.limiter.js';
16
+ import { SmtpState } from '../delivery/interfaces.js';
17
+ import { EmailActionExecutor } from './classes.email.action.executor.js';
18
+ import { DkimManager } from './classes.dkim.manager.js';
19
+ /**
20
+ * Unified email server that handles all email traffic with pattern-based routing
21
+ */
22
+ export class UnifiedEmailServer extends EventEmitter {
23
+ dcRouter;
24
+ options;
25
+ emailRouter;
26
+ domainRegistry;
27
+ servers = [];
28
+ stats;
29
+ // Add components needed for sending and securing emails
30
+ dkimCreator;
31
+ rustBridge;
32
+ bounceManager;
33
+ deliveryQueue;
34
+ deliverySystem;
35
+ rateLimiter; // TODO: Implement rate limiting in SMTP server handlers
36
+ // Extracted subsystems
37
+ actionExecutor;
38
+ dkimManager;
39
+ constructor(dcRouter, options) {
40
+ super();
41
+ this.dcRouter = dcRouter;
42
+ // Set default options
43
+ this.options = {
44
+ ...options,
45
+ banner: options.banner || `${options.hostname} ESMTP UnifiedEmailServer`,
46
+ maxMessageSize: options.maxMessageSize || 10 * 1024 * 1024, // 10MB
47
+ maxClients: options.maxClients || 100,
48
+ maxConnections: options.maxConnections || 1000,
49
+ connectionTimeout: options.connectionTimeout || 60000, // 1 minute
50
+ socketTimeout: options.socketTimeout || 60000 // 1 minute
51
+ };
52
+ // Initialize Rust security bridge (singleton)
53
+ this.rustBridge = RustSecurityBridge.getInstance();
54
+ // Initialize DKIM creator with storage manager
55
+ this.dkimCreator = new DKIMCreator(paths.keysDir, dcRouter.storageManager);
56
+ // Initialize bounce manager with storage manager
57
+ this.bounceManager = new BounceManager({
58
+ maxCacheSize: 10000,
59
+ cacheTTL: 30 * 24 * 60 * 60 * 1000, // 30 days
60
+ storageManager: dcRouter.storageManager
61
+ });
62
+ // Initialize domain registry
63
+ this.domainRegistry = new DomainRegistry(options.domains, options.defaults);
64
+ // Initialize email router with routes and storage manager
65
+ this.emailRouter = new EmailRouter(options.routes || [], {
66
+ storageManager: dcRouter.storageManager,
67
+ persistChanges: true
68
+ });
69
+ // Initialize rate limiter
70
+ this.rateLimiter = new UnifiedRateLimiter(options.rateLimits || {
71
+ global: {
72
+ maxConnectionsPerIP: 10,
73
+ maxMessagesPerMinute: 100,
74
+ maxRecipientsPerMessage: 50,
75
+ maxErrorsPerIP: 10,
76
+ maxAuthFailuresPerIP: 5,
77
+ blockDuration: 300000 // 5 minutes
78
+ }
79
+ });
80
+ // Initialize delivery components
81
+ const queueOptions = {
82
+ storageType: 'memory', // Default to memory storage
83
+ maxRetries: 3,
84
+ baseRetryDelay: 300000, // 5 minutes
85
+ maxRetryDelay: 3600000 // 1 hour
86
+ };
87
+ this.deliveryQueue = new UnifiedDeliveryQueue(queueOptions);
88
+ const deliveryOptions = {
89
+ globalRateLimit: 100, // Default to 100 emails per minute
90
+ concurrentDeliveries: 10,
91
+ processBounces: true,
92
+ bounceHandler: {
93
+ processSmtpFailure: this.processSmtpFailure.bind(this)
94
+ },
95
+ onDeliverySuccess: async (_item, _result) => {
96
+ // Delivery success recorded via delivery system
97
+ }
98
+ };
99
+ this.deliverySystem = new MultiModeDeliverySystem(this.deliveryQueue, deliveryOptions, this);
100
+ // Initialize action executor
101
+ this.actionExecutor = new EmailActionExecutor({
102
+ sendOutboundEmail: this.sendOutboundEmail.bind(this),
103
+ bounceManager: this.bounceManager,
104
+ deliveryQueue: this.deliveryQueue,
105
+ });
106
+ // Initialize DKIM manager
107
+ this.dkimManager = new DkimManager(this.dkimCreator, this.domainRegistry, dcRouter, this.rustBridge);
108
+ // Initialize statistics
109
+ this.stats = {
110
+ startTime: new Date(),
111
+ connections: {
112
+ current: 0,
113
+ total: 0
114
+ },
115
+ messages: {
116
+ processed: 0,
117
+ delivered: 0,
118
+ failed: 0
119
+ },
120
+ processingTime: {
121
+ avg: 0,
122
+ max: 0,
123
+ min: 0
124
+ }
125
+ };
126
+ // We'll create the SMTP servers during the start() method
127
+ }
128
+ /**
129
+ * Send an outbound email via the Rust SMTP client.
130
+ * Uses connection pooling in the Rust binary for efficiency.
131
+ */
132
+ async sendOutboundEmail(host, port, email, options) {
133
+ // Build DKIM config if domain has keys
134
+ let dkim;
135
+ if (options?.dkimDomain) {
136
+ try {
137
+ const { privateKey } = await this.dkimCreator.readDKIMKeys(options.dkimDomain);
138
+ dkim = { domain: options.dkimDomain, selector: options.dkimSelector || 'default', privateKey };
139
+ }
140
+ catch (err) {
141
+ logger.log('warn', `Failed to read DKIM keys for ${options.dkimDomain}: ${err.message}`);
142
+ }
143
+ }
144
+ // Serialize the Email to the outbound format
145
+ const outboundEmail = {
146
+ from: email.from,
147
+ to: email.to,
148
+ cc: email.cc || [],
149
+ bcc: email.bcc || [],
150
+ subject: email.subject || '',
151
+ text: email.text || '',
152
+ html: email.html || undefined,
153
+ headers: email.headers || {},
154
+ };
155
+ return this.rustBridge.sendOutboundEmail({
156
+ host,
157
+ port,
158
+ secure: port === 465,
159
+ domain: this.options.hostname,
160
+ auth: options?.auth,
161
+ email: outboundEmail,
162
+ dkim,
163
+ connectionTimeoutSecs: Math.floor((this.options.outbound?.connectionTimeout || 30000) / 1000),
164
+ socketTimeoutSecs: Math.floor((this.options.outbound?.socketTimeout || 120000) / 1000),
165
+ poolKey: `${host}:${port}`,
166
+ maxPoolConnections: this.options.outbound?.maxConnections || 10,
167
+ tlsOpportunistic: options?.tlsOpportunistic ?? (port === 25),
168
+ });
169
+ }
170
+ /**
171
+ * Start the unified email server
172
+ */
173
+ async start() {
174
+ logger.log('info', `Starting UnifiedEmailServer on ports: ${this.options.ports.join(', ')}`);
175
+ try {
176
+ await this.startDeliveryPipeline();
177
+ await this.startRustBridge();
178
+ await this.initializeDkimAndDns();
179
+ this.registerBridgeEventHandlers();
180
+ await this.startSmtpServer();
181
+ logger.log('info', 'UnifiedEmailServer started successfully');
182
+ this.emit('started');
183
+ }
184
+ catch (error) {
185
+ logger.log('error', `Failed to start UnifiedEmailServer: ${error.message}`);
186
+ throw error;
187
+ }
188
+ }
189
+ async startDeliveryPipeline() {
190
+ await this.deliveryQueue.initialize();
191
+ logger.log('info', 'Email delivery queue initialized');
192
+ await this.deliverySystem.start();
193
+ logger.log('info', 'Email delivery system started');
194
+ }
195
+ async startRustBridge() {
196
+ const bridgeOk = await this.rustBridge.start();
197
+ if (!bridgeOk) {
198
+ throw new Error('Rust security bridge failed to start. The mailer-bin binary is required. Run "pnpm build" to compile it.');
199
+ }
200
+ logger.log('info', 'Rust security bridge started — Rust is the primary security backend');
201
+ this.rustBridge.on('stateChange', ({ oldState, newState }) => {
202
+ if (newState === 'failed')
203
+ this.emit('bridgeFailed');
204
+ else if (newState === 'restarting')
205
+ this.emit('bridgeRestarting');
206
+ else if (newState === 'running' && oldState === 'restarting')
207
+ this.emit('bridgeRecovered');
208
+ });
209
+ }
210
+ async initializeDkimAndDns() {
211
+ await this.dkimManager.setupDkimForDomains();
212
+ logger.log('info', 'DKIM configuration completed for all domains');
213
+ const dnsManager = new DnsManager(this.dcRouter);
214
+ await dnsManager.ensureDnsRecords(this.domainRegistry.getAllConfigs(), this.dkimCreator);
215
+ logger.log('info', 'DNS records ensured for all configured domains');
216
+ this.applyDomainRateLimits();
217
+ logger.log('info', 'Per-domain rate limits configured');
218
+ await this.dkimManager.checkAndRotateDkimKeys();
219
+ logger.log('info', 'DKIM key rotation check completed');
220
+ }
221
+ registerBridgeEventHandlers() {
222
+ this.rustBridge.onEmailReceived(async (data) => {
223
+ try {
224
+ await this.handleRustEmailReceived(data);
225
+ }
226
+ catch (err) {
227
+ logger.log('error', `Error handling email from Rust SMTP: ${err.message}`);
228
+ try {
229
+ await this.rustBridge.sendEmailProcessingResult({
230
+ correlationId: data.correlationId,
231
+ accepted: false,
232
+ smtpCode: 451,
233
+ smtpMessage: 'Internal processing error',
234
+ });
235
+ }
236
+ catch (sendErr) {
237
+ logger.log('warn', `Could not send rejection back to Rust: ${sendErr.message}`);
238
+ }
239
+ }
240
+ });
241
+ this.rustBridge.onAuthRequest(async (data) => {
242
+ try {
243
+ await this.handleRustAuthRequest(data);
244
+ }
245
+ catch (err) {
246
+ logger.log('error', `Error handling auth from Rust SMTP: ${err.message}`);
247
+ try {
248
+ await this.rustBridge.sendAuthResult({
249
+ correlationId: data.correlationId,
250
+ success: false,
251
+ message: 'Internal auth error',
252
+ });
253
+ }
254
+ catch (sendErr) {
255
+ logger.log('warn', `Could not send auth rejection back to Rust: ${sendErr.message}`);
256
+ }
257
+ }
258
+ });
259
+ this.rustBridge.onScramCredentialRequest(async (data) => {
260
+ try {
261
+ await this.handleScramCredentialRequest(data);
262
+ }
263
+ catch (err) {
264
+ logger.log('error', `Error handling SCRAM credential request: ${err.message}`);
265
+ try {
266
+ await this.rustBridge.sendScramCredentialResult({
267
+ correlationId: data.correlationId,
268
+ found: false,
269
+ });
270
+ }
271
+ catch (sendErr) {
272
+ logger.log('warn', `Could not send SCRAM credential rejection: ${sendErr.message}`);
273
+ }
274
+ }
275
+ });
276
+ }
277
+ async startSmtpServer() {
278
+ const hasTlsConfig = this.options.tls?.keyPath && this.options.tls?.certPath;
279
+ let tlsCertPem;
280
+ let tlsKeyPem;
281
+ if (hasTlsConfig) {
282
+ try {
283
+ tlsKeyPem = plugins.fs.readFileSync(this.options.tls.keyPath, 'utf8');
284
+ tlsCertPem = plugins.fs.readFileSync(this.options.tls.certPath, 'utf8');
285
+ logger.log('info', 'TLS certificates loaded successfully');
286
+ }
287
+ catch (error) {
288
+ logger.log('warn', `Failed to load TLS certificates: ${error.message}`);
289
+ }
290
+ }
291
+ const smtpPorts = this.options.ports.filter(p => p !== 465);
292
+ const securePort = this.options.ports.find(p => p === 465);
293
+ const started = await this.rustBridge.startSmtpServer({
294
+ hostname: this.options.hostname,
295
+ ports: smtpPorts,
296
+ securePort: securePort,
297
+ tlsCertPem,
298
+ tlsKeyPem,
299
+ maxMessageSize: this.options.maxMessageSize || 10 * 1024 * 1024,
300
+ maxConnections: this.options.maxConnections || this.options.maxClients || 100,
301
+ maxRecipients: 100,
302
+ connectionTimeoutSecs: this.options.connectionTimeout ? Math.floor(this.options.connectionTimeout / 1000) : 30,
303
+ dataTimeoutSecs: 60,
304
+ authEnabled: !!this.options.auth?.required || !!(this.options.auth?.users?.length),
305
+ maxAuthFailures: 3,
306
+ socketTimeoutSecs: this.options.socketTimeout ? Math.floor(this.options.socketTimeout / 1000) : 300,
307
+ processingTimeoutSecs: 30,
308
+ rateLimits: this.options.rateLimits ? {
309
+ maxConnectionsPerIp: this.options.rateLimits.global?.maxConnectionsPerIP || 50,
310
+ maxMessagesPerSender: this.options.rateLimits.global?.maxMessagesPerMinute || 100,
311
+ maxAuthFailuresPerIp: this.options.rateLimits.global?.maxAuthFailuresPerIP || 5,
312
+ windowSecs: 60,
313
+ } : undefined,
314
+ });
315
+ if (!started) {
316
+ throw new Error('Failed to start Rust SMTP server');
317
+ }
318
+ logger.log('info', `Rust SMTP server listening on ports: ${smtpPorts.join(', ')}${securePort ? ` + ${securePort} (TLS)` : ''}`);
319
+ }
320
+ /**
321
+ * Stop the unified email server
322
+ */
323
+ async stop() {
324
+ logger.log('info', 'Stopping UnifiedEmailServer');
325
+ try {
326
+ // Stop the Rust SMTP server first
327
+ try {
328
+ await this.rustBridge.stopSmtpServer();
329
+ logger.log('info', 'Rust SMTP server stopped');
330
+ }
331
+ catch (err) {
332
+ logger.log('warn', `Error stopping Rust SMTP server: ${err.message}`);
333
+ }
334
+ // Clear the servers array - servers will be garbage collected
335
+ this.servers = [];
336
+ // Remove bridge state change listener and stop bridge
337
+ this.rustBridge.removeAllListeners('stateChange');
338
+ await this.rustBridge.stop();
339
+ // Stop the delivery system
340
+ if (this.deliverySystem) {
341
+ await this.deliverySystem.stop();
342
+ logger.log('info', 'Email delivery system stopped');
343
+ }
344
+ // Shut down the delivery queue
345
+ if (this.deliveryQueue) {
346
+ await this.deliveryQueue.shutdown();
347
+ logger.log('info', 'Email delivery queue shut down');
348
+ }
349
+ // Close all Rust SMTP client connection pools
350
+ try {
351
+ await this.rustBridge.closeSmtpPool();
352
+ }
353
+ catch {
354
+ // Bridge may already be stopped
355
+ }
356
+ logger.log('info', 'UnifiedEmailServer stopped successfully');
357
+ this.emit('stopped');
358
+ }
359
+ catch (error) {
360
+ logger.log('error', `Error stopping UnifiedEmailServer: ${error.message}`);
361
+ throw error;
362
+ }
363
+ }
364
+ // -----------------------------------------------------------------------
365
+ // Rust SMTP server event handlers
366
+ // -----------------------------------------------------------------------
367
+ /**
368
+ * Handle an emailReceived event from the Rust SMTP server.
369
+ */
370
+ async handleRustEmailReceived(data) {
371
+ const { correlationId, mailFrom, rcptTo, remoteAddr, clientHostname, secure, authenticatedUser } = data;
372
+ logger.log('info', `Rust SMTP received email from=${mailFrom} to=${rcptTo.join(',')} remote=${remoteAddr}`);
373
+ try {
374
+ // Decode the email data
375
+ let rawMessageBuffer;
376
+ if (data.data.type === 'inline' && data.data.base64) {
377
+ rawMessageBuffer = Buffer.from(data.data.base64, 'base64');
378
+ }
379
+ else if (data.data.type === 'file' && data.data.path) {
380
+ rawMessageBuffer = plugins.fs.readFileSync(data.data.path);
381
+ // Clean up temp file
382
+ try {
383
+ plugins.fs.unlinkSync(data.data.path);
384
+ }
385
+ catch {
386
+ // Ignore cleanup errors
387
+ }
388
+ }
389
+ else {
390
+ throw new Error('Invalid email data transport');
391
+ }
392
+ // Build a session-like object for processEmailByMode
393
+ const session = {
394
+ id: data.sessionId || 'rust-' + Math.random().toString(36).substring(2),
395
+ state: SmtpState.FINISHED,
396
+ mailFrom: mailFrom,
397
+ rcptTo: rcptTo,
398
+ emailData: rawMessageBuffer.toString('utf8'),
399
+ useTLS: secure,
400
+ connectionEnded: false,
401
+ remoteAddress: remoteAddr,
402
+ clientHostname: clientHostname || '',
403
+ secure: secure,
404
+ authenticated: !!authenticatedUser,
405
+ envelope: {
406
+ mailFrom: { address: mailFrom, args: {} },
407
+ rcptTo: rcptTo.map(addr => ({ address: addr, args: {} })),
408
+ },
409
+ };
410
+ if (authenticatedUser) {
411
+ session.user = { username: authenticatedUser };
412
+ }
413
+ // Attach pre-computed security results from Rust in-process pipeline
414
+ if (data.securityResults) {
415
+ session._precomputedSecurityResults = data.securityResults;
416
+ }
417
+ // Process the email through the routing system
418
+ await this.processEmailByMode(rawMessageBuffer, session);
419
+ // Send acceptance back to Rust
420
+ await this.rustBridge.sendEmailProcessingResult({
421
+ correlationId,
422
+ accepted: true,
423
+ smtpCode: 250,
424
+ smtpMessage: '2.0.0 Message accepted for delivery',
425
+ });
426
+ }
427
+ catch (err) {
428
+ logger.log('error', `Failed to process email from Rust SMTP: ${err.message}`);
429
+ await this.rustBridge.sendEmailProcessingResult({
430
+ correlationId,
431
+ accepted: false,
432
+ smtpCode: 550,
433
+ smtpMessage: `5.0.0 Processing failed: ${err.message}`,
434
+ });
435
+ }
436
+ }
437
+ /**
438
+ * Handle an authRequest event from the Rust SMTP server.
439
+ */
440
+ async handleRustAuthRequest(data) {
441
+ const { correlationId, username, password, remoteAddr } = data;
442
+ logger.log('info', `Rust SMTP auth request for user=${username} from=${remoteAddr}`);
443
+ // Check against configured users
444
+ const users = this.options.auth?.users || [];
445
+ const matched = users.find(u => u.username === username && u.password === password);
446
+ if (matched) {
447
+ await this.rustBridge.sendAuthResult({
448
+ correlationId,
449
+ success: true,
450
+ });
451
+ }
452
+ else {
453
+ logger.log('warn', `Auth failed for user=${username} from=${remoteAddr}`);
454
+ await this.rustBridge.sendAuthResult({
455
+ correlationId,
456
+ success: false,
457
+ message: 'Invalid credentials',
458
+ });
459
+ }
460
+ }
461
+ /**
462
+ * Handle a SCRAM credential request from the Rust SMTP server.
463
+ * Computes SCRAM-SHA-256 credentials from the stored password for the given user.
464
+ */
465
+ async handleScramCredentialRequest(data) {
466
+ const { correlationId, username, remoteAddr } = data;
467
+ const crypto = await import('crypto');
468
+ logger.log('info', `SCRAM credential request for user=${username} from=${remoteAddr}`);
469
+ const users = this.options.auth?.users || [];
470
+ const matched = users.find(u => u.username === username);
471
+ if (!matched) {
472
+ await this.rustBridge.sendScramCredentialResult({
473
+ correlationId,
474
+ found: false,
475
+ });
476
+ return;
477
+ }
478
+ // Compute SCRAM-SHA-256 credentials from plaintext password
479
+ const salt = crypto.randomBytes(16);
480
+ const iterations = 4096;
481
+ // SaltedPassword = PBKDF2-HMAC-SHA256(password, salt, iterations, 32)
482
+ const saltedPassword = crypto.pbkdf2Sync(matched.password, salt, iterations, 32, 'sha256');
483
+ // ClientKey = HMAC-SHA256(SaltedPassword, "Client Key")
484
+ const clientKey = crypto.createHmac('sha256', saltedPassword).update('Client Key').digest();
485
+ // StoredKey = SHA256(ClientKey)
486
+ const storedKey = crypto.createHash('sha256').update(clientKey).digest();
487
+ // ServerKey = HMAC-SHA256(SaltedPassword, "Server Key")
488
+ const serverKey = crypto.createHmac('sha256', saltedPassword).update('Server Key').digest();
489
+ await this.rustBridge.sendScramCredentialResult({
490
+ correlationId,
491
+ found: true,
492
+ salt: salt.toString('base64'),
493
+ iterations,
494
+ storedKey: storedKey.toString('base64'),
495
+ serverKey: serverKey.toString('base64'),
496
+ });
497
+ }
498
+ /**
499
+ * Verify inbound email security (DKIM/SPF/DMARC) using pre-computed Rust results
500
+ * or falling back to IPC call if no pre-computed results are available.
501
+ */
502
+ async verifyInboundSecurity(email, session) {
503
+ try {
504
+ // Check for pre-computed results from Rust in-process security pipeline
505
+ const precomputed = session._precomputedSecurityResults;
506
+ let result;
507
+ if (precomputed) {
508
+ logger.log('info', 'Using pre-computed security results from Rust in-process pipeline');
509
+ result = precomputed;
510
+ }
511
+ else {
512
+ // Fallback: IPC round-trip to Rust (for backward compat)
513
+ const rawMessage = session.emailData || email.toRFC822String();
514
+ result = await this.rustBridge.verifyEmail({
515
+ rawMessage,
516
+ ip: session.remoteAddress,
517
+ heloDomain: session.clientHostname || '',
518
+ hostname: this.options.hostname,
519
+ mailFrom: session.envelope?.mailFrom?.address || session.mailFrom || '',
520
+ });
521
+ }
522
+ // Apply DKIM result headers
523
+ if (result.dkim && result.dkim.length > 0) {
524
+ const dkimSummary = result.dkim
525
+ .map((d) => `${d.status}${d.domain ? ` (${d.domain})` : ''}`)
526
+ .join(', ');
527
+ email.addHeader('X-DKIM-Result', dkimSummary);
528
+ }
529
+ // Apply SPF result header
530
+ if (result.spf) {
531
+ email.addHeader('Received-SPF', `${result.spf.result} (domain: ${result.spf.domain}, ip: ${result.spf.ip})`);
532
+ // Mark as spam on SPF hard fail
533
+ if (result.spf.result === 'fail') {
534
+ email.mightBeSpam = true;
535
+ logger.log('warn', `SPF fail for ${session.remoteAddress} — marking as potential spam`);
536
+ }
537
+ }
538
+ // Apply DMARC result header and policy
539
+ if (result.dmarc) {
540
+ email.addHeader('X-DMARC-Result', `${result.dmarc.action} (policy=${result.dmarc.policy}, dkim=${result.dmarc.dkim_result}, spf=${result.dmarc.spf_result})`);
541
+ if (result.dmarc.action === 'reject') {
542
+ email.mightBeSpam = true;
543
+ logger.log('warn', `DMARC reject for domain ${result.dmarc.domain} — marking as spam`);
544
+ }
545
+ else if (result.dmarc.action === 'quarantine') {
546
+ email.mightBeSpam = true;
547
+ logger.log('info', `DMARC quarantine for domain ${result.dmarc.domain} — marking as potential spam`);
548
+ }
549
+ }
550
+ // Apply content scan results (from pre-computed pipeline)
551
+ if (result.contentScan) {
552
+ const scan = result.contentScan;
553
+ if (scan.threatScore > 0) {
554
+ email.addHeader('X-Spam-Score', String(scan.threatScore));
555
+ if (scan.threatType) {
556
+ email.addHeader('X-Spam-Type', scan.threatType);
557
+ }
558
+ if (scan.threatScore >= 50) {
559
+ email.mightBeSpam = true;
560
+ logger.log('warn', `Content scan threat score ${scan.threatScore} (${scan.threatType}) — marking as potential spam`);
561
+ }
562
+ }
563
+ }
564
+ // Apply IP reputation results (from pre-computed pipeline)
565
+ if (result.ipReputation) {
566
+ const rep = result.ipReputation;
567
+ email.addHeader('X-IP-Reputation-Score', String(rep.score));
568
+ if (rep.is_spam) {
569
+ email.mightBeSpam = true;
570
+ logger.log('warn', `IP ${rep.ip} flagged by reputation check (score=${rep.score}) — marking as potential spam`);
571
+ }
572
+ }
573
+ 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'}`);
574
+ }
575
+ catch (err) {
576
+ logger.log('warn', `Inbound security verification failed: ${err.message} — accepting email`);
577
+ }
578
+ }
579
+ /**
580
+ * Process email based on routing rules
581
+ */
582
+ async processEmailByMode(emailData, session) {
583
+ // Convert Buffer to Email if needed
584
+ let email;
585
+ if (Buffer.isBuffer(emailData)) {
586
+ // Parse the email data buffer into an Email object
587
+ try {
588
+ const parsed = await plugins.mailparser.simpleParser(emailData);
589
+ email = new Email({
590
+ from: parsed.from?.value[0]?.address || session.envelope.mailFrom.address,
591
+ to: session.envelope.rcptTo[0]?.address || '',
592
+ subject: parsed.subject || '',
593
+ text: parsed.text || '',
594
+ html: parsed.html || undefined,
595
+ attachments: parsed.attachments?.map(att => ({
596
+ filename: att.filename || '',
597
+ content: att.content,
598
+ contentType: att.contentType
599
+ })) || []
600
+ });
601
+ }
602
+ catch (error) {
603
+ logger.log('error', `Error parsing email data: ${error.message}`);
604
+ throw new Error(`Error parsing email data: ${error.message}`);
605
+ }
606
+ }
607
+ else {
608
+ email = emailData;
609
+ }
610
+ // Run inbound security verification (DKIM/SPF/DMARC) via Rust bridge
611
+ if (session.remoteAddress && session.remoteAddress !== '127.0.0.1') {
612
+ await this.verifyInboundSecurity(email, session);
613
+ }
614
+ // First check if this is a bounce notification email
615
+ const subject = email.subject || '';
616
+ const isBounceLike = /mail delivery|delivery (failed|status|notification)|failure notice|returned mail|undeliverable|delivery problem/i.test(subject);
617
+ if (isBounceLike) {
618
+ logger.log('info', `Email subject matches bounce notification pattern: "${subject}"`);
619
+ const isBounce = await this.processBounceNotification(email);
620
+ if (isBounce) {
621
+ logger.log('info', 'Successfully processed as bounce notification, skipping regular processing');
622
+ return email;
623
+ }
624
+ logger.log('info', 'Not a valid bounce notification, continuing with regular processing');
625
+ }
626
+ // Find matching route
627
+ const context = { email, session };
628
+ const route = await this.emailRouter.evaluateRoutes(context);
629
+ if (!route) {
630
+ throw new Error('No matching route for email');
631
+ }
632
+ // Store matched route in session
633
+ session.matchedRoute = route;
634
+ // Execute action based on route
635
+ await this.actionExecutor.executeAction(route.action, email, context);
636
+ // Return the processed email
637
+ return email;
638
+ }
639
+ /**
640
+ * Apply per-domain rate limits from domain configurations
641
+ */
642
+ applyDomainRateLimits() {
643
+ const domainConfigs = this.domainRegistry.getAllConfigs();
644
+ for (const domainConfig of domainConfigs) {
645
+ if (domainConfig.rateLimits) {
646
+ const domain = domainConfig.domain;
647
+ const rateLimitConfig = {};
648
+ if (domainConfig.rateLimits.outbound) {
649
+ if (domainConfig.rateLimits.outbound.messagesPerMinute) {
650
+ rateLimitConfig.maxMessagesPerMinute = domainConfig.rateLimits.outbound.messagesPerMinute;
651
+ }
652
+ }
653
+ if (domainConfig.rateLimits.inbound) {
654
+ if (domainConfig.rateLimits.inbound.messagesPerMinute) {
655
+ rateLimitConfig.maxMessagesPerMinute = domainConfig.rateLimits.inbound.messagesPerMinute;
656
+ }
657
+ if (domainConfig.rateLimits.inbound.connectionsPerIp) {
658
+ rateLimitConfig.maxConnectionsPerIP = domainConfig.rateLimits.inbound.connectionsPerIp;
659
+ }
660
+ if (domainConfig.rateLimits.inbound.recipientsPerMessage) {
661
+ rateLimitConfig.maxRecipientsPerMessage = domainConfig.rateLimits.inbound.recipientsPerMessage;
662
+ }
663
+ }
664
+ if (Object.keys(rateLimitConfig).length > 0) {
665
+ this.rateLimiter.applyDomainLimits(domain, rateLimitConfig);
666
+ logger.log('info', `Applied rate limits for domain ${domain}:`, rateLimitConfig);
667
+ }
668
+ }
669
+ }
670
+ }
671
+ /**
672
+ * Generate SmartProxy routes for email ports
673
+ */
674
+ generateProxyRoutes(portMapping) {
675
+ const routes = [];
676
+ const defaultPortMapping = {
677
+ 25: 10025,
678
+ 587: 10587,
679
+ 465: 10465
680
+ };
681
+ const actualPortMapping = portMapping || defaultPortMapping;
682
+ for (const externalPort of this.options.ports) {
683
+ const internalPort = actualPortMapping[externalPort] || externalPort + 10000;
684
+ let routeName = 'email-route';
685
+ let tlsMode = 'passthrough';
686
+ switch (externalPort) {
687
+ case 25:
688
+ routeName = 'smtp-route';
689
+ tlsMode = 'passthrough';
690
+ break;
691
+ case 587:
692
+ routeName = 'submission-route';
693
+ tlsMode = 'passthrough';
694
+ break;
695
+ case 465:
696
+ routeName = 'smtps-route';
697
+ tlsMode = 'terminate';
698
+ break;
699
+ default:
700
+ routeName = `email-port-${externalPort}-route`;
701
+ }
702
+ routes.push({
703
+ name: routeName,
704
+ match: {
705
+ ports: [externalPort]
706
+ },
707
+ action: {
708
+ type: 'forward',
709
+ target: {
710
+ host: 'localhost',
711
+ port: internalPort
712
+ },
713
+ tls: {
714
+ mode: tlsMode
715
+ }
716
+ }
717
+ });
718
+ }
719
+ return routes;
720
+ }
721
+ /**
722
+ * Update server configuration
723
+ */
724
+ updateOptions(options) {
725
+ const portsChanged = options.ports &&
726
+ (!this.options.ports ||
727
+ JSON.stringify(options.ports) !== JSON.stringify(this.options.ports));
728
+ if (portsChanged) {
729
+ this.stop().then(() => {
730
+ this.options = { ...this.options, ...options };
731
+ this.start();
732
+ });
733
+ }
734
+ else {
735
+ this.options = { ...this.options, ...options };
736
+ if (options.domains) {
737
+ this.domainRegistry = new DomainRegistry(options.domains, options.defaults || this.options.defaults);
738
+ }
739
+ if (options.routes) {
740
+ this.emailRouter.updateRoutes(options.routes);
741
+ }
742
+ }
743
+ }
744
+ /**
745
+ * Update email routes
746
+ */
747
+ updateEmailRoutes(routes) {
748
+ this.options.routes = routes;
749
+ this.emailRouter.updateRoutes(routes);
750
+ }
751
+ /**
752
+ * Get server statistics
753
+ */
754
+ getStats() {
755
+ return { ...this.stats };
756
+ }
757
+ /**
758
+ * Get domain registry
759
+ */
760
+ getDomainRegistry() {
761
+ return this.domainRegistry;
762
+ }
763
+ /**
764
+ * Send an email through the delivery system
765
+ */
766
+ async sendEmail(email, mode = 'mta', route, options) {
767
+ logger.log('info', `Sending email: ${email.subject} to ${email.to.join(', ')}`);
768
+ try {
769
+ if (!email.from) {
770
+ throw new Error('Email must have a sender address');
771
+ }
772
+ if (!email.to || email.to.length === 0) {
773
+ throw new Error('Email must have at least one recipient');
774
+ }
775
+ // Check if any recipients are on the suppression list
776
+ if (!options?.skipSuppressionCheck) {
777
+ const suppressedRecipients = email.to.filter(recipient => this.isEmailSuppressed(recipient));
778
+ if (suppressedRecipients.length > 0) {
779
+ const originalCount = email.to.length;
780
+ const suppressed = suppressedRecipients.map(recipient => {
781
+ const info = this.getSuppressionInfo(recipient);
782
+ return {
783
+ email: recipient,
784
+ reason: info?.reason || 'Unknown',
785
+ until: info?.expiresAt ? new Date(info.expiresAt).toISOString() : 'permanent'
786
+ };
787
+ });
788
+ logger.log('warn', `Filtering out ${suppressedRecipients.length} suppressed recipient(s)`, { suppressed });
789
+ if (suppressedRecipients.length === originalCount) {
790
+ throw new Error('All recipients are on the suppression list');
791
+ }
792
+ email.to = email.to.filter(recipient => !this.isEmailSuppressed(recipient));
793
+ }
794
+ }
795
+ // Sign with DKIM if configured
796
+ if (mode === 'mta' && route?.action.options?.mtaOptions?.dkimSign) {
797
+ const domain = email.from.split('@')[1];
798
+ await this.dkimManager.handleDkimSigning(email, domain, route.action.options.mtaOptions.dkimOptions?.keySelector || 'mta');
799
+ }
800
+ const id = plugins.uuid.v4();
801
+ await this.deliveryQueue.enqueue(email, mode, route);
802
+ logger.log('info', `Email queued with ID: ${id}`);
803
+ return id;
804
+ }
805
+ catch (error) {
806
+ logger.log('error', `Failed to send email: ${error.message}`);
807
+ throw error;
808
+ }
809
+ }
810
+ // -----------------------------------------------------------------------
811
+ // Bounce/suppression methods
812
+ // -----------------------------------------------------------------------
813
+ async processBounceNotification(bounceEmail) {
814
+ logger.log('info', 'Processing potential bounce notification email');
815
+ try {
816
+ const bounceRecord = await this.bounceManager.processBounceEmail(bounceEmail);
817
+ if (bounceRecord) {
818
+ logger.log('info', `Successfully processed bounce notification for ${bounceRecord.recipient}`, {
819
+ bounceType: bounceRecord.bounceType,
820
+ bounceCategory: bounceRecord.bounceCategory
821
+ });
822
+ this.emit('bounceProcessed', bounceRecord);
823
+ SecurityLogger.getInstance().logEvent({
824
+ level: SecurityLogLevel.INFO,
825
+ type: SecurityEventType.EMAIL_VALIDATION,
826
+ message: `Bounce notification processed for recipient`,
827
+ domain: bounceRecord.domain,
828
+ details: {
829
+ recipient: bounceRecord.recipient,
830
+ bounceType: bounceRecord.bounceType,
831
+ bounceCategory: bounceRecord.bounceCategory
832
+ },
833
+ success: true
834
+ });
835
+ return true;
836
+ }
837
+ else {
838
+ logger.log('info', 'Email not recognized as a bounce notification');
839
+ return false;
840
+ }
841
+ }
842
+ catch (error) {
843
+ logger.log('error', `Error processing bounce notification: ${error.message}`);
844
+ SecurityLogger.getInstance().logEvent({
845
+ level: SecurityLogLevel.ERROR,
846
+ type: SecurityEventType.EMAIL_VALIDATION,
847
+ message: 'Failed to process bounce notification',
848
+ details: { error: error.message, subject: bounceEmail.subject },
849
+ success: false
850
+ });
851
+ return false;
852
+ }
853
+ }
854
+ async processSmtpFailure(recipient, smtpResponse, options = {}) {
855
+ logger.log('info', `Processing SMTP failure for ${recipient}: ${smtpResponse}`);
856
+ try {
857
+ const bounceRecord = await this.bounceManager.processSmtpFailure(recipient, smtpResponse, options);
858
+ logger.log('info', `Successfully processed SMTP failure for ${recipient} as ${bounceRecord.bounceCategory} bounce`, {
859
+ bounceType: bounceRecord.bounceType
860
+ });
861
+ this.emit('bounceProcessed', bounceRecord);
862
+ SecurityLogger.getInstance().logEvent({
863
+ level: SecurityLogLevel.INFO,
864
+ type: SecurityEventType.EMAIL_VALIDATION,
865
+ message: `SMTP failure processed for recipient`,
866
+ domain: bounceRecord.domain,
867
+ details: {
868
+ recipient: bounceRecord.recipient,
869
+ bounceType: bounceRecord.bounceType,
870
+ bounceCategory: bounceRecord.bounceCategory,
871
+ smtpResponse
872
+ },
873
+ success: true
874
+ });
875
+ return true;
876
+ }
877
+ catch (error) {
878
+ logger.log('error', `Error processing SMTP failure: ${error.message}`);
879
+ SecurityLogger.getInstance().logEvent({
880
+ level: SecurityLogLevel.ERROR,
881
+ type: SecurityEventType.EMAIL_VALIDATION,
882
+ message: 'Failed to process SMTP failure',
883
+ details: { recipient, smtpResponse, error: error.message },
884
+ success: false
885
+ });
886
+ return false;
887
+ }
888
+ }
889
+ isEmailSuppressed(email) {
890
+ return this.bounceManager.isEmailSuppressed(email);
891
+ }
892
+ getSuppressionInfo(email) {
893
+ return this.bounceManager.getSuppressionInfo(email);
894
+ }
895
+ getBounceHistory(email) {
896
+ return this.bounceManager.getBounceInfo(email);
897
+ }
898
+ getSuppressionList() {
899
+ return this.bounceManager.getSuppressionList();
900
+ }
901
+ getHardBouncedAddresses() {
902
+ return this.bounceManager.getHardBouncedAddresses();
903
+ }
904
+ addToSuppressionList(email, reason, expiresAt) {
905
+ this.bounceManager.addToSuppressionList(email, reason, expiresAt);
906
+ logger.log('info', `Added ${email} to suppression list: ${reason}`);
907
+ }
908
+ removeFromSuppressionList(email) {
909
+ this.bounceManager.removeFromSuppressionList(email);
910
+ logger.log('info', `Removed ${email} from suppression list`);
911
+ }
912
+ recordBounce(domain, receivingDomain, bounceType, reason) {
913
+ const bounceRecord = {
914
+ id: `bounce_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
915
+ recipient: `user@${receivingDomain}`,
916
+ sender: `user@${domain}`,
917
+ domain: domain,
918
+ bounceType: bounceType === 'hard' ? BounceType.INVALID_RECIPIENT : BounceType.TEMPORARY_FAILURE,
919
+ bounceCategory: bounceType === 'hard' ? BounceCategory.HARD : BounceCategory.SOFT,
920
+ timestamp: Date.now(),
921
+ smtpResponse: reason,
922
+ diagnosticCode: reason,
923
+ statusCode: bounceType === 'hard' ? '550' : '450',
924
+ processed: false
925
+ };
926
+ this.bounceManager.processBounce(bounceRecord);
927
+ }
928
+ /**
929
+ * Get the rate limiter instance
930
+ */
931
+ getRateLimiter() {
932
+ return this.rateLimiter;
933
+ }
934
+ }
935
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy51bmlmaWVkLmVtYWlsLnNlcnZlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL21haWwvcm91dGluZy9jbGFzc2VzLnVuaWZpZWQuZW1haWwuc2VydmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxLQUFLLEtBQUssTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sUUFBUSxDQUFDO0FBQ3RDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUN6QyxPQUFPLEVBQ0wsY0FBYyxFQUNkLGdCQUFnQixFQUNoQixpQkFBaUIsRUFDbEIsTUFBTSx5QkFBeUIsQ0FBQztBQUNqQyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFDakUsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sOENBQThDLENBQUM7QUFFbEYsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBRXhELE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUNqRCxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDOUQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLDBCQUEwQixDQUFDO0FBQ3RELE9BQU8sRUFBRSxhQUFhLEVBQUUsVUFBVSxFQUFFLGNBQWMsRUFBRSxNQUFNLGtDQUFrQyxDQUFDO0FBRTdGLE9BQU8sRUFBRSx1QkFBdUIsRUFBa0MsTUFBTSx3Q0FBd0MsQ0FBQztBQUNqSCxPQUFPLEVBQUUsb0JBQW9CLEVBQXNCLE1BQU0sdUNBQXVDLENBQUM7QUFDakcsT0FBTyxFQUFFLGtCQUFrQixFQUFnQyxNQUFNLDZDQUE2QyxDQUFDO0FBQy9HLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUV0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