@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,630 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { EventEmitter } from 'node:events';
3
+ import { logger } from '../../logger.js';
4
+ import { SecurityLogger, SecurityLogLevel, SecurityEventType } from '../../security/index.js';
5
+ import { UnifiedDeliveryQueue } from './classes.delivery.queue.js';
6
+ import { Email } from '../core/classes.email.js';
7
+ import { RustSecurityBridge } from '../../security/classes.rustsecuritybridge.js';
8
+ const dns = plugins.dns;
9
+ /**
10
+ * Delivery status enumeration
11
+ */
12
+ export var DeliveryStatus;
13
+ (function (DeliveryStatus) {
14
+ DeliveryStatus["PENDING"] = "pending";
15
+ DeliveryStatus["DELIVERING"] = "delivering";
16
+ DeliveryStatus["DELIVERED"] = "delivered";
17
+ DeliveryStatus["DEFERRED"] = "deferred";
18
+ DeliveryStatus["FAILED"] = "failed";
19
+ })(DeliveryStatus || (DeliveryStatus = {}));
20
+ /**
21
+ * Handles delivery for all email processing modes
22
+ */
23
+ export class MultiModeDeliverySystem extends EventEmitter {
24
+ queue;
25
+ options;
26
+ stats;
27
+ deliveryTimes = [];
28
+ activeDeliveries = new Set();
29
+ running = false;
30
+ throttled = false;
31
+ rateLimitLastCheck = Date.now();
32
+ rateLimitCounter = 0;
33
+ emailServer;
34
+ /**
35
+ * Create a new multi-mode delivery system
36
+ * @param queue Unified delivery queue
37
+ * @param options Delivery options
38
+ * @param emailServer Optional reference to unified email server for outbound delivery
39
+ */
40
+ constructor(queue, options, emailServer) {
41
+ super();
42
+ this.queue = queue;
43
+ this.emailServer = emailServer;
44
+ // Set default options
45
+ this.options = {
46
+ connectionPoolSize: options.connectionPoolSize || 10,
47
+ socketTimeout: options.socketTimeout || 30000, // 30 seconds
48
+ concurrentDeliveries: options.concurrentDeliveries || 10,
49
+ sendTimeout: options.sendTimeout || 60000, // 1 minute
50
+ verifyCertificates: options.verifyCertificates !== false, // Default to true
51
+ tlsMinVersion: options.tlsMinVersion || 'TLSv1.2',
52
+ forwardHandler: options.forwardHandler || {
53
+ deliver: this.handleForwardDelivery.bind(this)
54
+ },
55
+ deliveryHandler: options.deliveryHandler || {
56
+ deliver: this.handleMtaDelivery.bind(this)
57
+ },
58
+ processHandler: options.processHandler || {
59
+ deliver: this.handleProcessDelivery.bind(this)
60
+ },
61
+ globalRateLimit: options.globalRateLimit || 100, // 100 emails per minute
62
+ perPatternRateLimit: options.perPatternRateLimit || {},
63
+ processBounces: options.processBounces !== false, // Default to true
64
+ bounceHandler: options.bounceHandler || null,
65
+ onDeliveryStart: options.onDeliveryStart || (async () => { }),
66
+ onDeliverySuccess: options.onDeliverySuccess || (async () => { }),
67
+ onDeliveryFailed: options.onDeliveryFailed || (async () => { })
68
+ };
69
+ // Initialize statistics
70
+ this.stats = {
71
+ activeDeliveries: 0,
72
+ totalSuccessful: 0,
73
+ totalFailed: 0,
74
+ avgDeliveryTime: 0,
75
+ byMode: {
76
+ forward: {
77
+ successful: 0,
78
+ failed: 0
79
+ },
80
+ mta: {
81
+ successful: 0,
82
+ failed: 0
83
+ },
84
+ process: {
85
+ successful: 0,
86
+ failed: 0
87
+ }
88
+ },
89
+ rateLimiting: {
90
+ currentRate: 0,
91
+ globalLimit: this.options.globalRateLimit,
92
+ throttled: 0
93
+ }
94
+ };
95
+ // Set up event listeners
96
+ this.queue.on('itemsReady', this.processItems.bind(this));
97
+ }
98
+ /**
99
+ * Start the delivery system
100
+ */
101
+ async start() {
102
+ logger.log('info', 'Starting MultiModeDeliverySystem');
103
+ if (this.running) {
104
+ logger.log('warn', 'MultiModeDeliverySystem is already running');
105
+ return;
106
+ }
107
+ this.running = true;
108
+ // Emit started event
109
+ this.emit('started');
110
+ logger.log('info', 'MultiModeDeliverySystem started successfully');
111
+ }
112
+ /**
113
+ * Stop the delivery system
114
+ */
115
+ async stop() {
116
+ logger.log('info', 'Stopping MultiModeDeliverySystem');
117
+ if (!this.running) {
118
+ logger.log('warn', 'MultiModeDeliverySystem is already stopped');
119
+ return;
120
+ }
121
+ this.running = false;
122
+ // Wait for active deliveries to complete
123
+ if (this.activeDeliveries.size > 0) {
124
+ logger.log('info', `Waiting for ${this.activeDeliveries.size} active deliveries to complete`);
125
+ // Wait for a maximum of 30 seconds
126
+ await new Promise(resolve => {
127
+ const checkInterval = setInterval(() => {
128
+ if (this.activeDeliveries.size === 0) {
129
+ clearInterval(checkInterval);
130
+ clearTimeout(forceTimeout);
131
+ resolve();
132
+ }
133
+ }, 1000);
134
+ // Force resolve after 30 seconds
135
+ const forceTimeout = setTimeout(() => {
136
+ clearInterval(checkInterval);
137
+ resolve();
138
+ }, 30000);
139
+ });
140
+ }
141
+ // Emit stopped event
142
+ this.emit('stopped');
143
+ logger.log('info', 'MultiModeDeliverySystem stopped successfully');
144
+ }
145
+ /**
146
+ * Process ready items from the queue
147
+ * @param items Queue items ready for processing
148
+ */
149
+ async processItems(items) {
150
+ if (!this.running) {
151
+ return;
152
+ }
153
+ // Check if we're already at max concurrent deliveries
154
+ if (this.activeDeliveries.size >= this.options.concurrentDeliveries) {
155
+ logger.log('debug', `Already at max concurrent deliveries (${this.activeDeliveries.size})`);
156
+ return;
157
+ }
158
+ // Check rate limiting
159
+ if (this.checkRateLimit()) {
160
+ logger.log('debug', 'Rate limit exceeded, throttling deliveries');
161
+ return;
162
+ }
163
+ // Calculate how many more deliveries we can start
164
+ const availableSlots = this.options.concurrentDeliveries - this.activeDeliveries.size;
165
+ const itemsToProcess = items.slice(0, availableSlots);
166
+ if (itemsToProcess.length === 0) {
167
+ return;
168
+ }
169
+ logger.log('info', `Processing ${itemsToProcess.length} items for delivery`);
170
+ // Process each item
171
+ for (const item of itemsToProcess) {
172
+ // Mark as processing
173
+ await this.queue.markProcessing(item.id);
174
+ // Add to active deliveries
175
+ this.activeDeliveries.add(item.id);
176
+ this.stats.activeDeliveries = this.activeDeliveries.size;
177
+ // Deliver asynchronously
178
+ this.deliverItem(item).catch(err => {
179
+ logger.log('error', `Unhandled error in delivery: ${err.message}`);
180
+ });
181
+ }
182
+ // Update statistics
183
+ this.emit('statsUpdated', this.stats);
184
+ }
185
+ /**
186
+ * Deliver an item from the queue
187
+ * @param item Queue item to deliver
188
+ */
189
+ async deliverItem(item) {
190
+ const startTime = Date.now();
191
+ try {
192
+ // Call delivery start hook
193
+ await this.options.onDeliveryStart(item);
194
+ // Emit delivery start event
195
+ this.emit('deliveryStart', item);
196
+ logger.log('info', `Starting delivery of item ${item.id}, mode: ${item.processingMode}`);
197
+ // Choose the appropriate handler based on mode
198
+ let result;
199
+ switch (item.processingMode) {
200
+ case 'forward':
201
+ result = await this.options.forwardHandler.deliver(item);
202
+ break;
203
+ case 'mta':
204
+ result = await this.options.deliveryHandler.deliver(item);
205
+ break;
206
+ case 'process':
207
+ result = await this.options.processHandler.deliver(item);
208
+ break;
209
+ default:
210
+ throw new Error(`Unknown processing mode: ${item.processingMode}`);
211
+ }
212
+ // Mark as delivered
213
+ await this.queue.markDelivered(item.id);
214
+ // Update statistics
215
+ this.stats.totalSuccessful++;
216
+ this.stats.byMode[item.processingMode].successful++;
217
+ // Calculate delivery time
218
+ const deliveryTime = Date.now() - startTime;
219
+ this.deliveryTimes.push(deliveryTime);
220
+ this.updateDeliveryTimeStats();
221
+ // Call delivery success hook
222
+ await this.options.onDeliverySuccess(item, result);
223
+ // Emit delivery success event
224
+ this.emit('deliverySuccess', item, result);
225
+ logger.log('info', `Item ${item.id} delivered successfully in ${deliveryTime}ms`);
226
+ SecurityLogger.getInstance().logEvent({
227
+ level: SecurityLogLevel.INFO,
228
+ type: SecurityEventType.EMAIL_DELIVERY,
229
+ message: 'Email delivery successful',
230
+ details: {
231
+ itemId: item.id,
232
+ mode: item.processingMode,
233
+ routeName: item.route?.name || 'unknown',
234
+ deliveryTime
235
+ },
236
+ success: true
237
+ });
238
+ }
239
+ catch (error) {
240
+ // Calculate delivery attempt time even for failures
241
+ const deliveryTime = Date.now() - startTime;
242
+ // Mark as failed
243
+ await this.queue.markFailed(item.id, error.message);
244
+ // Update statistics
245
+ this.stats.totalFailed++;
246
+ this.stats.byMode[item.processingMode].failed++;
247
+ // Call delivery failed hook
248
+ await this.options.onDeliveryFailed(item, error.message);
249
+ // Process as bounce if enabled and we have a bounce handler
250
+ if (this.options.processBounces && this.options.bounceHandler) {
251
+ try {
252
+ const email = item.processingResult;
253
+ // Extract recipient and error message
254
+ // For multiple recipients, we'd need more sophisticated parsing
255
+ const recipient = email.to.length > 0 ? email.to[0] : '';
256
+ if (recipient) {
257
+ logger.log('info', `Processing delivery failure as bounce for recipient ${recipient}`);
258
+ // Process SMTP failure through bounce handler
259
+ await this.options.bounceHandler.processSmtpFailure(recipient, error.message, {
260
+ sender: email.from,
261
+ originalEmailId: item.id,
262
+ headers: email.headers
263
+ });
264
+ logger.log('info', `Bounce record created for failed delivery to ${recipient}`);
265
+ }
266
+ }
267
+ catch (bounceError) {
268
+ logger.log('error', `Failed to process bounce: ${bounceError.message}`);
269
+ }
270
+ }
271
+ // Emit delivery failed event
272
+ this.emit('deliveryFailed', item, error);
273
+ logger.log('error', `Item ${item.id} delivery failed: ${error.message}`);
274
+ SecurityLogger.getInstance().logEvent({
275
+ level: SecurityLogLevel.ERROR,
276
+ type: SecurityEventType.EMAIL_DELIVERY,
277
+ message: 'Email delivery failed',
278
+ details: {
279
+ itemId: item.id,
280
+ mode: item.processingMode,
281
+ routeName: item.route?.name || 'unknown',
282
+ error: error.message,
283
+ deliveryTime
284
+ },
285
+ success: false
286
+ });
287
+ }
288
+ finally {
289
+ // Remove from active deliveries
290
+ this.activeDeliveries.delete(item.id);
291
+ this.stats.activeDeliveries = this.activeDeliveries.size;
292
+ // Update statistics
293
+ this.emit('statsUpdated', this.stats);
294
+ }
295
+ }
296
+ /**
297
+ * Default handler for forward mode delivery
298
+ * @param item Queue item
299
+ */
300
+ async handleForwardDelivery(item) {
301
+ logger.log('info', `Forward delivery for item ${item.id}`);
302
+ const email = item.processingResult;
303
+ const route = item.route;
304
+ // Get target server information
305
+ const targetServer = route?.action.forward?.host;
306
+ const targetPort = route?.action.forward?.port || 25;
307
+ if (!targetServer) {
308
+ throw new Error('No target server configured for forward mode');
309
+ }
310
+ logger.log('info', `Forwarding email to ${targetServer}:${targetPort}`);
311
+ try {
312
+ if (!this.emailServer) {
313
+ throw new Error('No email server available for forward delivery');
314
+ }
315
+ // Build DKIM options from route config
316
+ const dkimDomain = item.route?.action.options?.mtaOptions?.dkimSign
317
+ ? (item.route.action.options.mtaOptions.dkimOptions?.domainName || email.from.split('@')[1])
318
+ : undefined;
319
+ const dkimSelector = item.route?.action.options?.mtaOptions?.dkimOptions?.keySelector || 'default';
320
+ // Build auth options from route forward config
321
+ const auth = route?.action.forward?.auth;
322
+ // Send via Rust SMTP client
323
+ const result = await this.emailServer.sendOutboundEmail(targetServer, targetPort, email, {
324
+ auth,
325
+ dkimDomain,
326
+ dkimSelector,
327
+ });
328
+ logger.log('info', `Email forwarded successfully to ${targetServer}:${targetPort}`);
329
+ return {
330
+ targetServer,
331
+ targetPort,
332
+ recipients: result.accepted.length,
333
+ messageId: result.messageId,
334
+ rejectedRecipients: result.rejected,
335
+ };
336
+ }
337
+ catch (error) {
338
+ logger.log('error', `Failed to forward email: ${error.message}`);
339
+ throw error;
340
+ }
341
+ }
342
+ /**
343
+ * Resolve MX records for a domain, sorted by priority (lowest first).
344
+ * Falls back to the domain itself as an A record per RFC 5321.
345
+ */
346
+ async resolveMxForDomain(domain) {
347
+ const resolver = new dns.promises.Resolver();
348
+ try {
349
+ const mxRecords = await resolver.resolveMx(domain);
350
+ return mxRecords.sort((a, b) => a.priority - b.priority);
351
+ }
352
+ catch (err) {
353
+ logger.log('warn', `No MX records for ${domain}, falling back to A record`);
354
+ return [{ exchange: domain, priority: 0 }];
355
+ }
356
+ }
357
+ /**
358
+ * Group recipient addresses by their domain part.
359
+ */
360
+ groupRecipientsByDomain(recipients) {
361
+ const groups = new Map();
362
+ for (const rcpt of recipients) {
363
+ const domain = rcpt.split('@')[1]?.toLowerCase();
364
+ if (!domain)
365
+ continue;
366
+ const list = groups.get(domain) || [];
367
+ list.push(rcpt);
368
+ groups.set(domain, list);
369
+ }
370
+ return groups;
371
+ }
372
+ /**
373
+ * Default handler for MTA mode delivery
374
+ * @param item Queue item
375
+ */
376
+ async handleMtaDelivery(item) {
377
+ logger.log('info', `MTA delivery for item ${item.id}`);
378
+ const email = item.processingResult;
379
+ if (!this.emailServer) {
380
+ throw new Error('No email server available for MTA delivery');
381
+ }
382
+ // Build DKIM options from route config
383
+ const dkimDomain = item.route?.action.options?.mtaOptions?.dkimSign
384
+ ? (item.route.action.options.mtaOptions.dkimOptions?.domainName || email.from.split('@')[1])
385
+ : undefined;
386
+ const dkimSelector = item.route?.action.options?.mtaOptions?.dkimOptions?.keySelector || 'default';
387
+ const allRecipients = email.getAllRecipients();
388
+ if (allRecipients.length === 0) {
389
+ throw new Error('No recipients specified for MTA delivery');
390
+ }
391
+ const domainGroups = this.groupRecipientsByDomain(allRecipients);
392
+ const results = [];
393
+ for (const [domain, recipients] of domainGroups) {
394
+ const mxHosts = await this.resolveMxForDomain(domain);
395
+ let delivered = false;
396
+ let lastError;
397
+ for (const mx of mxHosts) {
398
+ try {
399
+ logger.log('info', `MTA: trying MX ${mx.exchange}:25 for domain ${domain} (priority ${mx.priority})`);
400
+ // Create a temporary Email scoped to this domain's recipients
401
+ const domainEmail = new Email({
402
+ from: email.from,
403
+ to: recipients.filter(r => email.to.includes(r)),
404
+ cc: recipients.filter(r => (email.cc || []).includes(r)),
405
+ bcc: recipients.filter(r => (email.bcc || []).includes(r)),
406
+ subject: email.subject,
407
+ text: email.text,
408
+ html: email.html,
409
+ });
410
+ const result = await this.emailServer.sendOutboundEmail(mx.exchange, 25, domainEmail, {
411
+ dkimDomain,
412
+ dkimSelector,
413
+ });
414
+ results.push({
415
+ domain,
416
+ success: true,
417
+ accepted: result.accepted,
418
+ rejected: result.rejected,
419
+ });
420
+ delivered = true;
421
+ logger.log('info', `MTA: delivered to ${domain} via ${mx.exchange}`);
422
+ break;
423
+ }
424
+ catch (err) {
425
+ lastError = err.message;
426
+ logger.log('warn', `MTA: MX ${mx.exchange} failed for ${domain}: ${err.message}`);
427
+ }
428
+ }
429
+ if (!delivered) {
430
+ results.push({ domain, success: false, error: lastError });
431
+ logger.log('error', `MTA: all MX hosts failed for ${domain}`);
432
+ }
433
+ }
434
+ const allFailed = results.every(r => !r.success);
435
+ if (allFailed) {
436
+ const summary = results.map(r => `${r.domain}: ${r.error}`).join('; ');
437
+ throw new Error(`MTA delivery failed for all domains: ${summary}`);
438
+ }
439
+ return {
440
+ recipients: allRecipients.length,
441
+ domainResults: results,
442
+ };
443
+ }
444
+ /**
445
+ * Default handler for process mode delivery
446
+ * @param item Queue item
447
+ */
448
+ async handleProcessDelivery(item) {
449
+ logger.log('info', `Process delivery for item ${item.id}`);
450
+ const email = item.processingResult;
451
+ const route = item.route;
452
+ try {
453
+ // Apply content scanning if enabled
454
+ if (route?.action.options?.contentScanning && route?.action.options?.scanners && route.action.options.scanners.length > 0) {
455
+ logger.log('info', 'Performing content scanning');
456
+ // Apply each scanner
457
+ for (const scanner of route.action.options.scanners) {
458
+ switch (scanner.type) {
459
+ case 'spam':
460
+ logger.log('info', 'Scanning for spam content');
461
+ // Implement spam scanning
462
+ break;
463
+ case 'virus':
464
+ logger.log('info', 'Scanning for virus content');
465
+ // Implement virus scanning
466
+ break;
467
+ case 'attachment':
468
+ logger.log('info', 'Scanning attachments');
469
+ // Check for blocked extensions
470
+ if (scanner.blockedExtensions && scanner.blockedExtensions.length > 0) {
471
+ for (const attachment of email.attachments) {
472
+ const ext = this.getFileExtension(attachment.filename);
473
+ if (scanner.blockedExtensions.includes(ext)) {
474
+ if (scanner.action === 'reject') {
475
+ throw new Error(`Blocked attachment type: ${ext}`);
476
+ }
477
+ else { // tag
478
+ email.addHeader('X-Attachment-Warning', `Potentially unsafe attachment: ${attachment.filename}`);
479
+ }
480
+ }
481
+ }
482
+ }
483
+ break;
484
+ }
485
+ }
486
+ }
487
+ // Apply transformations if defined
488
+ if (route?.action.options?.transformations && route?.action.options?.transformations.length > 0) {
489
+ logger.log('info', 'Applying email transformations');
490
+ for (const transform of route.action.options.transformations) {
491
+ switch (transform.type) {
492
+ case 'addHeader':
493
+ if (transform.header && transform.value) {
494
+ email.addHeader(transform.header, transform.value);
495
+ }
496
+ break;
497
+ }
498
+ }
499
+ }
500
+ // Apply DKIM signing if configured (after all transformations)
501
+ if (item.route?.action.options?.mtaOptions?.dkimSign || item.route?.action.process?.dkim) {
502
+ await this.applyDkimSigning(email, item.route.action.options?.mtaOptions || {});
503
+ }
504
+ logger.log('info', `Email successfully processed in store-and-forward mode, delivering via MTA`);
505
+ // After scanning + transformations, deliver via MTA
506
+ return await this.handleMtaDelivery(item);
507
+ }
508
+ catch (error) {
509
+ logger.log('error', `Failed to process email: ${error.message}`);
510
+ throw error;
511
+ }
512
+ }
513
+ /**
514
+ * Get file extension from filename
515
+ */
516
+ getFileExtension(filename) {
517
+ return filename.substring(filename.lastIndexOf('.')).toLowerCase();
518
+ }
519
+ /**
520
+ * Apply DKIM signing to an email
521
+ */
522
+ async applyDkimSigning(email, mtaOptions) {
523
+ if (!this.emailServer) {
524
+ logger.log('warn', 'Cannot apply DKIM signing without email server reference');
525
+ return;
526
+ }
527
+ const domainName = mtaOptions.dkimOptions?.domainName || email.from.split('@')[1];
528
+ const keySelector = mtaOptions.dkimOptions?.keySelector || 'default';
529
+ try {
530
+ // Ensure DKIM keys exist for the domain
531
+ await this.emailServer.dkimCreator.handleDKIMKeysForDomain(domainName);
532
+ // Get the private key
533
+ const dkimPrivateKey = (await this.emailServer.dkimCreator.readDKIMKeys(domainName)).privateKey;
534
+ // Convert Email to raw format for signing
535
+ const rawEmail = email.toRFC822String();
536
+ // Sign via Rust bridge
537
+ const bridge = RustSecurityBridge.getInstance();
538
+ const signResult = await bridge.signDkim({
539
+ rawMessage: rawEmail,
540
+ domain: domainName,
541
+ selector: keySelector,
542
+ privateKey: dkimPrivateKey,
543
+ });
544
+ if (signResult.header) {
545
+ email.addHeader('DKIM-Signature', signResult.header);
546
+ logger.log('info', `Successfully added DKIM signature for ${domainName}`);
547
+ }
548
+ }
549
+ catch (error) {
550
+ logger.log('error', `Failed to apply DKIM signature: ${error.message}`);
551
+ // Don't throw - allow email to be sent without DKIM if signing fails
552
+ }
553
+ }
554
+ /**
555
+ * Update delivery time statistics
556
+ */
557
+ updateDeliveryTimeStats() {
558
+ if (this.deliveryTimes.length === 0)
559
+ return;
560
+ // Keep only the last 1000 delivery times
561
+ if (this.deliveryTimes.length > 1000) {
562
+ this.deliveryTimes = this.deliveryTimes.slice(-1000);
563
+ }
564
+ // Calculate average
565
+ const sum = this.deliveryTimes.reduce((acc, time) => acc + time, 0);
566
+ this.stats.avgDeliveryTime = sum / this.deliveryTimes.length;
567
+ }
568
+ /**
569
+ * Check if rate limit is exceeded
570
+ * @returns True if rate limited, false otherwise
571
+ */
572
+ checkRateLimit() {
573
+ const now = Date.now();
574
+ const elapsed = now - this.rateLimitLastCheck;
575
+ // Reset counter if more than a minute has passed
576
+ if (elapsed >= 60000) {
577
+ this.rateLimitLastCheck = now;
578
+ this.rateLimitCounter = 0;
579
+ this.throttled = false;
580
+ this.stats.rateLimiting.currentRate = 0;
581
+ return false;
582
+ }
583
+ // Check if we're already throttled
584
+ if (this.throttled) {
585
+ return true;
586
+ }
587
+ // Increment counter
588
+ this.rateLimitCounter++;
589
+ // Calculate current rate (emails per minute)
590
+ const rate = (this.rateLimitCounter / elapsed) * 60000;
591
+ this.stats.rateLimiting.currentRate = rate;
592
+ // Check if rate limit is exceeded
593
+ if (rate > this.options.globalRateLimit) {
594
+ this.throttled = true;
595
+ this.stats.rateLimiting.throttled++;
596
+ // Schedule throttle reset
597
+ const resetDelay = 60000 - elapsed;
598
+ setTimeout(() => {
599
+ this.throttled = false;
600
+ this.rateLimitLastCheck = Date.now();
601
+ this.rateLimitCounter = 0;
602
+ this.stats.rateLimiting.currentRate = 0;
603
+ }, resetDelay);
604
+ return true;
605
+ }
606
+ return false;
607
+ }
608
+ /**
609
+ * Update delivery options
610
+ * @param options New options
611
+ */
612
+ updateOptions(options) {
613
+ this.options = {
614
+ ...this.options,
615
+ ...options
616
+ };
617
+ // Update rate limit statistics
618
+ if (options.globalRateLimit) {
619
+ this.stats.rateLimiting.globalLimit = options.globalRateLimit;
620
+ }
621
+ logger.log('info', 'MultiModeDeliverySystem options updated');
622
+ }
623
+ /**
624
+ * Get delivery statistics
625
+ */
626
+ getStats() {
627
+ return { ...this.stats };
628
+ }
629
+ }
630
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5kZWxpdmVyeS5zeXN0ZW0uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL2RlbGl2ZXJ5L2NsYXNzZXMuZGVsaXZlcnkuc3lzdGVtLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUMzQyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDekMsT0FBTyxFQUNMLGNBQWMsRUFDZCxnQkFBZ0IsRUFDaEIsaUJBQWlCLEVBQ2xCLE1BQU0seUJBQXlCLENBQUM7QUFDakMsT0FBTyxFQUFFLG9CQUFvQixFQUFtQixNQUFNLDZCQUE2QixDQUFDO0FBQ3BGLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUVqRCxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSw4Q0FBOEMsQ0FBQztBQUVsRixNQUFNLEdBQUcsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDO0FBRXhCOztHQUVHO0FBQ0gsTUFBTSxDQUFOLElBQVksY0FNWDtBQU5ELFdBQVksY0FBYztJQUN4QixxQ0FBbUIsQ0FBQTtJQUNuQiwyQ0FBeUIsQ0FBQTtJQUN6Qix5Q0FBdUIsQ0FBQTtJQUN2Qix1Q0FBcUIsQ0FBQTtJQUNyQixtQ0FBaUIsQ0FBQTtBQUNuQixDQUFDLEVBTlcsY0FBYyxLQUFkLGNBQWMsUUFNekI7QUEyRUQ7O0dBRUc7QUFDSCxNQUFNLE9BQU8sdUJBQXdCLFNBQVEsWUFBWTtJQUMvQyxLQUFLLENBQXVCO0lBQzVCLE9BQU8sQ0FBc0M7SUFDN0MsS0FBSyxDQUFpQjtJQUN0QixhQUFhLEdBQWEsRUFBRSxDQUFDO0lBQzdCLGdCQUFnQixHQUFnQixJQUFJLEdBQUcsRUFBRSxDQUFDO0lBQzFDLE9BQU8sR0FBWSxLQUFLLENBQUM7SUFDekIsU0FBUyxHQUFZ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