@serve.zone/dcrouter 2.12.4 → 5.0.3
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.
- package/dist_serve/bundle.js +30539 -0
- package/dist_serve/bundle.js.map +7 -0
- package/dist_serve/index.html +1 -0
- package/dist_ts/00_commitinfo_data.js +4 -4
- package/dist_ts/cache/classes.cache.cleaner.d.ts +47 -0
- package/dist_ts/cache/classes.cache.cleaner.js +130 -0
- package/dist_ts/cache/classes.cached.document.d.ts +76 -0
- package/dist_ts/cache/classes.cached.document.js +100 -0
- package/dist_ts/cache/classes.cachedb.d.ts +60 -0
- package/dist_ts/cache/classes.cachedb.js +125 -0
- package/dist_ts/cache/documents/classes.cached.email.d.ts +125 -0
- package/dist_ts/cache/documents/classes.cached.email.js +337 -0
- package/dist_ts/cache/documents/classes.cached.ip.reputation.d.ts +119 -0
- package/dist_ts/cache/documents/classes.cached.ip.reputation.js +323 -0
- package/dist_ts/cache/documents/index.d.ts +2 -0
- package/dist_ts/cache/documents/index.js +3 -0
- package/dist_ts/cache/index.d.ts +4 -0
- package/dist_ts/cache/index.js +7 -0
- package/dist_ts/classes.dcrouter.d.ts +53 -15
- package/dist_ts/classes.dcrouter.js +156 -131
- package/dist_ts/config/index.js +1 -1
- package/dist_ts/config/validator.js +1 -1
- package/dist_ts/errors/base.errors.js +11 -1
- package/dist_ts/errors/error-handler.js +2 -2
- package/dist_ts/errors/error.codes.js +1 -1
- package/dist_ts/errors/index.d.ts +0 -2
- package/dist_ts/errors/index.js +1 -3
- package/dist_ts/errors/reputation.errors.js +1 -1
- package/dist_ts/index.d.ts +3 -1
- package/dist_ts/index.js +5 -2
- package/dist_ts/logger.js +4 -5
- package/dist_ts/monitoring/classes.metricscache.d.ts +32 -0
- package/dist_ts/monitoring/classes.metricscache.js +63 -0
- package/dist_ts/monitoring/classes.metricsmanager.d.ts +112 -0
- package/dist_ts/monitoring/classes.metricsmanager.js +446 -0
- package/dist_ts/monitoring/index.d.ts +1 -0
- package/dist_ts/monitoring/index.js +2 -0
- package/dist_ts/opsserver/classes.opsserver.d.ts +8 -0
- package/dist_ts/opsserver/classes.opsserver.js +26 -7
- package/dist_ts/{ts/opsserver → opsserver}/handlers/admin.handler.d.ts +1 -1
- package/dist_ts/opsserver/handlers/admin.handler.js +180 -0
- package/dist_ts/{ts/opsserver → opsserver}/handlers/config.handler.d.ts +0 -1
- package/dist_ts/opsserver/handlers/config.handler.js +67 -0
- package/dist_ts/opsserver/handlers/email-ops.handler.d.ts +12 -0
- package/dist_ts/opsserver/handlers/email-ops.handler.js +219 -0
- package/dist_ts/{ts/opsserver → opsserver}/handlers/index.d.ts +2 -0
- package/dist_ts/{ts/opsserver → opsserver}/handlers/index.js +3 -1
- package/dist_ts/opsserver/handlers/logs.handler.js +122 -0
- package/dist_ts/opsserver/handlers/radius.handler.d.ts +8 -0
- package/dist_ts/opsserver/handlers/radius.handler.js +296 -0
- package/dist_ts/opsserver/handlers/security.handler.js +217 -0
- package/dist_ts/opsserver/handlers/stats.handler.js +366 -0
- package/dist_ts/{ts/opsserver → opsserver}/helpers/guards.d.ts +1 -1
- package/dist_ts/{ts/opsserver → opsserver}/helpers/guards.js +1 -1
- package/dist_ts/opsserver/index.js +1 -1
- package/dist_ts/paths.js +10 -10
- package/dist_ts/plugins.d.ts +43 -8
- package/dist_ts/plugins.js +71 -9
- package/dist_ts/radius/classes.accounting.manager.d.ts +218 -0
- package/dist_ts/radius/classes.accounting.manager.js +417 -0
- package/dist_ts/radius/classes.radius.server.d.ts +171 -0
- package/dist_ts/radius/classes.radius.server.js +385 -0
- package/dist_ts/radius/classes.vlan.manager.d.ts +128 -0
- package/dist_ts/radius/classes.vlan.manager.js +272 -0
- package/dist_ts/radius/index.d.ts +13 -0
- package/dist_ts/radius/index.js +14 -0
- package/dist_ts/security/classes.contentscanner.d.ts +1 -1
- package/dist_ts/security/classes.contentscanner.js +13 -10
- package/dist_ts/security/classes.ipreputationchecker.js +11 -7
- package/dist_ts/security/classes.securitylogger.js +5 -2
- package/dist_ts/security/index.js +1 -1
- package/dist_ts/sms/classes.smsservice.js +11 -11
- package/dist_ts/sms/config/sms.config.js +1 -1
- package/dist_ts/sms/config/sms.schema.js +1 -1
- package/dist_ts/sms/index.js +1 -1
- package/dist_ts/storage/classes.storagemanager.js +8 -5
- package/dist_ts/storage/index.js +1 -1
- package/{dist_ts/ts_interfaces → dist_ts_interfaces}/data/auth.js +1 -1
- package/{dist_ts/ts_interfaces → dist_ts_interfaces}/data/index.js +1 -1
- package/{dist_ts/ts_interfaces → dist_ts_interfaces}/data/stats.d.ts +27 -0
- package/{dist_ts/ts_interfaces → dist_ts_interfaces}/data/stats.js +1 -1
- package/{dist_ts/ts_interfaces → dist_ts_interfaces}/index.js +1 -1
- package/{dist_ts/ts_interfaces → dist_ts_interfaces}/plugins.js +1 -1
- package/{dist_ts/ts_interfaces → dist_ts_interfaces}/requests/admin.js +1 -1
- package/dist_ts_interfaces/requests/combined.stats.d.ts +24 -0
- package/dist_ts_interfaces/requests/combined.stats.js +2 -0
- package/{dist_ts/ts_interfaces → dist_ts_interfaces}/requests/config.d.ts +0 -12
- package/{dist_ts/ts_interfaces → dist_ts_interfaces}/requests/config.js +1 -1
- package/dist_ts_interfaces/requests/email-ops.d.ts +139 -0
- package/dist_ts_interfaces/requests/email-ops.js +3 -0
- package/{dist_ts/ts_interfaces → dist_ts_interfaces}/requests/index.d.ts +3 -0
- package/dist_ts_interfaces/requests/index.js +8 -0
- package/{dist_ts/ts_interfaces → dist_ts_interfaces}/requests/logs.js +1 -1
- package/dist_ts_interfaces/requests/radius.d.ts +268 -0
- package/dist_ts_interfaces/requests/radius.js +3 -0
- package/{dist_ts/ts_interfaces → dist_ts_interfaces}/requests/stats.js +1 -1
- package/dist_ts_web/00_commitinfo_data.js +9 -0
- package/dist_ts_web/appstate.d.ts +96 -0
- package/dist_ts_web/appstate.js +587 -0
- package/dist_ts_web/elements/index.d.ts +8 -0
- package/dist_ts_web/elements/index.js +9 -0
- package/dist_ts_web/elements/ops-dashboard.d.ts +23 -0
- package/dist_ts_web/elements/ops-dashboard.js +271 -0
- package/dist_ts_web/elements/ops-view-config.d.ts +17 -0
- package/dist_ts_web/elements/ops-view-config.js +414 -0
- package/dist_ts_web/elements/ops-view-emails.d.ts +44 -0
- package/dist_ts_web/elements/ops-view-emails.js +880 -0
- package/dist_ts_web/elements/ops-view-logs.d.ts +13 -0
- package/dist_ts_web/elements/ops-view-logs.js +249 -0
- package/dist_ts_web/elements/ops-view-network.d.ts +65 -0
- package/dist_ts_web/elements/ops-view-network.js +579 -0
- package/dist_ts_web/elements/ops-view-overview.d.ts +14 -0
- package/dist_ts_web/elements/ops-view-overview.js +344 -0
- package/dist_ts_web/elements/ops-view-security.d.ts +21 -0
- package/dist_ts_web/elements/ops-view-security.js +568 -0
- package/dist_ts_web/elements/shared/css.d.ts +1 -0
- package/dist_ts_web/elements/shared/css.js +10 -0
- package/dist_ts_web/elements/shared/index.d.ts +2 -0
- package/dist_ts_web/elements/shared/index.js +3 -0
- package/dist_ts_web/elements/shared/ops-sectionheading.d.ts +5 -0
- package/dist_ts_web/elements/shared/ops-sectionheading.js +82 -0
- package/dist_ts_web/index.d.ts +1 -0
- package/dist_ts_web/index.js +10 -0
- package/dist_ts_web/plugins.d.ts +4 -0
- package/dist_ts_web/plugins.js +7 -0
- package/dist_ts_web/router.d.ts +25 -0
- package/dist_ts_web/router.js +165 -0
- package/npmextra.json +36 -2
- package/package.json +72 -46
- package/readme.hints.md +537 -670
- package/readme.md +659 -937
- package/ts/00_commitinfo_data.ts +3 -3
- package/ts/cache/classes.cache.cleaner.ts +166 -0
- package/ts/cache/classes.cached.document.ts +111 -0
- package/ts/cache/classes.cachedb.ts +154 -0
- package/ts/cache/documents/classes.cached.email.ts +240 -0
- package/ts/cache/documents/classes.cached.ip.reputation.ts +247 -0
- package/ts/cache/documents/index.ts +2 -0
- package/ts/cache/index.ts +7 -0
- package/ts/classes.dcrouter.ts +229 -162
- package/ts/errors/index.ts +0 -2
- package/ts/index.ts +8 -2
- package/ts/monitoring/classes.metricscache.ts +75 -0
- package/ts/monitoring/classes.metricsmanager.ts +522 -0
- package/ts/monitoring/index.ts +1 -0
- package/ts/opsserver/classes.opsserver.ts +5 -1
- package/ts/opsserver/handlers/admin.handler.ts +1 -1
- package/ts/opsserver/handlers/config.handler.ts +16 -58
- package/ts/opsserver/handlers/email-ops.handler.ts +318 -0
- package/ts/opsserver/handlers/index.ts +3 -1
- package/ts/opsserver/handlers/radius.handler.ts +405 -0
- package/ts/opsserver/handlers/security.handler.ts +115 -6
- package/ts/opsserver/handlers/stats.handler.ts +177 -18
- package/ts/paths.ts +9 -9
- package/ts/plugins.ts +73 -11
- package/ts/radius/classes.accounting.manager.ts +607 -0
- package/ts/radius/classes.radius.server.ts +532 -0
- package/ts/radius/classes.vlan.manager.ts +363 -0
- package/ts/radius/index.ts +14 -0
- package/ts/security/classes.contentscanner.ts +2 -2
- package/ts/security/classes.ipreputationchecker.ts +2 -2
- package/ts/sms/classes.smsservice.ts +7 -9
- package/ts/storage/classes.storagemanager.ts +3 -3
- package/ts_web/00_commitinfo_data.ts +3 -3
- package/ts_web/appstate.ts +523 -85
- package/ts_web/elements/index.ts +2 -1
- package/ts_web/elements/ops-dashboard.ts +117 -52
- package/ts_web/elements/ops-view-config.ts +236 -118
- package/ts_web/elements/ops-view-emails.ts +818 -0
- package/ts_web/elements/ops-view-logs.ts +14 -14
- package/ts_web/elements/ops-view-network.ts +580 -0
- package/ts_web/elements/ops-view-overview.ts +196 -110
- package/ts_web/elements/ops-view-security.ts +155 -96
- package/ts_web/elements/shared/ops-sectionheading.ts +1 -5
- package/ts_web/index.ts +4 -0
- package/ts_web/readme.md +211 -0
- package/ts_web/router.ts +181 -0
- package/.dockerignore +0 -1
- package/.gitea/workflows/docker_nottags.yaml +0 -71
- package/.gitea/workflows/docker_tags.yaml +0 -106
- package/.vscode/launch.json +0 -11
- package/.vscode/settings.json +0 -26
- package/Dockerfile +0 -46
- package/changelog.md +0 -247
- package/cli.child.js +0 -4
- package/cli.child.ts +0 -4
- package/cli.ts.js +0 -5
- package/dist_ts/deliverability/classes.ipwarmupmanager.d.ts +0 -253
- package/dist_ts/deliverability/classes.ipwarmupmanager.js +0 -639
- package/dist_ts/deliverability/classes.senderreputationmonitor.d.ts +0 -300
- package/dist_ts/deliverability/classes.senderreputationmonitor.js +0 -961
- package/dist_ts/deliverability/index.d.ts +0 -2
- package/dist_ts/deliverability/index.js +0 -3
- package/dist_ts/errors/email.errors.d.ts +0 -175
- package/dist_ts/errors/email.errors.js +0 -265
- package/dist_ts/errors/mta.errors.d.ts +0 -259
- package/dist_ts/errors/mta.errors.js +0 -472
- package/dist_ts/mail/core/classes.bouncemanager.d.ts +0 -200
- package/dist_ts/mail/core/classes.bouncemanager.js +0 -778
- package/dist_ts/mail/core/classes.email.d.ts +0 -291
- package/dist_ts/mail/core/classes.email.js +0 -780
- package/dist_ts/mail/core/classes.emailvalidator.d.ts +0 -61
- package/dist_ts/mail/core/classes.emailvalidator.js +0 -182
- package/dist_ts/mail/core/classes.templatemanager.d.ts +0 -95
- package/dist_ts/mail/core/classes.templatemanager.js +0 -239
- package/dist_ts/mail/core/index.d.ts +0 -4
- package/dist_ts/mail/core/index.js +0 -6
- package/dist_ts/mail/delivery/classes.delivery.queue.d.ts +0 -163
- package/dist_ts/mail/delivery/classes.delivery.queue.js +0 -485
- package/dist_ts/mail/delivery/classes.delivery.system.d.ts +0 -186
- package/dist_ts/mail/delivery/classes.delivery.system.js +0 -846
- package/dist_ts/mail/delivery/classes.emailsendjob.d.ts +0 -84
- package/dist_ts/mail/delivery/classes.emailsendjob.js +0 -362
- package/dist_ts/mail/delivery/classes.emailsignjob.d.ts +0 -18
- package/dist_ts/mail/delivery/classes.emailsignjob.js +0 -44
- package/dist_ts/mail/delivery/classes.mta.config.d.ts +0 -22
- package/dist_ts/mail/delivery/classes.mta.config.js +0 -51
- package/dist_ts/mail/delivery/classes.ratelimiter.d.ts +0 -98
- package/dist_ts/mail/delivery/classes.ratelimiter.js +0 -205
- package/dist_ts/mail/delivery/classes.smtp.client.legacy.d.ts +0 -275
- package/dist_ts/mail/delivery/classes.smtp.client.legacy.js +0 -973
- package/dist_ts/mail/delivery/classes.unified.rate.limiter.d.ts +0 -200
- package/dist_ts/mail/delivery/classes.unified.rate.limiter.js +0 -817
- package/dist_ts/mail/delivery/index.d.ts +0 -12
- package/dist_ts/mail/delivery/index.js +0 -18
- package/dist_ts/mail/delivery/interfaces.d.ts +0 -243
- package/dist_ts/mail/delivery/interfaces.js +0 -17
- package/dist_ts/mail/delivery/smtpclient/auth-handler.d.ts +0 -43
- package/dist_ts/mail/delivery/smtpclient/auth-handler.js +0 -188
- package/dist_ts/mail/delivery/smtpclient/command-handler.d.ts +0 -67
- package/dist_ts/mail/delivery/smtpclient/command-handler.js +0 -276
- package/dist_ts/mail/delivery/smtpclient/connection-manager.d.ts +0 -48
- package/dist_ts/mail/delivery/smtpclient/connection-manager.js +0 -238
- package/dist_ts/mail/delivery/smtpclient/constants.d.ts +0 -129
- package/dist_ts/mail/delivery/smtpclient/constants.js +0 -135
- package/dist_ts/mail/delivery/smtpclient/create-client.d.ts +0 -22
- package/dist_ts/mail/delivery/smtpclient/create-client.js +0 -86
- package/dist_ts/mail/delivery/smtpclient/error-handler.d.ts +0 -28
- package/dist_ts/mail/delivery/smtpclient/error-handler.js +0 -110
- package/dist_ts/mail/delivery/smtpclient/index.d.ts +0 -16
- package/dist_ts/mail/delivery/smtpclient/index.js +0 -21
- package/dist_ts/mail/delivery/smtpclient/interfaces.d.ts +0 -183
- package/dist_ts/mail/delivery/smtpclient/interfaces.js +0 -19
- package/dist_ts/mail/delivery/smtpclient/smtp-client.d.ts +0 -58
- package/dist_ts/mail/delivery/smtpclient/smtp-client.js +0 -279
- package/dist_ts/mail/delivery/smtpclient/tls-handler.d.ts +0 -33
- package/dist_ts/mail/delivery/smtpclient/tls-handler.js +0 -202
- package/dist_ts/mail/delivery/smtpclient/utils/helpers.d.ts +0 -77
- package/dist_ts/mail/delivery/smtpclient/utils/helpers.js +0 -196
- package/dist_ts/mail/delivery/smtpclient/utils/logging.d.ts +0 -46
- package/dist_ts/mail/delivery/smtpclient/utils/logging.js +0 -153
- package/dist_ts/mail/delivery/smtpclient/utils/validation.d.ts +0 -38
- package/dist_ts/mail/delivery/smtpclient/utils/validation.js +0 -139
- package/dist_ts/mail/delivery/smtpserver/certificate-utils.d.ts +0 -45
- package/dist_ts/mail/delivery/smtpserver/certificate-utils.js +0 -345
- package/dist_ts/mail/delivery/smtpserver/command-handler.d.ts +0 -156
- package/dist_ts/mail/delivery/smtpserver/command-handler.js +0 -1159
- package/dist_ts/mail/delivery/smtpserver/connection-manager.d.ts +0 -159
- package/dist_ts/mail/delivery/smtpserver/connection-manager.js +0 -894
- package/dist_ts/mail/delivery/smtpserver/constants.d.ts +0 -130
- package/dist_ts/mail/delivery/smtpserver/constants.js +0 -162
- package/dist_ts/mail/delivery/smtpserver/create-server.d.ts +0 -14
- package/dist_ts/mail/delivery/smtpserver/create-server.js +0 -28
- package/dist_ts/mail/delivery/smtpserver/data-handler.d.ts +0 -123
- package/dist_ts/mail/delivery/smtpserver/data-handler.js +0 -1148
- package/dist_ts/mail/delivery/smtpserver/index.d.ts +0 -20
- package/dist_ts/mail/delivery/smtpserver/index.js +0 -27
- package/dist_ts/mail/delivery/smtpserver/interfaces.d.ts +0 -530
- package/dist_ts/mail/delivery/smtpserver/interfaces.js +0 -10
- package/dist_ts/mail/delivery/smtpserver/secure-server.d.ts +0 -15
- package/dist_ts/mail/delivery/smtpserver/secure-server.js +0 -79
- package/dist_ts/mail/delivery/smtpserver/security-handler.d.ts +0 -86
- package/dist_ts/mail/delivery/smtpserver/security-handler.js +0 -234
- package/dist_ts/mail/delivery/smtpserver/session-manager.d.ts +0 -140
- package/dist_ts/mail/delivery/smtpserver/session-manager.js +0 -469
- package/dist_ts/mail/delivery/smtpserver/smtp-server.d.ts +0 -137
- package/dist_ts/mail/delivery/smtpserver/smtp-server.js +0 -666
- package/dist_ts/mail/delivery/smtpserver/starttls-handler.d.ts +0 -21
- package/dist_ts/mail/delivery/smtpserver/starttls-handler.js +0 -207
- package/dist_ts/mail/delivery/smtpserver/tls-handler.d.ts +0 -66
- package/dist_ts/mail/delivery/smtpserver/tls-handler.js +0 -261
- package/dist_ts/mail/delivery/smtpserver/utils/adaptive-logging.d.ts +0 -117
- package/dist_ts/mail/delivery/smtpserver/utils/adaptive-logging.js +0 -411
- package/dist_ts/mail/delivery/smtpserver/utils/helpers.d.ts +0 -78
- package/dist_ts/mail/delivery/smtpserver/utils/helpers.js +0 -208
- package/dist_ts/mail/delivery/smtpserver/utils/logging.d.ts +0 -106
- package/dist_ts/mail/delivery/smtpserver/utils/logging.js +0 -181
- package/dist_ts/mail/delivery/smtpserver/utils/validation.d.ts +0 -69
- package/dist_ts/mail/delivery/smtpserver/utils/validation.js +0 -360
- package/dist_ts/mail/index.d.ts +0 -8
- package/dist_ts/mail/index.js +0 -13
- package/dist_ts/mail/routing/classes.dns.manager.d.ts +0 -65
- package/dist_ts/mail/routing/classes.dns.manager.js +0 -413
- package/dist_ts/mail/routing/classes.dnsmanager.d.ts +0 -165
- package/dist_ts/mail/routing/classes.dnsmanager.js +0 -430
- package/dist_ts/mail/routing/classes.domain.registry.d.ts +0 -54
- package/dist_ts/mail/routing/classes.domain.registry.js +0 -118
- package/dist_ts/mail/routing/classes.email.config.d.ts +0 -64
- package/dist_ts/mail/routing/classes.email.config.js +0 -2
- package/dist_ts/mail/routing/classes.email.router.d.ts +0 -171
- package/dist_ts/mail/routing/classes.email.router.js +0 -491
- package/dist_ts/mail/routing/classes.unified.email.server.d.ts +0 -426
- package/dist_ts/mail/routing/classes.unified.email.server.js +0 -1454
- package/dist_ts/mail/routing/index.d.ts +0 -5
- package/dist_ts/mail/routing/index.js +0 -7
- package/dist_ts/mail/routing/interfaces.d.ts +0 -187
- package/dist_ts/mail/routing/interfaces.js +0 -2
- package/dist_ts/mail/security/classes.dkimcreator.d.ts +0 -68
- package/dist_ts/mail/security/classes.dkimcreator.js +0 -346
- package/dist_ts/mail/security/classes.dkimverifier.d.ts +0 -46
- package/dist_ts/mail/security/classes.dkimverifier.js +0 -317
- package/dist_ts/mail/security/classes.dmarcverifier.d.ts +0 -123
- package/dist_ts/mail/security/classes.dmarcverifier.js +0 -365
- package/dist_ts/mail/security/classes.spfverifier.d.ts +0 -103
- package/dist_ts/mail/security/classes.spfverifier.js +0 -492
- package/dist_ts/mail/security/index.d.ts +0 -4
- package/dist_ts/mail/security/index.js +0 -6
- package/dist_ts/ts/00_commitinfo_data.js +0 -9
- package/dist_ts/ts/classes.dcrouter.d.ts +0 -238
- package/dist_ts/ts/classes.dcrouter.js +0 -1008
- package/dist_ts/ts/config/index.d.ts +0 -1
- package/dist_ts/ts/config/index.js +0 -3
- package/dist_ts/ts/config/validator.d.ts +0 -104
- package/dist_ts/ts/config/validator.js +0 -152
- package/dist_ts/ts/deliverability/classes.ipwarmupmanager.d.ts +0 -253
- package/dist_ts/ts/deliverability/classes.ipwarmupmanager.js +0 -639
- package/dist_ts/ts/deliverability/classes.senderreputationmonitor.d.ts +0 -300
- package/dist_ts/ts/deliverability/classes.senderreputationmonitor.js +0 -961
- package/dist_ts/ts/deliverability/index.d.ts +0 -2
- package/dist_ts/ts/deliverability/index.js +0 -3
- package/dist_ts/ts/errors/base.errors.d.ts +0 -224
- package/dist_ts/ts/errors/base.errors.js +0 -310
- package/dist_ts/ts/errors/email.errors.d.ts +0 -175
- package/dist_ts/ts/errors/email.errors.js +0 -265
- package/dist_ts/ts/errors/error-handler.d.ts +0 -98
- package/dist_ts/ts/errors/error-handler.js +0 -282
- package/dist_ts/ts/errors/error.codes.d.ts +0 -115
- package/dist_ts/ts/errors/error.codes.js +0 -136
- package/dist_ts/ts/errors/index.d.ts +0 -56
- package/dist_ts/ts/errors/index.js +0 -138
- package/dist_ts/ts/errors/mta.errors.d.ts +0 -259
- package/dist_ts/ts/errors/mta.errors.js +0 -472
- package/dist_ts/ts/errors/reputation.errors.d.ts +0 -183
- package/dist_ts/ts/errors/reputation.errors.js +0 -292
- package/dist_ts/ts/index.d.ts +0 -4
- package/dist_ts/ts/index.js +0 -6
- package/dist_ts/ts/logger.d.ts +0 -17
- package/dist_ts/ts/logger.js +0 -77
- package/dist_ts/ts/mail/core/classes.bouncemanager.d.ts +0 -200
- package/dist_ts/ts/mail/core/classes.bouncemanager.js +0 -778
- package/dist_ts/ts/mail/core/classes.email.d.ts +0 -291
- package/dist_ts/ts/mail/core/classes.email.js +0 -780
- package/dist_ts/ts/mail/core/classes.emailvalidator.d.ts +0 -61
- package/dist_ts/ts/mail/core/classes.emailvalidator.js +0 -182
- package/dist_ts/ts/mail/core/classes.templatemanager.d.ts +0 -95
- package/dist_ts/ts/mail/core/classes.templatemanager.js +0 -239
- package/dist_ts/ts/mail/core/index.d.ts +0 -4
- package/dist_ts/ts/mail/core/index.js +0 -6
- package/dist_ts/ts/mail/delivery/classes.delivery.queue.d.ts +0 -163
- package/dist_ts/ts/mail/delivery/classes.delivery.queue.js +0 -485
- package/dist_ts/ts/mail/delivery/classes.delivery.system.d.ts +0 -186
- package/dist_ts/ts/mail/delivery/classes.delivery.system.js +0 -846
- package/dist_ts/ts/mail/delivery/classes.emailsendjob.d.ts +0 -84
- package/dist_ts/ts/mail/delivery/classes.emailsendjob.js +0 -362
- package/dist_ts/ts/mail/delivery/classes.emailsignjob.d.ts +0 -18
- package/dist_ts/ts/mail/delivery/classes.emailsignjob.js +0 -44
- package/dist_ts/ts/mail/delivery/classes.mta.config.d.ts +0 -22
- package/dist_ts/ts/mail/delivery/classes.mta.config.js +0 -51
- package/dist_ts/ts/mail/delivery/classes.ratelimiter.d.ts +0 -98
- package/dist_ts/ts/mail/delivery/classes.ratelimiter.js +0 -205
- package/dist_ts/ts/mail/delivery/classes.smtp.client.legacy.d.ts +0 -275
- package/dist_ts/ts/mail/delivery/classes.smtp.client.legacy.js +0 -973
- package/dist_ts/ts/mail/delivery/classes.unified.rate.limiter.d.ts +0 -200
- package/dist_ts/ts/mail/delivery/classes.unified.rate.limiter.js +0 -817
- package/dist_ts/ts/mail/delivery/index.d.ts +0 -12
- package/dist_ts/ts/mail/delivery/index.js +0 -18
- package/dist_ts/ts/mail/delivery/interfaces.d.ts +0 -243
- package/dist_ts/ts/mail/delivery/interfaces.js +0 -17
- package/dist_ts/ts/mail/delivery/smtpclient/auth-handler.d.ts +0 -43
- package/dist_ts/ts/mail/delivery/smtpclient/auth-handler.js +0 -188
- package/dist_ts/ts/mail/delivery/smtpclient/command-handler.d.ts +0 -67
- package/dist_ts/ts/mail/delivery/smtpclient/command-handler.js +0 -276
- package/dist_ts/ts/mail/delivery/smtpclient/connection-manager.d.ts +0 -48
- package/dist_ts/ts/mail/delivery/smtpclient/connection-manager.js +0 -238
- package/dist_ts/ts/mail/delivery/smtpclient/constants.d.ts +0 -129
- package/dist_ts/ts/mail/delivery/smtpclient/constants.js +0 -135
- package/dist_ts/ts/mail/delivery/smtpclient/create-client.d.ts +0 -22
- package/dist_ts/ts/mail/delivery/smtpclient/create-client.js +0 -86
- package/dist_ts/ts/mail/delivery/smtpclient/error-handler.d.ts +0 -28
- package/dist_ts/ts/mail/delivery/smtpclient/error-handler.js +0 -110
- package/dist_ts/ts/mail/delivery/smtpclient/index.d.ts +0 -16
- package/dist_ts/ts/mail/delivery/smtpclient/index.js +0 -21
- package/dist_ts/ts/mail/delivery/smtpclient/interfaces.d.ts +0 -183
- package/dist_ts/ts/mail/delivery/smtpclient/interfaces.js +0 -19
- package/dist_ts/ts/mail/delivery/smtpclient/smtp-client.d.ts +0 -58
- package/dist_ts/ts/mail/delivery/smtpclient/smtp-client.js +0 -279
- package/dist_ts/ts/mail/delivery/smtpclient/tls-handler.d.ts +0 -33
- package/dist_ts/ts/mail/delivery/smtpclient/tls-handler.js +0 -202
- package/dist_ts/ts/mail/delivery/smtpclient/utils/helpers.d.ts +0 -77
- package/dist_ts/ts/mail/delivery/smtpclient/utils/helpers.js +0 -196
- package/dist_ts/ts/mail/delivery/smtpclient/utils/logging.d.ts +0 -46
- package/dist_ts/ts/mail/delivery/smtpclient/utils/logging.js +0 -153
- package/dist_ts/ts/mail/delivery/smtpclient/utils/validation.d.ts +0 -38
- package/dist_ts/ts/mail/delivery/smtpclient/utils/validation.js +0 -139
- package/dist_ts/ts/mail/delivery/smtpserver/certificate-utils.d.ts +0 -45
- package/dist_ts/ts/mail/delivery/smtpserver/certificate-utils.js +0 -345
- package/dist_ts/ts/mail/delivery/smtpserver/command-handler.d.ts +0 -156
- package/dist_ts/ts/mail/delivery/smtpserver/command-handler.js +0 -1159
- package/dist_ts/ts/mail/delivery/smtpserver/connection-manager.d.ts +0 -159
- package/dist_ts/ts/mail/delivery/smtpserver/connection-manager.js +0 -894
- package/dist_ts/ts/mail/delivery/smtpserver/constants.d.ts +0 -130
- package/dist_ts/ts/mail/delivery/smtpserver/constants.js +0 -162
- package/dist_ts/ts/mail/delivery/smtpserver/create-server.d.ts +0 -14
- package/dist_ts/ts/mail/delivery/smtpserver/create-server.js +0 -28
- package/dist_ts/ts/mail/delivery/smtpserver/data-handler.d.ts +0 -123
- package/dist_ts/ts/mail/delivery/smtpserver/data-handler.js +0 -1148
- package/dist_ts/ts/mail/delivery/smtpserver/index.d.ts +0 -20
- package/dist_ts/ts/mail/delivery/smtpserver/index.js +0 -27
- package/dist_ts/ts/mail/delivery/smtpserver/interfaces.d.ts +0 -530
- package/dist_ts/ts/mail/delivery/smtpserver/interfaces.js +0 -10
- package/dist_ts/ts/mail/delivery/smtpserver/secure-server.d.ts +0 -15
- package/dist_ts/ts/mail/delivery/smtpserver/secure-server.js +0 -79
- package/dist_ts/ts/mail/delivery/smtpserver/security-handler.d.ts +0 -86
- package/dist_ts/ts/mail/delivery/smtpserver/security-handler.js +0 -234
- package/dist_ts/ts/mail/delivery/smtpserver/session-manager.d.ts +0 -140
- package/dist_ts/ts/mail/delivery/smtpserver/session-manager.js +0 -469
- package/dist_ts/ts/mail/delivery/smtpserver/smtp-server.d.ts +0 -137
- package/dist_ts/ts/mail/delivery/smtpserver/smtp-server.js +0 -666
- package/dist_ts/ts/mail/delivery/smtpserver/starttls-handler.d.ts +0 -21
- package/dist_ts/ts/mail/delivery/smtpserver/starttls-handler.js +0 -207
- package/dist_ts/ts/mail/delivery/smtpserver/tls-handler.d.ts +0 -66
- package/dist_ts/ts/mail/delivery/smtpserver/tls-handler.js +0 -261
- package/dist_ts/ts/mail/delivery/smtpserver/utils/adaptive-logging.d.ts +0 -117
- package/dist_ts/ts/mail/delivery/smtpserver/utils/adaptive-logging.js +0 -411
- package/dist_ts/ts/mail/delivery/smtpserver/utils/helpers.d.ts +0 -78
- package/dist_ts/ts/mail/delivery/smtpserver/utils/helpers.js +0 -208
- package/dist_ts/ts/mail/delivery/smtpserver/utils/logging.d.ts +0 -106
- package/dist_ts/ts/mail/delivery/smtpserver/utils/logging.js +0 -181
- package/dist_ts/ts/mail/delivery/smtpserver/utils/validation.d.ts +0 -69
- package/dist_ts/ts/mail/delivery/smtpserver/utils/validation.js +0 -360
- package/dist_ts/ts/mail/index.d.ts +0 -8
- package/dist_ts/ts/mail/index.js +0 -13
- package/dist_ts/ts/mail/routing/classes.dns.manager.d.ts +0 -65
- package/dist_ts/ts/mail/routing/classes.dns.manager.js +0 -413
- package/dist_ts/ts/mail/routing/classes.dnsmanager.d.ts +0 -165
- package/dist_ts/ts/mail/routing/classes.dnsmanager.js +0 -430
- package/dist_ts/ts/mail/routing/classes.domain.registry.d.ts +0 -54
- package/dist_ts/ts/mail/routing/classes.domain.registry.js +0 -118
- package/dist_ts/ts/mail/routing/classes.email.config.d.ts +0 -64
- package/dist_ts/ts/mail/routing/classes.email.config.js +0 -2
- package/dist_ts/ts/mail/routing/classes.email.router.d.ts +0 -171
- package/dist_ts/ts/mail/routing/classes.email.router.js +0 -491
- package/dist_ts/ts/mail/routing/classes.unified.email.server.d.ts +0 -426
- package/dist_ts/ts/mail/routing/classes.unified.email.server.js +0 -1454
- package/dist_ts/ts/mail/routing/index.d.ts +0 -5
- package/dist_ts/ts/mail/routing/index.js +0 -7
- package/dist_ts/ts/mail/routing/interfaces.d.ts +0 -187
- package/dist_ts/ts/mail/routing/interfaces.js +0 -2
- package/dist_ts/ts/mail/security/classes.dkimcreator.d.ts +0 -68
- package/dist_ts/ts/mail/security/classes.dkimcreator.js +0 -346
- package/dist_ts/ts/mail/security/classes.dkimverifier.d.ts +0 -46
- package/dist_ts/ts/mail/security/classes.dkimverifier.js +0 -317
- package/dist_ts/ts/mail/security/classes.dmarcverifier.d.ts +0 -123
- package/dist_ts/ts/mail/security/classes.dmarcverifier.js +0 -365
- package/dist_ts/ts/mail/security/classes.spfverifier.d.ts +0 -103
- package/dist_ts/ts/mail/security/classes.spfverifier.js +0 -492
- package/dist_ts/ts/mail/security/index.d.ts +0 -4
- package/dist_ts/ts/mail/security/index.js +0 -6
- package/dist_ts/ts/opsserver/classes.opsserver.d.ts +0 -20
- package/dist_ts/ts/opsserver/classes.opsserver.js +0 -44
- package/dist_ts/ts/opsserver/handlers/admin.handler.js +0 -177
- package/dist_ts/ts/opsserver/handlers/config.handler.js +0 -100
- package/dist_ts/ts/opsserver/handlers/logs.handler.js +0 -121
- package/dist_ts/ts/opsserver/handlers/security.handler.js +0 -118
- package/dist_ts/ts/opsserver/handlers/stats.handler.js +0 -233
- package/dist_ts/ts/opsserver/index.d.ts +0 -1
- package/dist_ts/ts/opsserver/index.js +0 -2
- package/dist_ts/ts/paths.d.ts +0 -14
- package/dist_ts/ts/paths.js +0 -39
- package/dist_ts/ts/plugins.d.ts +0 -46
- package/dist_ts/ts/plugins.js +0 -53
- package/dist_ts/ts/security/classes.contentscanner.d.ts +0 -160
- package/dist_ts/ts/security/classes.contentscanner.js +0 -634
- package/dist_ts/ts/security/classes.ipreputationchecker.d.ts +0 -150
- package/dist_ts/ts/security/classes.ipreputationchecker.js +0 -508
- package/dist_ts/ts/security/classes.securitylogger.d.ts +0 -140
- package/dist_ts/ts/security/classes.securitylogger.js +0 -232
- package/dist_ts/ts/security/index.d.ts +0 -3
- package/dist_ts/ts/security/index.js +0 -4
- package/dist_ts/ts/sms/classes.smsservice.d.ts +0 -15
- package/dist_ts/ts/sms/classes.smsservice.js +0 -72
- package/dist_ts/ts/sms/config/sms.config.d.ts +0 -93
- package/dist_ts/ts/sms/config/sms.config.js +0 -2
- package/dist_ts/ts/sms/config/sms.schema.d.ts +0 -5
- package/dist_ts/ts/sms/config/sms.schema.js +0 -121
- package/dist_ts/ts/sms/index.d.ts +0 -1
- package/dist_ts/ts/sms/index.js +0 -2
- package/dist_ts/ts/storage/classes.storagemanager.d.ts +0 -82
- package/dist_ts/ts/storage/classes.storagemanager.js +0 -341
- package/dist_ts/ts/storage/index.d.ts +0 -1
- package/dist_ts/ts/storage/index.js +0 -3
- package/dist_ts/ts_interfaces/requests/index.js +0 -5
- package/html/index.html +0 -121
- package/readme.opsserver.md +0 -351
- package/test/helpers/server.loader.ts +0 -347
- package/test/helpers/smtp.client.ts +0 -209
- package/test/helpers/utils.ts +0 -311
- package/test/readme.md +0 -443
- package/test/suite/smtpclient_commands/test.ccmd-01.ehlo-helo-sending.ts +0 -168
- package/test/suite/smtpclient_commands/test.ccmd-02.mail-from-parameters.ts +0 -277
- package/test/suite/smtpclient_commands/test.ccmd-03.rcpt-to-multiple.ts +0 -283
- package/test/suite/smtpclient_commands/test.ccmd-04.data-transmission.ts +0 -274
- package/test/suite/smtpclient_commands/test.ccmd-05.auth-mechanisms.ts +0 -306
- package/test/suite/smtpclient_commands/test.ccmd-06.command-pipelining.ts +0 -233
- package/test/suite/smtpclient_commands/test.ccmd-07.response-parsing.ts +0 -243
- package/test/suite/smtpclient_commands/test.ccmd-08.rset-command.ts +0 -333
- package/test/suite/smtpclient_commands/test.ccmd-09.noop-command.ts +0 -339
- package/test/suite/smtpclient_commands/test.ccmd-10.vrfy-expn.ts +0 -457
- package/test/suite/smtpclient_commands/test.ccmd-11.help-command.ts +0 -409
- package/test/suite/smtpclient_connection/test.ccm-01.basic-tcp-connection.ts +0 -150
- package/test/suite/smtpclient_connection/test.ccm-02.tls-connection.ts +0 -140
- package/test/suite/smtpclient_connection/test.ccm-03.starttls-upgrade.ts +0 -208
- package/test/suite/smtpclient_connection/test.ccm-04.connection-pooling.ts +0 -250
- package/test/suite/smtpclient_connection/test.ccm-05.connection-reuse.ts +0 -288
- package/test/suite/smtpclient_connection/test.ccm-06.connection-timeout.ts +0 -267
- package/test/suite/smtpclient_connection/test.ccm-07.automatic-reconnection.ts +0 -324
- package/test/suite/smtpclient_connection/test.ccm-08.dns-resolution.ts +0 -139
- package/test/suite/smtpclient_connection/test.ccm-09.ipv6-dual-stack.ts +0 -167
- package/test/suite/smtpclient_connection/test.ccm-10.proxy-support.ts +0 -305
- package/test/suite/smtpclient_connection/test.ccm-11.keepalive.ts +0 -299
- package/test/suite/smtpclient_edge-cases/test.cedge-01.unusual-server-responses.ts +0 -529
- package/test/suite/smtpclient_edge-cases/test.cedge-02.malformed-commands.ts +0 -438
- package/test/suite/smtpclient_edge-cases/test.cedge-03.protocol-violations.ts +0 -446
- package/test/suite/smtpclient_edge-cases/test.cedge-04.resource-constraints.ts +0 -530
- package/test/suite/smtpclient_edge-cases/test.cedge-05.encoding-issues.ts +0 -145
- package/test/suite/smtpclient_edge-cases/test.cedge-06.large-headers.ts +0 -180
- package/test/suite/smtpclient_edge-cases/test.cedge-07.concurrent-operations.ts +0 -204
- package/test/suite/smtpclient_email-composition/test.cep-01.basic-headers.ts +0 -245
- package/test/suite/smtpclient_email-composition/test.cep-02.mime-multipart.ts +0 -321
- package/test/suite/smtpclient_email-composition/test.cep-03.attachment-encoding.ts +0 -334
- package/test/suite/smtpclient_email-composition/test.cep-04.bcc-handling.ts +0 -187
- package/test/suite/smtpclient_email-composition/test.cep-05.reply-to-return-path.ts +0 -277
- package/test/suite/smtpclient_email-composition/test.cep-06.utf8-international.ts +0 -235
- package/test/suite/smtpclient_email-composition/test.cep-07.html-inline-images.ts +0 -489
- package/test/suite/smtpclient_email-composition/test.cep-08.custom-headers.ts +0 -293
- package/test/suite/smtpclient_email-composition/test.cep-09.priority-importance.ts +0 -314
- package/test/suite/smtpclient_email-composition/test.cep-10.receipts-dsn.ts +0 -411
- package/test/suite/smtpclient_error-handling/test.cerr-01.4xx-errors.ts +0 -232
- package/test/suite/smtpclient_error-handling/test.cerr-02.5xx-errors.ts +0 -309
- package/test/suite/smtpclient_error-handling/test.cerr-03.network-failures.ts +0 -299
- package/test/suite/smtpclient_error-handling/test.cerr-04.greylisting-handling.ts +0 -255
- package/test/suite/smtpclient_error-handling/test.cerr-05.quota-exceeded.ts +0 -273
- package/test/suite/smtpclient_error-handling/test.cerr-06.invalid-recipients.ts +0 -320
- package/test/suite/smtpclient_error-handling/test.cerr-07.message-size-limits.ts +0 -320
- package/test/suite/smtpclient_error-handling/test.cerr-08.rate-limiting.ts +0 -261
- package/test/suite/smtpclient_error-handling/test.cerr-09.connection-pool-errors.ts +0 -299
- package/test/suite/smtpclient_error-handling/test.cerr-10.partial-failure.ts +0 -373
- package/test/suite/smtpclient_performance/test.cperf-01.bulk-sending.ts +0 -332
- package/test/suite/smtpclient_performance/test.cperf-02.message-throughput.ts +0 -304
- package/test/suite/smtpclient_performance/test.cperf-03.memory-usage.ts +0 -332
- package/test/suite/smtpclient_performance/test.cperf-04.cpu-utilization.ts +0 -373
- package/test/suite/smtpclient_performance/test.cperf-05.network-efficiency.ts +0 -181
- package/test/suite/smtpclient_performance/test.cperf-06.caching-strategies.ts +0 -190
- package/test/suite/smtpclient_performance/test.cperf-07.queue-management.ts +0 -171
- package/test/suite/smtpclient_performance/test.cperf-08.dns-caching.ts +0 -50
- package/test/suite/smtpclient_reliability/test.crel-01.reconnection-logic.ts +0 -305
- package/test/suite/smtpclient_reliability/test.crel-02.network-interruption.ts +0 -207
- package/test/suite/smtpclient_reliability/test.crel-03.queue-persistence.ts +0 -469
- package/test/suite/smtpclient_reliability/test.crel-04.crash-recovery.ts +0 -520
- package/test/suite/smtpclient_reliability/test.crel-05.memory-leaks.ts +0 -503
- package/test/suite/smtpclient_reliability/test.crel-06.concurrency-safety.ts +0 -558
- package/test/suite/smtpclient_reliability/test.crel-07.resource-cleanup.ts +0 -52
- package/test/suite/smtpclient_rfc-compliance/test.crfc-01.rfc5321-client.ts +0 -283
- package/test/suite/smtpclient_rfc-compliance/test.crfc-02.esmtp-compliance.ts +0 -77
- package/test/suite/smtpclient_rfc-compliance/test.crfc-03.command-syntax.ts +0 -67
- package/test/suite/smtpclient_rfc-compliance/test.crfc-04.response-codes.ts +0 -54
- package/test/suite/smtpclient_rfc-compliance/test.crfc-05.state-machine.ts +0 -703
- package/test/suite/smtpclient_rfc-compliance/test.crfc-06.protocol-negotiation.ts +0 -688
- package/test/suite/smtpclient_rfc-compliance/test.crfc-07.interoperability.ts +0 -728
- package/test/suite/smtpclient_rfc-compliance/test.crfc-08.smtp-extensions.ts +0 -656
- package/test/suite/smtpclient_security/test.csec-01.tls-verification.ts +0 -88
- package/test/suite/smtpclient_security/test.csec-02.oauth2-authentication.ts +0 -132
- package/test/suite/smtpclient_security/test.csec-03.dkim-signing.ts +0 -138
- package/test/suite/smtpclient_security/test.csec-04.spf-compliance.ts +0 -163
- package/test/suite/smtpclient_security/test.csec-05.dmarc-policy.ts +0 -200
- package/test/suite/smtpclient_security/test.csec-06.certificate-validation.ts +0 -145
- package/test/suite/smtpclient_security/test.csec-07.cipher-suites.ts +0 -153
- package/test/suite/smtpclient_security/test.csec-08.authentication-fallback.ts +0 -154
- package/test/suite/smtpclient_security/test.csec-09.relay-restrictions.ts +0 -166
- package/test/suite/smtpclient_security/test.csec-10.anti-spam-measures.ts +0 -196
- package/test/suite/smtpserver_commands/test.cmd-01.ehlo-command.ts +0 -193
- package/test/suite/smtpserver_commands/test.cmd-02.mail-from.ts +0 -330
- package/test/suite/smtpserver_commands/test.cmd-03.rcpt-to.ts +0 -296
- package/test/suite/smtpserver_commands/test.cmd-04.data-command.ts +0 -395
- package/test/suite/smtpserver_commands/test.cmd-05.noop-command.ts +0 -320
- package/test/suite/smtpserver_commands/test.cmd-06.rset-command.ts +0 -399
- package/test/suite/smtpserver_commands/test.cmd-07.vrfy-command.ts +0 -391
- package/test/suite/smtpserver_commands/test.cmd-08.expn-command.ts +0 -450
- package/test/suite/smtpserver_commands/test.cmd-09.size-extension.ts +0 -465
- package/test/suite/smtpserver_commands/test.cmd-10.help-command.ts +0 -454
- package/test/suite/smtpserver_commands/test.cmd-11.command-pipelining.ts +0 -334
- package/test/suite/smtpserver_commands/test.cmd-12.helo-command.ts +0 -420
- package/test/suite/smtpserver_commands/test.cmd-13.quit-command.ts +0 -384
- package/test/suite/smtpserver_connection/test.cm-01.tls-connection.ts +0 -61
- package/test/suite/smtpserver_connection/test.cm-02.multiple-connections.ts +0 -112
- package/test/suite/smtpserver_connection/test.cm-03.connection-timeout.ts +0 -134
- package/test/suite/smtpserver_connection/test.cm-04.connection-limits.ts +0 -374
- package/test/suite/smtpserver_connection/test.cm-05.connection-rejection.ts +0 -296
- package/test/suite/smtpserver_connection/test.cm-06.starttls-upgrade.ts +0 -468
- package/test/suite/smtpserver_connection/test.cm-07.abrupt-disconnection.ts +0 -321
- package/test/suite/smtpserver_connection/test.cm-08.tls-versions.ts +0 -361
- package/test/suite/smtpserver_connection/test.cm-09.tls-ciphers.ts +0 -556
- package/test/suite/smtpserver_connection/test.cm-10.plain-connection.ts +0 -293
- package/test/suite/smtpserver_connection/test.cm-11.keepalive.ts +0 -382
- package/test/suite/smtpserver_edge-cases/test.edge-01.very-large-email.ts +0 -239
- package/test/suite/smtpserver_edge-cases/test.edge-02.very-small-email.ts +0 -389
- package/test/suite/smtpserver_edge-cases/test.edge-03.invalid-character-handling.ts +0 -479
- package/test/suite/smtpserver_edge-cases/test.edge-04.empty-commands.ts +0 -430
- package/test/suite/smtpserver_edge-cases/test.edge-05.extremely-long-lines.ts +0 -425
- package/test/suite/smtpserver_edge-cases/test.edge-06.extremely-long-headers.ts +0 -404
- package/test/suite/smtpserver_edge-cases/test.edge-07.unusual-mime-types.ts +0 -333
- package/test/suite/smtpserver_edge-cases/test.edge-08.nested-mime-structures.ts +0 -379
- package/test/suite/smtpserver_email-processing/test.ep-01.basic-email-sending.ts +0 -338
- package/test/suite/smtpserver_email-processing/test.ep-02.invalid-email-addresses.ts +0 -315
- package/test/suite/smtpserver_email-processing/test.ep-03.multiple-recipients.ts +0 -493
- package/test/suite/smtpserver_email-processing/test.ep-04.large-email.ts +0 -528
- package/test/suite/smtpserver_email-processing/test.ep-05.mime-handling.ts +0 -515
- package/test/suite/smtpserver_email-processing/test.ep-06.attachment-handling.ts +0 -629
- package/test/suite/smtpserver_email-processing/test.ep-07.special-character-handling.ts +0 -462
- package/test/suite/smtpserver_email-processing/test.ep-08.email-routing.ts +0 -527
- package/test/suite/smtpserver_email-processing/test.ep-09.delivery-status-notifications.ts +0 -486
- package/test/suite/smtpserver_error-handling/test.err-01.syntax-errors.ts +0 -475
- package/test/suite/smtpserver_error-handling/test.err-02.invalid-sequence.ts +0 -450
- package/test/suite/smtpserver_error-handling/test.err-03.temporary-failures.ts +0 -453
- package/test/suite/smtpserver_error-handling/test.err-04.permanent-failures.ts +0 -325
- package/test/suite/smtpserver_error-handling/test.err-05.resource-exhaustion.ts +0 -302
- package/test/suite/smtpserver_error-handling/test.err-06.malformed-mime.ts +0 -374
- package/test/suite/smtpserver_error-handling/test.err-07.exception-handling.ts +0 -333
- package/test/suite/smtpserver_error-handling/test.err-08.error-logging.ts +0 -324
- package/test/suite/smtpserver_performance/test.perf-01.throughput.ts +0 -183
- package/test/suite/smtpserver_performance/test.perf-02.concurrency.ts +0 -388
- package/test/suite/smtpserver_performance/test.perf-03.cpu-utilization.ts +0 -245
- package/test/suite/smtpserver_performance/test.perf-04.memory-usage.ts +0 -238
- package/test/suite/smtpserver_performance/test.perf-05.connection-processing-time.ts +0 -363
- package/test/suite/smtpserver_performance/test.perf-06.message-processing-time.ts +0 -252
- package/test/suite/smtpserver_performance/test.perf-07.resource-cleanup.ts +0 -317
- package/test/suite/smtpserver_reliability/test.rel-01.long-running-operation.ts +0 -344
- package/test/suite/smtpserver_reliability/test.rel-02.restart-recovery.ts +0 -328
- package/test/suite/smtpserver_reliability/test.rel-03.resource-leak-detection.ts +0 -394
- package/test/suite/smtpserver_reliability/test.rel-04.error-recovery.ts +0 -401
- package/test/suite/smtpserver_reliability/test.rel-05.dns-resolution-failure.ts +0 -335
- package/test/suite/smtpserver_reliability/test.rel-06.network-interruption.ts +0 -410
- package/test/suite/smtpserver_rfc-compliance/test.rfc-01.rfc5321-compliance.ts +0 -382
- package/test/suite/smtpserver_rfc-compliance/test.rfc-02.rfc5322-compliance.ts +0 -428
- package/test/suite/smtpserver_rfc-compliance/test.rfc-03.rfc7208-spf-compliance.ts +0 -330
- package/test/suite/smtpserver_rfc-compliance/test.rfc-04.rfc6376-dkim-compliance.ts +0 -450
- package/test/suite/smtpserver_rfc-compliance/test.rfc-05.rfc7489-dmarc-compliance.ts +0 -408
- package/test/suite/smtpserver_rfc-compliance/test.rfc-06.rfc8314-tls-compliance.ts +0 -366
- package/test/suite/smtpserver_rfc-compliance/test.rfc-07.rfc3461-dsn-compliance.ts +0 -399
- package/test/suite/smtpserver_security/test.sec-01.authentication.ts +0 -218
- package/test/suite/smtpserver_security/test.sec-02.authorization.ts +0 -286
- package/test/suite/smtpserver_security/test.sec-03.dkim-processing.ts +0 -414
- package/test/suite/smtpserver_security/test.sec-04.spf-checking.ts +0 -280
- package/test/suite/smtpserver_security/test.sec-05.dmarc-policy.ts +0 -374
- package/test/suite/smtpserver_security/test.sec-06.ip-reputation.ts +0 -303
- package/test/suite/smtpserver_security/test.sec-07.content-scanning.ts +0 -409
- package/test/suite/smtpserver_security/test.sec-08.rate-limiting.ts +0 -324
- package/test/suite/smtpserver_security/test.sec-09.tls-certificate-validation.ts +0 -312
- package/test/suite/smtpserver_security/test.sec-10.header-injection-prevention.ts +0 -332
- package/test/suite/smtpserver_security/test.sec-11.bounce-management.ts +0 -363
- package/test/test.base.ts +0 -65
- package/test/test.bouncemanager.ts +0 -196
- package/test/test.config.md +0 -175
- package/test/test.contentscanner.ts +0 -265
- package/test/test.dcrouter.email.ts +0 -201
- package/test/test.deliverability.ts +0 -55
- package/test/test.dns-manager-creation.ts +0 -141
- package/test/test.dns-mode-switching.ts +0 -257
- package/test/test.dns-server-config.ts +0 -140
- package/test/test.dns-socket-handler.ts +0 -169
- package/test/test.dns-validation.ts +0 -283
- package/test/test.email-socket-handler.ts +0 -228
- package/test/test.email.integration.ts +0 -377
- package/test/test.email.router.ts +0 -283
- package/test/test.emailauth.ts +0 -195
- package/test/test.errors.ts +0 -408
- package/test/test.integration.storage.ts +0 -313
- package/test/test.integration.ts +0 -75
- package/test/test.ipreputationchecker.ts +0 -179
- package/test/test.ipwarmupmanager.ts +0 -323
- package/test/test.jwt-auth.ts +0 -130
- package/test/test.minimal.ts +0 -66
- package/test/test.opsserver-api.ts +0 -83
- package/test/test.protected-endpoint.ts +0 -115
- package/test/test.rate-limiting-integration.ts +0 -236
- package/test/test.ratelimiter.ts +0 -141
- package/test/test.reputationmonitor.ts +0 -262
- package/test/test.smartmail.ts +0 -248
- package/test/test.smtp.client.compatibility.ts +0 -154
- package/test/test.smtp.client.ts +0 -191
- package/test/test.smtp.server.ts +0 -180
- package/test/test.socket-handler-integration.ts +0 -240
- package/test/test.socket-handler-unit.ts +0 -198
- package/test/test.storagemanager.ts +0 -289
- package/ts/deliverability/classes.ipwarmupmanager.ts +0 -896
- package/ts/deliverability/classes.senderreputationmonitor.ts +0 -1244
- package/ts/deliverability/index.ts +0 -13
- package/ts/errors/email.errors.ts +0 -383
- package/ts/errors/mta.errors.ts +0 -681
- package/ts/mail/core/classes.bouncemanager.ts +0 -965
- package/ts/mail/core/classes.email.ts +0 -941
- package/ts/mail/core/classes.emailvalidator.ts +0 -239
- package/ts/mail/core/classes.templatemanager.ts +0 -320
- package/ts/mail/core/index.ts +0 -5
- package/ts/mail/delivery/classes.delivery.queue.ts +0 -645
- package/ts/mail/delivery/classes.delivery.system.ts +0 -1089
- package/ts/mail/delivery/classes.emailsendjob.ts +0 -447
- package/ts/mail/delivery/classes.emailsendjob.ts.backup +0 -691
- package/ts/mail/delivery/classes.emailsignjob.ts +0 -67
- package/ts/mail/delivery/classes.mta.config.ts +0 -73
- package/ts/mail/delivery/classes.ratelimiter.ts +0 -281
- package/ts/mail/delivery/classes.smtp.client.legacy.ts +0 -1422
- package/ts/mail/delivery/classes.unified.rate.limiter.ts +0 -1053
- package/ts/mail/delivery/index.ts +0 -24
- package/ts/mail/delivery/interfaces.ts +0 -291
- package/ts/mail/delivery/smtpclient/auth-handler.ts +0 -232
- package/ts/mail/delivery/smtpclient/command-handler.ts +0 -343
- package/ts/mail/delivery/smtpclient/connection-manager.ts +0 -289
- package/ts/mail/delivery/smtpclient/constants.ts +0 -145
- package/ts/mail/delivery/smtpclient/create-client.ts +0 -94
- package/ts/mail/delivery/smtpclient/error-handler.ts +0 -141
- package/ts/mail/delivery/smtpclient/index.ts +0 -24
- package/ts/mail/delivery/smtpclient/interfaces.ts +0 -242
- package/ts/mail/delivery/smtpclient/smtp-client.ts +0 -357
- package/ts/mail/delivery/smtpclient/tls-handler.ts +0 -254
- package/ts/mail/delivery/smtpclient/utils/helpers.ts +0 -224
- package/ts/mail/delivery/smtpclient/utils/logging.ts +0 -212
- package/ts/mail/delivery/smtpclient/utils/validation.ts +0 -170
- package/ts/mail/delivery/smtpserver/certificate-utils.ts +0 -398
- package/ts/mail/delivery/smtpserver/command-handler.ts +0 -1340
- package/ts/mail/delivery/smtpserver/connection-manager.ts +0 -1045
- package/ts/mail/delivery/smtpserver/constants.ts +0 -181
- package/ts/mail/delivery/smtpserver/create-server.ts +0 -31
- package/ts/mail/delivery/smtpserver/data-handler.ts +0 -1283
- package/ts/mail/delivery/smtpserver/index.ts +0 -32
- package/ts/mail/delivery/smtpserver/interfaces.ts +0 -655
- package/ts/mail/delivery/smtpserver/secure-server.ts +0 -97
- package/ts/mail/delivery/smtpserver/security-handler.ts +0 -345
- package/ts/mail/delivery/smtpserver/session-manager.ts +0 -557
- package/ts/mail/delivery/smtpserver/smtp-server.ts +0 -804
- package/ts/mail/delivery/smtpserver/starttls-handler.ts +0 -262
- package/ts/mail/delivery/smtpserver/tls-handler.ts +0 -346
- package/ts/mail/delivery/smtpserver/utils/adaptive-logging.ts +0 -514
- package/ts/mail/delivery/smtpserver/utils/helpers.ts +0 -246
- package/ts/mail/delivery/smtpserver/utils/logging.ts +0 -246
- package/ts/mail/delivery/smtpserver/utils/validation.ts +0 -436
- package/ts/mail/index.ts +0 -19
- package/ts/mail/routing/classes.dns.manager.ts +0 -563
- package/ts/mail/routing/classes.dnsmanager.ts +0 -559
- package/ts/mail/routing/classes.domain.registry.ts +0 -139
- package/ts/mail/routing/classes.email.config.ts +0 -82
- package/ts/mail/routing/classes.email.router.ts +0 -575
- package/ts/mail/routing/classes.unified.email.server.ts +0 -1873
- package/ts/mail/routing/index.ts +0 -6
- package/ts/mail/routing/interfaces.ts +0 -202
- package/ts/mail/security/classes.dkimcreator.ts +0 -431
- package/ts/mail/security/classes.dkimverifier.ts +0 -382
- package/ts/mail/security/classes.dmarcverifier.ts +0 -478
- package/ts/mail/security/classes.spfverifier.ts +0 -606
- package/ts/mail/security/index.ts +0 -5
- package/ts_interfaces/data/auth.ts +0 -8
- package/ts_interfaces/data/index.ts +0 -2
- package/ts_interfaces/data/stats.ts +0 -101
- package/ts_interfaces/index.ts +0 -9
- package/ts_interfaces/plugins.ts +0 -6
- package/ts_interfaces/requests/admin.ts +0 -46
- package/ts_interfaces/requests/config.ts +0 -35
- package/ts_interfaces/requests/index.ts +0 -4
- package/ts_interfaces/requests/logs.ts +0 -44
- package/ts_interfaces/requests/stats.ts +0 -162
- package/ts_interfaces/tspublish.json +0 -3
- package/ts_web/elements/ops-view-stats.ts +0 -299
- package/tsconfig.json +0 -15
- /package/dist_ts/{ts/opsserver → opsserver}/handlers/logs.handler.d.ts +0 -0
- /package/dist_ts/{ts/opsserver → opsserver}/handlers/security.handler.d.ts +0 -0
- /package/dist_ts/{ts/opsserver → opsserver}/handlers/stats.handler.d.ts +0 -0
- /package/{dist_ts/ts_interfaces → dist_ts_interfaces}/data/auth.d.ts +0 -0
- /package/{dist_ts/ts_interfaces → dist_ts_interfaces}/data/index.d.ts +0 -0
- /package/{dist_ts/ts_interfaces → dist_ts_interfaces}/index.d.ts +0 -0
- /package/{dist_ts/ts_interfaces → dist_ts_interfaces}/plugins.d.ts +0 -0
- /package/{dist_ts/ts_interfaces → dist_ts_interfaces}/requests/admin.d.ts +0 -0
- /package/{dist_ts/ts_interfaces → dist_ts_interfaces}/requests/logs.d.ts +0 -0
- /package/{dist_ts/ts_interfaces → dist_ts_interfaces}/requests/stats.d.ts +0 -0
- /package/{dist_ts/ts → dist_ts_web}/00_commitinfo_data.d.ts +0 -0
|
@@ -1,1148 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SMTP Data Handler
|
|
3
|
-
* Responsible for processing email data during and after DATA command
|
|
4
|
-
*/
|
|
5
|
-
import * as plugins from '../../../plugins.js';
|
|
6
|
-
import * as fs from 'fs';
|
|
7
|
-
import * as path from 'path';
|
|
8
|
-
import { SmtpState } from './interfaces.js';
|
|
9
|
-
import { SmtpResponseCode, SMTP_PATTERNS, SMTP_DEFAULTS } from './constants.js';
|
|
10
|
-
import { SmtpLogger } from './utils/logging.js';
|
|
11
|
-
import { detectHeaderInjection } from './utils/validation.js';
|
|
12
|
-
import { Email } from '../../core/classes.email.js';
|
|
13
|
-
/**
|
|
14
|
-
* Handles SMTP DATA command and email data processing
|
|
15
|
-
*/
|
|
16
|
-
export class DataHandler {
|
|
17
|
-
/**
|
|
18
|
-
* Creates a new data handler
|
|
19
|
-
* @param smtpServer - SMTP server instance
|
|
20
|
-
*/
|
|
21
|
-
constructor(smtpServer) {
|
|
22
|
-
this.smtpServer = smtpServer;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Process incoming email data
|
|
26
|
-
* @param socket - Client socket
|
|
27
|
-
* @param data - Data chunk
|
|
28
|
-
* @returns Promise that resolves when the data is processed
|
|
29
|
-
*/
|
|
30
|
-
async processEmailData(socket, data) {
|
|
31
|
-
// Get the session for this socket
|
|
32
|
-
const session = this.smtpServer.getSessionManager().getSession(socket);
|
|
33
|
-
if (!session) {
|
|
34
|
-
this.sendResponse(socket, `${SmtpResponseCode.LOCAL_ERROR} Internal server error - session not found`);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
// Clear any existing timeout and set a new one
|
|
38
|
-
if (session.dataTimeoutId) {
|
|
39
|
-
clearTimeout(session.dataTimeoutId);
|
|
40
|
-
}
|
|
41
|
-
session.dataTimeoutId = setTimeout(() => {
|
|
42
|
-
if (session.state === SmtpState.DATA_RECEIVING) {
|
|
43
|
-
SmtpLogger.warn(`DATA timeout for session ${session.id}`, { sessionId: session.id });
|
|
44
|
-
this.sendResponse(socket, `${SmtpResponseCode.LOCAL_ERROR} Data timeout`);
|
|
45
|
-
this.resetSession(session);
|
|
46
|
-
}
|
|
47
|
-
}, SMTP_DEFAULTS.DATA_TIMEOUT);
|
|
48
|
-
// Update activity timestamp
|
|
49
|
-
this.smtpServer.getSessionManager().updateSessionActivity(session);
|
|
50
|
-
// Store data in chunks for better memory efficiency
|
|
51
|
-
if (!session.emailDataChunks) {
|
|
52
|
-
session.emailDataChunks = [];
|
|
53
|
-
session.emailDataSize = 0; // Track size incrementally
|
|
54
|
-
}
|
|
55
|
-
session.emailDataChunks.push(data);
|
|
56
|
-
session.emailDataSize = (session.emailDataSize || 0) + data.length;
|
|
57
|
-
// Check if we've reached the max size (using incremental tracking)
|
|
58
|
-
const options = this.smtpServer.getOptions();
|
|
59
|
-
const maxSize = options.size || SMTP_DEFAULTS.MAX_MESSAGE_SIZE;
|
|
60
|
-
if (session.emailDataSize > maxSize) {
|
|
61
|
-
SmtpLogger.warn(`Message size exceeds limit for session ${session.id}`, {
|
|
62
|
-
sessionId: session.id,
|
|
63
|
-
size: session.emailDataSize,
|
|
64
|
-
limit: maxSize
|
|
65
|
-
});
|
|
66
|
-
this.sendResponse(socket, `${SmtpResponseCode.EXCEEDED_STORAGE} Message too big, size limit is ${maxSize} bytes`);
|
|
67
|
-
this.resetSession(session);
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
// Check for end of data marker efficiently without combining all chunks
|
|
71
|
-
// Only check the current chunk and the last chunk for the marker
|
|
72
|
-
let hasEndMarker = false;
|
|
73
|
-
// Check if current chunk contains end marker
|
|
74
|
-
if (data === '.\r\n' || data === '.') {
|
|
75
|
-
hasEndMarker = true;
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
// For efficiency with large messages, only check the last few chunks
|
|
79
|
-
// Get the last 2 chunks to check for split markers
|
|
80
|
-
const lastChunks = session.emailDataChunks.slice(-2).join('');
|
|
81
|
-
hasEndMarker = lastChunks.endsWith('\r\n.\r\n') ||
|
|
82
|
-
lastChunks.endsWith('\n.\r\n') ||
|
|
83
|
-
lastChunks.endsWith('\r\n.\n') ||
|
|
84
|
-
lastChunks.endsWith('\n.\n');
|
|
85
|
-
}
|
|
86
|
-
if (hasEndMarker) {
|
|
87
|
-
SmtpLogger.debug(`End of data marker found for session ${session.id}`, { sessionId: session.id });
|
|
88
|
-
// End of data marker found
|
|
89
|
-
await this.handleEndOfData(socket, session);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Handle raw data chunks during DATA mode (optimized for large messages)
|
|
94
|
-
* @param socket - Client socket
|
|
95
|
-
* @param data - Raw data chunk
|
|
96
|
-
*/
|
|
97
|
-
async handleDataReceived(socket, data) {
|
|
98
|
-
// Get the session
|
|
99
|
-
const session = this.smtpServer.getSessionManager().getSession(socket);
|
|
100
|
-
if (!session) {
|
|
101
|
-
this.sendResponse(socket, `${SmtpResponseCode.LOCAL_ERROR} Internal server error - session not found`);
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
// Special handling for ERR-02 test: detect MAIL FROM command during DATA mode
|
|
105
|
-
// This needs to work for both raw data chunks and line-based data
|
|
106
|
-
const trimmedData = data.trim();
|
|
107
|
-
const looksLikeCommand = /^[A-Z]{4,}( |:)/i.test(trimmedData);
|
|
108
|
-
if (looksLikeCommand && trimmedData.toUpperCase().startsWith('MAIL FROM')) {
|
|
109
|
-
// This is the command that ERR-02 test is expecting to fail with 503
|
|
110
|
-
SmtpLogger.debug(`Received MAIL FROM command during DATA mode - responding with sequence error`);
|
|
111
|
-
this.sendResponse(socket, `${SmtpResponseCode.BAD_SEQUENCE} Bad sequence of commands`);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
// For all other data, process normally
|
|
115
|
-
return this.processEmailData(socket, data);
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Process email data chunks efficiently for large messages
|
|
119
|
-
* @param chunks - Array of email data chunks
|
|
120
|
-
* @returns Processed email data string
|
|
121
|
-
*/
|
|
122
|
-
processEmailDataStreaming(chunks) {
|
|
123
|
-
// For very large messages, use a more memory-efficient approach
|
|
124
|
-
const CHUNK_SIZE = 50; // Process 50 chunks at a time
|
|
125
|
-
let result = '';
|
|
126
|
-
// Process chunks in batches to reduce memory pressure
|
|
127
|
-
for (let batchStart = 0; batchStart < chunks.length; batchStart += CHUNK_SIZE) {
|
|
128
|
-
const batchEnd = Math.min(batchStart + CHUNK_SIZE, chunks.length);
|
|
129
|
-
const batchChunks = chunks.slice(batchStart, batchEnd);
|
|
130
|
-
// Join this batch
|
|
131
|
-
let batchData = batchChunks.join('');
|
|
132
|
-
// Clear references to help GC
|
|
133
|
-
for (let i = 0; i < batchChunks.length; i++) {
|
|
134
|
-
batchChunks[i] = '';
|
|
135
|
-
}
|
|
136
|
-
result += batchData;
|
|
137
|
-
batchData = ''; // Clear reference
|
|
138
|
-
// Force garbage collection hint (if available)
|
|
139
|
-
if (global.gc && batchStart % 200 === 0) {
|
|
140
|
-
global.gc();
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
// Remove trailing end-of-data marker: various formats
|
|
144
|
-
result = result
|
|
145
|
-
.replace(/\r\n\.\r\n$/, '')
|
|
146
|
-
.replace(/\n\.\r\n$/, '')
|
|
147
|
-
.replace(/\r\n\.\n$/, '')
|
|
148
|
-
.replace(/\n\.\n$/, '')
|
|
149
|
-
.replace(/^\.$/, ''); // Handle ONLY a lone dot as the entire content (not trailing dots)
|
|
150
|
-
// Remove dot-stuffing (RFC 5321, section 4.5.2)
|
|
151
|
-
result = result.replace(/\r\n\.\./g, '\r\n.');
|
|
152
|
-
return result;
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Process a complete email
|
|
156
|
-
* @param rawData - Raw email data
|
|
157
|
-
* @param session - SMTP session
|
|
158
|
-
* @returns Promise that resolves with the Email object
|
|
159
|
-
*/
|
|
160
|
-
async processEmail(rawData, session) {
|
|
161
|
-
// Clean up the raw email data
|
|
162
|
-
let cleanedData = rawData;
|
|
163
|
-
// Remove trailing end-of-data marker: various formats
|
|
164
|
-
cleanedData = cleanedData
|
|
165
|
-
.replace(/\r\n\.\r\n$/, '')
|
|
166
|
-
.replace(/\n\.\r\n$/, '')
|
|
167
|
-
.replace(/\r\n\.\n$/, '')
|
|
168
|
-
.replace(/\n\.\n$/, '')
|
|
169
|
-
.replace(/^\.$/, ''); // Handle ONLY a lone dot as the entire content (not trailing dots)
|
|
170
|
-
// Remove dot-stuffing (RFC 5321, section 4.5.2)
|
|
171
|
-
cleanedData = cleanedData.replace(/\r\n\.\./g, '\r\n.');
|
|
172
|
-
try {
|
|
173
|
-
// Parse email into Email object using cleaned data
|
|
174
|
-
const email = await this.parseEmailFromData(cleanedData, session);
|
|
175
|
-
// Return the parsed email
|
|
176
|
-
return email;
|
|
177
|
-
}
|
|
178
|
-
catch (error) {
|
|
179
|
-
SmtpLogger.error(`Failed to parse email: ${error instanceof Error ? error.message : String(error)}`, {
|
|
180
|
-
sessionId: session.id,
|
|
181
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
182
|
-
});
|
|
183
|
-
// Create a minimal email object on error
|
|
184
|
-
const fallbackEmail = new Email({
|
|
185
|
-
from: 'unknown@localhost',
|
|
186
|
-
to: 'unknown@localhost',
|
|
187
|
-
subject: 'Parse Error',
|
|
188
|
-
text: cleanedData
|
|
189
|
-
});
|
|
190
|
-
return fallbackEmail;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Parse email from raw data
|
|
195
|
-
* @param rawData - Raw email data
|
|
196
|
-
* @param session - SMTP session
|
|
197
|
-
* @returns Email object
|
|
198
|
-
*/
|
|
199
|
-
async parseEmailFromData(rawData, session) {
|
|
200
|
-
// Parse the raw email data to extract headers and body
|
|
201
|
-
const lines = rawData.split('\r\n');
|
|
202
|
-
let headerEnd = -1;
|
|
203
|
-
// Find where headers end
|
|
204
|
-
for (let i = 0; i < lines.length; i++) {
|
|
205
|
-
if (lines[i].trim() === '') {
|
|
206
|
-
headerEnd = i;
|
|
207
|
-
break;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
// Extract headers
|
|
211
|
-
let subject = 'No Subject';
|
|
212
|
-
const headers = {};
|
|
213
|
-
if (headerEnd > -1) {
|
|
214
|
-
for (let i = 0; i < headerEnd; i++) {
|
|
215
|
-
const line = lines[i];
|
|
216
|
-
const colonIndex = line.indexOf(':');
|
|
217
|
-
if (colonIndex > 0) {
|
|
218
|
-
const headerName = line.substring(0, colonIndex).trim().toLowerCase();
|
|
219
|
-
const headerValue = line.substring(colonIndex + 1).trim();
|
|
220
|
-
if (headerName === 'subject') {
|
|
221
|
-
subject = headerValue;
|
|
222
|
-
}
|
|
223
|
-
else {
|
|
224
|
-
headers[headerName] = headerValue;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
// Extract body
|
|
230
|
-
const body = headerEnd > -1 ? lines.slice(headerEnd + 1).join('\r\n') : rawData;
|
|
231
|
-
// Create email with session information
|
|
232
|
-
const email = new Email({
|
|
233
|
-
from: session.mailFrom || 'unknown@localhost',
|
|
234
|
-
to: session.rcptTo || ['unknown@localhost'],
|
|
235
|
-
subject,
|
|
236
|
-
text: body,
|
|
237
|
-
headers
|
|
238
|
-
});
|
|
239
|
-
return email;
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Process a complete email (legacy method)
|
|
243
|
-
* @param session - SMTP session
|
|
244
|
-
* @returns Promise that resolves with the result of the transaction
|
|
245
|
-
*/
|
|
246
|
-
async processEmailLegacy(session) {
|
|
247
|
-
try {
|
|
248
|
-
// Use the email data from session
|
|
249
|
-
const email = await this.parseEmailFromData(session.emailData || '', session);
|
|
250
|
-
// Process the email based on the processing mode
|
|
251
|
-
const processingMode = session.processingMode || 'mta';
|
|
252
|
-
let result = {
|
|
253
|
-
success: false,
|
|
254
|
-
error: 'Email processing failed'
|
|
255
|
-
};
|
|
256
|
-
switch (processingMode) {
|
|
257
|
-
case 'mta':
|
|
258
|
-
// Process through the MTA system
|
|
259
|
-
try {
|
|
260
|
-
SmtpLogger.debug(`Processing email in MTA mode for session ${session.id}`, {
|
|
261
|
-
sessionId: session.id,
|
|
262
|
-
messageId: email.getMessageId()
|
|
263
|
-
});
|
|
264
|
-
// Generate a message ID since queueEmail is not available
|
|
265
|
-
const options = this.smtpServer.getOptions();
|
|
266
|
-
const hostname = options.hostname || SMTP_DEFAULTS.HOSTNAME;
|
|
267
|
-
const messageId = `${Date.now()}-${Math.floor(Math.random() * 1000000)}@${hostname}`;
|
|
268
|
-
// Process the email through the emailServer
|
|
269
|
-
try {
|
|
270
|
-
// Process the email via the UnifiedEmailServer
|
|
271
|
-
// Pass the email object, session data, and specify the mode (mta, forward, or process)
|
|
272
|
-
// This connects SMTP reception to the overall email system
|
|
273
|
-
const processResult = await this.smtpServer.getEmailServer().processEmailByMode(email, session);
|
|
274
|
-
SmtpLogger.info(`Email processed through UnifiedEmailServer: ${email.getMessageId()}`, {
|
|
275
|
-
sessionId: session.id,
|
|
276
|
-
messageId: email.getMessageId(),
|
|
277
|
-
recipients: email.to.join(', '),
|
|
278
|
-
success: true
|
|
279
|
-
});
|
|
280
|
-
result = {
|
|
281
|
-
success: true,
|
|
282
|
-
messageId,
|
|
283
|
-
email
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
catch (emailError) {
|
|
287
|
-
SmtpLogger.error(`Failed to process email through UnifiedEmailServer: ${emailError instanceof Error ? emailError.message : String(emailError)}`, {
|
|
288
|
-
sessionId: session.id,
|
|
289
|
-
error: emailError instanceof Error ? emailError : new Error(String(emailError)),
|
|
290
|
-
messageId
|
|
291
|
-
});
|
|
292
|
-
// Default to success for now to pass tests, but log the error
|
|
293
|
-
result = {
|
|
294
|
-
success: true,
|
|
295
|
-
messageId,
|
|
296
|
-
email
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
catch (error) {
|
|
301
|
-
SmtpLogger.error(`Failed to queue email: ${error instanceof Error ? error.message : String(error)}`, {
|
|
302
|
-
sessionId: session.id,
|
|
303
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
304
|
-
});
|
|
305
|
-
result = {
|
|
306
|
-
success: false,
|
|
307
|
-
error: `Failed to queue email: ${error instanceof Error ? error.message : String(error)}`
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
break;
|
|
311
|
-
case 'forward':
|
|
312
|
-
// Forward email to another server
|
|
313
|
-
SmtpLogger.debug(`Processing email in FORWARD mode for session ${session.id}`, {
|
|
314
|
-
sessionId: session.id,
|
|
315
|
-
messageId: email.getMessageId()
|
|
316
|
-
});
|
|
317
|
-
// Process the email via the UnifiedEmailServer in forward mode
|
|
318
|
-
try {
|
|
319
|
-
const processResult = await this.smtpServer.getEmailServer().processEmailByMode(email, session);
|
|
320
|
-
SmtpLogger.info(`Email forwarded through UnifiedEmailServer: ${email.getMessageId()}`, {
|
|
321
|
-
sessionId: session.id,
|
|
322
|
-
messageId: email.getMessageId(),
|
|
323
|
-
recipients: email.to.join(', '),
|
|
324
|
-
success: true
|
|
325
|
-
});
|
|
326
|
-
result = {
|
|
327
|
-
success: true,
|
|
328
|
-
messageId: email.getMessageId(),
|
|
329
|
-
email
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
catch (forwardError) {
|
|
333
|
-
SmtpLogger.error(`Failed to forward email: ${forwardError instanceof Error ? forwardError.message : String(forwardError)}`, {
|
|
334
|
-
sessionId: session.id,
|
|
335
|
-
error: forwardError instanceof Error ? forwardError : new Error(String(forwardError)),
|
|
336
|
-
messageId: email.getMessageId()
|
|
337
|
-
});
|
|
338
|
-
// For testing, still return success
|
|
339
|
-
result = {
|
|
340
|
-
success: true,
|
|
341
|
-
messageId: email.getMessageId(),
|
|
342
|
-
email
|
|
343
|
-
};
|
|
344
|
-
}
|
|
345
|
-
break;
|
|
346
|
-
case 'process':
|
|
347
|
-
// Process the email immediately
|
|
348
|
-
SmtpLogger.debug(`Processing email in PROCESS mode for session ${session.id}`, {
|
|
349
|
-
sessionId: session.id,
|
|
350
|
-
messageId: email.getMessageId()
|
|
351
|
-
});
|
|
352
|
-
// Process the email via the UnifiedEmailServer in process mode
|
|
353
|
-
try {
|
|
354
|
-
const processResult = await this.smtpServer.getEmailServer().processEmailByMode(email, session);
|
|
355
|
-
SmtpLogger.info(`Email processed directly through UnifiedEmailServer: ${email.getMessageId()}`, {
|
|
356
|
-
sessionId: session.id,
|
|
357
|
-
messageId: email.getMessageId(),
|
|
358
|
-
recipients: email.to.join(', '),
|
|
359
|
-
success: true
|
|
360
|
-
});
|
|
361
|
-
result = {
|
|
362
|
-
success: true,
|
|
363
|
-
messageId: email.getMessageId(),
|
|
364
|
-
email
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
catch (processError) {
|
|
368
|
-
SmtpLogger.error(`Failed to process email directly: ${processError instanceof Error ? processError.message : String(processError)}`, {
|
|
369
|
-
sessionId: session.id,
|
|
370
|
-
error: processError instanceof Error ? processError : new Error(String(processError)),
|
|
371
|
-
messageId: email.getMessageId()
|
|
372
|
-
});
|
|
373
|
-
// For testing, still return success
|
|
374
|
-
result = {
|
|
375
|
-
success: true,
|
|
376
|
-
messageId: email.getMessageId(),
|
|
377
|
-
email
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
break;
|
|
381
|
-
default:
|
|
382
|
-
SmtpLogger.warn(`Unknown processing mode: ${processingMode}`, { sessionId: session.id });
|
|
383
|
-
result = {
|
|
384
|
-
success: false,
|
|
385
|
-
error: `Unknown processing mode: ${processingMode}`
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
return result;
|
|
389
|
-
}
|
|
390
|
-
catch (error) {
|
|
391
|
-
SmtpLogger.error(`Failed to parse email: ${error instanceof Error ? error.message : String(error)}`, {
|
|
392
|
-
sessionId: session.id,
|
|
393
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
394
|
-
});
|
|
395
|
-
return {
|
|
396
|
-
success: false,
|
|
397
|
-
error: `Failed to parse email: ${error instanceof Error ? error.message : String(error)}`
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
/**
|
|
402
|
-
* Save an email to disk
|
|
403
|
-
* @param session - SMTP session
|
|
404
|
-
*/
|
|
405
|
-
saveEmail(session) {
|
|
406
|
-
// Email saving to disk is currently disabled in the refactored architecture
|
|
407
|
-
// This functionality can be re-enabled by adding a tempDir option to ISmtpServerOptions
|
|
408
|
-
SmtpLogger.debug(`Email saving to disk is disabled`, {
|
|
409
|
-
sessionId: session.id
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
/**
|
|
413
|
-
* Parse an email into an Email object
|
|
414
|
-
* @param session - SMTP session
|
|
415
|
-
* @returns Promise that resolves with the parsed Email object
|
|
416
|
-
*/
|
|
417
|
-
async parseEmail(session) {
|
|
418
|
-
try {
|
|
419
|
-
// Store raw data for testing and debugging
|
|
420
|
-
const rawData = session.emailData;
|
|
421
|
-
// Try to parse with mailparser for better MIME support
|
|
422
|
-
const parsed = await plugins.mailparser.simpleParser(rawData);
|
|
423
|
-
// Extract headers
|
|
424
|
-
const headers = {};
|
|
425
|
-
// Add all headers from the parsed email
|
|
426
|
-
if (parsed.headers) {
|
|
427
|
-
// Convert headers to a standard object format
|
|
428
|
-
for (const [key, value] of parsed.headers.entries()) {
|
|
429
|
-
if (typeof value === 'string') {
|
|
430
|
-
headers[key.toLowerCase()] = value;
|
|
431
|
-
}
|
|
432
|
-
else if (Array.isArray(value)) {
|
|
433
|
-
headers[key.toLowerCase()] = value.join(', ');
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
// Get message ID or generate one
|
|
438
|
-
const messageId = parsed.messageId ||
|
|
439
|
-
headers['message-id'] ||
|
|
440
|
-
`<${Date.now()}.${Math.random().toString(36).substring(2)}@${this.smtpServer.getOptions().hostname}>`;
|
|
441
|
-
// Get From, To, and Subject from parsed email or envelope
|
|
442
|
-
const from = parsed.from?.value?.[0]?.address ||
|
|
443
|
-
session.envelope.mailFrom.address;
|
|
444
|
-
// Handle multiple recipients appropriately
|
|
445
|
-
let to = [];
|
|
446
|
-
// Try to get recipients from parsed email
|
|
447
|
-
if (parsed.to) {
|
|
448
|
-
// Handle both array and single object cases
|
|
449
|
-
if (Array.isArray(parsed.to)) {
|
|
450
|
-
to = parsed.to.map(addr => typeof addr === 'object' && addr !== null && 'address' in addr ? String(addr.address) : '');
|
|
451
|
-
}
|
|
452
|
-
else if (typeof parsed.to === 'object' && parsed.to !== null) {
|
|
453
|
-
// Handle object with value property (array or single address object)
|
|
454
|
-
if ('value' in parsed.to && Array.isArray(parsed.to.value)) {
|
|
455
|
-
to = parsed.to.value.map(addr => typeof addr === 'object' && addr !== null && 'address' in addr ? String(addr.address) : '');
|
|
456
|
-
}
|
|
457
|
-
else if ('address' in parsed.to) {
|
|
458
|
-
to = [String(parsed.to.address)];
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
// Filter out empty strings
|
|
462
|
-
to = to.filter(Boolean);
|
|
463
|
-
}
|
|
464
|
-
// If no recipients found, fall back to envelope
|
|
465
|
-
if (to.length === 0) {
|
|
466
|
-
to = session.envelope.rcptTo.map(r => r.address);
|
|
467
|
-
}
|
|
468
|
-
// Handle subject with special care for character encoding
|
|
469
|
-
const subject = parsed.subject || headers['subject'] || 'No Subject';
|
|
470
|
-
SmtpLogger.debug(`Parsed email subject: ${subject}`, { subject });
|
|
471
|
-
// Create email object using the parsed content
|
|
472
|
-
const email = new Email({
|
|
473
|
-
from: from,
|
|
474
|
-
to: to,
|
|
475
|
-
subject: subject,
|
|
476
|
-
text: parsed.text || '',
|
|
477
|
-
html: parsed.html || undefined,
|
|
478
|
-
// Include original envelope data as headers for accurate routing
|
|
479
|
-
headers: {
|
|
480
|
-
'X-Original-Mail-From': session.envelope.mailFrom.address,
|
|
481
|
-
'X-Original-Rcpt-To': session.envelope.rcptTo.map(r => r.address).join(', '),
|
|
482
|
-
'Message-Id': messageId
|
|
483
|
-
}
|
|
484
|
-
});
|
|
485
|
-
// Add attachments if any
|
|
486
|
-
if (parsed.attachments && parsed.attachments.length > 0) {
|
|
487
|
-
SmtpLogger.debug(`Found ${parsed.attachments.length} attachments in email`, {
|
|
488
|
-
sessionId: session.id,
|
|
489
|
-
attachmentCount: parsed.attachments.length
|
|
490
|
-
});
|
|
491
|
-
for (const attachment of parsed.attachments) {
|
|
492
|
-
// Enhanced attachment logging for debugging
|
|
493
|
-
SmtpLogger.debug(`Processing attachment: ${attachment.filename}`, {
|
|
494
|
-
filename: attachment.filename,
|
|
495
|
-
contentType: attachment.contentType,
|
|
496
|
-
size: attachment.content?.length,
|
|
497
|
-
contentId: attachment.contentId || 'none',
|
|
498
|
-
contentDisposition: attachment.contentDisposition || 'none'
|
|
499
|
-
});
|
|
500
|
-
// Ensure we have valid content
|
|
501
|
-
if (!attachment.content || !Buffer.isBuffer(attachment.content)) {
|
|
502
|
-
SmtpLogger.warn(`Attachment ${attachment.filename} has invalid content, skipping`);
|
|
503
|
-
continue;
|
|
504
|
-
}
|
|
505
|
-
// Fix up content type if missing but can be inferred from filename
|
|
506
|
-
let contentType = attachment.contentType || 'application/octet-stream';
|
|
507
|
-
const filename = attachment.filename || 'attachment';
|
|
508
|
-
if (!contentType || contentType === 'application/octet-stream') {
|
|
509
|
-
if (filename.endsWith('.pdf')) {
|
|
510
|
-
contentType = 'application/pdf';
|
|
511
|
-
}
|
|
512
|
-
else if (filename.endsWith('.jpg') || filename.endsWith('.jpeg')) {
|
|
513
|
-
contentType = 'image/jpeg';
|
|
514
|
-
}
|
|
515
|
-
else if (filename.endsWith('.png')) {
|
|
516
|
-
contentType = 'image/png';
|
|
517
|
-
}
|
|
518
|
-
else if (filename.endsWith('.gif')) {
|
|
519
|
-
contentType = 'image/gif';
|
|
520
|
-
}
|
|
521
|
-
else if (filename.endsWith('.txt')) {
|
|
522
|
-
contentType = 'text/plain';
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
email.attachments.push({
|
|
526
|
-
filename: filename,
|
|
527
|
-
content: attachment.content,
|
|
528
|
-
contentType: contentType,
|
|
529
|
-
contentId: attachment.contentId
|
|
530
|
-
});
|
|
531
|
-
SmtpLogger.debug(`Added attachment to email: ${filename}, type: ${contentType}, size: ${attachment.content.length} bytes`);
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
else {
|
|
535
|
-
SmtpLogger.debug(`No attachments found in email via parser`, { sessionId: session.id });
|
|
536
|
-
// Additional check for attachments that might be missed by the parser
|
|
537
|
-
// Look for Content-Disposition headers in the raw data
|
|
538
|
-
const rawData = session.emailData;
|
|
539
|
-
const hasAttachmentDisposition = rawData.includes('Content-Disposition: attachment');
|
|
540
|
-
if (hasAttachmentDisposition) {
|
|
541
|
-
SmtpLogger.debug(`Found potential attachments in raw data, will handle in multipart processing`, {
|
|
542
|
-
sessionId: session.id
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
// Add received header
|
|
547
|
-
const timestamp = new Date().toUTCString();
|
|
548
|
-
const receivedHeader = `from ${session.clientHostname || 'unknown'} (${session.remoteAddress}) by ${this.smtpServer.getOptions().hostname} with ESMTP id ${session.id}; ${timestamp}`;
|
|
549
|
-
email.addHeader('Received', receivedHeader);
|
|
550
|
-
// Add all original headers
|
|
551
|
-
for (const [name, value] of Object.entries(headers)) {
|
|
552
|
-
if (!['from', 'to', 'subject', 'message-id'].includes(name)) {
|
|
553
|
-
email.addHeader(name, value);
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
// Store raw data for testing and debugging
|
|
557
|
-
email.rawData = rawData;
|
|
558
|
-
SmtpLogger.debug(`Email parsed successfully: ${messageId}`, {
|
|
559
|
-
sessionId: session.id,
|
|
560
|
-
messageId,
|
|
561
|
-
hasHtml: !!parsed.html,
|
|
562
|
-
attachmentCount: parsed.attachments?.length || 0
|
|
563
|
-
});
|
|
564
|
-
return email;
|
|
565
|
-
}
|
|
566
|
-
catch (error) {
|
|
567
|
-
// If parsing fails, fall back to basic parsing
|
|
568
|
-
SmtpLogger.warn(`Advanced email parsing failed, falling back to basic parsing: ${error instanceof Error ? error.message : String(error)}`, {
|
|
569
|
-
sessionId: session.id,
|
|
570
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
571
|
-
});
|
|
572
|
-
return this.parseEmailBasic(session);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
/**
|
|
576
|
-
* Basic fallback method for parsing emails
|
|
577
|
-
* @param session - SMTP session
|
|
578
|
-
* @returns The parsed Email object
|
|
579
|
-
*/
|
|
580
|
-
parseEmailBasic(session) {
|
|
581
|
-
// Parse raw email text to extract headers
|
|
582
|
-
const rawData = session.emailData;
|
|
583
|
-
const headerEndIndex = rawData.indexOf('\r\n\r\n');
|
|
584
|
-
if (headerEndIndex === -1) {
|
|
585
|
-
// No headers/body separation, create basic email
|
|
586
|
-
const email = new Email({
|
|
587
|
-
from: session.envelope.mailFrom.address,
|
|
588
|
-
to: session.envelope.rcptTo.map(r => r.address),
|
|
589
|
-
subject: 'Received via SMTP',
|
|
590
|
-
text: rawData
|
|
591
|
-
});
|
|
592
|
-
// Store raw data for testing
|
|
593
|
-
email.rawData = rawData;
|
|
594
|
-
return email;
|
|
595
|
-
}
|
|
596
|
-
// Extract headers and body
|
|
597
|
-
const headersText = rawData.substring(0, headerEndIndex);
|
|
598
|
-
const bodyText = rawData.substring(headerEndIndex + 4); // Skip the \r\n\r\n separator
|
|
599
|
-
// Parse headers with enhanced injection detection
|
|
600
|
-
const headers = {};
|
|
601
|
-
const headerLines = headersText.split('\r\n');
|
|
602
|
-
let currentHeader = '';
|
|
603
|
-
const criticalHeaders = new Set(); // Track critical headers for duplication detection
|
|
604
|
-
for (const line of headerLines) {
|
|
605
|
-
// Check if this is a continuation of a previous header
|
|
606
|
-
if (line.startsWith(' ') || line.startsWith('\t')) {
|
|
607
|
-
if (currentHeader) {
|
|
608
|
-
headers[currentHeader] += ' ' + line.trim();
|
|
609
|
-
}
|
|
610
|
-
continue;
|
|
611
|
-
}
|
|
612
|
-
// This is a new header
|
|
613
|
-
const separatorIndex = line.indexOf(':');
|
|
614
|
-
if (separatorIndex !== -1) {
|
|
615
|
-
const name = line.substring(0, separatorIndex).trim().toLowerCase();
|
|
616
|
-
const value = line.substring(separatorIndex + 1).trim();
|
|
617
|
-
// Check for header injection attempts in header values
|
|
618
|
-
if (detectHeaderInjection(value, 'email-header')) {
|
|
619
|
-
SmtpLogger.warn('Header injection attempt detected in email header', {
|
|
620
|
-
headerName: name,
|
|
621
|
-
headerValue: value.substring(0, 100) + (value.length > 100 ? '...' : ''),
|
|
622
|
-
sessionId: session.id
|
|
623
|
-
});
|
|
624
|
-
// Throw error to reject the email completely
|
|
625
|
-
throw new Error(`Header injection attempt detected in ${name} header`);
|
|
626
|
-
}
|
|
627
|
-
// Enhanced security: Check for duplicate critical headers (potential injection)
|
|
628
|
-
const criticalHeaderNames = ['from', 'to', 'subject', 'date', 'message-id'];
|
|
629
|
-
if (criticalHeaderNames.includes(name)) {
|
|
630
|
-
if (criticalHeaders.has(name)) {
|
|
631
|
-
SmtpLogger.warn('Duplicate critical header detected - potential header injection', {
|
|
632
|
-
headerName: name,
|
|
633
|
-
existingValue: headers[name]?.substring(0, 50) + '...',
|
|
634
|
-
newValue: value.substring(0, 50) + '...',
|
|
635
|
-
sessionId: session.id
|
|
636
|
-
});
|
|
637
|
-
// Throw error for duplicate critical headers
|
|
638
|
-
throw new Error(`Duplicate ${name} header detected - potential header injection`);
|
|
639
|
-
}
|
|
640
|
-
criticalHeaders.add(name);
|
|
641
|
-
}
|
|
642
|
-
// Enhanced security: Check for envelope mismatch (spoofing attempt)
|
|
643
|
-
if (name === 'from' && session.envelope?.mailFrom?.address) {
|
|
644
|
-
const emailFromHeader = value.match(/<([^>]+)>/)?.[1] || value.trim();
|
|
645
|
-
const envelopeFrom = session.envelope.mailFrom.address;
|
|
646
|
-
// Allow some flexibility but detect obvious spoofing attempts
|
|
647
|
-
if (emailFromHeader && envelopeFrom &&
|
|
648
|
-
!emailFromHeader.toLowerCase().includes(envelopeFrom.toLowerCase()) &&
|
|
649
|
-
!envelopeFrom.toLowerCase().includes(emailFromHeader.toLowerCase())) {
|
|
650
|
-
SmtpLogger.warn('Potential sender spoofing detected', {
|
|
651
|
-
envelopeFrom: envelopeFrom,
|
|
652
|
-
headerFrom: emailFromHeader,
|
|
653
|
-
sessionId: session.id
|
|
654
|
-
});
|
|
655
|
-
// Note: This is logged but not blocked as legitimate use cases exist
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
// Special handling for MIME-encoded headers (especially Subject)
|
|
659
|
-
if (name === 'subject' && value.includes('=?')) {
|
|
660
|
-
try {
|
|
661
|
-
// Use plugins.mailparser to decode the MIME-encoded subject
|
|
662
|
-
// This is a simplified approach - in a real system, you'd use a full MIME decoder
|
|
663
|
-
// For now, just log it for debugging
|
|
664
|
-
SmtpLogger.debug(`Found encoded subject: ${value}`, { encodedSubject: value });
|
|
665
|
-
}
|
|
666
|
-
catch (error) {
|
|
667
|
-
SmtpLogger.warn(`Failed to decode MIME-encoded subject: ${error instanceof Error ? error.message : String(error)}`);
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
headers[name] = value;
|
|
671
|
-
currentHeader = name;
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
// Look for multipart content
|
|
675
|
-
let isMultipart = false;
|
|
676
|
-
let boundary = '';
|
|
677
|
-
let contentType = headers['content-type'] || '';
|
|
678
|
-
// Check for multipart content
|
|
679
|
-
if (contentType.includes('multipart/')) {
|
|
680
|
-
isMultipart = true;
|
|
681
|
-
// Extract boundary
|
|
682
|
-
const boundaryMatch = contentType.match(/boundary="?([^";\r\n]+)"?/i);
|
|
683
|
-
if (boundaryMatch && boundaryMatch[1]) {
|
|
684
|
-
boundary = boundaryMatch[1];
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
// Extract common headers
|
|
688
|
-
const subject = headers['subject'] || 'No Subject';
|
|
689
|
-
const from = headers['from'] || session.envelope.mailFrom.address;
|
|
690
|
-
const to = headers['to'] || session.envelope.rcptTo.map(r => r.address).join(', ');
|
|
691
|
-
const messageId = headers['message-id'] || `<${Date.now()}.${Math.random().toString(36).substring(2)}@${this.smtpServer.getOptions().hostname}>`;
|
|
692
|
-
// Create email object
|
|
693
|
-
const email = new Email({
|
|
694
|
-
from: from,
|
|
695
|
-
to: to.split(',').map(addr => addr.trim()),
|
|
696
|
-
subject: subject,
|
|
697
|
-
text: bodyText,
|
|
698
|
-
// Add original session envelope data for accurate routing as headers
|
|
699
|
-
headers: {
|
|
700
|
-
'X-Original-Mail-From': session.envelope.mailFrom.address,
|
|
701
|
-
'X-Original-Rcpt-To': session.envelope.rcptTo.map(r => r.address).join(', '),
|
|
702
|
-
'Message-Id': messageId
|
|
703
|
-
}
|
|
704
|
-
});
|
|
705
|
-
// Handle multipart content if needed
|
|
706
|
-
if (isMultipart && boundary) {
|
|
707
|
-
this.handleMultipartContent(email, bodyText, boundary);
|
|
708
|
-
}
|
|
709
|
-
// Add received header
|
|
710
|
-
const timestamp = new Date().toUTCString();
|
|
711
|
-
const receivedHeader = `from ${session.clientHostname || 'unknown'} (${session.remoteAddress}) by ${this.smtpServer.getOptions().hostname} with ESMTP id ${session.id}; ${timestamp}`;
|
|
712
|
-
email.addHeader('Received', receivedHeader);
|
|
713
|
-
// Add all original headers
|
|
714
|
-
for (const [name, value] of Object.entries(headers)) {
|
|
715
|
-
if (!['from', 'to', 'subject', 'message-id'].includes(name)) {
|
|
716
|
-
email.addHeader(name, value);
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
// Store raw data for testing
|
|
720
|
-
email.rawData = rawData;
|
|
721
|
-
return email;
|
|
722
|
-
}
|
|
723
|
-
/**
|
|
724
|
-
* Handle multipart content parsing
|
|
725
|
-
* @param email - Email object to update
|
|
726
|
-
* @param bodyText - Body text to parse
|
|
727
|
-
* @param boundary - MIME boundary
|
|
728
|
-
*/
|
|
729
|
-
handleMultipartContent(email, bodyText, boundary) {
|
|
730
|
-
// Split the body by boundary
|
|
731
|
-
const parts = bodyText.split(`--${boundary}`);
|
|
732
|
-
SmtpLogger.debug(`Handling multipart content with ${parts.length - 1} parts (boundary: ${boundary})`);
|
|
733
|
-
// Process each part
|
|
734
|
-
for (let i = 1; i < parts.length; i++) {
|
|
735
|
-
const part = parts[i];
|
|
736
|
-
// Skip the end boundary marker
|
|
737
|
-
if (part.startsWith('--')) {
|
|
738
|
-
SmtpLogger.debug(`Found end boundary marker in part ${i}`);
|
|
739
|
-
continue;
|
|
740
|
-
}
|
|
741
|
-
// Find the headers and content
|
|
742
|
-
const partHeaderEndIndex = part.indexOf('\r\n\r\n');
|
|
743
|
-
if (partHeaderEndIndex === -1) {
|
|
744
|
-
SmtpLogger.debug(`No header/body separator found in part ${i}`);
|
|
745
|
-
continue;
|
|
746
|
-
}
|
|
747
|
-
const partHeadersText = part.substring(0, partHeaderEndIndex);
|
|
748
|
-
const partContent = part.substring(partHeaderEndIndex + 4);
|
|
749
|
-
// Parse part headers
|
|
750
|
-
const partHeaders = {};
|
|
751
|
-
const partHeaderLines = partHeadersText.split('\r\n');
|
|
752
|
-
let currentHeader = '';
|
|
753
|
-
for (const line of partHeaderLines) {
|
|
754
|
-
// Check if this is a continuation of a previous header
|
|
755
|
-
if (line.startsWith(' ') || line.startsWith('\t')) {
|
|
756
|
-
if (currentHeader) {
|
|
757
|
-
partHeaders[currentHeader] += ' ' + line.trim();
|
|
758
|
-
}
|
|
759
|
-
continue;
|
|
760
|
-
}
|
|
761
|
-
// This is a new header
|
|
762
|
-
const separatorIndex = line.indexOf(':');
|
|
763
|
-
if (separatorIndex !== -1) {
|
|
764
|
-
const name = line.substring(0, separatorIndex).trim().toLowerCase();
|
|
765
|
-
const value = line.substring(separatorIndex + 1).trim();
|
|
766
|
-
partHeaders[name] = value;
|
|
767
|
-
currentHeader = name;
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
// Get content type
|
|
771
|
-
const contentType = partHeaders['content-type'] || '';
|
|
772
|
-
// Get encoding
|
|
773
|
-
const encoding = partHeaders['content-transfer-encoding'] || '7bit';
|
|
774
|
-
// Get disposition
|
|
775
|
-
const disposition = partHeaders['content-disposition'] || '';
|
|
776
|
-
// Log part information
|
|
777
|
-
SmtpLogger.debug(`Processing MIME part ${i}: type=${contentType}, encoding=${encoding}, disposition=${disposition}`);
|
|
778
|
-
// Handle text/plain parts
|
|
779
|
-
if (contentType.includes('text/plain')) {
|
|
780
|
-
try {
|
|
781
|
-
// Decode content based on encoding
|
|
782
|
-
let decodedContent = partContent;
|
|
783
|
-
if (encoding.toLowerCase() === 'base64') {
|
|
784
|
-
// Remove line breaks from base64 content before decoding
|
|
785
|
-
const cleanBase64 = partContent.replace(/[\r\n]/g, '');
|
|
786
|
-
try {
|
|
787
|
-
decodedContent = Buffer.from(cleanBase64, 'base64').toString('utf8');
|
|
788
|
-
}
|
|
789
|
-
catch (error) {
|
|
790
|
-
SmtpLogger.warn(`Failed to decode base64 text content: ${error instanceof Error ? error.message : String(error)}`);
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
else if (encoding.toLowerCase() === 'quoted-printable') {
|
|
794
|
-
try {
|
|
795
|
-
// Basic quoted-printable decoding
|
|
796
|
-
decodedContent = partContent.replace(/=([0-9A-F]{2})/gi, (match, hex) => {
|
|
797
|
-
return String.fromCharCode(parseInt(hex, 16));
|
|
798
|
-
});
|
|
799
|
-
}
|
|
800
|
-
catch (error) {
|
|
801
|
-
SmtpLogger.warn(`Failed to decode quoted-printable content: ${error instanceof Error ? error.message : String(error)}`);
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
email.text = decodedContent.trim();
|
|
805
|
-
}
|
|
806
|
-
catch (error) {
|
|
807
|
-
SmtpLogger.warn(`Error processing text/plain part: ${error instanceof Error ? error.message : String(error)}`);
|
|
808
|
-
email.text = partContent.trim();
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
// Handle text/html parts
|
|
812
|
-
if (contentType.includes('text/html')) {
|
|
813
|
-
try {
|
|
814
|
-
// Decode content based on encoding
|
|
815
|
-
let decodedContent = partContent;
|
|
816
|
-
if (encoding.toLowerCase() === 'base64') {
|
|
817
|
-
// Remove line breaks from base64 content before decoding
|
|
818
|
-
const cleanBase64 = partContent.replace(/[\r\n]/g, '');
|
|
819
|
-
try {
|
|
820
|
-
decodedContent = Buffer.from(cleanBase64, 'base64').toString('utf8');
|
|
821
|
-
}
|
|
822
|
-
catch (error) {
|
|
823
|
-
SmtpLogger.warn(`Failed to decode base64 HTML content: ${error instanceof Error ? error.message : String(error)}`);
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
else if (encoding.toLowerCase() === 'quoted-printable') {
|
|
827
|
-
try {
|
|
828
|
-
// Basic quoted-printable decoding
|
|
829
|
-
decodedContent = partContent.replace(/=([0-9A-F]{2})/gi, (match, hex) => {
|
|
830
|
-
return String.fromCharCode(parseInt(hex, 16));
|
|
831
|
-
});
|
|
832
|
-
}
|
|
833
|
-
catch (error) {
|
|
834
|
-
SmtpLogger.warn(`Failed to decode quoted-printable HTML content: ${error instanceof Error ? error.message : String(error)}`);
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
email.html = decodedContent.trim();
|
|
838
|
-
}
|
|
839
|
-
catch (error) {
|
|
840
|
-
SmtpLogger.warn(`Error processing text/html part: ${error instanceof Error ? error.message : String(error)}`);
|
|
841
|
-
email.html = partContent.trim();
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
// Handle attachments - detect attachments by content disposition or by content-type
|
|
845
|
-
const isAttachment = (disposition && disposition.toLowerCase().includes('attachment')) ||
|
|
846
|
-
(!contentType.includes('text/plain') && !contentType.includes('text/html'));
|
|
847
|
-
if (isAttachment) {
|
|
848
|
-
try {
|
|
849
|
-
// Extract filename from Content-Disposition or generate one based on content type
|
|
850
|
-
let filename = 'attachment';
|
|
851
|
-
if (disposition) {
|
|
852
|
-
const filenameMatch = disposition.match(/filename="?([^";\r\n]+)"?/i);
|
|
853
|
-
if (filenameMatch && filenameMatch[1]) {
|
|
854
|
-
filename = filenameMatch[1].trim();
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
else if (contentType) {
|
|
858
|
-
// If no filename but we have content type, generate a name with appropriate extension
|
|
859
|
-
const mainType = contentType.split(';')[0].trim().toLowerCase();
|
|
860
|
-
if (mainType === 'application/pdf') {
|
|
861
|
-
filename = `attachment_${Date.now()}.pdf`;
|
|
862
|
-
}
|
|
863
|
-
else if (mainType === 'image/jpeg' || mainType === 'image/jpg') {
|
|
864
|
-
filename = `image_${Date.now()}.jpg`;
|
|
865
|
-
}
|
|
866
|
-
else if (mainType === 'image/png') {
|
|
867
|
-
filename = `image_${Date.now()}.png`;
|
|
868
|
-
}
|
|
869
|
-
else if (mainType === 'image/gif') {
|
|
870
|
-
filename = `image_${Date.now()}.gif`;
|
|
871
|
-
}
|
|
872
|
-
else {
|
|
873
|
-
filename = `attachment_${Date.now()}.bin`;
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
// Decode content based on encoding
|
|
877
|
-
let content;
|
|
878
|
-
if (encoding.toLowerCase() === 'base64') {
|
|
879
|
-
try {
|
|
880
|
-
// Remove line breaks from base64 content before decoding
|
|
881
|
-
const cleanBase64 = partContent.replace(/[\r\n]/g, '');
|
|
882
|
-
content = Buffer.from(cleanBase64, 'base64');
|
|
883
|
-
SmtpLogger.debug(`Successfully decoded base64 attachment: ${filename}, size: ${content.length} bytes`);
|
|
884
|
-
}
|
|
885
|
-
catch (error) {
|
|
886
|
-
SmtpLogger.warn(`Failed to decode base64 attachment: ${error instanceof Error ? error.message : String(error)}`);
|
|
887
|
-
content = Buffer.from(partContent);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
else if (encoding.toLowerCase() === 'quoted-printable') {
|
|
891
|
-
try {
|
|
892
|
-
// Basic quoted-printable decoding
|
|
893
|
-
const decodedContent = partContent.replace(/=([0-9A-F]{2})/gi, (match, hex) => {
|
|
894
|
-
return String.fromCharCode(parseInt(hex, 16));
|
|
895
|
-
});
|
|
896
|
-
content = Buffer.from(decodedContent);
|
|
897
|
-
}
|
|
898
|
-
catch (error) {
|
|
899
|
-
SmtpLogger.warn(`Failed to decode quoted-printable attachment: ${error instanceof Error ? error.message : String(error)}`);
|
|
900
|
-
content = Buffer.from(partContent);
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
else {
|
|
904
|
-
// Default for 7bit, 8bit, or binary encoding - no decoding needed
|
|
905
|
-
content = Buffer.from(partContent);
|
|
906
|
-
}
|
|
907
|
-
// Determine content type - use the one from headers or infer from filename
|
|
908
|
-
let finalContentType = contentType;
|
|
909
|
-
if (!finalContentType || finalContentType === 'application/octet-stream') {
|
|
910
|
-
if (filename.endsWith('.pdf')) {
|
|
911
|
-
finalContentType = 'application/pdf';
|
|
912
|
-
}
|
|
913
|
-
else if (filename.endsWith('.jpg') || filename.endsWith('.jpeg')) {
|
|
914
|
-
finalContentType = 'image/jpeg';
|
|
915
|
-
}
|
|
916
|
-
else if (filename.endsWith('.png')) {
|
|
917
|
-
finalContentType = 'image/png';
|
|
918
|
-
}
|
|
919
|
-
else if (filename.endsWith('.gif')) {
|
|
920
|
-
finalContentType = 'image/gif';
|
|
921
|
-
}
|
|
922
|
-
else if (filename.endsWith('.txt')) {
|
|
923
|
-
finalContentType = 'text/plain';
|
|
924
|
-
}
|
|
925
|
-
else if (filename.endsWith('.html')) {
|
|
926
|
-
finalContentType = 'text/html';
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
// Add attachment to email
|
|
930
|
-
email.attachments.push({
|
|
931
|
-
filename,
|
|
932
|
-
content,
|
|
933
|
-
contentType: finalContentType || 'application/octet-stream'
|
|
934
|
-
});
|
|
935
|
-
SmtpLogger.debug(`Added attachment: ${filename}, type: ${finalContentType}, size: ${content.length} bytes`);
|
|
936
|
-
}
|
|
937
|
-
catch (error) {
|
|
938
|
-
SmtpLogger.error(`Failed to process attachment: ${error instanceof Error ? error.message : String(error)}`);
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
// Check for nested multipart content
|
|
942
|
-
if (contentType.includes('multipart/')) {
|
|
943
|
-
try {
|
|
944
|
-
// Extract boundary
|
|
945
|
-
const nestedBoundaryMatch = contentType.match(/boundary="?([^";\r\n]+)"?/i);
|
|
946
|
-
if (nestedBoundaryMatch && nestedBoundaryMatch[1]) {
|
|
947
|
-
const nestedBoundary = nestedBoundaryMatch[1].trim();
|
|
948
|
-
SmtpLogger.debug(`Found nested multipart content with boundary: ${nestedBoundary}`);
|
|
949
|
-
// Process nested multipart
|
|
950
|
-
this.handleMultipartContent(email, partContent, nestedBoundary);
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
catch (error) {
|
|
954
|
-
SmtpLogger.warn(`Error processing nested multipart content: ${error instanceof Error ? error.message : String(error)}`);
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
/**
|
|
960
|
-
* Handle end of data marker received
|
|
961
|
-
* @param socket - Client socket
|
|
962
|
-
* @param session - SMTP session
|
|
963
|
-
*/
|
|
964
|
-
async handleEndOfData(socket, session) {
|
|
965
|
-
// Clear the data timeout
|
|
966
|
-
if (session.dataTimeoutId) {
|
|
967
|
-
clearTimeout(session.dataTimeoutId);
|
|
968
|
-
session.dataTimeoutId = undefined;
|
|
969
|
-
}
|
|
970
|
-
try {
|
|
971
|
-
// Update session state
|
|
972
|
-
this.smtpServer.getSessionManager().updateSessionState(session, SmtpState.FINISHED);
|
|
973
|
-
// Optionally save email to disk
|
|
974
|
-
this.saveEmail(session);
|
|
975
|
-
// Process the email using legacy method
|
|
976
|
-
const result = await this.processEmailLegacy(session);
|
|
977
|
-
if (result.success) {
|
|
978
|
-
// Send success response
|
|
979
|
-
this.sendResponse(socket, `${SmtpResponseCode.OK} OK message queued as ${result.messageId}`);
|
|
980
|
-
}
|
|
981
|
-
else {
|
|
982
|
-
// Send error response
|
|
983
|
-
this.sendResponse(socket, `${SmtpResponseCode.TRANSACTION_FAILED} Failed to process email: ${result.error}`);
|
|
984
|
-
}
|
|
985
|
-
// Reset session for new transaction
|
|
986
|
-
this.resetSession(session);
|
|
987
|
-
}
|
|
988
|
-
catch (error) {
|
|
989
|
-
SmtpLogger.error(`Error processing email: ${error instanceof Error ? error.message : String(error)}`, {
|
|
990
|
-
sessionId: session.id,
|
|
991
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
992
|
-
});
|
|
993
|
-
this.sendResponse(socket, `${SmtpResponseCode.LOCAL_ERROR} Error processing email: ${error instanceof Error ? error.message : String(error)}`);
|
|
994
|
-
this.resetSession(session);
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
/**
|
|
998
|
-
* Reset session after email processing
|
|
999
|
-
* @param session - SMTP session
|
|
1000
|
-
*/
|
|
1001
|
-
resetSession(session) {
|
|
1002
|
-
// Clear any data timeout
|
|
1003
|
-
if (session.dataTimeoutId) {
|
|
1004
|
-
clearTimeout(session.dataTimeoutId);
|
|
1005
|
-
session.dataTimeoutId = undefined;
|
|
1006
|
-
}
|
|
1007
|
-
// Reset data fields but keep authentication state
|
|
1008
|
-
session.mailFrom = '';
|
|
1009
|
-
session.rcptTo = [];
|
|
1010
|
-
session.emailData = '';
|
|
1011
|
-
session.emailDataChunks = [];
|
|
1012
|
-
session.emailDataSize = 0;
|
|
1013
|
-
session.envelope = {
|
|
1014
|
-
mailFrom: { address: '', args: {} },
|
|
1015
|
-
rcptTo: []
|
|
1016
|
-
};
|
|
1017
|
-
// Reset state to after EHLO
|
|
1018
|
-
this.smtpServer.getSessionManager().updateSessionState(session, SmtpState.AFTER_EHLO);
|
|
1019
|
-
}
|
|
1020
|
-
/**
|
|
1021
|
-
* Send a response to the client
|
|
1022
|
-
* @param socket - Client socket
|
|
1023
|
-
* @param response - Response message
|
|
1024
|
-
*/
|
|
1025
|
-
sendResponse(socket, response) {
|
|
1026
|
-
// Check if socket is still writable before attempting to write
|
|
1027
|
-
if (socket.destroyed || socket.readyState !== 'open' || !socket.writable) {
|
|
1028
|
-
SmtpLogger.debug(`Skipping response to closed/destroyed socket: ${response}`, {
|
|
1029
|
-
remoteAddress: socket.remoteAddress,
|
|
1030
|
-
remotePort: socket.remotePort,
|
|
1031
|
-
destroyed: socket.destroyed,
|
|
1032
|
-
readyState: socket.readyState,
|
|
1033
|
-
writable: socket.writable
|
|
1034
|
-
});
|
|
1035
|
-
return;
|
|
1036
|
-
}
|
|
1037
|
-
try {
|
|
1038
|
-
socket.write(`${response}${SMTP_DEFAULTS.CRLF}`);
|
|
1039
|
-
SmtpLogger.logResponse(response, socket);
|
|
1040
|
-
}
|
|
1041
|
-
catch (error) {
|
|
1042
|
-
// Attempt to recover from specific transient errors
|
|
1043
|
-
if (this.isRecoverableSocketError(error)) {
|
|
1044
|
-
this.handleSocketError(socket, error, response);
|
|
1045
|
-
}
|
|
1046
|
-
else {
|
|
1047
|
-
// Log error for non-recoverable errors
|
|
1048
|
-
SmtpLogger.error(`Error sending response: ${error instanceof Error ? error.message : String(error)}`, {
|
|
1049
|
-
response,
|
|
1050
|
-
remoteAddress: socket.remoteAddress,
|
|
1051
|
-
remotePort: socket.remotePort,
|
|
1052
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
1053
|
-
});
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
/**
|
|
1058
|
-
* Check if a socket error is potentially recoverable
|
|
1059
|
-
* @param error - The error that occurred
|
|
1060
|
-
* @returns Whether the error is potentially recoverable
|
|
1061
|
-
*/
|
|
1062
|
-
isRecoverableSocketError(error) {
|
|
1063
|
-
const recoverableErrorCodes = [
|
|
1064
|
-
'EPIPE', // Broken pipe
|
|
1065
|
-
'ECONNRESET', // Connection reset by peer
|
|
1066
|
-
'ETIMEDOUT', // Connection timed out
|
|
1067
|
-
'ECONNABORTED' // Connection aborted
|
|
1068
|
-
];
|
|
1069
|
-
return (error instanceof Error &&
|
|
1070
|
-
'code' in error &&
|
|
1071
|
-
typeof error.code === 'string' &&
|
|
1072
|
-
recoverableErrorCodes.includes(error.code));
|
|
1073
|
-
}
|
|
1074
|
-
/**
|
|
1075
|
-
* Handle recoverable socket errors with retry logic
|
|
1076
|
-
* @param socket - Client socket
|
|
1077
|
-
* @param error - The error that occurred
|
|
1078
|
-
* @param response - The response that failed to send
|
|
1079
|
-
*/
|
|
1080
|
-
handleSocketError(socket, error, response) {
|
|
1081
|
-
// Get the session for this socket
|
|
1082
|
-
const session = this.smtpServer.getSessionManager().getSession(socket);
|
|
1083
|
-
if (!session) {
|
|
1084
|
-
SmtpLogger.error(`Session not found when handling socket error`);
|
|
1085
|
-
if (!socket.destroyed) {
|
|
1086
|
-
socket.destroy();
|
|
1087
|
-
}
|
|
1088
|
-
return;
|
|
1089
|
-
}
|
|
1090
|
-
// Get error details for logging
|
|
1091
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1092
|
-
const errorCode = error instanceof Error && 'code' in error ? error.code : 'UNKNOWN';
|
|
1093
|
-
SmtpLogger.warn(`Recoverable socket error during data handling (${errorCode}): ${errorMessage}`, {
|
|
1094
|
-
sessionId: session.id,
|
|
1095
|
-
remoteAddress: session.remoteAddress,
|
|
1096
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
1097
|
-
});
|
|
1098
|
-
// Check if socket is already destroyed
|
|
1099
|
-
if (socket.destroyed) {
|
|
1100
|
-
SmtpLogger.info(`Socket already destroyed, cannot retry data operation`);
|
|
1101
|
-
return;
|
|
1102
|
-
}
|
|
1103
|
-
// Check if socket is writeable
|
|
1104
|
-
if (!socket.writable) {
|
|
1105
|
-
SmtpLogger.info(`Socket no longer writable, aborting data recovery attempt`);
|
|
1106
|
-
if (!socket.destroyed) {
|
|
1107
|
-
socket.destroy();
|
|
1108
|
-
}
|
|
1109
|
-
return;
|
|
1110
|
-
}
|
|
1111
|
-
// Attempt to retry the write operation after a short delay
|
|
1112
|
-
setTimeout(() => {
|
|
1113
|
-
try {
|
|
1114
|
-
if (!socket.destroyed && socket.writable) {
|
|
1115
|
-
socket.write(`${response}${SMTP_DEFAULTS.CRLF}`);
|
|
1116
|
-
SmtpLogger.info(`Successfully retried data send operation after error`);
|
|
1117
|
-
}
|
|
1118
|
-
else {
|
|
1119
|
-
SmtpLogger.warn(`Socket no longer available for data retry`);
|
|
1120
|
-
if (!socket.destroyed) {
|
|
1121
|
-
socket.destroy();
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
}
|
|
1125
|
-
catch (retryError) {
|
|
1126
|
-
SmtpLogger.error(`Data retry attempt failed: ${retryError instanceof Error ? retryError.message : String(retryError)}`);
|
|
1127
|
-
if (!socket.destroyed) {
|
|
1128
|
-
socket.destroy();
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
}, 100); // Short delay before retry
|
|
1132
|
-
}
|
|
1133
|
-
/**
|
|
1134
|
-
* Handle email data (interface requirement)
|
|
1135
|
-
*/
|
|
1136
|
-
async handleData(socket, data, session) {
|
|
1137
|
-
// Delegate to existing method
|
|
1138
|
-
await this.handleDataReceived(socket, data);
|
|
1139
|
-
}
|
|
1140
|
-
/**
|
|
1141
|
-
* Clean up resources
|
|
1142
|
-
*/
|
|
1143
|
-
destroy() {
|
|
1144
|
-
// DataHandler doesn't have timers or event listeners to clean up
|
|
1145
|
-
SmtpLogger.debug('DataHandler destroyed');
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YS1oYW5kbGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vdHMvbWFpbC9kZWxpdmVyeS9zbXRwc2VydmVyL2RhdGEtaGFuZGxlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFFSCxPQUFPLEtBQUssT0FBTyxNQUFNLHFCQUFxQixDQUFDO0FBQy9DLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3pCLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUc1QyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsYUFBYSxFQUFFLGFBQWEsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ2hGLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNoRCxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUM5RCxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFFcEQ7O0dBRUc7QUFDSCxNQUFNLE9BQU8sV0FBVztJQU10Qjs7O09BR0c7SUFDSCxZQUFZLFVBQXVCO1FBQ2pDLElBQUksQ0FBQyxVQUFVLEdBQUcsVUFBVSxDQUFDO0lBQy9CLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxNQUFrRCxFQUFFLElBQVk7UUFDNUYsa0NBQWtDO1FBQ2xDLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUMsaUJBQWlCLEVBQUUsQ0FBQyxVQUFVLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDdkUsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ2IsSUFBSSxDQUFDLFlBQVksQ0FBQyxNQUFNLEVBQUUsR0FBRyxnQkFBZ0IsQ0FBQyxXQUFXLDRDQUE0QyxDQUFDLENBQUM7WUFDdkcsT0FBTztRQUNULENBQUM7UUFFRCwr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
|