@blamejs/core 0.9.46 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/CHANGELOG.md +951 -893
  2. package/index.js +30 -0
  3. package/lib/_test/crypto-fixtures.js +67 -0
  4. package/lib/agent-event-bus.js +52 -6
  5. package/lib/agent-idempotency.js +169 -16
  6. package/lib/agent-orchestrator.js +263 -9
  7. package/lib/agent-posture-chain.js +163 -5
  8. package/lib/agent-saga.js +146 -16
  9. package/lib/agent-snapshot.js +349 -19
  10. package/lib/agent-stream.js +34 -2
  11. package/lib/agent-tenant.js +179 -23
  12. package/lib/agent-trace.js +84 -21
  13. package/lib/auth/aal.js +8 -1
  14. package/lib/auth/ciba.js +6 -1
  15. package/lib/auth/dpop.js +7 -2
  16. package/lib/auth/fal.js +17 -8
  17. package/lib/auth/jwt-external.js +128 -4
  18. package/lib/auth/oauth.js +232 -10
  19. package/lib/auth/oid4vci.js +67 -7
  20. package/lib/auth/openid-federation.js +71 -25
  21. package/lib/auth/passkey.js +140 -6
  22. package/lib/auth/sd-jwt-vc.js +67 -5
  23. package/lib/circuit-breaker.js +10 -2
  24. package/lib/compliance.js +176 -8
  25. package/lib/crypto-field.js +114 -14
  26. package/lib/crypto.js +216 -20
  27. package/lib/db.js +1 -0
  28. package/lib/guard-imap-command.js +335 -0
  29. package/lib/guard-jmap.js +321 -0
  30. package/lib/guard-managesieve-command.js +566 -0
  31. package/lib/guard-pop3-command.js +317 -0
  32. package/lib/guard-smtp-command.js +58 -3
  33. package/lib/mail-agent.js +20 -7
  34. package/lib/mail-arc-sign.js +12 -8
  35. package/lib/mail-auth.js +323 -34
  36. package/lib/mail-crypto-pgp.js +934 -0
  37. package/lib/mail-crypto-smime.js +340 -0
  38. package/lib/mail-crypto.js +108 -0
  39. package/lib/mail-dav.js +1224 -0
  40. package/lib/mail-deploy.js +492 -0
  41. package/lib/mail-dkim.js +431 -26
  42. package/lib/mail-journal.js +435 -0
  43. package/lib/mail-scan.js +502 -0
  44. package/lib/mail-server-imap.js +1102 -0
  45. package/lib/mail-server-jmap.js +488 -0
  46. package/lib/mail-server-managesieve.js +853 -0
  47. package/lib/mail-server-mx.js +164 -34
  48. package/lib/mail-server-pop3.js +836 -0
  49. package/lib/mail-server-rate-limit.js +269 -0
  50. package/lib/mail-server-submission.js +1032 -0
  51. package/lib/mail-server-tls.js +445 -0
  52. package/lib/mail-sieve.js +557 -0
  53. package/lib/mail-spam-score.js +284 -0
  54. package/lib/mail.js +99 -0
  55. package/lib/metrics.js +130 -10
  56. package/lib/middleware/dpop.js +58 -3
  57. package/lib/middleware/idempotency-key.js +255 -42
  58. package/lib/middleware/protected-resource-metadata.js +114 -2
  59. package/lib/network-dns-resolver.js +33 -0
  60. package/lib/network-tls.js +46 -0
  61. package/lib/outbox.js +62 -12
  62. package/lib/pqc-agent.js +13 -5
  63. package/lib/retry.js +23 -9
  64. package/lib/router.js +23 -1
  65. package/lib/safe-ical.js +634 -0
  66. package/lib/safe-icap.js +502 -0
  67. package/lib/safe-mime.js +15 -0
  68. package/lib/safe-sieve.js +684 -0
  69. package/lib/safe-smtp.js +57 -0
  70. package/lib/safe-url.js +37 -0
  71. package/lib/safe-vcard.js +473 -0
  72. package/lib/self-update-standalone-verifier.js +32 -3
  73. package/lib/self-update.js +168 -17
  74. package/lib/vendor/MANIFEST.json +161 -156
  75. package/lib/vendor-data.js +127 -9
  76. package/lib/vex.js +324 -59
  77. package/package.json +1 -1
  78. package/sbom.cdx.json +6 -6
@@ -0,0 +1,445 @@
1
+ "use strict";
2
+ /**
3
+ * @module b.mail.server.tls
4
+ * @nav Mail
5
+ * @title Mail Server TLS Bootstrap
6
+ * @order 538
7
+ *
8
+ * @intro
9
+ * Operator-UX helper for the `tlsContext` opt on `b.mail.server.mx`
10
+ * and `b.mail.server.submission`. Both listeners refuse to boot
11
+ * without a `tlsContext` by design (no implicit plaintext mode);
12
+ * pre-this-primitive operators had to wire `node:tls.createSecureContext`
13
+ * themselves plus solve cert renewal + sealed-storage-of-keys + in-
14
+ * process reload-on-rotation. This primitive owns the wiring.
15
+ *
16
+ * ```js
17
+ * var tlsCtx = b.mail.server.tls.context({
18
+ * certFile: "/etc/letsencrypt/live/mail.example.com/fullchain.pem",
19
+ * keyFile: "/var/lib/blamejs/mail.example.com.key.sealed",
20
+ * vault: b.vault, // for keyFile unseal
21
+ * watch: true, // auto-reload on rotation
22
+ * });
23
+ *
24
+ * var mx = b.mail.server.mx.create({
25
+ * tlsContext: tlsCtx.secureContext,
26
+ * ...
27
+ * });
28
+ * ```
29
+ *
30
+ * The helper handles the three things operators need but were
31
+ * reinventing per-deployment:
32
+ *
33
+ * 1. **Sealed-key unwrap** — operators who store the private key on
34
+ * disk via `b.vault.sealPemFile` (recommended posture per
35
+ * SECURITY.md) pass `vault: b.vault` here and the helper unseals
36
+ * at load-time, never holding the plaintext key longer than the
37
+ * `tls.createSecureContext` call.
38
+ *
39
+ * 2. **Cert-rotation in-process reload** — when `watch: true`, the
40
+ * helper polls `certFile` + `keyFile` for mtime changes (default
41
+ * 30s poll, matching the framework's vault-pem-file convention).
42
+ * On change, the helper builds a fresh `SecureContext` and emits
43
+ * a `mail.server.tls.context_reloaded` audit event. Operators
44
+ * who wire `tlsCtx.onReload(fn)` get a callback so the running
45
+ * listener's `SecureContext` reference can be swapped.
46
+ *
47
+ * 3. **Boot-fail surface** — missing/unreadable file, unsealable
48
+ * key, mismatched cert/key pair, expired cert — all surfaced at
49
+ * `context()` call with a typed `MailServerTlsError` so the
50
+ * operator's boot path fails fast at the right line, not 20
51
+ * stack frames deep inside the listener.
52
+ *
53
+ * ## ACME provisioning
54
+ *
55
+ * This primitive does NOT drive ACME issuance — that's `b.acme`'s
56
+ * job (RFC 8555 + RFC 9773 ARI). The operator's deployment script /
57
+ * sidecar / systemd-timer orchestrates `b.acme.renewIfDue` and
58
+ * writes the renewed cert + key to `certFile` / `keyFile`. The
59
+ * watch-loop here picks up the change and reloads. Composing this
60
+ * way keeps the TLS-context helper unaware of which ACME provider
61
+ * the operator picked (Let's Encrypt / ZeroSSL / Buypass / step-ca /
62
+ * internal PKI) and unaware of which challenge type (HTTP-01 /
63
+ * DNS-01 / TLS-ALPN-01) the deployment uses.
64
+ *
65
+ * For a turnkey ACME-and-then-load path operators wire the two
66
+ * primitives at deploy-time:
67
+ *
68
+ * ```js
69
+ * // Once per deploy (sidecar / systemd-timer / k8s CronJob):
70
+ * var acme = b.acme.create({ directoryUrl: "https://acme-v02.api.letsencrypt.org/directory", ... });
71
+ * // ... acme.newAccount + acme.newOrder + challenge-solve + acme.finalize ...
72
+ * // → write the issued cert.pem + key.pem to the watched paths
73
+ *
74
+ * // Once per process at boot:
75
+ * var tls = b.mail.server.tls.context({ certFile, keyFile, watch: true });
76
+ * var mx = b.mail.server.mx.create({ tlsContext: tls.secureContext, ... });
77
+ * tls.onReload(function (newCtx) { mx.replaceTlsContext(newCtx); });
78
+ * ```
79
+ *
80
+ * The cleartext-refused error message from `b.mail.server.mx` /
81
+ * `b.mail.server.submission` points at this primitive so the
82
+ * operator's boot dead-end becomes a one-line fix.
83
+ *
84
+ * @card
85
+ * Operator-UX helper for the TLS context required by b.mail.server.mx /
86
+ * .submission. Loads cert + key (with optional vault-sealed-key unwrap),
87
+ * watches for rotation, builds a node:tls SecureContext + emits an
88
+ * audit event on every reload. ACME provisioning stays in b.acme;
89
+ * this primitive just loads what's on disk and reloads when it changes.
90
+ */
91
+
92
+ var nodeFs = require("node:fs");
93
+ var nodeTls = require("node:tls");
94
+ var lazyRequire = require("./lazy-require");
95
+ var C = require("./constants");
96
+ var validateOpts = require("./validate-opts");
97
+ var { defineClass } = require("./framework-error");
98
+
99
+ var audit = lazyRequire(function () { return require("./audit"); });
100
+
101
+ var MailServerTlsError = defineClass("MailServerTlsError", { alwaysPermanent: true });
102
+
103
+ var DEFAULT_POLL_MS = C.TIME.seconds(30);
104
+
105
+ /**
106
+ * @primitive b.mail.server.tls.context
107
+ * @signature b.mail.server.tls.context(opts)
108
+ * @since 0.9.48
109
+ * @status stable
110
+ * @related b.mail.server.mx.create, b.mail.server.submission.create, b.vault.sealPemFile, b.acme.create
111
+ *
112
+ * Build a `node:tls` `SecureContext` from cert + key PEM file paths.
113
+ * Returns a handle exposing `secureContext`, `reload()`, `onReload(fn)`,
114
+ * and `stop()`. When `watch: true`, the helper polls both files for
115
+ * mtime changes (default every 30s) and rebuilds the context in-place
116
+ * on change — operators wire `onReload` to swap the running listener's
117
+ * context after cert rotation.
118
+ *
119
+ * @opts
120
+ * certFile: string, // required — PEM-encoded fullchain
121
+ * keyFile: string, // required — PEM-encoded private key (raw OR sealed)
122
+ * vault: object, // optional — b.vault; when supplied + keyFile
123
+ * // starts with the b.vault.sealPemFile magic
124
+ * // ("vault:"), unsealed before use
125
+ * watch: boolean, // default false — when true, poll for rotation
126
+ * pollMs: number, // default 30000; min 1000
127
+ *
128
+ * @example
129
+ * var tls = b.mail.server.tls.context({
130
+ * certFile: "/etc/letsencrypt/live/mail.example.com/fullchain.pem",
131
+ * keyFile: "/etc/letsencrypt/live/mail.example.com/privkey.pem",
132
+ * watch: true,
133
+ * });
134
+ * // Wire `tls.secureContext` into b.mail.server.mx.create / submission.create
135
+ * tls.onReload(function (newCtx) {
136
+ * // operator swaps the running listener's SecureContext via the
137
+ * // listener's reload hook (when the listener exposes one) or via
138
+ * // restart-on-rotation flow
139
+ * });
140
+ *
141
+ * // ... later, on shutdown:
142
+ * tls.stop(); // clears the poll timer
143
+ */
144
+ function context(opts) {
145
+ validateOpts.requireObject(opts, "b.mail.server.tls.context",
146
+ MailServerTlsError, "mail-server-tls/bad-opts");
147
+ validateOpts.requireNonEmptyString(opts.certFile,
148
+ "b.mail.server.tls.context: opts.certFile",
149
+ MailServerTlsError, "mail-server-tls/bad-cert-file");
150
+ validateOpts.requireNonEmptyString(opts.keyFile,
151
+ "b.mail.server.tls.context: opts.keyFile",
152
+ MailServerTlsError, "mail-server-tls/bad-key-file");
153
+ if (opts.vault !== undefined &&
154
+ (typeof opts.vault !== "object" || opts.vault === null ||
155
+ typeof opts.vault.unseal !== "function")) {
156
+ throw new MailServerTlsError("mail-server-tls/bad-vault",
157
+ "b.mail.server.tls.context: opts.vault must be a b.vault handle (.unseal fn)");
158
+ }
159
+ validateOpts.optionalBoolean(opts.watch,
160
+ "b.mail.server.tls.context: opts.watch",
161
+ MailServerTlsError, "mail-server-tls/bad-watch");
162
+ var pollMs = opts.pollMs === undefined ? DEFAULT_POLL_MS : opts.pollMs;
163
+ if (typeof pollMs !== "number" || !isFinite(pollMs) || pollMs < C.TIME.seconds(1)) {
164
+ throw new MailServerTlsError("mail-server-tls/bad-poll-ms",
165
+ "b.mail.server.tls.context: opts.pollMs must be a finite number >= 1000");
166
+ }
167
+
168
+ var certFile = opts.certFile;
169
+ var keyFile = opts.keyFile;
170
+ var vault = opts.vault || null;
171
+ var watch = opts.watch === true;
172
+ var reloadListeners = [];
173
+ var secureContext = null;
174
+ var lastCertMtime = 0;
175
+ var lastKeyMtime = 0;
176
+ var pollTimer = null;
177
+ var stopped = false;
178
+
179
+ function _readKey() {
180
+ var raw = nodeFs.readFileSync(keyFile, "utf8");
181
+ // b.vault.sealPemFile produces blobs that decrypt via vault.unseal.
182
+ // Detect by the sealed-cell prefix the framework's vault layer
183
+ // already documents (everything else passes through as plain PEM).
184
+ if (vault && raw.indexOf("vault:") === 0) {
185
+ try {
186
+ return vault.unseal(raw).toString("utf8");
187
+ } catch (e) {
188
+ throw new MailServerTlsError("mail-server-tls/unseal-failed",
189
+ "b.mail.server.tls.context: failed to unseal " + keyFile +
190
+ " via b.vault.unseal: " + (e && e.message ? e.message : String(e)));
191
+ }
192
+ }
193
+ return raw;
194
+ }
195
+
196
+ function _build() {
197
+ var certPem;
198
+ try {
199
+ certPem = nodeFs.readFileSync(certFile, "utf8");
200
+ } catch (e) {
201
+ throw new MailServerTlsError("mail-server-tls/cert-unreadable",
202
+ "b.mail.server.tls.context: cannot read certFile " + certFile + ": " +
203
+ (e && e.message ? e.message : String(e)));
204
+ }
205
+ var keyPem;
206
+ try {
207
+ keyPem = _readKey();
208
+ } catch (e) {
209
+ if (e && e.isFrameworkError) throw e;
210
+ throw new MailServerTlsError("mail-server-tls/key-unreadable",
211
+ "b.mail.server.tls.context: cannot read keyFile " + keyFile + ": " +
212
+ (e && e.message ? e.message : String(e)));
213
+ }
214
+ var ctx;
215
+ try {
216
+ ctx = nodeTls.createSecureContext({ cert: certPem, key: keyPem });
217
+ } catch (e) {
218
+ throw new MailServerTlsError("mail-server-tls/secure-context-failed",
219
+ "b.mail.server.tls.context: createSecureContext threw (mismatched cert/key? " +
220
+ "expired? bad PEM?): " + (e && e.message ? e.message : String(e)));
221
+ }
222
+ return ctx;
223
+ }
224
+
225
+ function _emit(action, metadata) {
226
+ try {
227
+ audit().safeEmit({
228
+ action: action,
229
+ outcome: "success",
230
+ metadata: metadata || {},
231
+ });
232
+ } catch (_e) { /* drop-silent — audit best-effort */ }
233
+ }
234
+
235
+ function reload() {
236
+ var fresh = _build();
237
+ secureContext = fresh;
238
+ try {
239
+ var cstat = nodeFs.statSync(certFile);
240
+ lastCertMtime = cstat.mtimeMs;
241
+ } catch (_e) { /* file disappeared between read + stat; tolerate */ }
242
+ try {
243
+ var kstat = nodeFs.statSync(keyFile);
244
+ lastKeyMtime = kstat.mtimeMs;
245
+ } catch (_e) { /* same */ }
246
+ _emit("mail.server.tls.context_reloaded",
247
+ { certFile: certFile, keyFile: keyFile });
248
+ for (var i = 0; i < reloadListeners.length; i++) {
249
+ try { reloadListeners[i](secureContext); }
250
+ catch (_e) { /* listener errors must not break the loop */ }
251
+ }
252
+ return secureContext;
253
+ }
254
+
255
+ function onReload(fn) {
256
+ if (typeof fn !== "function") {
257
+ throw new MailServerTlsError("mail-server-tls/bad-listener",
258
+ "b.mail.server.tls.context: onReload(fn) requires a function");
259
+ }
260
+ reloadListeners.push(fn);
261
+ }
262
+
263
+ function _poll() {
264
+ if (stopped) return;
265
+ var changed = false;
266
+ try {
267
+ var cs = nodeFs.statSync(certFile);
268
+ if (cs.mtimeMs !== lastCertMtime) changed = true;
269
+ } catch (_e) { /* file removed transiently mid-rotation; skip */ }
270
+ try {
271
+ var ks = nodeFs.statSync(keyFile);
272
+ if (ks.mtimeMs !== lastKeyMtime) changed = true;
273
+ } catch (_e) { /* same */ }
274
+ if (changed) {
275
+ try { reload(); }
276
+ catch (e) {
277
+ // Reload failed (likely mid-rotation, file half-written).
278
+ // Surface as audit but DON'T overwrite the live context —
279
+ // the listener keeps serving with the prior good cert until
280
+ // the next poll catches a clean snapshot.
281
+ try {
282
+ audit().safeEmit({
283
+ action: "mail.server.tls.reload_failed",
284
+ outcome: "failure",
285
+ metadata: { error: e && e.message ? e.message : String(e) },
286
+ });
287
+ } catch (_e) { /* drop-silent */ }
288
+ }
289
+ }
290
+ }
291
+
292
+ function stop() {
293
+ stopped = true;
294
+ if (pollTimer) {
295
+ clearInterval(pollTimer);
296
+ pollTimer = null;
297
+ }
298
+ }
299
+
300
+ // Initial build — propagates boot-fail typed errors to the caller.
301
+ secureContext = _build();
302
+ try {
303
+ lastCertMtime = nodeFs.statSync(certFile).mtimeMs;
304
+ lastKeyMtime = nodeFs.statSync(keyFile).mtimeMs;
305
+ } catch (_e) { /* file disappeared between read + stat; tolerate */ }
306
+
307
+ if (watch) {
308
+ pollTimer = setInterval(_poll, pollMs);
309
+ if (typeof pollTimer.unref === "function") pollTimer.unref();
310
+ }
311
+
312
+ return {
313
+ get secureContext() { return secureContext; },
314
+ reload: reload,
315
+ onReload: onReload,
316
+ stop: stop,
317
+ };
318
+ }
319
+
320
+ /**
321
+ * @primitive b.mail.server.tls.upgradeSocket
322
+ * @signature b.mail.server.tls.upgradeSocket(opts)
323
+ * @since 0.9.57
324
+ * @status stable
325
+ * @related b.mail.server.tls.context, b.mail.server.mx.create, b.mail.server.submission.create
326
+ *
327
+ * STARTTLS / STLS upgrade primitive shared by every mail-protocol
328
+ * listener (MX / submission / IMAP / POP3). Wraps the four-step dance
329
+ * every listener was inlining and that has been a recurring source
330
+ * of cleartext-injection bugs (CVE-2021-33515 Dovecot,
331
+ * CVE-2021-38371 Exim) when even one of the four steps is forgotten:
332
+ *
333
+ * 1. Remove ALL `"data"` listeners from the plain socket so any
334
+ * bytes the peer queued in the TCP receive buffer before the
335
+ * handshake do NOT reach the plaintext state machine after the
336
+ * socket has been re-typed as a TLSSocket. Without listener
337
+ * removal, plain-mode bytes pipelined ahead of the handshake
338
+ * reach the post-TLS dispatcher and execute under the
339
+ * authenticated TLS context.
340
+ * 2. Pause the plain socket so no further bytes flow through the
341
+ * old handler in the window before the TLSSocket attaches.
342
+ * 3. Re-arm the idle timeout on the new TLSSocket (the plain
343
+ * socket's `setTimeout` does not survive the upgrade — RFC 5321
344
+ * §4.5.3.2.7 idle timeouts must keep running post-handshake).
345
+ * 4. Wire `"secure"` / `"data"` / `"error"` handlers via callbacks
346
+ * so the caller's per-protocol state machine keeps owning the
347
+ * ingest logic.
348
+ *
349
+ * @opts
350
+ * plainSocket: net.Socket, // pre-upgrade socket
351
+ * secureContext: tls.SecureContext, // from b.mail.server.tls.context
352
+ * idleTimeoutMs: number, // re-armed post-handshake
353
+ * onSecure: function(tlsSocket), // called once "secure" fires
354
+ * onData: function(tlsSocket, chunk), // post-handshake ingest
355
+ * onError: function(err), // handshake / runtime error
356
+ * onTimeout: function(tlsSocket), // optional idle timeout cb
357
+ *
358
+ * @example
359
+ * b.mail.server.tls.upgradeSocket({
360
+ * plainSocket: socket,
361
+ * secureContext: opts.tlsContext,
362
+ * idleTimeoutMs: idleTimeoutMs,
363
+ * onSecure: function (tlsSocket) { state.tls = true; },
364
+ * onData: function (tlsSocket, chunk) { _ingest(state, tlsSocket, chunk); },
365
+ * onError: function (err) { _emit("tls.handshake_failed", { err: err.message }); },
366
+ * });
367
+ */
368
+ function upgradeSocket(opts) {
369
+ if (!opts || typeof opts !== "object") {
370
+ throw new MailServerTlsError("mail-server-tls/bad-upgrade-opts",
371
+ "upgradeSocket: opts required");
372
+ }
373
+ var plainSocket = opts.plainSocket;
374
+ if (!plainSocket || typeof plainSocket.removeAllListeners !== "function") {
375
+ throw new MailServerTlsError("mail-server-tls/bad-upgrade-socket",
376
+ "upgradeSocket: opts.plainSocket must be a net.Socket");
377
+ }
378
+ if (!opts.secureContext) {
379
+ throw new MailServerTlsError("mail-server-tls/bad-upgrade-context",
380
+ "upgradeSocket: opts.secureContext required");
381
+ }
382
+ if (typeof opts.onSecure !== "function") {
383
+ throw new MailServerTlsError("mail-server-tls/bad-upgrade-onsecure",
384
+ "upgradeSocket: opts.onSecure(tlsSocket) required");
385
+ }
386
+ if (typeof opts.onData !== "function") {
387
+ throw new MailServerTlsError("mail-server-tls/bad-upgrade-ondata",
388
+ "upgradeSocket: opts.onData(tlsSocket, chunk) required");
389
+ }
390
+ if (typeof opts.onError !== "function") {
391
+ throw new MailServerTlsError("mail-server-tls/bad-upgrade-onerror",
392
+ "upgradeSocket: opts.onError(err) required");
393
+ }
394
+ var idleTimeoutMs = opts.idleTimeoutMs;
395
+ if (idleTimeoutMs !== undefined &&
396
+ (typeof idleTimeoutMs !== "number" || !isFinite(idleTimeoutMs) || idleTimeoutMs < 0)) {
397
+ throw new MailServerTlsError("mail-server-tls/bad-upgrade-idle-timeout",
398
+ "upgradeSocket: opts.idleTimeoutMs must be a non-negative finite number");
399
+ }
400
+
401
+ // CVE-2021-33515 / CVE-2021-38371 defense: strip every "data"
402
+ // listener on the plain socket BEFORE the TLSSocket wraps it.
403
+ // Without this, plain-mode bytes the peer queued pre-handshake
404
+ // (RFC 2920 PIPELINING + an unsuspecting parser) reach the
405
+ // post-TLS dispatcher and execute as if they had been sent over
406
+ // the authenticated channel.
407
+ plainSocket.removeAllListeners("data");
408
+ // Pause so the kernel TCP buffer doesn't drain into the old
409
+ // handler in the window before TLSSocket attaches its own.
410
+ if (typeof plainSocket.pause === "function") {
411
+ try { plainSocket.pause(); } catch (_e) { /* tolerate already-closed */ }
412
+ }
413
+
414
+ var tlsSocket = new nodeTls.TLSSocket(plainSocket, {
415
+ isServer: true,
416
+ secureContext: opts.secureContext,
417
+ });
418
+
419
+ tlsSocket.on("secure", function () {
420
+ if (idleTimeoutMs !== undefined && typeof tlsSocket.setTimeout === "function") {
421
+ try { tlsSocket.setTimeout(idleTimeoutMs); }
422
+ catch (_e) { /* tolerate */ }
423
+ }
424
+ if (typeof opts.onTimeout === "function") {
425
+ tlsSocket.on("timeout", function () { opts.onTimeout(tlsSocket); });
426
+ }
427
+ try { opts.onSecure(tlsSocket); }
428
+ catch (e) { try { opts.onError(e); } catch (_e) { /* drop-silent */ } }
429
+ });
430
+ tlsSocket.on("data", function (chunk) {
431
+ try { opts.onData(tlsSocket, chunk); }
432
+ catch (e) { try { opts.onError(e); } catch (_e) { /* drop-silent */ } }
433
+ });
434
+ tlsSocket.on("error", function (err) {
435
+ try { opts.onError(err); } catch (_e) { /* drop-silent */ }
436
+ });
437
+
438
+ return tlsSocket;
439
+ }
440
+
441
+ module.exports = {
442
+ context: context,
443
+ upgradeSocket: upgradeSocket,
444
+ MailServerTlsError: MailServerTlsError,
445
+ };