elit 3.6.4 → 3.6.6

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 (52) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/README.md +14 -1
  4. package/dist/build.d.ts +4 -1
  5. package/dist/cli.cjs +746 -166
  6. package/dist/cli.mjs +746 -166
  7. package/dist/config.d.ts +8 -1
  8. package/dist/coverage.d.ts +4 -1
  9. package/dist/desktop-auto-render.cjs +5 -4
  10. package/dist/desktop-auto-render.d.ts +4 -1
  11. package/dist/desktop-auto-render.js +5 -4
  12. package/dist/desktop-auto-render.mjs +5 -4
  13. package/dist/dom.cjs +5 -4
  14. package/dist/dom.d.ts +2 -0
  15. package/dist/dom.js +5 -4
  16. package/dist/dom.mjs +5 -4
  17. package/dist/el.d.ts +2 -0
  18. package/dist/index.cjs +5 -4
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.js +5 -4
  21. package/dist/index.mjs +5 -4
  22. package/dist/native.cjs +5 -4
  23. package/dist/native.d.ts +2 -0
  24. package/dist/native.js +5 -4
  25. package/dist/native.mjs +5 -4
  26. package/dist/render-context.d.ts +4 -1
  27. package/dist/router.cjs +5 -4
  28. package/dist/router.d.ts +2 -0
  29. package/dist/router.js +5 -4
  30. package/dist/router.mjs +5 -4
  31. package/dist/{server-CcBFc2F5.d.ts → server-uMQvZAll.d.ts} +9 -0
  32. package/dist/server.cjs +146 -4
  33. package/dist/server.d.ts +4 -1
  34. package/dist/server.js +4494 -285
  35. package/dist/server.mjs +146 -4
  36. package/dist/smtp-server.cjs +115 -0
  37. package/dist/smtp-server.d.ts +41 -0
  38. package/dist/smtp-server.js +4186 -0
  39. package/dist/smtp-server.mjs +87 -0
  40. package/dist/state.cjs +5 -4
  41. package/dist/state.d.ts +2 -0
  42. package/dist/state.js +5 -4
  43. package/dist/state.mjs +5 -4
  44. package/dist/test-runtime.cjs +184 -141
  45. package/dist/test-runtime.js +193 -150
  46. package/dist/test-runtime.mjs +184 -141
  47. package/dist/test.cjs +143 -134
  48. package/dist/test.js +152 -143
  49. package/dist/test.mjs +143 -134
  50. package/dist/types.d.ts +34 -2
  51. package/dist/universal.d.ts +2 -0
  52. package/package.json +9 -1
@@ -0,0 +1,4186 @@
1
+ "use strict";
2
+ (() => {
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
10
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
11
+ }) : x)(function(x) {
12
+ if (typeof require !== "undefined") return require.apply(this, arguments);
13
+ throw Error('Dynamic require of "' + x + '" is not supported');
14
+ });
15
+ var __esm = (fn, res) => function __init() {
16
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
17
+ };
18
+ var __commonJS = (cb, mod) => function __require2() {
19
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
20
+ };
21
+ var __export = (target, all) => {
22
+ for (var name in all)
23
+ __defProp(target, name, { get: all[name], enumerable: true });
24
+ };
25
+ var __copyProps = (to, from, except, desc) => {
26
+ if (from && typeof from === "object" || typeof from === "function") {
27
+ for (let key of __getOwnPropNames(from))
28
+ if (!__hasOwnProp.call(to, key) && key !== except)
29
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
30
+ }
31
+ return to;
32
+ };
33
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
34
+ // If the importer is in node compatibility mode or this is not an ESM
35
+ // file that has been converted to a CommonJS file using a Babel-
36
+ // compatible transform (i.e. "__esModule" has not been set), then set
37
+ // "default" to the CommonJS "module.exports" for node compatibility.
38
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
39
+ mod
40
+ ));
41
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
42
+
43
+ // node_modules/smtp-server/lib/smtp-stream.js
44
+ var require_smtp_stream = __commonJS({
45
+ "node_modules/smtp-server/lib/smtp-stream.js"(exports, module) {
46
+ "use strict";
47
+ var stream = __require("stream");
48
+ var Writable = stream.Writable;
49
+ var PassThrough = stream.PassThrough;
50
+ var SMTPStream = class extends Writable {
51
+ constructor(options) {
52
+ super(options);
53
+ this._dataMode = false;
54
+ this._dataStream = null;
55
+ this._maxBytes = Infinity;
56
+ this.dataBytes = 0;
57
+ this._continueCallback = false;
58
+ this._remainder = "";
59
+ this._lastBytes = false;
60
+ this._maxCommandLength = options && options.maxCommandLength || 4 * 1024;
61
+ this.isClosed = false;
62
+ this.on("finish", () => this._flushData());
63
+ }
64
+ /**
65
+ * Placeholder command handler. Override this with your own.
66
+ */
67
+ oncommand() {
68
+ throw new Error("Command handler is not set");
69
+ }
70
+ /**
71
+ * Switch to data mode and return output stream. The dots in the stream are unescaped.
72
+ *
73
+ * @returns {Stream} Data stream
74
+ */
75
+ startDataMode(maxBytes) {
76
+ this._dataMode = true;
77
+ this._maxBytes = maxBytes && Number(maxBytes) || Infinity;
78
+ this.dataBytes = 0;
79
+ this._dataStream = new PassThrough();
80
+ return this._dataStream;
81
+ }
82
+ /**
83
+ * Call this once data mode is over and you have finished processing the data stream
84
+ */
85
+ continue() {
86
+ if (typeof this._continueCallback === "function") {
87
+ this._continueCallback();
88
+ this._continueCallback = false;
89
+ } else {
90
+ this._continueCallback = true;
91
+ }
92
+ }
93
+ // PRIVATE METHODS
94
+ /**
95
+ * Writable._write method.
96
+ */
97
+ _write(chunk, encoding, next) {
98
+ if (!chunk || !chunk.length) {
99
+ return next();
100
+ }
101
+ let data;
102
+ let pos = 0;
103
+ let newlineRegex;
104
+ let called = false;
105
+ let done = (...args) => {
106
+ if (called) {
107
+ return;
108
+ }
109
+ called = true;
110
+ next(...args);
111
+ };
112
+ if (this.isClosed) {
113
+ return done();
114
+ }
115
+ if (!this._dataMode) {
116
+ newlineRegex = /\r?\n/g;
117
+ data = this._remainder + chunk.toString("binary");
118
+ let readLine = () => {
119
+ let match;
120
+ let line;
121
+ let buf;
122
+ if (this._dataMode) {
123
+ buf = Buffer.from(data.substr(pos), "binary");
124
+ this._remainder = "";
125
+ return this._write(buf, "buffer", done);
126
+ }
127
+ if (match = newlineRegex.exec(data)) {
128
+ line = data.substr(pos, match.index - pos);
129
+ pos += line.length + match[0].length;
130
+ } else {
131
+ this._remainder = pos < data.length ? data.substr(pos) : "";
132
+ if (this._remainder.length > this._maxCommandLength) {
133
+ this._remainder = "";
134
+ return done(new Error("Command line too long"));
135
+ }
136
+ return done();
137
+ }
138
+ this.oncommand(Buffer.from(line, "binary"), readLine);
139
+ };
140
+ readLine();
141
+ } else {
142
+ this._feedDataStream(chunk, done);
143
+ }
144
+ }
145
+ /**
146
+ * Processes a chunk in data mode. Escape dots are removed and final dot ends the data mode.
147
+ */
148
+ _feedDataStream(chunk, done) {
149
+ let i;
150
+ let endseq = Buffer.from("\r\n.\r\n");
151
+ let len;
152
+ let handled;
153
+ let buf;
154
+ if (this._lastBytes && this._lastBytes.length) {
155
+ chunk = Buffer.concat([this._lastBytes, chunk], this._lastBytes.length + chunk.length);
156
+ this._lastBytes = false;
157
+ }
158
+ len = chunk.length;
159
+ if (!this.dataBytes && len >= 3 && Buffer.compare(chunk.slice(0, 3), Buffer.from(".\r\n")) === 0) {
160
+ this._endDataMode(false, chunk.slice(3), done);
161
+ return;
162
+ }
163
+ if (!this.dataBytes && len >= 2 && chunk[0] === 46 && chunk[1] === 46) {
164
+ chunk = chunk.slice(1);
165
+ len--;
166
+ }
167
+ for (i = 2; i < len - 2; i++) {
168
+ if (chunk[i] === 46 && chunk[i - 1] === 10) {
169
+ if (Buffer.compare(chunk.slice(i - 2, i + 3), endseq) === 0) {
170
+ if (i > 2) {
171
+ buf = chunk.slice(0, i);
172
+ this.dataBytes += buf.length;
173
+ this._endDataMode(buf, chunk.slice(i + 3), done);
174
+ } else {
175
+ this._endDataMode(false, chunk.slice(i + 3), done);
176
+ }
177
+ return;
178
+ }
179
+ if (chunk[i + 1] === 46) {
180
+ buf = chunk.slice(0, i);
181
+ this._lastBytes = false;
182
+ this.dataBytes += buf.length;
183
+ if (this._dataStream.writable) {
184
+ this._dataStream.write(buf);
185
+ }
186
+ return setImmediate(() => this._feedDataStream(chunk.slice(i + 1), done));
187
+ }
188
+ }
189
+ }
190
+ if (chunk.length < 4) {
191
+ this._lastBytes = chunk;
192
+ } else {
193
+ this._lastBytes = chunk.slice(chunk.length - 4);
194
+ }
195
+ if (this._lastBytes.length < chunk.length) {
196
+ buf = chunk.slice(0, chunk.length - this._lastBytes.length);
197
+ this.dataBytes += buf.length;
198
+ if (this._dataStream.writable) {
199
+ handled = this._dataStream.write(buf);
200
+ if (!handled) {
201
+ this._dataStream.once("drain", done);
202
+ } else {
203
+ return done();
204
+ }
205
+ } else {
206
+ return done();
207
+ }
208
+ } else {
209
+ return done();
210
+ }
211
+ }
212
+ /**
213
+ * Flushes remaining bytes
214
+ */
215
+ _flushData() {
216
+ let line;
217
+ if (this._remainder && !this.isClosed) {
218
+ line = this._remainder;
219
+ this._remainder = "";
220
+ this.oncommand(Buffer.from(line, "binary"));
221
+ }
222
+ }
223
+ /**
224
+ * Ends data mode and returns to command mode. Stream is not resumed before #continue is called
225
+ */
226
+ _endDataMode(chunk, remainder, callback) {
227
+ if (this._continueCallback === true) {
228
+ this._continueCallback = false;
229
+ this._dataStream.once("end", callback);
230
+ } else {
231
+ this._continueCallback = () => this._write(remainder, "buffer", callback);
232
+ }
233
+ this._dataStream.byteLength = this.dataBytes;
234
+ this._dataStream.sizeExceeded = this.dataBytes > this._maxBytes;
235
+ if (chunk && chunk.length && this._dataStream.writable) {
236
+ this._dataStream.end(chunk);
237
+ } else {
238
+ this._dataStream.end();
239
+ }
240
+ this._dataMode = false;
241
+ this._remainder = "";
242
+ this._dataStream = null;
243
+ }
244
+ };
245
+ module.exports.SMTPStream = SMTPStream;
246
+ }
247
+ });
248
+
249
+ // node_modules/ipv6-normalize/index.js
250
+ var require_ipv6_normalize = __commonJS({
251
+ "node_modules/ipv6-normalize/index.js"(exports, module) {
252
+ "use strict";
253
+ module.exports = function(address) {
254
+ var _address = address.toLowerCase();
255
+ var segments = _address.split(":");
256
+ var length = segments.length;
257
+ var total = 8;
258
+ if (segments[0] === "" && segments[1] === "" && segments[2] === "") {
259
+ segments.shift();
260
+ segments.shift();
261
+ } else if (segments[0] === "" && segments[1] === "") {
262
+ segments.shift();
263
+ } else if (segments[length - 1] === "" && segments[length - 2] === "") {
264
+ segments.pop();
265
+ }
266
+ length = segments.length;
267
+ if (segments[length - 1].indexOf(".") !== -1) {
268
+ total = 7;
269
+ }
270
+ var pos;
271
+ for (pos = 0; pos < length; pos++) {
272
+ if (segments[pos] === "") {
273
+ break;
274
+ }
275
+ }
276
+ if (pos < total) {
277
+ segments.splice(pos, 1, "0000");
278
+ while (segments.length < total) {
279
+ segments.splice(pos, 0, "0000");
280
+ }
281
+ length = segments.length;
282
+ }
283
+ var _segments;
284
+ for (var i = 0; i < total; i++) {
285
+ _segments = segments[i].split("");
286
+ for (var j = 0; j < 3; j++) {
287
+ if (_segments[0] === "0" && _segments.length > 1) {
288
+ _segments.splice(0, 1);
289
+ } else {
290
+ break;
291
+ }
292
+ }
293
+ segments[i] = _segments.join("");
294
+ }
295
+ var best = -1;
296
+ var _best = 0;
297
+ var _current = 0;
298
+ var current = -1;
299
+ var inzeroes = false;
300
+ for (i = 0; i < total; i++) {
301
+ if (inzeroes) {
302
+ if (segments[i] === "0") {
303
+ _current += 1;
304
+ } else {
305
+ inzeroes = false;
306
+ if (_current > _best) {
307
+ best = current;
308
+ _best = _current;
309
+ }
310
+ }
311
+ } else {
312
+ if (segments[i] === "0") {
313
+ inzeroes = true;
314
+ current = i;
315
+ _current = 1;
316
+ }
317
+ }
318
+ }
319
+ if (_current > _best) {
320
+ best = current;
321
+ _best = _current;
322
+ }
323
+ if (_best > 1) {
324
+ segments.splice(best, _best, "");
325
+ }
326
+ length = segments.length;
327
+ var result = "";
328
+ if (segments[0] === "") {
329
+ result = ":";
330
+ }
331
+ for (i = 0; i < length; i++) {
332
+ result += segments[i];
333
+ if (i === length - 1) {
334
+ break;
335
+ }
336
+ result += ":";
337
+ }
338
+ if (segments[length - 1] === "") {
339
+ result += ":";
340
+ }
341
+ return result;
342
+ };
343
+ }
344
+ });
345
+
346
+ // node_modules/smtp-server/lib/sasl.js
347
+ var require_sasl = __commonJS({
348
+ "node_modules/smtp-server/lib/sasl.js"(exports, module) {
349
+ "use strict";
350
+ var util = __require("util");
351
+ var crypto = __require("crypto");
352
+ var SASL = module.exports = {
353
+ SASL_PLAIN(args, callback) {
354
+ if (args.length > 1) {
355
+ this.send(501, "Error: syntax: AUTH PLAIN token");
356
+ return callback();
357
+ }
358
+ if (!args.length) {
359
+ this._nextHandler = SASL.PLAIN_token.bind(this, true);
360
+ this.send(334);
361
+ return callback();
362
+ }
363
+ SASL.PLAIN_token.call(this, false, args[0], callback);
364
+ },
365
+ SASL_LOGIN(args, callback) {
366
+ if (args.length > 1) {
367
+ this.send(501, "Error: syntax: AUTH LOGIN");
368
+ return callback();
369
+ }
370
+ if (!args.length) {
371
+ this._nextHandler = SASL.LOGIN_username.bind(this, true);
372
+ this.send(334, "VXNlcm5hbWU6");
373
+ return callback();
374
+ }
375
+ SASL.LOGIN_username.call(this, false, args[0], callback);
376
+ },
377
+ SASL_XOAUTH2(args, callback) {
378
+ if (args.length > 1) {
379
+ this.send(501, "Error: syntax: AUTH XOAUTH2 token");
380
+ return callback();
381
+ }
382
+ if (!args.length) {
383
+ this._nextHandler = SASL.XOAUTH2_token.bind(this, true);
384
+ this.send(334);
385
+ return callback();
386
+ }
387
+ SASL.XOAUTH2_token.call(this, false, args[0], callback);
388
+ },
389
+ "SASL_CRAM-MD5"(args, callback) {
390
+ if (args.length) {
391
+ this.send(501, "Error: syntax: AUTH CRAM-MD5");
392
+ return callback();
393
+ }
394
+ let challenge = util.format(
395
+ "<%s%s@%s>",
396
+ String(Math.random()).replace(/^[0.]+/, "").substr(0, 8),
397
+ // random numbers
398
+ Math.floor(Date.now() / 1e3),
399
+ // timestamp
400
+ this.name
401
+ // hostname
402
+ );
403
+ this._nextHandler = SASL["CRAM-MD5_token"].bind(this, true, challenge);
404
+ this.send(334, Buffer.from(challenge).toString("base64"));
405
+ return callback();
406
+ },
407
+ PLAIN_token(canAbort, token, callback) {
408
+ token = (token || "").toString().trim();
409
+ if (canAbort && token === "*") {
410
+ this.send(501, "Authentication aborted");
411
+ return callback();
412
+ }
413
+ let data = Buffer.from(token, "base64").toString().split("\0");
414
+ if (data.length !== 3) {
415
+ this.send(500, "Error: invalid userdata");
416
+ return callback();
417
+ }
418
+ let username = data[1] || data[0] || "";
419
+ let password = data[2] || "";
420
+ this._server.onAuth(
421
+ {
422
+ method: "PLAIN",
423
+ username,
424
+ password
425
+ },
426
+ this.session,
427
+ (err, response) => {
428
+ if (err) {
429
+ this._server.logger.info(
430
+ {
431
+ err,
432
+ tnx: "autherror",
433
+ cid: this.id,
434
+ method: "PLAIN",
435
+ user: username
436
+ },
437
+ "Authentication error for %s using %s. %s",
438
+ username,
439
+ "PLAIN",
440
+ err.message
441
+ );
442
+ this.send(err.responseCode || 535, err.message);
443
+ return callback();
444
+ }
445
+ if (!response.user) {
446
+ this._server.logger.info(
447
+ {
448
+ tnx: "authfail",
449
+ cid: this.id,
450
+ method: "PLAIN",
451
+ user: username
452
+ },
453
+ "Authentication failed for %s using %s",
454
+ username,
455
+ "PLAIN"
456
+ );
457
+ this.send(response.responseCode || 535, response.message || "Error: Authentication credentials invalid");
458
+ return callback();
459
+ }
460
+ this._server.logger.info(
461
+ {
462
+ tnx: "auth",
463
+ cid: this.id,
464
+ method: "PLAIN",
465
+ user: username
466
+ },
467
+ "%s authenticated using %s",
468
+ username,
469
+ "PLAIN"
470
+ );
471
+ this.session.user = response.user;
472
+ this.session.transmissionType = this._transmissionType();
473
+ this.send(235, "Authentication successful");
474
+ callback();
475
+ }
476
+ );
477
+ },
478
+ LOGIN_username(canAbort, username, callback) {
479
+ username = (username || "").toString().trim();
480
+ if (canAbort && username === "*") {
481
+ this.send(501, "Authentication aborted");
482
+ return callback();
483
+ }
484
+ username = Buffer.from(username, "base64").toString();
485
+ if (!username) {
486
+ this.send(500, "Error: missing username");
487
+ return callback();
488
+ }
489
+ this._nextHandler = SASL.LOGIN_password.bind(this, username);
490
+ this.send(334, "UGFzc3dvcmQ6");
491
+ return callback();
492
+ },
493
+ LOGIN_password(username, password, callback) {
494
+ password = (password || "").toString().trim();
495
+ if (password === "*") {
496
+ this.send(501, "Authentication aborted");
497
+ return callback();
498
+ }
499
+ password = Buffer.from(password, "base64").toString();
500
+ this._server.onAuth(
501
+ {
502
+ method: "LOGIN",
503
+ username,
504
+ password
505
+ },
506
+ this.session,
507
+ (err, response) => {
508
+ if (err) {
509
+ this._server.logger.info(
510
+ {
511
+ err,
512
+ tnx: "autherror",
513
+ cid: this.id,
514
+ method: "LOGIN",
515
+ user: username
516
+ },
517
+ "Authentication error for %s using %s. %s",
518
+ username,
519
+ "LOGIN",
520
+ err.message
521
+ );
522
+ this.send(err.responseCode || 535, err.message);
523
+ return callback();
524
+ }
525
+ if (!response.user) {
526
+ this._server.logger.info(
527
+ {
528
+ tnx: "authfail",
529
+ cid: this.id,
530
+ method: "LOGIN",
531
+ user: username
532
+ },
533
+ "Authentication failed for %s using %s",
534
+ username,
535
+ "LOGIN"
536
+ );
537
+ this.send(response.responseCode || 535, response.message || "Error: Authentication credentials invalid");
538
+ return callback();
539
+ }
540
+ this._server.logger.info(
541
+ {
542
+ tnx: "auth",
543
+ cid: this.id,
544
+ method: "PLAIN",
545
+ user: username
546
+ },
547
+ "%s authenticated using %s",
548
+ username,
549
+ "LOGIN"
550
+ );
551
+ this.session.user = response.user;
552
+ this.session.transmissionType = this._transmissionType();
553
+ this.send(235, "Authentication successful");
554
+ callback();
555
+ }
556
+ );
557
+ },
558
+ XOAUTH2_token(canAbort, token, callback) {
559
+ token = (token || "").toString().trim();
560
+ if (canAbort && token === "*") {
561
+ this.send(501, "Authentication aborted");
562
+ return callback();
563
+ }
564
+ let username;
565
+ let accessToken;
566
+ Buffer.from(token, "base64").toString().split("").forEach((part) => {
567
+ part = part.split("=");
568
+ let key = part.shift().toLowerCase();
569
+ let value = part.join("=").trim();
570
+ if (key === "user") {
571
+ username = value;
572
+ } else if (key === "auth") {
573
+ value = value.split(/\s+/);
574
+ if (value.shift().toLowerCase() === "bearer") {
575
+ accessToken = value.join(" ");
576
+ }
577
+ }
578
+ });
579
+ if (!username || !accessToken) {
580
+ this.send(500, "Error: invalid userdata");
581
+ return callback();
582
+ }
583
+ this._server.onAuth(
584
+ {
585
+ method: "XOAUTH2",
586
+ username,
587
+ accessToken
588
+ },
589
+ this.session,
590
+ (err, response) => {
591
+ if (err) {
592
+ this._server.logger.info(
593
+ {
594
+ err,
595
+ tnx: "autherror",
596
+ cid: this.id,
597
+ method: "XOAUTH2",
598
+ user: username
599
+ },
600
+ "Authentication error for %s using %s. %s",
601
+ username,
602
+ "XOAUTH2",
603
+ err.message
604
+ );
605
+ this.send(err.responseCode || 535, err.message);
606
+ return callback();
607
+ }
608
+ if (!response.user) {
609
+ this._server.logger.info(
610
+ {
611
+ tnx: "authfail",
612
+ cid: this.id,
613
+ method: "XOAUTH2",
614
+ user: username
615
+ },
616
+ "Authentication failed for %s using %s",
617
+ username,
618
+ "XOAUTH2"
619
+ );
620
+ this._nextHandler = SASL.XOAUTH2_error.bind(this);
621
+ this.send(response.responseCode || 334, Buffer.from(JSON.stringify(response.data || {})).toString("base64"));
622
+ return callback();
623
+ }
624
+ this._server.logger.info(
625
+ {
626
+ tnx: "auth",
627
+ cid: this.id,
628
+ method: "XOAUTH2",
629
+ user: username
630
+ },
631
+ "%s authenticated using %s",
632
+ username,
633
+ "XOAUTH2"
634
+ );
635
+ this.session.user = response.user;
636
+ this.session.transmissionType = this._transmissionType();
637
+ this.send(235, "Authentication successful");
638
+ callback();
639
+ }
640
+ );
641
+ },
642
+ XOAUTH2_error(data, callback) {
643
+ this.send(535, "Error: Username and Password not accepted");
644
+ return callback();
645
+ },
646
+ "CRAM-MD5_token"(canAbort, challenge, token, callback) {
647
+ token = (token || "").toString().trim();
648
+ if (canAbort && token === "*") {
649
+ this.send(501, "Authentication aborted");
650
+ return callback();
651
+ }
652
+ let tokenParts = Buffer.from(token, "base64").toString().split(" ");
653
+ let username = tokenParts.shift();
654
+ let challengeResponse = (tokenParts.shift() || "").toLowerCase();
655
+ this._server.onAuth(
656
+ {
657
+ method: "CRAM-MD5",
658
+ username,
659
+ challenge,
660
+ challengeResponse,
661
+ validatePassword(password) {
662
+ let hmac = crypto.createHmac("md5", password);
663
+ return hmac.update(challenge).digest("hex").toLowerCase() === challengeResponse;
664
+ }
665
+ },
666
+ this.session,
667
+ (err, response) => {
668
+ if (err) {
669
+ this._server.logger.info(
670
+ {
671
+ err,
672
+ tnx: "autherror",
673
+ cid: this.id,
674
+ method: "CRAM-MD5",
675
+ user: username
676
+ },
677
+ "Authentication error for %s using %s. %s",
678
+ username,
679
+ "CRAM-MD5",
680
+ err.message
681
+ );
682
+ this.send(err.responseCode || 535, err.message);
683
+ return callback();
684
+ }
685
+ if (!response.user) {
686
+ this._server.logger.info(
687
+ {
688
+ tnx: "authfail",
689
+ cid: this.id,
690
+ method: "CRAM-MD5",
691
+ user: username
692
+ },
693
+ "Authentication failed for %s using %s",
694
+ username,
695
+ "CRAM-MD5"
696
+ );
697
+ this.send(response.responseCode || 535, response.message || "Error: Authentication credentials invalid");
698
+ return callback();
699
+ }
700
+ this._server.logger.info(
701
+ {
702
+ tnx: "auth",
703
+ cid: this.id,
704
+ method: "CRAM-MD5",
705
+ user: username
706
+ },
707
+ "%s authenticated using %s",
708
+ username,
709
+ "CRAM-MD5"
710
+ );
711
+ this.session.user = response.user;
712
+ this.session.transmissionType = this._transmissionType();
713
+ this.send(235, "Authentication successful");
714
+ callback();
715
+ }
716
+ );
717
+ },
718
+ // this is not a real auth but a username validation initiated by SMTP proxy
719
+ SASL_XCLIENT(args, callback) {
720
+ const username = (args && args[0] || "").toString().trim();
721
+ this._server.onAuth(
722
+ {
723
+ method: "XCLIENT",
724
+ username,
725
+ password: null
726
+ },
727
+ this.session,
728
+ (err, response) => {
729
+ if (err) {
730
+ this._server.logger.info(
731
+ {
732
+ err,
733
+ tnx: "autherror",
734
+ cid: this.id,
735
+ method: "XCLIENT",
736
+ user: username
737
+ },
738
+ "Authentication error for %s using %s. %s",
739
+ username,
740
+ "XCLIENT",
741
+ err.message
742
+ );
743
+ return callback(err);
744
+ }
745
+ if (!response.user) {
746
+ this._server.logger.info(
747
+ {
748
+ tnx: "authfail",
749
+ cid: this.id,
750
+ method: "XCLIENT",
751
+ user: username
752
+ },
753
+ "Authentication failed for %s using %s",
754
+ username,
755
+ "XCLIENT"
756
+ );
757
+ return callback(new Error("Authentication credentials invalid"));
758
+ }
759
+ this._server.logger.info(
760
+ {
761
+ tnx: "auth",
762
+ cid: this.id,
763
+ method: "XCLIENT",
764
+ user: username
765
+ },
766
+ "%s authenticated using %s",
767
+ username,
768
+ "XCLIENT"
769
+ );
770
+ this.session.user = response.user;
771
+ this.session.transmissionType = this._transmissionType();
772
+ callback();
773
+ }
774
+ );
775
+ }
776
+ };
777
+ }
778
+ });
779
+
780
+ // node_modules/punycode.js/punycode.es6.js
781
+ var punycode_es6_exports = {};
782
+ __export(punycode_es6_exports, {
783
+ decode: () => decode,
784
+ default: () => punycode_es6_default,
785
+ encode: () => encode,
786
+ toASCII: () => toASCII,
787
+ toUnicode: () => toUnicode,
788
+ ucs2decode: () => ucs2decode,
789
+ ucs2encode: () => ucs2encode
790
+ });
791
+ function error(type) {
792
+ throw new RangeError(errors[type]);
793
+ }
794
+ function map(array, callback) {
795
+ const result = [];
796
+ let length = array.length;
797
+ while (length--) {
798
+ result[length] = callback(array[length]);
799
+ }
800
+ return result;
801
+ }
802
+ function mapDomain(domain, callback) {
803
+ const parts = domain.split("@");
804
+ let result = "";
805
+ if (parts.length > 1) {
806
+ result = parts[0] + "@";
807
+ domain = parts[1];
808
+ }
809
+ domain = domain.replace(regexSeparators, ".");
810
+ const labels = domain.split(".");
811
+ const encoded = map(labels, callback).join(".");
812
+ return result + encoded;
813
+ }
814
+ function ucs2decode(string) {
815
+ const output = [];
816
+ let counter = 0;
817
+ const length = string.length;
818
+ while (counter < length) {
819
+ const value = string.charCodeAt(counter++);
820
+ if (value >= 55296 && value <= 56319 && counter < length) {
821
+ const extra = string.charCodeAt(counter++);
822
+ if ((extra & 64512) == 56320) {
823
+ output.push(((value & 1023) << 10) + (extra & 1023) + 65536);
824
+ } else {
825
+ output.push(value);
826
+ counter--;
827
+ }
828
+ } else {
829
+ output.push(value);
830
+ }
831
+ }
832
+ return output;
833
+ }
834
+ var maxInt, base, tMin, tMax, skew, damp, initialBias, initialN, delimiter, regexPunycode, regexNonASCII, regexSeparators, errors, baseMinusTMin, floor, stringFromCharCode, ucs2encode, basicToDigit, digitToBasic, adapt, decode, encode, toUnicode, toASCII, punycode, punycode_es6_default;
835
+ var init_punycode_es6 = __esm({
836
+ "node_modules/punycode.js/punycode.es6.js"() {
837
+ "use strict";
838
+ maxInt = 2147483647;
839
+ base = 36;
840
+ tMin = 1;
841
+ tMax = 26;
842
+ skew = 38;
843
+ damp = 700;
844
+ initialBias = 72;
845
+ initialN = 128;
846
+ delimiter = "-";
847
+ regexPunycode = /^xn--/;
848
+ regexNonASCII = /[^\0-\x7F]/;
849
+ regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g;
850
+ errors = {
851
+ "overflow": "Overflow: input needs wider integers to process",
852
+ "not-basic": "Illegal input >= 0x80 (not a basic code point)",
853
+ "invalid-input": "Invalid input"
854
+ };
855
+ baseMinusTMin = base - tMin;
856
+ floor = Math.floor;
857
+ stringFromCharCode = String.fromCharCode;
858
+ ucs2encode = (codePoints) => String.fromCodePoint(...codePoints);
859
+ basicToDigit = function(codePoint) {
860
+ if (codePoint >= 48 && codePoint < 58) {
861
+ return 26 + (codePoint - 48);
862
+ }
863
+ if (codePoint >= 65 && codePoint < 91) {
864
+ return codePoint - 65;
865
+ }
866
+ if (codePoint >= 97 && codePoint < 123) {
867
+ return codePoint - 97;
868
+ }
869
+ return base;
870
+ };
871
+ digitToBasic = function(digit, flag) {
872
+ return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
873
+ };
874
+ adapt = function(delta, numPoints, firstTime) {
875
+ let k = 0;
876
+ delta = firstTime ? floor(delta / damp) : delta >> 1;
877
+ delta += floor(delta / numPoints);
878
+ for (; delta > baseMinusTMin * tMax >> 1; k += base) {
879
+ delta = floor(delta / baseMinusTMin);
880
+ }
881
+ return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
882
+ };
883
+ decode = function(input) {
884
+ const output = [];
885
+ const inputLength = input.length;
886
+ let i = 0;
887
+ let n = initialN;
888
+ let bias = initialBias;
889
+ let basic = input.lastIndexOf(delimiter);
890
+ if (basic < 0) {
891
+ basic = 0;
892
+ }
893
+ for (let j = 0; j < basic; ++j) {
894
+ if (input.charCodeAt(j) >= 128) {
895
+ error("not-basic");
896
+ }
897
+ output.push(input.charCodeAt(j));
898
+ }
899
+ for (let index = basic > 0 ? basic + 1 : 0; index < inputLength; ) {
900
+ const oldi = i;
901
+ for (let w = 1, k = base; ; k += base) {
902
+ if (index >= inputLength) {
903
+ error("invalid-input");
904
+ }
905
+ const digit = basicToDigit(input.charCodeAt(index++));
906
+ if (digit >= base) {
907
+ error("invalid-input");
908
+ }
909
+ if (digit > floor((maxInt - i) / w)) {
910
+ error("overflow");
911
+ }
912
+ i += digit * w;
913
+ const t = k <= bias ? tMin : k >= bias + tMax ? tMax : k - bias;
914
+ if (digit < t) {
915
+ break;
916
+ }
917
+ const baseMinusT = base - t;
918
+ if (w > floor(maxInt / baseMinusT)) {
919
+ error("overflow");
920
+ }
921
+ w *= baseMinusT;
922
+ }
923
+ const out = output.length + 1;
924
+ bias = adapt(i - oldi, out, oldi == 0);
925
+ if (floor(i / out) > maxInt - n) {
926
+ error("overflow");
927
+ }
928
+ n += floor(i / out);
929
+ i %= out;
930
+ output.splice(i++, 0, n);
931
+ }
932
+ return String.fromCodePoint(...output);
933
+ };
934
+ encode = function(input) {
935
+ const output = [];
936
+ input = ucs2decode(input);
937
+ const inputLength = input.length;
938
+ let n = initialN;
939
+ let delta = 0;
940
+ let bias = initialBias;
941
+ for (const currentValue of input) {
942
+ if (currentValue < 128) {
943
+ output.push(stringFromCharCode(currentValue));
944
+ }
945
+ }
946
+ const basicLength = output.length;
947
+ let handledCPCount = basicLength;
948
+ if (basicLength) {
949
+ output.push(delimiter);
950
+ }
951
+ while (handledCPCount < inputLength) {
952
+ let m = maxInt;
953
+ for (const currentValue of input) {
954
+ if (currentValue >= n && currentValue < m) {
955
+ m = currentValue;
956
+ }
957
+ }
958
+ const handledCPCountPlusOne = handledCPCount + 1;
959
+ if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
960
+ error("overflow");
961
+ }
962
+ delta += (m - n) * handledCPCountPlusOne;
963
+ n = m;
964
+ for (const currentValue of input) {
965
+ if (currentValue < n && ++delta > maxInt) {
966
+ error("overflow");
967
+ }
968
+ if (currentValue === n) {
969
+ let q = delta;
970
+ for (let k = base; ; k += base) {
971
+ const t = k <= bias ? tMin : k >= bias + tMax ? tMax : k - bias;
972
+ if (q < t) {
973
+ break;
974
+ }
975
+ const qMinusT = q - t;
976
+ const baseMinusT = base - t;
977
+ output.push(
978
+ stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
979
+ );
980
+ q = floor(qMinusT / baseMinusT);
981
+ }
982
+ output.push(stringFromCharCode(digitToBasic(q, 0)));
983
+ bias = adapt(delta, handledCPCountPlusOne, handledCPCount === basicLength);
984
+ delta = 0;
985
+ ++handledCPCount;
986
+ }
987
+ }
988
+ ++delta;
989
+ ++n;
990
+ }
991
+ return output.join("");
992
+ };
993
+ toUnicode = function(input) {
994
+ return mapDomain(input, function(string) {
995
+ return regexPunycode.test(string) ? decode(string.slice(4).toLowerCase()) : string;
996
+ });
997
+ };
998
+ toASCII = function(input) {
999
+ return mapDomain(input, function(string) {
1000
+ return regexNonASCII.test(string) ? "xn--" + encode(string) : string;
1001
+ });
1002
+ };
1003
+ punycode = {
1004
+ /**
1005
+ * A string representing the current Punycode.js version number.
1006
+ * @memberOf punycode
1007
+ * @type String
1008
+ */
1009
+ "version": "2.3.1",
1010
+ /**
1011
+ * An object of methods to convert from JavaScript's internal character
1012
+ * representation (UCS-2) to Unicode code points, and back.
1013
+ * @see <https://mathiasbynens.be/notes/javascript-encoding>
1014
+ * @memberOf punycode
1015
+ * @type Object
1016
+ */
1017
+ "ucs2": {
1018
+ "decode": ucs2decode,
1019
+ "encode": ucs2encode
1020
+ },
1021
+ "decode": decode,
1022
+ "encode": encode,
1023
+ "toASCII": toASCII,
1024
+ "toUnicode": toUnicode
1025
+ };
1026
+ punycode_es6_default = punycode;
1027
+ }
1028
+ });
1029
+
1030
+ // node_modules/smtp-server/lib/smtp-connection.js
1031
+ var require_smtp_connection = __commonJS({
1032
+ "node_modules/smtp-server/lib/smtp-connection.js"(exports, module) {
1033
+ "use strict";
1034
+ var SMTPStream = require_smtp_stream().SMTPStream;
1035
+ var dns = __require("dns");
1036
+ var tls = __require("tls");
1037
+ var net = __require("net");
1038
+ var ipv6normalize = require_ipv6_normalize();
1039
+ var sasl = require_sasl();
1040
+ var crypto = __require("crypto");
1041
+ var os = __require("os");
1042
+ var punycode2 = (init_punycode_es6(), __toCommonJS(punycode_es6_exports));
1043
+ var EventEmitter = __require("events");
1044
+ var SOCKET_TIMEOUT = 60 * 1e3;
1045
+ var ENHANCED_STATUS_CODES = {
1046
+ // Success codes (2xx)
1047
+ 200: "2.0.0",
1048
+ // System status, or system help reply
1049
+ 211: "2.0.0",
1050
+ // System status, or system help reply
1051
+ 214: "2.0.0",
1052
+ // Help message
1053
+ 220: "2.0.0",
1054
+ // Service ready
1055
+ 221: "2.0.0",
1056
+ // Service closing transmission channel
1057
+ 235: "2.7.0",
1058
+ // Authentication successful
1059
+ 250: "2.0.0",
1060
+ // Requested mail action okay, completed
1061
+ 251: "2.1.5",
1062
+ // User not local; will forward
1063
+ 252: "2.1.5",
1064
+ // Cannot VRFY user, but will accept message
1065
+ 334: "3.7.0",
1066
+ // Server challenge for authentication
1067
+ 354: "2.0.0",
1068
+ // Start mail input; end with <CRLF>.<CRLF>
1069
+ // Temporary failure codes (4xx)
1070
+ 420: "4.4.2",
1071
+ // Timeout or connection lost (non-standard, used by some servers)
1072
+ 421: "4.4.2",
1073
+ // Service not available, closing transmission channel
1074
+ 450: "4.2.1",
1075
+ // Requested mail action not taken: mailbox unavailable
1076
+ 451: "4.3.0",
1077
+ // Requested action aborted: local error in processing
1078
+ 452: "4.2.2",
1079
+ // Requested action not taken: insufficient system storage
1080
+ 454: "4.7.0",
1081
+ // Temporary authentication failure
1082
+ // Permanent failure codes (5xx)
1083
+ 500: "5.5.2",
1084
+ // Syntax error, command unrecognized
1085
+ 501: "5.5.4",
1086
+ // Syntax error in parameters or arguments
1087
+ 502: "5.5.1",
1088
+ // Command not implemented
1089
+ 503: "5.5.1",
1090
+ // Bad sequence of commands
1091
+ 504: "5.5.4",
1092
+ // Command parameter not implemented
1093
+ 521: "5.3.2",
1094
+ // Machine does not accept mail
1095
+ 523: "5.3.4",
1096
+ // Message size exceeds server limit (non-standard, used by some servers)
1097
+ 530: "5.7.0",
1098
+ // Authentication required
1099
+ 535: "5.7.8",
1100
+ // Authentication credentials invalid
1101
+ 538: "5.7.0",
1102
+ // Must issue a STARTTLS command first (non-standard)
1103
+ 550: "5.1.1",
1104
+ // Requested action not taken: mailbox unavailable
1105
+ 551: "5.1.6",
1106
+ // User not local; please try forwarding
1107
+ 552: "5.2.2",
1108
+ // Requested mail action aborted: exceeded storage allocation
1109
+ 553: "5.1.3",
1110
+ // Requested action not taken: mailbox name not allowed
1111
+ 554: "5.6.0",
1112
+ // Transaction failed
1113
+ 555: "5.5.4",
1114
+ // MAIL FROM/RCPT TO parameters not recognized or not implemented
1115
+ 556: "5.1.10",
1116
+ // RCPT TO syntax error (non-standard)
1117
+ 557: "5.7.1",
1118
+ // Delivery not authorized (non-standard, used by some servers)
1119
+ 558: "5.2.3"
1120
+ // Message too large for recipient (non-standard, used by some servers)
1121
+ };
1122
+ var SKIPPED_COMMANDS_FOR_ENHANCED_STATUS_CODES = /* @__PURE__ */ new Set(["HELO", "EHLO", "LHLO"]);
1123
+ var CONTEXTUAL_STATUS_CODES = {
1124
+ // Mail transaction specific codes
1125
+ MAIL_FROM_OK: "2.1.0",
1126
+ // Originator address valid
1127
+ RCPT_TO_OK: "2.1.5",
1128
+ // Destination address valid
1129
+ DATA_OK: "2.6.0",
1130
+ // Message accepted for delivery
1131
+ // Authentication specific codes
1132
+ AUTH_SUCCESS: "2.7.0",
1133
+ // Authentication successful
1134
+ AUTH_REQUIRED: "5.7.0",
1135
+ // Authentication required
1136
+ AUTH_INVALID: "5.7.8",
1137
+ // Authentication credentials invalid
1138
+ // Policy specific codes
1139
+ POLICY_VIOLATION: "5.7.1",
1140
+ // Delivery not authorized
1141
+ SPAM_REJECTED: "5.7.1",
1142
+ // Message refused
1143
+ // Mailbox specific codes
1144
+ MAILBOX_FULL: "4.2.2",
1145
+ // Mailbox full
1146
+ MAILBOX_NOT_FOUND: "5.1.1",
1147
+ // Mailbox does not exist
1148
+ MAILBOX_SYNTAX_ERROR: "5.1.3",
1149
+ // Invalid mailbox syntax
1150
+ // System specific codes
1151
+ SYSTEM_ERROR: "4.3.0",
1152
+ // System error
1153
+ SYSTEM_FULL: "4.3.1",
1154
+ // System storage exceeded
1155
+ // Network specific codes
1156
+ NETWORK_ERROR: "4.4.0",
1157
+ // Network routing error
1158
+ CONNECTION_TIMEOUT: "4.4.2"
1159
+ // Connection timeout
1160
+ };
1161
+ var SMTPConnection = class extends EventEmitter {
1162
+ constructor(server, socket, options) {
1163
+ super();
1164
+ options = options || {};
1165
+ this.id = options.id || BigInt("0x" + crypto.randomBytes(10).toString("hex")).toString(32).padStart(16, "0");
1166
+ this.ignore = options.ignore;
1167
+ this._server = server;
1168
+ this._socket = socket;
1169
+ this.session = this.session = {
1170
+ id: this.id
1171
+ };
1172
+ this._transactionCounter = 0;
1173
+ this._ready = false;
1174
+ this._upgrading = false;
1175
+ this._nextHandler = false;
1176
+ this._parser = new SMTPStream({
1177
+ maxCommandLength: this._server.options.maxCommandLength
1178
+ });
1179
+ this._parser.oncommand = (...args) => this._onCommand(...args);
1180
+ this._dataStream = false;
1181
+ this.session.secure = this.secure = !!this._server.options.secure;
1182
+ this.needsUpgrade = !!this._server.options.needsUpgrade;
1183
+ this.tlsOptions = this.secure && !this.needsUpgrade && this._socket.getCipher ? this._socket.getCipher() : false;
1184
+ this.localAddress = (options.localAddress || this._socket.localAddress || "").replace(/^::ffff:/, "");
1185
+ this.localPort = Number(options.localPort || this._socket.localPort) || 0;
1186
+ this.remoteAddress = (options.remoteAddress || this._socket.remoteAddress || "").replace(/^::ffff:/, "");
1187
+ this.remotePort = Number(options.remotePort || this._socket.remotePort) || 0;
1188
+ if (this.localAddress && net.isIPv6(this.localAddress)) {
1189
+ this.localAddress = ipv6normalize(this.localAddress);
1190
+ }
1191
+ if (this.remoteAddress && net.isIPv6(this.remoteAddress)) {
1192
+ this.remoteAddress = ipv6normalize(this.remoteAddress);
1193
+ }
1194
+ this._unauthenticatedCommands = 0;
1195
+ this._maxAllowedUnauthenticatedCommands = this._server.options.maxAllowedUnauthenticatedCommands || 10;
1196
+ this._unrecognizedCommands = 0;
1197
+ this.name = this._server.options.name || os.hostname();
1198
+ this.clientHostname = false;
1199
+ this.openingCommand = false;
1200
+ this.hostNameAppearsAs = false;
1201
+ this._xClient = /* @__PURE__ */ new Map();
1202
+ this._xForward = /* @__PURE__ */ new Map();
1203
+ this._canEmitConnection = true;
1204
+ this._closing = false;
1205
+ this._closed = false;
1206
+ }
1207
+ /**
1208
+ * Initiates the connection. Checks connection limits and reverse resolves client hostname. The client
1209
+ * is not allowed to send anything before init has finished otherwise 'You talk too soon' error is returned
1210
+ */
1211
+ init() {
1212
+ this._setListeners(() => {
1213
+ if (this._server.options.maxClients && this._server.connections.size > this._server.options.maxClients) {
1214
+ return this.send(421, this.name + " Too many connected clients, try again in a moment", false);
1215
+ }
1216
+ let readyTimer = setTimeout(() => this.connectionReady(), 100);
1217
+ readyTimer.unref();
1218
+ });
1219
+ }
1220
+ connectionReady(next) {
1221
+ let reverseCb = (err, hostnames) => {
1222
+ if (err) {
1223
+ this._server.logger.error(
1224
+ {
1225
+ tnx: "connection",
1226
+ cid: this.id,
1227
+ host: this.remoteAddress,
1228
+ hostname: this.clientHostname,
1229
+ err
1230
+ },
1231
+ "Reverse resolve for %s: %s",
1232
+ this.remoteAddress,
1233
+ err.message
1234
+ );
1235
+ }
1236
+ if (this._closing || this._closed) {
1237
+ return;
1238
+ }
1239
+ this.clientHostname = hostnames && hostnames.shift() || "[" + this.remoteAddress + "]";
1240
+ this._resetSession();
1241
+ let onSecureIfNeeded = (next2) => {
1242
+ if (!this.session.secure) {
1243
+ return next2();
1244
+ }
1245
+ this.session.servername = this._socket.servername;
1246
+ this._server.onSecure(this._socket, this.session, (err2) => {
1247
+ if (err2) {
1248
+ return this._onError(err2);
1249
+ }
1250
+ next2();
1251
+ });
1252
+ };
1253
+ this._server.onConnect(this.session, (err2) => {
1254
+ this._server.logger.info(
1255
+ {
1256
+ tnx: "connection",
1257
+ cid: this.id,
1258
+ host: this.remoteAddress,
1259
+ hostname: this.clientHostname
1260
+ },
1261
+ "Connection from %s",
1262
+ this.clientHostname
1263
+ );
1264
+ if (err2) {
1265
+ this.send(err2.responseCode || 554, err2.message, false);
1266
+ return this.close();
1267
+ }
1268
+ onSecureIfNeeded(() => {
1269
+ this._ready = true;
1270
+ if (!this._server.options.useXClient && !this._server.options.useXForward) {
1271
+ this.emitConnection();
1272
+ }
1273
+ this.send(
1274
+ 220,
1275
+ this.name + " " + (this._server.options.lmtp ? "LMTP" : "ESMTP") + (this._server.options.banner ? " " + this._server.options.banner : ""),
1276
+ false
1277
+ );
1278
+ if (typeof next === "function") {
1279
+ next();
1280
+ }
1281
+ });
1282
+ });
1283
+ };
1284
+ if (this._server.options.disableReverseLookup) {
1285
+ return reverseCb(null, false);
1286
+ }
1287
+ let greetingSent = false;
1288
+ let reverseTimer = setTimeout(() => {
1289
+ clearTimeout(reverseTimer);
1290
+ if (greetingSent) {
1291
+ return;
1292
+ }
1293
+ greetingSent = true;
1294
+ reverseCb(new Error("Timeout"));
1295
+ }, 1500);
1296
+ reverseTimer.unref();
1297
+ const handleResolverResult = (...args) => {
1298
+ clearTimeout(reverseTimer);
1299
+ if (greetingSent) {
1300
+ return;
1301
+ }
1302
+ greetingSent = true;
1303
+ reverseCb(...args);
1304
+ };
1305
+ try {
1306
+ if (this._server.options.resolver && typeof this._server.options.resolver.reverse === "function") {
1307
+ this._server.options.resolver.reverse(this.remoteAddress.toString(), handleResolverResult);
1308
+ } else {
1309
+ dns.reverse(this.remoteAddress.toString(), handleResolverResult);
1310
+ }
1311
+ } catch (E) {
1312
+ clearTimeout(reverseTimer);
1313
+ if (greetingSent) {
1314
+ return;
1315
+ }
1316
+ greetingSent = true;
1317
+ reverseCb(E);
1318
+ }
1319
+ }
1320
+ /**
1321
+ * Send data to socket
1322
+ *
1323
+ * @param {Number} code Response code
1324
+ * @param {String|Array} data If data is Array, send a multi-line response
1325
+ * @param {String|Boolean} context Optional context for enhanced status codes
1326
+ */
1327
+ send(code, data, context) {
1328
+ let payload;
1329
+ let enhancedCode = this._getEnhancedStatusCode(code, context);
1330
+ if (Array.isArray(data)) {
1331
+ payload = data.map((line, i, arr) => {
1332
+ let prefix = code + (i < arr.length - 1 ? "-" : " ");
1333
+ if (enhancedCode) {
1334
+ prefix += enhancedCode + " ";
1335
+ }
1336
+ return prefix + line;
1337
+ }).join("\r\n");
1338
+ } else {
1339
+ let parts = [code];
1340
+ if (enhancedCode) {
1341
+ parts.push(enhancedCode);
1342
+ }
1343
+ if (data) {
1344
+ parts.push(data);
1345
+ }
1346
+ payload = parts.join(" ");
1347
+ }
1348
+ if (code >= 400) {
1349
+ this.session.error = payload;
1350
+ }
1351
+ if (code === 334 && payload === "334") {
1352
+ payload += " ";
1353
+ }
1354
+ if (this._socket && !this._socket.destroyed && this._socket.readyState === "open") {
1355
+ this._socket.write(payload + "\r\n");
1356
+ this._server.logger.debug(
1357
+ {
1358
+ tnx: "send",
1359
+ cid: this.id,
1360
+ user: this.session.user && this.session.user.username || this.session.user
1361
+ },
1362
+ "S:",
1363
+ payload
1364
+ );
1365
+ }
1366
+ if (code === 421) {
1367
+ this.close();
1368
+ }
1369
+ }
1370
+ /**
1371
+ * Close socket
1372
+ */
1373
+ close() {
1374
+ if (!this._socket.destroyed && this._socket.writable) {
1375
+ this._socket.end();
1376
+ }
1377
+ this._server.connections.delete(this);
1378
+ this._closing = true;
1379
+ }
1380
+ // PRIVATE METHODS
1381
+ /**
1382
+ * Setup socket event handlers
1383
+ */
1384
+ _setListeners(callback) {
1385
+ this._socket.on("close", (hadError) => this._onCloseEvent(hadError));
1386
+ this._socket.on("error", (err) => this._onError(err));
1387
+ this._parser.on("error", (err) => {
1388
+ this.send(421, "Error: " + err.message);
1389
+ });
1390
+ this._socket.setTimeout(this._server.options.socketTimeout || SOCKET_TIMEOUT, () => this._onTimeout());
1391
+ this._socket.pipe(this._parser);
1392
+ if (!this.needsUpgrade) {
1393
+ return callback();
1394
+ }
1395
+ this.upgrade(() => false, callback);
1396
+ }
1397
+ _onCloseEvent(hadError) {
1398
+ this._server.logger.info(
1399
+ {
1400
+ tnx: "close",
1401
+ cid: this.id,
1402
+ host: this.remoteAddress,
1403
+ user: this.session.user && this.session.user.username || this.session.user,
1404
+ hadError
1405
+ },
1406
+ '%s received "close" event from %s' + (hadError ? " after error" : ""),
1407
+ this.id,
1408
+ this.remoteAddress
1409
+ );
1410
+ this._onClose();
1411
+ }
1412
+ /**
1413
+ * Fired when the socket is closed
1414
+ * @event
1415
+ */
1416
+ _onClose() {
1417
+ if (this._parser) {
1418
+ this._parser.isClosed = true;
1419
+ this._socket.unpipe(this._parser);
1420
+ this._parser = false;
1421
+ }
1422
+ if (this._dataStream) {
1423
+ this._dataStream.unpipe();
1424
+ this._dataStream = null;
1425
+ }
1426
+ this._server.connections.delete(this);
1427
+ if (this._closed) {
1428
+ return;
1429
+ }
1430
+ this._closed = true;
1431
+ this._closing = false;
1432
+ this._server.logger.info(
1433
+ {
1434
+ tnx: "close",
1435
+ cid: this.id,
1436
+ host: this.remoteAddress,
1437
+ user: this.session.user && this.session.user.username || this.session.user
1438
+ },
1439
+ "Connection closed to %s",
1440
+ this.clientHostname || this.remoteAddress
1441
+ );
1442
+ setImmediate(() => this._server.onClose(this.session));
1443
+ }
1444
+ /**
1445
+ * Fired when an error occurs with the socket
1446
+ *
1447
+ * @event
1448
+ * @param {Error} err Error object
1449
+ */
1450
+ _onError(err) {
1451
+ err.remoteAddress = this.remoteAddress;
1452
+ this._server.logger.error(
1453
+ {
1454
+ err,
1455
+ tnx: "error",
1456
+ user: this.session.user && this.session.user.username || this.session.user
1457
+ },
1458
+ "%s %s %s",
1459
+ this.id,
1460
+ this.remoteAddress,
1461
+ err.message
1462
+ );
1463
+ if ((err.code === "ECONNRESET" || err.code === "EPIPE") && (!this.session.envelope || !this.session.envelope.mailFrom)) {
1464
+ this.close();
1465
+ return;
1466
+ }
1467
+ this.emit("error", err);
1468
+ }
1469
+ /**
1470
+ * Fired when socket timeouts. Closes connection
1471
+ *
1472
+ * @event
1473
+ */
1474
+ _onTimeout() {
1475
+ this.send(421, "Timeout - closing connection");
1476
+ }
1477
+ /**
1478
+ * Checks if a selected command is available and invokes it
1479
+ *
1480
+ * @param {Buffer} command Single line of data from the client
1481
+ * @param {Function} callback Callback to run once the command is processed
1482
+ */
1483
+ _onCommand(command, callback) {
1484
+ let commandName = (command || "").toString().split(" ").shift().toUpperCase();
1485
+ this._server.logger.debug(
1486
+ {
1487
+ tnx: "command",
1488
+ cid: this.id,
1489
+ command: commandName,
1490
+ user: this.session.user && this.session.user.username || this.session.user
1491
+ },
1492
+ "C:",
1493
+ (command || "").toString()
1494
+ );
1495
+ let handler;
1496
+ callback = callback || (() => false);
1497
+ if (this._server._closeTimeout) {
1498
+ return this.send(421, "Server shutting down", commandName);
1499
+ }
1500
+ if (!this._ready) {
1501
+ return this.send(421, this.name + " You talk too soon", commandName);
1502
+ }
1503
+ if (/^(OPTIONS|GET|HEAD|POST|PUT|DELETE|TRACE|CONNECT) \/.* HTTP\/\d\.\d$/i.test(command)) {
1504
+ return this.send(421, "HTTP requests not allowed", commandName);
1505
+ }
1506
+ if (this._upgrading) {
1507
+ return callback();
1508
+ }
1509
+ if (this._nextHandler) {
1510
+ handler = this._nextHandler;
1511
+ this._nextHandler = false;
1512
+ } else {
1513
+ switch (commandName) {
1514
+ case "HELO":
1515
+ case "EHLO":
1516
+ case "LHLO":
1517
+ this.openingCommand = commandName;
1518
+ break;
1519
+ }
1520
+ if (this._server.options.lmtp) {
1521
+ switch (commandName) {
1522
+ case "HELO":
1523
+ case "EHLO":
1524
+ this.send(500, "Error: " + commandName + " not allowed in LMTP server", false);
1525
+ return setImmediate(callback);
1526
+ case "LHLO":
1527
+ commandName = "EHLO";
1528
+ break;
1529
+ }
1530
+ }
1531
+ if (this._isSupported(commandName)) {
1532
+ handler = this["handler_" + commandName];
1533
+ }
1534
+ }
1535
+ if (!handler) {
1536
+ this._unrecognizedCommands++;
1537
+ if (this._unrecognizedCommands >= 10) {
1538
+ return this.send(421, "Error: too many unrecognized commands", commandName);
1539
+ }
1540
+ this.send(500, "Error: command not recognized", commandName);
1541
+ return setImmediate(callback);
1542
+ }
1543
+ if (!this.session.user && this._isSupported("AUTH") && !this._server.options.authOptional && commandName !== "AUTH" && this._maxAllowedUnauthenticatedCommands !== false) {
1544
+ this._unauthenticatedCommands++;
1545
+ if (this._unauthenticatedCommands >= this._maxAllowedUnauthenticatedCommands) {
1546
+ return this.send(421, "Error: too many unauthenticated commands", commandName);
1547
+ }
1548
+ }
1549
+ if (!this.hostNameAppearsAs && commandName && ["MAIL", "RCPT", "DATA", "AUTH"].includes(commandName)) {
1550
+ this.send(503, "Error: send " + (this._server.options.lmtp ? "LHLO" : "HELO/EHLO") + " first");
1551
+ return setImmediate(callback);
1552
+ }
1553
+ if (!this.session.user && this._isSupported("AUTH") && ["MAIL", "RCPT", "DATA"].includes(commandName) && !this._server.options.authOptional) {
1554
+ this.send(
1555
+ 530,
1556
+ typeof this._server.options.authRequiredMessage === "string" ? this._server.options.authRequiredMessage : "Error: authentication Required"
1557
+ );
1558
+ return setImmediate(callback);
1559
+ }
1560
+ handler.call(this, command, callback);
1561
+ }
1562
+ /**
1563
+ * Checks that a command is available and is not listed in the disabled commands array
1564
+ *
1565
+ * @param {String} command Command name
1566
+ * @returns {Boolean} Returns true if the command can be used
1567
+ */
1568
+ _isSupported(command) {
1569
+ command = (command || "").toString().trim().toUpperCase();
1570
+ return !this._server.options.disabledCommands.includes(command) && typeof this["handler_" + command] === "function";
1571
+ }
1572
+ /**
1573
+ * Determines if enhanced status codes should be used
1574
+ * @returns {Boolean} True if enhanced status codes should be included in responses
1575
+ */
1576
+ _useEnhancedStatusCodes() {
1577
+ return !this._server.options.hideENHANCEDSTATUSCODES;
1578
+ }
1579
+ /**
1580
+ * Gets the appropriate enhanced status code for a given SMTP response code and context
1581
+ * @param {Number} code SMTP response code
1582
+ * @param {String|Boolean} context Optional context for more specific status codes
1583
+ * @returns {String} Enhanced status code or empty string if not applicable
1584
+ */
1585
+ _getEnhancedStatusCode(code, context) {
1586
+ if (context === false || !this._useEnhancedStatusCodes()) {
1587
+ return "";
1588
+ }
1589
+ if (code >= 300 && code < 400) {
1590
+ return "";
1591
+ }
1592
+ if (context && SKIPPED_COMMANDS_FOR_ENHANCED_STATUS_CODES.has(context)) {
1593
+ return "";
1594
+ }
1595
+ if (context && CONTEXTUAL_STATUS_CODES[context]) {
1596
+ return CONTEXTUAL_STATUS_CODES[context];
1597
+ }
1598
+ if (ENHANCED_STATUS_CODES[code]) {
1599
+ return ENHANCED_STATUS_CODES[code];
1600
+ }
1601
+ if (code >= 200 && code < 300) {
1602
+ return "2.0.0";
1603
+ }
1604
+ if (code >= 400 && code < 500) {
1605
+ return "4.0.0";
1606
+ }
1607
+ if (code >= 500) {
1608
+ return "5.0.0";
1609
+ }
1610
+ return "";
1611
+ }
1612
+ /**
1613
+ * Parses commands like MAIL FROM and RCPT TO. Returns an object with the address and optional arguments.
1614
+ *
1615
+ * @param {[type]} name Address type, eg 'mail from' or 'rcpt to'
1616
+ * @param {[type]} command Data payload to parse
1617
+ * @returns {Object|Boolean} Parsed address in the form of {address:, args: {}} or false if parsing failed
1618
+ */
1619
+ _parseAddressCommand(name, command) {
1620
+ command = (command || "").toString();
1621
+ name = (name || "").toString().trim().toUpperCase();
1622
+ let parts = command.split(":");
1623
+ command = parts.shift().trim().toUpperCase();
1624
+ parts = parts.join(":").trim().split(/\s+/);
1625
+ let address = parts.shift();
1626
+ let args = false;
1627
+ let invalid = false;
1628
+ if (name !== command) {
1629
+ return false;
1630
+ }
1631
+ if (!/^<[^<>]*>$/.test(address)) {
1632
+ invalid = true;
1633
+ } else {
1634
+ address = address.substr(1, address.length - 2);
1635
+ }
1636
+ parts.forEach((part) => {
1637
+ part = part.split("=");
1638
+ let key = part.shift().toUpperCase();
1639
+ let value = part.join("=") || true;
1640
+ if (!key || key.trim() === "") {
1641
+ return;
1642
+ }
1643
+ if (typeof value === "string") {
1644
+ value = value.replace(/\+([0-9A-F]{2})/g, (match, hex) => unescape("%" + hex));
1645
+ }
1646
+ if (!args) {
1647
+ args = {};
1648
+ }
1649
+ args[key] = value;
1650
+ });
1651
+ if (address) {
1652
+ address = address.split("@");
1653
+ if (address.length !== 2 || !address[0] || !address[1]) {
1654
+ invalid = true;
1655
+ } else {
1656
+ let localPart = address[0];
1657
+ let domain = address[1];
1658
+ if (localPart.length + 1 + domain.length > 253) {
1659
+ invalid = true;
1660
+ } else {
1661
+ if (localPart.startsWith(".") || localPart.endsWith(".") || localPart.includes("..")) {
1662
+ invalid = true;
1663
+ }
1664
+ let isIPLiteral = !invalid && domain.startsWith("[") && domain.endsWith("]");
1665
+ if (isIPLiteral) {
1666
+ let ipContent = domain.slice(1, -1);
1667
+ let isValidIP = false;
1668
+ if (ipContent.toUpperCase().startsWith("IPV6:")) {
1669
+ let ipv6Addr = ipContent.slice(5);
1670
+ if (net.isIPv6(ipv6Addr)) {
1671
+ isValidIP = true;
1672
+ try {
1673
+ ipv6Addr = ipv6normalize(ipv6Addr);
1674
+ domain = "[IPv6:" + ipv6Addr + "]";
1675
+ } catch {
1676
+ }
1677
+ }
1678
+ } else {
1679
+ if (net.isIPv4(ipContent)) {
1680
+ isValidIP = true;
1681
+ }
1682
+ }
1683
+ if (!isValidIP) {
1684
+ invalid = true;
1685
+ }
1686
+ if (!invalid) {
1687
+ address = [localPart, "@", domain].join("");
1688
+ }
1689
+ } else {
1690
+ if (!invalid && (domain.startsWith(".") || domain.endsWith(".") || domain.includes("..") || domain.includes(".-") || domain.includes("-.") || !/^[a-zA-Z0-9\u0080-\uFFFF.-]+$/.test(domain))) {
1691
+ invalid = true;
1692
+ }
1693
+ if (!invalid) {
1694
+ try {
1695
+ address = [localPart, "@", punycode2.toUnicode(domain)].join("");
1696
+ } catch (E) {
1697
+ this._server.logger.error(
1698
+ {
1699
+ tnx: "punycode",
1700
+ cid: this.id,
1701
+ user: this.session.user && this.session.user.username || this.session.user
1702
+ },
1703
+ 'Failed to process punycode domain "%s". error=%s',
1704
+ domain,
1705
+ E.message
1706
+ );
1707
+ invalid = true;
1708
+ }
1709
+ }
1710
+ }
1711
+ }
1712
+ }
1713
+ }
1714
+ return invalid ? false : {
1715
+ address,
1716
+ args
1717
+ };
1718
+ }
1719
+ /**
1720
+ * Resets or sets up a new session. We reuse existing session object to keep
1721
+ * application specific data.
1722
+ */
1723
+ _resetSession() {
1724
+ let session = this.session;
1725
+ session.localAddress = this.localAddress;
1726
+ session.localPort = this.localPort;
1727
+ session.remoteAddress = this.remoteAddress;
1728
+ session.remotePort = this.remotePort;
1729
+ session.clientHostname = this.clientHostname;
1730
+ session.openingCommand = this.openingCommand;
1731
+ session.hostNameAppearsAs = this.hostNameAppearsAs;
1732
+ session.xClient = this._xClient;
1733
+ session.xForward = this._xForward;
1734
+ session.transmissionType = this._transmissionType();
1735
+ session.tlsOptions = this.tlsOptions;
1736
+ session.envelope = {
1737
+ mailFrom: false,
1738
+ rcptTo: [],
1739
+ /** @property {boolean} requireTLS - RFC 8689: Indicates client requires TLS for entire delivery chain */
1740
+ requireTLS: false,
1741
+ /** @property {string} bodyType - RFC 6152: Message body encoding type (7bit or 8bitmime) */
1742
+ bodyType: "7bit",
1743
+ /** @property {boolean} smtpUtf8 - RFC 6531: Indicates UTF-8 support is requested */
1744
+ smtpUtf8: false
1745
+ };
1746
+ if (!this._server.options.hideDSN)
1747
+ session.envelope.dsn = {
1748
+ ret: null,
1749
+ // RET parameter from MAIL FROM (FULL or HDRS)
1750
+ envid: null
1751
+ // ENVID parameter from MAIL FROM
1752
+ };
1753
+ session.transaction = this._transactionCounter + 1;
1754
+ }
1755
+ /**
1756
+ * Returns current transmission type
1757
+ *
1758
+ * @return {String} Transmission type
1759
+ */
1760
+ _transmissionType() {
1761
+ let type = this._server.options.lmtp ? "LMTP" : "SMTP";
1762
+ if (this.openingCommand === "EHLO") {
1763
+ type = "E" + type;
1764
+ }
1765
+ if (this.secure) {
1766
+ type += "S";
1767
+ }
1768
+ if (this.session.user) {
1769
+ type += "A";
1770
+ }
1771
+ return type;
1772
+ }
1773
+ emitConnection() {
1774
+ if (!this._canEmitConnection) {
1775
+ return;
1776
+ }
1777
+ this._canEmitConnection = false;
1778
+ this.emit("connect", {
1779
+ id: this.id,
1780
+ localAddress: this.localAddress,
1781
+ localPort: this.localPort,
1782
+ remoteAddress: this.remoteAddress,
1783
+ remotePort: this.remotePort,
1784
+ hostNameAppearsAs: this.hostNameAppearsAs,
1785
+ clientHostname: this.clientHostname
1786
+ });
1787
+ }
1788
+ // COMMAND HANDLERS
1789
+ /**
1790
+ * Processes EHLO. Requires valid hostname as the single argument.
1791
+ */
1792
+ handler_EHLO(command, callback) {
1793
+ let parts = command.toString().trim().split(/\s+/);
1794
+ let hostname = parts[1] || "";
1795
+ if (parts.length !== 2) {
1796
+ this.send(501, "Error: syntax: " + (this._server.options.lmtp ? "LHLO" : "EHLO") + " hostname", false);
1797
+ return callback();
1798
+ }
1799
+ this.hostNameAppearsAs = hostname.toLowerCase();
1800
+ let features = ["PIPELINING", "8BITMIME", "SMTPUTF8", "ENHANCEDSTATUSCODES", "DSN"].filter((feature) => !this._server.options["hide" + feature]);
1801
+ if (this._server.options.authMethods.length && this._isSupported("AUTH") && !this.session.user) {
1802
+ features.push(["AUTH"].concat(this._server.options.authMethods).join(" "));
1803
+ }
1804
+ if (!this.secure && this._isSupported("STARTTLS") && !this._server.options.hideSTARTTLS) {
1805
+ features.push("STARTTLS");
1806
+ }
1807
+ if (this.secure && !this._server.options.hideREQUIRETLS) {
1808
+ features.push("REQUIRETLS");
1809
+ }
1810
+ if (this._server.options.size) {
1811
+ features.push("SIZE" + (this._server.options.hideSize ? "" : " " + this._server.options.size));
1812
+ }
1813
+ if (!this._xClient.has("ADDR") && this._server.options.useXClient && this._isSupported("XCLIENT")) {
1814
+ features.push("XCLIENT NAME ADDR PORT PROTO HELO LOGIN");
1815
+ }
1816
+ if (!this._xClient.has("ADDR") && this._server.options.useXForward && this._isSupported("XFORWARD")) {
1817
+ features.push("XFORWARD NAME ADDR PORT PROTO HELO IDENT SOURCE");
1818
+ }
1819
+ this._resetSession();
1820
+ let heloResponse = this._server.options.heloResponse || "%s Nice to meet you, %s";
1821
+ let replacements = [this.name, this.clientHostname];
1822
+ let replacementIndex = 0;
1823
+ let formattedResponse = heloResponse.replace(/%s/g, () => replacements[replacementIndex++] || "");
1824
+ this.send(250, [formattedResponse].concat(features || []), false);
1825
+ callback();
1826
+ }
1827
+ /**
1828
+ * Processes HELO. Requires valid hostname as the single argument.
1829
+ */
1830
+ handler_HELO(command, callback) {
1831
+ let parts = command.toString().trim().split(/\s+/);
1832
+ let hostname = parts[1] || "";
1833
+ if (parts.length !== 2) {
1834
+ this.send(501, "Error: Syntax: HELO hostname", false);
1835
+ return callback();
1836
+ }
1837
+ this.hostNameAppearsAs = hostname.toLowerCase();
1838
+ this._resetSession();
1839
+ let heloResponse = this._server.options.heloResponse || "%s Nice to meet you, %s";
1840
+ let replacements = [this.name, this.clientHostname];
1841
+ let replacementIndex = 0;
1842
+ let formattedResponse = heloResponse.replace(/%s/g, () => replacements[replacementIndex++] || "");
1843
+ this.send(250, formattedResponse, false);
1844
+ callback();
1845
+ }
1846
+ /**
1847
+ * Processes QUIT. Closes the connection
1848
+ */
1849
+ handler_QUIT(command, callback) {
1850
+ this.send(221, "Bye");
1851
+ this.close();
1852
+ callback();
1853
+ }
1854
+ /**
1855
+ * Processes NOOP. Does nothing but keeps the connection alive
1856
+ */
1857
+ handler_NOOP(command, callback) {
1858
+ this.send(250, "OK");
1859
+ callback();
1860
+ }
1861
+ /**
1862
+ * Processes RSET. Resets user and session info
1863
+ */
1864
+ handler_RSET(command, callback) {
1865
+ this._resetSession();
1866
+ this.send(250, "Flushed");
1867
+ callback();
1868
+ }
1869
+ /**
1870
+ * Processes HELP. Responds with url to RFC
1871
+ */
1872
+ handler_HELP(command, callback) {
1873
+ this.send(214, "See https://tools.ietf.org/html/rfc5321 for details");
1874
+ callback();
1875
+ }
1876
+ /**
1877
+ * Processes VRFY. Does not verify anything
1878
+ */
1879
+ handler_VRFY(command, callback) {
1880
+ this.send(252, "Try to send something. No promises though");
1881
+ callback();
1882
+ }
1883
+ /**
1884
+ * Overrides connection info
1885
+ * http://www.postfix.org/XCLIENT_README.html
1886
+ *
1887
+ * TODO: add unit tests
1888
+ */
1889
+ handler_XCLIENT(command, callback) {
1890
+ if (this._xClient.has("ADDR") || !this._server.options.useXClient) {
1891
+ this.send(550, "Error: Not allowed");
1892
+ return callback();
1893
+ }
1894
+ if (this.session.envelope.mailFrom) {
1895
+ this.send(503, "Error: Mail transaction in progress");
1896
+ return callback();
1897
+ }
1898
+ let allowedKeys = ["NAME", "ADDR", "PORT", "PROTO", "HELO", "LOGIN"];
1899
+ let parts = command.toString().trim().split(/\s+/);
1900
+ let key, value;
1901
+ let data = /* @__PURE__ */ new Map();
1902
+ parts.shift();
1903
+ if (!parts.length) {
1904
+ this.send(501, "Error: Bad command parameter syntax");
1905
+ return callback();
1906
+ }
1907
+ let loginValue = false;
1908
+ for (let i = 0, len = parts.length; i < len; i++) {
1909
+ value = parts[i].split("=");
1910
+ key = value.shift();
1911
+ if (value.length !== 1 || !allowedKeys.includes(key.toUpperCase())) {
1912
+ this.send(501, "Error: Bad command parameter syntax");
1913
+ return callback();
1914
+ }
1915
+ key = key.toUpperCase();
1916
+ value = (value[0] || "").replace(/\+([0-9A-F]{2})/g, (match, hex) => unescape("%" + hex));
1917
+ if (["[UNAVAILABLE]", "[TEMPUNAVAIL]"].includes(value.toUpperCase())) {
1918
+ value = false;
1919
+ }
1920
+ if (data.has(key)) {
1921
+ continue;
1922
+ }
1923
+ data.set(key, value);
1924
+ switch (key) {
1925
+ // handled outside the switch
1926
+ case "LOGIN":
1927
+ loginValue = value;
1928
+ break;
1929
+ case "ADDR":
1930
+ if (value) {
1931
+ value = value.replace(/^IPV6:/i, "");
1932
+ if (!net.isIP(value)) {
1933
+ this.send(501, "Error: Bad command parameter syntax. Invalid address");
1934
+ return callback();
1935
+ }
1936
+ if (net.isIPv6(value)) {
1937
+ value = ipv6normalize(value);
1938
+ }
1939
+ this._server.logger.info(
1940
+ {
1941
+ tnx: "xclient",
1942
+ cid: this.id,
1943
+ xclientKey: "ADDR",
1944
+ xclient: value,
1945
+ user: this.session.user && this.session.user.username || this.session.user
1946
+ },
1947
+ "XCLIENT from %s through %s",
1948
+ value,
1949
+ this.remoteAddress
1950
+ );
1951
+ if (!this._xClient.has("ADDR:DEFAULT")) {
1952
+ this._xClient.set("ADDR:DEFAULT", this.remoteAddress);
1953
+ }
1954
+ this.remoteAddress = value;
1955
+ this.hostNameAppearsAs = false;
1956
+ }
1957
+ break;
1958
+ case "NAME":
1959
+ value = value || "";
1960
+ this._server.logger.info(
1961
+ {
1962
+ tnx: "xclient",
1963
+ cid: this.id,
1964
+ xclientKey: "NAME",
1965
+ xclient: value,
1966
+ user: this.session.user && this.session.user.username || this.session.user
1967
+ },
1968
+ 'XCLIENT hostname resolved as "%s"',
1969
+ value
1970
+ );
1971
+ if (!this._xClient.has("NAME:DEFAULT")) {
1972
+ this._xClient.set("NAME:DEFAULT", this.clientHostname || "");
1973
+ }
1974
+ this.clientHostname = value.toLowerCase();
1975
+ break;
1976
+ case "PORT":
1977
+ value = Number(value) || "";
1978
+ this._server.logger.info(
1979
+ {
1980
+ tnx: "xclient",
1981
+ cid: this.id,
1982
+ xclientKey: "PORT",
1983
+ xclient: value,
1984
+ user: this.session.user && this.session.user.username || this.session.user
1985
+ },
1986
+ 'XCLIENT remote port resolved as "%s"',
1987
+ value
1988
+ );
1989
+ if (!this._xClient.has("PORT:DEFAULT")) {
1990
+ this._xClient.set("PORT:DEFAULT", this.remotePort || "");
1991
+ }
1992
+ this.remotePort = value;
1993
+ break;
1994
+ default:
1995
+ }
1996
+ this._xClient.set(key, value);
1997
+ }
1998
+ let checkLogin = (done) => {
1999
+ if (typeof loginValue !== "string") {
2000
+ return done();
2001
+ }
2002
+ if (!loginValue) {
2003
+ this._server.logger.info(
2004
+ {
2005
+ tnx: "deauth",
2006
+ cid: this.id,
2007
+ user: this.session.user && this.session.user.username || this.session.user
2008
+ },
2009
+ "User deauthenticated using %s",
2010
+ "XCLIENT"
2011
+ );
2012
+ this.session.user = false;
2013
+ return done();
2014
+ }
2015
+ let method = "SASL_XCLIENT";
2016
+ sasl[method].call(this, [loginValue], (err) => {
2017
+ if (err) {
2018
+ this.send(550, err.message);
2019
+ this.close();
2020
+ return;
2021
+ }
2022
+ done();
2023
+ });
2024
+ };
2025
+ if (this.remoteAddress && !this.clientHostname) {
2026
+ this.clientHostname = "[" + this.remoteAddress + "]";
2027
+ }
2028
+ if (data.has("ADDR")) {
2029
+ this.emitConnection();
2030
+ }
2031
+ checkLogin(() => {
2032
+ this.send(
2033
+ 220,
2034
+ this.name + " " + (this._server.options.lmtp ? "LMTP" : "ESMTP") + (this._server.options.banner ? " " + this._server.options.banner : "")
2035
+ );
2036
+ callback();
2037
+ });
2038
+ }
2039
+ /**
2040
+ * Processes XFORWARD data
2041
+ * http://www.postfix.org/XFORWARD_README.html
2042
+ *
2043
+ * TODO: add unit tests
2044
+ */
2045
+ handler_XFORWARD(command, callback) {
2046
+ if (!this._server.options.useXForward) {
2047
+ this.send(550, "Error: Not allowed");
2048
+ return callback();
2049
+ }
2050
+ if (this.session.envelope.mailFrom) {
2051
+ this.send(503, "Error: Mail transaction in progress");
2052
+ return callback();
2053
+ }
2054
+ let allowedKeys = ["NAME", "ADDR", "PORT", "PROTO", "HELO", "IDENT", "SOURCE"];
2055
+ let parts = command.toString().trim().split(/\s+/);
2056
+ let key, value;
2057
+ let data = /* @__PURE__ */ new Map();
2058
+ let hasAddr = false;
2059
+ parts.shift();
2060
+ if (!parts.length) {
2061
+ this.send(501, "Error: Bad command parameter syntax");
2062
+ return callback();
2063
+ }
2064
+ for (let i = 0, len = parts.length; i < len; i++) {
2065
+ value = parts[i].split("=");
2066
+ key = value.shift();
2067
+ if (value.length !== 1 || !allowedKeys.includes(key.toUpperCase())) {
2068
+ this.send(501, "Error: Bad command parameter syntax");
2069
+ return callback();
2070
+ }
2071
+ key = key.toUpperCase();
2072
+ if (data.has(key)) {
2073
+ continue;
2074
+ }
2075
+ value = (value[0] || "").replace(/\+([0-9A-F]{2})/g, (match, hex) => unescape("%" + hex));
2076
+ if (value.toUpperCase() === "[UNAVAILABLE]") {
2077
+ value = false;
2078
+ }
2079
+ data.set(key, value);
2080
+ switch (key) {
2081
+ case "ADDR":
2082
+ if (value) {
2083
+ value = value.replace(/^IPV6:/i, "");
2084
+ if (!net.isIP(value)) {
2085
+ this.send(501, "Error: Bad command parameter syntax. Invalid address");
2086
+ return callback();
2087
+ }
2088
+ if (net.isIPv6(value)) {
2089
+ value = ipv6normalize(value);
2090
+ }
2091
+ this._server.logger.info(
2092
+ {
2093
+ tnx: "xforward",
2094
+ cid: this.id,
2095
+ xforwardKey: "ADDR",
2096
+ xforward: value,
2097
+ user: this.session.user && this.session.user.username || this.session.user
2098
+ },
2099
+ "XFORWARD from %s through %s",
2100
+ value,
2101
+ this.remoteAddress
2102
+ );
2103
+ if (!this._xClient.has("ADDR:DEFAULT")) {
2104
+ this._xClient.set("ADDR:DEFAULT", this.remoteAddress);
2105
+ }
2106
+ hasAddr = true;
2107
+ this.remoteAddress = value;
2108
+ }
2109
+ break;
2110
+ case "NAME":
2111
+ value = value || "";
2112
+ this._server.logger.info(
2113
+ {
2114
+ tnx: "xforward",
2115
+ cid: this.id,
2116
+ xforwardKey: "NAME",
2117
+ xforward: value,
2118
+ user: this.session.user && this.session.user.username || this.session.user
2119
+ },
2120
+ 'XFORWARD hostname resolved as "%s"',
2121
+ value
2122
+ );
2123
+ this.clientHostname = value.toLowerCase();
2124
+ break;
2125
+ case "PORT":
2126
+ value = Number(value) || 0;
2127
+ this._server.logger.info(
2128
+ {
2129
+ tnx: "xforward",
2130
+ cid: this.id,
2131
+ xforwardKey: "PORT",
2132
+ xforward: value,
2133
+ user: this.session.user && this.session.user.username || this.session.user
2134
+ },
2135
+ 'XFORWARD port resolved as "%s"',
2136
+ value
2137
+ );
2138
+ this.remotePort = value;
2139
+ break;
2140
+ case "HELO":
2141
+ value = (value || "").toString().toLowerCase();
2142
+ this._server.logger.info(
2143
+ {
2144
+ tnx: "xforward",
2145
+ cid: this.id,
2146
+ xforwardKey: "HELO",
2147
+ xforward: value,
2148
+ user: this.session.user && this.session.user.username || this.session.user
2149
+ },
2150
+ 'XFORWARD HELO name resolved as "%s"',
2151
+ value
2152
+ );
2153
+ this.hostNameAppearsAs = value;
2154
+ break;
2155
+ default:
2156
+ }
2157
+ this._xForward.set(key, value);
2158
+ }
2159
+ if (hasAddr) {
2160
+ this._canEmitConnection = true;
2161
+ this.emitConnection();
2162
+ }
2163
+ this.send(250, "OK");
2164
+ callback();
2165
+ }
2166
+ /**
2167
+ * Upgrades connection to TLS if possible
2168
+ */
2169
+ handler_STARTTLS(command, callback) {
2170
+ if (this.secure) {
2171
+ this.send(503, "Error: TLS already active");
2172
+ return callback();
2173
+ }
2174
+ this.send(220, "Ready to start TLS");
2175
+ this.upgrade(callback);
2176
+ }
2177
+ /**
2178
+ * Check if selected authentication is available and delegate auth data to SASL
2179
+ */
2180
+ handler_AUTH(command, callback) {
2181
+ let args = command.toString().trim().split(/\s+/);
2182
+ let method;
2183
+ let handler;
2184
+ args.shift();
2185
+ method = (args.shift() || "").toString().toUpperCase();
2186
+ handler = sasl["SASL_" + method];
2187
+ handler = handler ? handler.bind(this) : handler;
2188
+ if (!this.secure && this._isSupported("STARTTLS") && !this._server.options.hideSTARTTLS && !this._server.options.allowInsecureAuth) {
2189
+ this.send(538, "Error: Must issue a STARTTLS command first");
2190
+ return callback();
2191
+ }
2192
+ if (this.session.user) {
2193
+ this.send(503, "Error: No identity changes permitted");
2194
+ return callback();
2195
+ }
2196
+ if (!this._server.options.authMethods.includes(method) || typeof handler !== "function") {
2197
+ this.send(504, "Error: Unrecognized authentication type");
2198
+ return callback();
2199
+ }
2200
+ handler(args, callback);
2201
+ }
2202
+ /**
2203
+ * Validates MAIL FROM parameters
2204
+ * @param {Object} parsed - Parsed address command with args
2205
+ * @returns {Object|null} Returns error object {code, message, enhancedCode} if validation fails, null if successful
2206
+ */
2207
+ _validateMailParams(parsed) {
2208
+ if (parsed.args.BODY) {
2209
+ const bodyType = parsed.args.BODY.toLowerCase();
2210
+ if (bodyType !== "7bit" && bodyType !== "8bitmime") {
2211
+ return {
2212
+ code: 501,
2213
+ message: "Invalid BODY parameter value. Must be 7BIT or 8BITMIME",
2214
+ enhancedCode: null
2215
+ };
2216
+ }
2217
+ }
2218
+ if (parsed.args.SMTPUTF8 !== void 0) {
2219
+ if (parsed.args.SMTPUTF8 !== true) {
2220
+ return {
2221
+ code: 501,
2222
+ message: "Invalid SMTPUTF8 parameter. This flag does not accept a value",
2223
+ enhancedCode: null
2224
+ };
2225
+ }
2226
+ }
2227
+ if (parsed.args.REQUIRETLS !== void 0) {
2228
+ if (!this.secure) {
2229
+ return {
2230
+ code: 530,
2231
+ message: "REQUIRETLS not permitted on non-TLS connections",
2232
+ enhancedCode: null
2233
+ };
2234
+ }
2235
+ if (parsed.args.REQUIRETLS !== true) {
2236
+ return {
2237
+ code: 501,
2238
+ message: "Invalid REQUIRETLS parameter. This flag does not accept a value",
2239
+ enhancedCode: null
2240
+ };
2241
+ }
2242
+ }
2243
+ if (!this._server.options.hideDSN) {
2244
+ if (parsed.args.RET) {
2245
+ const ret = parsed.args.RET.toUpperCase();
2246
+ if (ret !== "FULL" && ret !== "HDRS") {
2247
+ return {
2248
+ code: 501,
2249
+ message: "Invalid RET parameter value. Must be FULL or HDRS",
2250
+ enhancedCode: null
2251
+ };
2252
+ }
2253
+ }
2254
+ }
2255
+ return null;
2256
+ }
2257
+ /**
2258
+ * Applies validated MAIL FROM parameters to session envelope
2259
+ * @param {Object} parsed - Parsed address command with args
2260
+ */
2261
+ _applyMailParams(parsed) {
2262
+ if (parsed.args.BODY) {
2263
+ this.session.envelope.bodyType = parsed.args.BODY.toLowerCase();
2264
+ }
2265
+ if (parsed.args.SMTPUTF8 === true) {
2266
+ this.session.envelope.smtpUtf8 = true;
2267
+ }
2268
+ if (parsed.args.REQUIRETLS === true) {
2269
+ this.session.envelope.requireTLS = true;
2270
+ }
2271
+ if (!this._server.options.hideDSN) {
2272
+ if (parsed.args.RET) {
2273
+ this.session.envelope.dsn.ret = parsed.args.RET.toUpperCase();
2274
+ }
2275
+ if (parsed.args.ENVID) {
2276
+ this.session.envelope.dsn.envid = parsed.args.ENVID;
2277
+ }
2278
+ }
2279
+ }
2280
+ /**
2281
+ * Processes MAIL FROM command, parses address and extra arguments
2282
+ */
2283
+ handler_MAIL(command, callback) {
2284
+ let parsed = this._parseAddressCommand("mail from", command);
2285
+ this.emitConnection();
2286
+ if (!parsed) {
2287
+ this.send(501, "Error: Bad sender address syntax", "MAILBOX_SYNTAX_ERROR");
2288
+ return callback();
2289
+ }
2290
+ if (this.session.envelope.mailFrom) {
2291
+ this.send(503, "Error: nested MAIL command");
2292
+ return callback();
2293
+ }
2294
+ if (!this._server.options.hideSize && this._server.options.size && parsed.args.SIZE && Number(parsed.args.SIZE) > this._server.options.size) {
2295
+ this.send(552, "Error: message exceeds fixed maximum message size " + this._server.options.size, "SYSTEM_FULL");
2296
+ return callback();
2297
+ }
2298
+ const validationError = this._validateMailParams(parsed);
2299
+ if (validationError) {
2300
+ this.send(validationError.code, validationError.message, validationError.enhancedCode);
2301
+ return callback();
2302
+ }
2303
+ this._applyMailParams(parsed);
2304
+ this._server.onMailFrom(parsed, this.session, (err) => {
2305
+ if (err) {
2306
+ this.send(err.responseCode || 550, err.message);
2307
+ return callback();
2308
+ }
2309
+ this.session.envelope.mailFrom = parsed;
2310
+ this.send(250, "Accepted", "MAIL_FROM_OK");
2311
+ callback();
2312
+ });
2313
+ }
2314
+ /**
2315
+ * Processes RCPT TO command, parses address and extra arguments
2316
+ */
2317
+ handler_RCPT(command, callback) {
2318
+ let parsed = this._parseAddressCommand("rcpt to", command);
2319
+ if (!parsed || !parsed.address) {
2320
+ this.send(501, "Error: Bad recipient address syntax", "MAILBOX_SYNTAX_ERROR");
2321
+ return callback();
2322
+ }
2323
+ if (!this.session.envelope.mailFrom) {
2324
+ this.send(503, "Error: need MAIL command");
2325
+ return callback();
2326
+ }
2327
+ if (!this._server.options.hideDSN) {
2328
+ if (parsed.args.NOTIFY) {
2329
+ const notify = parsed.args.NOTIFY.toUpperCase();
2330
+ const validNotifyValues = ["NEVER", "SUCCESS", "FAILURE", "DELAY"];
2331
+ const notifyValues = notify.split(",");
2332
+ for (let value of notifyValues) {
2333
+ if (!validNotifyValues.includes(value)) {
2334
+ this.send(501, "Error: NOTIFY parameter must be NEVER, SUCCESS, FAILURE, or DELAY");
2335
+ return callback();
2336
+ }
2337
+ }
2338
+ if (notifyValues.includes("NEVER") && notifyValues.length > 1) {
2339
+ this.send(501, "Error: NOTIFY=NEVER cannot be combined with other values");
2340
+ return callback();
2341
+ }
2342
+ parsed.dsn = parsed.dsn || {};
2343
+ parsed.dsn.notify = notifyValues;
2344
+ }
2345
+ if (parsed.args.ORCPT) {
2346
+ parsed.dsn = parsed.dsn || {};
2347
+ parsed.dsn.orcpt = parsed.args.ORCPT;
2348
+ }
2349
+ }
2350
+ this._server.onRcptTo(parsed, this.session, (err) => {
2351
+ if (err) {
2352
+ this.send(err.responseCode || 550, err.message);
2353
+ return callback();
2354
+ }
2355
+ for (let i = 0, len = this.session.envelope.rcptTo.length; i < len; i++) {
2356
+ if (this.session.envelope.rcptTo[i].address.toLowerCase() === parsed.address.toLowerCase()) {
2357
+ this.session.envelope.rcptTo[i] = parsed;
2358
+ parsed = false;
2359
+ break;
2360
+ }
2361
+ }
2362
+ if (parsed) {
2363
+ this.session.envelope.rcptTo.push(parsed);
2364
+ }
2365
+ this.send(250, "Accepted", "RCPT_TO_OK");
2366
+ callback();
2367
+ });
2368
+ }
2369
+ /**
2370
+ * Processes DATA by forwarding incoming stream to the onData handler
2371
+ */
2372
+ handler_DATA(command, callback) {
2373
+ if (!this.session.envelope.rcptTo.length) {
2374
+ this.send(503, "Error: need RCPT command");
2375
+ return callback();
2376
+ }
2377
+ if (!this._parser) {
2378
+ return callback();
2379
+ }
2380
+ this._dataStream = this._parser.startDataMode(this._server.options.size);
2381
+ let close = (err, message) => {
2382
+ let i, len;
2383
+ this._server.logger.debug(
2384
+ {
2385
+ tnx: "data",
2386
+ cid: this.id,
2387
+ bytes: this._parser.dataBytes,
2388
+ user: this.session.user && this.session.user.username || this.session.user
2389
+ },
2390
+ "C: <%s bytes of DATA>",
2391
+ this._parser.dataBytes
2392
+ );
2393
+ if (typeof this._dataStream === "object" && this._dataStream && this._dataStream.readable) {
2394
+ this._dataStream.removeAllListeners();
2395
+ }
2396
+ if (err) {
2397
+ if (this._server.options.lmtp) {
2398
+ for (i = 0, len = this.session.envelope.rcptTo.length; i < len; i++) {
2399
+ this.send(err.responseCode || 450, err.message);
2400
+ }
2401
+ } else {
2402
+ this.send(err.responseCode || 450, err.message);
2403
+ }
2404
+ } else if (Array.isArray(message)) {
2405
+ message.forEach((response) => {
2406
+ if (/Error\]$/i.test(Object.prototype.toString.call(response))) {
2407
+ this.send(response.responseCode || 450, response.message);
2408
+ } else {
2409
+ this.send(250, typeof response === "string" ? response : "OK: message accepted", "DATA_OK");
2410
+ }
2411
+ });
2412
+ } else if (this._server.options.lmtp) {
2413
+ for (i = 0, len = this.session.envelope.rcptTo.length; i < len; i++) {
2414
+ this.send(250, typeof message === "string" ? message : "OK: message accepted", "DATA_OK");
2415
+ }
2416
+ } else {
2417
+ this.send(250, typeof message === "string" ? message : "OK: message queued", "DATA_OK");
2418
+ }
2419
+ this._transactionCounter++;
2420
+ this._unrecognizedCommands = 0;
2421
+ this._resetSession();
2422
+ if (typeof this._parser === "object" && this._parser) {
2423
+ this._parser.continue();
2424
+ }
2425
+ };
2426
+ this._server.onData(this._dataStream, this.session, (err, message) => {
2427
+ if (typeof this._dataStream === "object" && this._dataStream && this._dataStream.readable) {
2428
+ this._dataStream.on("end", () => close(err, message));
2429
+ return;
2430
+ }
2431
+ close(err, message);
2432
+ });
2433
+ this.send(354, "End data with <CR><LF>.<CR><LF>");
2434
+ callback();
2435
+ }
2436
+ // Dummy handlers for some old sendmail specific commands
2437
+ /**
2438
+ * Processes sendmail WIZ command, upgrades to "wizard mode"
2439
+ */
2440
+ handler_WIZ(command, callback) {
2441
+ let args = command.toString().trim().split(/\s+/);
2442
+ let password;
2443
+ args.shift();
2444
+ password = (args.shift() || "").toString();
2445
+ if (!password) {
2446
+ this.send(500, "You are no wizard!");
2447
+ return callback();
2448
+ }
2449
+ this.session.isWizard = true;
2450
+ this.send(200, "Please pass, oh mighty wizard");
2451
+ callback();
2452
+ }
2453
+ /**
2454
+ * Processes sendmail SHELL command, should return interactive shell but this is a dummy function
2455
+ * so no actual shell is provided to the client
2456
+ */
2457
+ handler_SHELL(command, callback) {
2458
+ this._server.logger.info(
2459
+ {
2460
+ tnx: "shell",
2461
+ cid: this.id,
2462
+ user: this.session.user && this.session.user.username || this.session.user
2463
+ },
2464
+ "Client tried to invoke SHELL"
2465
+ );
2466
+ if (!this.session.isWizard) {
2467
+ this.send(500, "Mere mortals must not mutter that mantra");
2468
+ return callback();
2469
+ }
2470
+ this.send(500, "Error: Invoking shell is not allowed. This incident will be reported.");
2471
+ callback();
2472
+ }
2473
+ /**
2474
+ * Processes sendmail KILL command
2475
+ */
2476
+ handler_KILL(command, callback) {
2477
+ this._server.logger.info(
2478
+ {
2479
+ tnx: "kill",
2480
+ cid: this.id,
2481
+ user: this.session.user && this.session.user.username || this.session.user
2482
+ },
2483
+ "Client tried to invoke KILL"
2484
+ );
2485
+ this.send(500, "Can not kill Mom");
2486
+ callback();
2487
+ }
2488
+ upgrade(callback, secureCallback) {
2489
+ this._socket.unpipe(this._parser);
2490
+ this._upgrading = true;
2491
+ setImmediate(callback);
2492
+ let secureSocket;
2493
+ let onError = (err) => {
2494
+ const meta = {};
2495
+ if (secureSocket) {
2496
+ meta.tlsProtocol = secureSocket.getProtocol();
2497
+ }
2498
+ meta.protocol = "smtp";
2499
+ meta.stage = "connect";
2500
+ if (err) {
2501
+ err.meta = meta;
2502
+ }
2503
+ if (err && /SSL[23]*_GET_CLIENT_HELLO|ssl[23]*_read_bytes|ssl_bytes_to_cipher_list/i.test(err.message)) {
2504
+ let message = err.message;
2505
+ err.message = "Failed to establish TLS session";
2506
+ err.responseCode = 500;
2507
+ err.code = err.code || "TLSError";
2508
+ meta.message = message;
2509
+ }
2510
+ if (!err || !err.message) {
2511
+ err = new Error("Socket closed while initiating TLS");
2512
+ err.responseCode = 500;
2513
+ err.code = "SocketError";
2514
+ err.report = false;
2515
+ err.meta = meta;
2516
+ }
2517
+ this._onError(err);
2518
+ };
2519
+ let secureContext = this._server.secureContext.get("*");
2520
+ let socketOptions = {
2521
+ secureContext,
2522
+ isServer: true,
2523
+ server: this._server.server,
2524
+ SNICallback: this._server.options.SNICallback
2525
+ };
2526
+ ["requestCert", "rejectUnauthorized", "NPNProtocols", "SNICallback", "session", "requestOCSP"].forEach((key) => {
2527
+ if (key in this._server.options) {
2528
+ socketOptions[key] = this._server.options[key];
2529
+ }
2530
+ });
2531
+ this._socket.removeAllListeners();
2532
+ this._socket.on("error", (err) => this._onError(err));
2533
+ secureSocket = new tls.TLSSocket(this._socket, socketOptions);
2534
+ secureSocket.once("close", (hadError) => this._onCloseEvent(hadError));
2535
+ secureSocket.once("error", (err) => onError(err));
2536
+ secureSocket.once("_tlsError", (err) => onError(err));
2537
+ secureSocket.once("clientError", (err) => onError(err));
2538
+ secureSocket.setTimeout(this._server.options.socketTimeout || SOCKET_TIMEOUT, () => this._onTimeout());
2539
+ secureSocket.on("secure", () => {
2540
+ this.session.secure = this.secure = true;
2541
+ this._socket = secureSocket;
2542
+ this._upgrading = false;
2543
+ this.session.tlsOptions = this.tlsOptions = this._socket.getCipher();
2544
+ this.session.servername = this._socket.servername;
2545
+ let cipher = this.session.tlsOptions && this.session.tlsOptions.name;
2546
+ this._server.logger.info(
2547
+ {
2548
+ tnx: "starttls",
2549
+ cid: this.id,
2550
+ user: this.session.user && this.session.user.username || this.session.user,
2551
+ cipher
2552
+ },
2553
+ "Connection upgraded to TLS using",
2554
+ cipher || "N/A"
2555
+ );
2556
+ this._server.onSecure(this._socket, this.session, (err) => {
2557
+ if (err) {
2558
+ return this._onError(err);
2559
+ }
2560
+ this._socket.pipe(this._parser);
2561
+ if (typeof secureCallback === "function") {
2562
+ secureCallback();
2563
+ }
2564
+ });
2565
+ });
2566
+ }
2567
+ };
2568
+ module.exports.SMTPConnection = SMTPConnection;
2569
+ }
2570
+ });
2571
+
2572
+ // node_modules/smtp-server/lib/tls-options.js
2573
+ var require_tls_options = __commonJS({
2574
+ "node_modules/smtp-server/lib/tls-options.js"(exports, module) {
2575
+ "use strict";
2576
+ var crypto = __require("crypto");
2577
+ module.exports = getTLSOptions;
2578
+ var tlsDefaults = {
2579
+ // pregenerated default certificates for localhost
2580
+ // obviusly, do not use in production
2581
+ key: "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA6Z5Qqhw+oWfhtEiMHE32Ht94mwTBpAfjt3vPpX8M7DMCTwHs\n1xcXvQ4lQ3rwreDTOWdoJeEEy7gMxXqH0jw0WfBx+8IIJU69xstOyT7FRFDvA1yT\nRXY2yt9K5s6SKken/ebMfmZR+03ND4UFsDzkz0FfgcjrkXmrMF5Eh5UXX/+9YHeU\nxlp0gMAt+/SumSmgCaysxZLjLpd4uXz+X+JVxsk1ACg1NoEO7lWJC/3WBP7MIcu2\nwVsMd2XegLT0gWYfT1/jsIH64U/mS/SVXC9QhxMl9Yfko2kx1OiYhDxhHs75RJZh\nrNRxgfiwgSb50Gw4NAQaDIxr/DJPdLhgnpY6UQIDAQABAoIBAE+tfzWFjJbgJ0ql\ns6Ozs020Sh4U8TZQuonJ4HhBbNbiTtdDgNObPK1uNadeNtgW5fOeIRdKN6iDjVeN\nAuXhQrmqGDYVZ1HSGUfD74sTrZQvRlWPLWtzdhybK6Css41YAyPFo9k4bJ2ZW2b/\np4EEQ8WsNja9oBpttMU6YYUchGxo1gujN8hmfDdXUQx3k5Xwx4KA68dveJ8GasIt\nd+0Jd/FVwCyyx8HTiF1FF8QZYQeAXxbXJgLBuCsMQJghlcpBEzWkscBR3Ap1U0Zi\n4oat8wrPZGCblaA6rNkRUVbc/+Vw0stnuJ/BLHbPxyBs6w495yBSjBqUWZMvljNz\nm9/aK0ECgYEA9oVIVAd0enjSVIyAZNbw11ElidzdtBkeIJdsxqhmXzeIFZbB39Gd\nbjtAVclVbq5mLsI1j22ER2rHA4Ygkn6vlLghK3ZMPxZa57oJtmL3oP0RvOjE4zRV\ndzKexNGo9gU/x9SQbuyOmuauvAYhXZxeLpv+lEfsZTqqrvPUGeBiEQcCgYEA8poG\nWVnykWuTmCe0bMmvYDsWpAEiZnFLDaKcSbz3O7RMGbPy1cypmqSinIYUpURBT/WY\nwVPAGtjkuTXtd1Cy58m7PqziB7NNWMcsMGj+lWrTPZ6hCHIBcAImKEPpd+Y9vGJX\noatFJguqAGOz7rigBq6iPfeQOCWpmprNAuah++cCgYB1gcybOT59TnA7mwlsh8Qf\nbm+tSllnin2A3Y0dGJJLmsXEPKtHS7x2Gcot2h1d98V/TlWHe5WNEUmx1VJbYgXB\npw8wj2ACxl4ojNYqWPxegaLd4DpRbtW6Tqe9e47FTnU7hIggR6QmFAWAXI+09l8y\namssNShqjE9lu5YDi6BTKwKBgQCuIlKGViLfsKjrYSyHnajNWPxiUhIgGBf4PI0T\n/Jg1ea/aDykxv0rKHnw9/5vYGIsM2st/kR7l5mMecg/2Qa145HsLfMptHo1ZOPWF\n9gcuttPTegY6aqKPhGthIYX2MwSDMM+X0ri6m0q2JtqjclAjG7yG4CjbtGTt/UlE\nWMlSZwKBgQDslGeLUnkW0bsV5EG3AKRUyPKz/6DVNuxaIRRhOeWVKV101claqXAT\nwXOpdKrvkjZbT4AzcNrlGtRl3l7dEVXTu+dN7/ZieJRu7zaStlAQZkIyP9O3DdQ3\nrIcetQpfrJ1cAqz6Ng0pD0mh77vQ13WG1BBmDFa2A9BuzLoBituf4g==\n-----END RSA PRIVATE KEY-----",
2582
+ cert: "-----BEGIN CERTIFICATE-----\nMIICpDCCAYwCCQCuVLVKVTXnAjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwls\nb2NhbGhvc3QwHhcNMTUwMjEyMTEzMjU4WhcNMjUwMjA5MTEzMjU4WjAUMRIwEAYD\nVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDp\nnlCqHD6hZ+G0SIwcTfYe33ibBMGkB+O3e8+lfwzsMwJPAezXFxe9DiVDevCt4NM5\nZ2gl4QTLuAzFeofSPDRZ8HH7wgglTr3Gy07JPsVEUO8DXJNFdjbK30rmzpIqR6f9\n5sx+ZlH7Tc0PhQWwPOTPQV+ByOuReaswXkSHlRdf/71gd5TGWnSAwC379K6ZKaAJ\nrKzFkuMul3i5fP5f4lXGyTUAKDU2gQ7uVYkL/dYE/swhy7bBWwx3Zd6AtPSBZh9P\nX+OwgfrhT+ZL9JVcL1CHEyX1h+SjaTHU6JiEPGEezvlElmGs1HGB+LCBJvnQbDg0\nBBoMjGv8Mk90uGCeljpRAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABXm8GPdY0sc\nmMUFlgDqFzcevjdGDce0QfboR+M7WDdm512Jz2SbRTgZD/4na42ThODOZz9z1AcM\nzLgx2ZNZzVhBz0odCU4JVhOCEks/OzSyKeGwjIb4JAY7dh+Kju1+6MNfQJ4r1Hza\nSVXH0+JlpJDaJ73NQ2JyfqELmJ1mTcptkA/N6rQWhlzycTBSlfogwf9xawgVPATP\n4AuwgjHl12JI2HVVs1gu65Y3slvaHRCr0B4+Kg1GYNLLcbFcK+NEHrHmPxy9TnTh\nZwp1dsNQU+Xkylz8IUANWSLHYZOMtN2e5SKIdwTtl5C8YxveuY8YKb1gDExnMraT\nVGXQDqPleug=\n-----END CERTIFICATE-----",
2583
+ honorCipherOrder: true,
2584
+ requestOCSP: false,
2585
+ sessionIdContext: crypto.createHash("sha1").update(process.argv.join(" ")).digest("hex").slice(0, 32),
2586
+ minVersion: "TLSv1"
2587
+ // sadly there are very old SMTP clients out there
2588
+ };
2589
+ function getTLSOptions(opts) {
2590
+ return Object.assign({}, tlsDefaults, opts || {});
2591
+ }
2592
+ }
2593
+ });
2594
+
2595
+ // node_modules/nodemailer/lib/fetch/cookies.js
2596
+ var require_cookies = __commonJS({
2597
+ "node_modules/nodemailer/lib/fetch/cookies.js"(exports, module) {
2598
+ "use strict";
2599
+ var urllib = __require("url");
2600
+ var SESSION_TIMEOUT = 1800;
2601
+ var Cookies = class {
2602
+ constructor(options) {
2603
+ this.options = options || {};
2604
+ this.cookies = [];
2605
+ }
2606
+ /**
2607
+ * Stores a cookie string to the cookie storage
2608
+ *
2609
+ * @param {String} cookieStr Value from the 'Set-Cookie:' header
2610
+ * @param {String} url Current URL
2611
+ */
2612
+ set(cookieStr, url) {
2613
+ const urlparts = urllib.parse(url || "");
2614
+ const cookie = this.parse(cookieStr);
2615
+ let domain;
2616
+ if (cookie.domain) {
2617
+ domain = cookie.domain.replace(/^\./, "");
2618
+ if (
2619
+ // can't be valid if the requested domain is shorter than current hostname
2620
+ urlparts.hostname.length < domain.length || // prefix domains with dot to be sure that partial matches are not used
2621
+ ("." + urlparts.hostname).substr(-domain.length + 1) !== "." + domain
2622
+ ) {
2623
+ cookie.domain = urlparts.hostname;
2624
+ }
2625
+ } else {
2626
+ cookie.domain = urlparts.hostname;
2627
+ }
2628
+ if (!cookie.path) {
2629
+ cookie.path = this.getPath(urlparts.pathname);
2630
+ }
2631
+ if (!cookie.expires) {
2632
+ cookie.expires = new Date(Date.now() + (Number(this.options.sessionTimeout || SESSION_TIMEOUT) || SESSION_TIMEOUT) * 1e3);
2633
+ }
2634
+ return this.add(cookie);
2635
+ }
2636
+ /**
2637
+ * Returns cookie string for the 'Cookie:' header.
2638
+ *
2639
+ * @param {String} url URL to check for
2640
+ * @returns {String} Cookie header or empty string if no matches were found
2641
+ */
2642
+ get(url) {
2643
+ return this.list(url).map((cookie) => cookie.name + "=" + cookie.value).join("; ");
2644
+ }
2645
+ /**
2646
+ * Lists all valied cookie objects for the specified URL
2647
+ *
2648
+ * @param {String} url URL to check for
2649
+ * @returns {Array} An array of cookie objects
2650
+ */
2651
+ list(url) {
2652
+ const result = [];
2653
+ for (let i = this.cookies.length - 1; i >= 0; i--) {
2654
+ const cookie = this.cookies[i];
2655
+ if (this.isExpired(cookie)) {
2656
+ this.cookies.splice(i, 1);
2657
+ continue;
2658
+ }
2659
+ if (this.match(cookie, url)) {
2660
+ result.unshift(cookie);
2661
+ }
2662
+ }
2663
+ return result;
2664
+ }
2665
+ /**
2666
+ * Parses cookie string from the 'Set-Cookie:' header
2667
+ *
2668
+ * @param {String} cookieStr String from the 'Set-Cookie:' header
2669
+ * @returns {Object} Cookie object
2670
+ */
2671
+ parse(cookieStr) {
2672
+ const cookie = {};
2673
+ (cookieStr || "").toString().split(";").forEach((cookiePart) => {
2674
+ const valueParts = cookiePart.split("=");
2675
+ const key = valueParts.shift().trim().toLowerCase();
2676
+ let value = valueParts.join("=").trim();
2677
+ let domain;
2678
+ if (!key) {
2679
+ return;
2680
+ }
2681
+ switch (key) {
2682
+ case "expires":
2683
+ value = new Date(value);
2684
+ if (value.toString() !== "Invalid Date") {
2685
+ cookie.expires = value;
2686
+ }
2687
+ break;
2688
+ case "path":
2689
+ cookie.path = value;
2690
+ break;
2691
+ case "domain":
2692
+ domain = value.toLowerCase();
2693
+ if (domain.length && domain.charAt(0) !== ".") {
2694
+ domain = "." + domain;
2695
+ }
2696
+ cookie.domain = domain;
2697
+ break;
2698
+ case "max-age":
2699
+ cookie.expires = new Date(Date.now() + (Number(value) || 0) * 1e3);
2700
+ break;
2701
+ case "secure":
2702
+ cookie.secure = true;
2703
+ break;
2704
+ case "httponly":
2705
+ cookie.httponly = true;
2706
+ break;
2707
+ default:
2708
+ if (!cookie.name) {
2709
+ cookie.name = key;
2710
+ cookie.value = value;
2711
+ }
2712
+ }
2713
+ });
2714
+ return cookie;
2715
+ }
2716
+ /**
2717
+ * Checks if a cookie object is valid for a specified URL
2718
+ *
2719
+ * @param {Object} cookie Cookie object
2720
+ * @param {String} url URL to check for
2721
+ * @returns {Boolean} true if cookie is valid for specifiec URL
2722
+ */
2723
+ match(cookie, url) {
2724
+ const urlparts = urllib.parse(url || "");
2725
+ if (urlparts.hostname !== cookie.domain && (cookie.domain.charAt(0) !== "." || ("." + urlparts.hostname).substr(-cookie.domain.length) !== cookie.domain)) {
2726
+ return false;
2727
+ }
2728
+ const path = this.getPath(urlparts.pathname);
2729
+ if (path.substr(0, cookie.path.length) !== cookie.path) {
2730
+ return false;
2731
+ }
2732
+ if (cookie.secure && urlparts.protocol !== "https:") {
2733
+ return false;
2734
+ }
2735
+ return true;
2736
+ }
2737
+ /**
2738
+ * Adds (or updates/removes if needed) a cookie object to the cookie storage
2739
+ *
2740
+ * @param {Object} cookie Cookie value to be stored
2741
+ */
2742
+ add(cookie) {
2743
+ if (!cookie || !cookie.name) {
2744
+ return false;
2745
+ }
2746
+ for (let i = 0, len = this.cookies.length; i < len; i++) {
2747
+ if (this.compare(this.cookies[i], cookie)) {
2748
+ if (this.isExpired(cookie)) {
2749
+ this.cookies.splice(i, 1);
2750
+ return false;
2751
+ }
2752
+ this.cookies[i] = cookie;
2753
+ return true;
2754
+ }
2755
+ }
2756
+ if (!this.isExpired(cookie)) {
2757
+ this.cookies.push(cookie);
2758
+ }
2759
+ return true;
2760
+ }
2761
+ /**
2762
+ * Checks if two cookie objects are the same
2763
+ *
2764
+ * @param {Object} a Cookie to check against
2765
+ * @param {Object} b Cookie to check against
2766
+ * @returns {Boolean} True, if the cookies are the same
2767
+ */
2768
+ compare(a, b) {
2769
+ return a.name === b.name && a.path === b.path && a.domain === b.domain && a.secure === b.secure && a.httponly === b.httponly;
2770
+ }
2771
+ /**
2772
+ * Checks if a cookie is expired
2773
+ *
2774
+ * @param {Object} cookie Cookie object to check against
2775
+ * @returns {Boolean} True, if the cookie is expired
2776
+ */
2777
+ isExpired(cookie) {
2778
+ return cookie.expires && cookie.expires < /* @__PURE__ */ new Date() || !cookie.value;
2779
+ }
2780
+ /**
2781
+ * Returns normalized cookie path for an URL path argument
2782
+ *
2783
+ * @param {String} pathname
2784
+ * @returns {String} Normalized path
2785
+ */
2786
+ getPath(pathname) {
2787
+ let path = (pathname || "/").split("/");
2788
+ path.pop();
2789
+ path = path.join("/").trim();
2790
+ if (path.charAt(0) !== "/") {
2791
+ path = "/" + path;
2792
+ }
2793
+ if (path.substr(-1) !== "/") {
2794
+ path += "/";
2795
+ }
2796
+ return path;
2797
+ }
2798
+ };
2799
+ module.exports = Cookies;
2800
+ }
2801
+ });
2802
+
2803
+ // node_modules/nodemailer/package.json
2804
+ var require_package = __commonJS({
2805
+ "node_modules/nodemailer/package.json"(exports, module) {
2806
+ module.exports = {
2807
+ name: "nodemailer",
2808
+ version: "8.0.5",
2809
+ description: "Easy as cake e-mail sending from your Node.js applications",
2810
+ main: "lib/nodemailer.js",
2811
+ scripts: {
2812
+ test: "node --test --test-concurrency=1 test/**/*.test.js test/**/*-test.js",
2813
+ "test:coverage": "c8 node --test --test-concurrency=1 test/**/*.test.js test/**/*-test.js",
2814
+ format: 'prettier --write "**/*.{js,json,md}"',
2815
+ "format:check": 'prettier --check "**/*.{js,json,md}"',
2816
+ lint: "eslint .",
2817
+ "lint:fix": "eslint . --fix",
2818
+ update: "rm -rf node_modules/ package-lock.json && ncu -u && npm install",
2819
+ "test:syntax": 'docker run --rm -v "$PWD:/app:ro" -w /app node:6-alpine node test/syntax-compat.js'
2820
+ },
2821
+ repository: {
2822
+ type: "git",
2823
+ url: "https://github.com/nodemailer/nodemailer.git"
2824
+ },
2825
+ keywords: [
2826
+ "Nodemailer"
2827
+ ],
2828
+ author: "Andris Reinman",
2829
+ license: "MIT-0",
2830
+ bugs: {
2831
+ url: "https://github.com/nodemailer/nodemailer/issues"
2832
+ },
2833
+ homepage: "https://nodemailer.com/",
2834
+ devDependencies: {
2835
+ "@aws-sdk/client-sesv2": "3.1025.0",
2836
+ bunyan: "1.8.15",
2837
+ c8: "11.0.0",
2838
+ eslint: "10.2.0",
2839
+ "eslint-config-prettier": "10.1.8",
2840
+ globals: "17.4.0",
2841
+ libbase64: "1.3.0",
2842
+ libmime: "5.3.7",
2843
+ libqp: "2.1.1",
2844
+ "nodemailer-ntlm-auth": "1.0.4",
2845
+ prettier: "3.8.1",
2846
+ proxy: "1.0.2",
2847
+ "proxy-test-server": "1.0.0",
2848
+ "smtp-server": "3.18.3"
2849
+ },
2850
+ engines: {
2851
+ node: ">=6.0.0"
2852
+ }
2853
+ };
2854
+ }
2855
+ });
2856
+
2857
+ // node_modules/nodemailer/lib/errors.js
2858
+ var require_errors = __commonJS({
2859
+ "node_modules/nodemailer/lib/errors.js"(exports, module) {
2860
+ "use strict";
2861
+ var ERROR_CODES = {
2862
+ // Connection errors
2863
+ ECONNECTION: "Connection closed unexpectedly",
2864
+ ETIMEDOUT: "Connection or operation timed out",
2865
+ ESOCKET: "Socket-level error",
2866
+ EDNS: "DNS resolution failed",
2867
+ // TLS/Security errors
2868
+ ETLS: "TLS handshake or STARTTLS failed",
2869
+ EREQUIRETLS: "REQUIRETLS not supported by server (RFC 8689)",
2870
+ // Protocol errors
2871
+ EPROTOCOL: "Invalid SMTP server response",
2872
+ EENVELOPE: "Invalid mail envelope (sender or recipients)",
2873
+ EMESSAGE: "Message delivery error",
2874
+ ESTREAM: "Stream processing error",
2875
+ // Authentication errors
2876
+ EAUTH: "Authentication failed",
2877
+ ENOAUTH: "Authentication credentials not provided",
2878
+ EOAUTH2: "OAuth2 token generation or refresh error",
2879
+ // Resource errors
2880
+ EMAXLIMIT: "Pool resource limit reached (max messages per connection)",
2881
+ // Transport-specific errors
2882
+ ESENDMAIL: "Sendmail command error",
2883
+ ESES: "AWS SES transport error",
2884
+ // Configuration and access errors
2885
+ ECONFIG: "Invalid configuration",
2886
+ EPROXY: "Proxy connection error",
2887
+ EFILEACCESS: "File access rejected (disableFileAccess is set)",
2888
+ EURLACCESS: "URL access rejected (disableUrlAccess is set)",
2889
+ EFETCH: "HTTP fetch error"
2890
+ };
2891
+ module.exports = { ERROR_CODES };
2892
+ for (const code of Object.keys(ERROR_CODES)) {
2893
+ module.exports[code] = code;
2894
+ }
2895
+ }
2896
+ });
2897
+
2898
+ // node_modules/nodemailer/lib/fetch/index.js
2899
+ var require_fetch = __commonJS({
2900
+ "node_modules/nodemailer/lib/fetch/index.js"(exports, module) {
2901
+ "use strict";
2902
+ var http = __require("http");
2903
+ var https = __require("https");
2904
+ var urllib = __require("url");
2905
+ var zlib = __require("zlib");
2906
+ var { PassThrough } = __require("stream");
2907
+ var Cookies = require_cookies();
2908
+ var packageData = require_package();
2909
+ var net = __require("net");
2910
+ var errors2 = require_errors();
2911
+ var MAX_REDIRECTS = 5;
2912
+ module.exports = function(url, options) {
2913
+ return nmfetch(url, options);
2914
+ };
2915
+ module.exports.Cookies = Cookies;
2916
+ function nmfetch(url, options) {
2917
+ options = options || {};
2918
+ options.fetchRes = options.fetchRes || new PassThrough();
2919
+ options.cookies = options.cookies || new Cookies();
2920
+ options.redirects = options.redirects || 0;
2921
+ options.maxRedirects = isNaN(options.maxRedirects) ? MAX_REDIRECTS : options.maxRedirects;
2922
+ if (options.cookie) {
2923
+ [].concat(options.cookie || []).forEach((cookie) => {
2924
+ options.cookies.set(cookie, url);
2925
+ });
2926
+ options.cookie = false;
2927
+ }
2928
+ const fetchRes = options.fetchRes;
2929
+ const parsed = urllib.parse(url);
2930
+ let method = (options.method || "").toString().trim().toUpperCase() || "GET";
2931
+ let finished = false;
2932
+ let cookies;
2933
+ let body;
2934
+ const handler = parsed.protocol === "https:" ? https : http;
2935
+ const headers = {
2936
+ "accept-encoding": "gzip,deflate",
2937
+ "user-agent": "nodemailer/" + packageData.version
2938
+ };
2939
+ Object.keys(options.headers || {}).forEach((key) => {
2940
+ headers[key.toLowerCase().trim()] = options.headers[key];
2941
+ });
2942
+ if (options.userAgent) {
2943
+ headers["user-agent"] = options.userAgent;
2944
+ }
2945
+ if (parsed.auth) {
2946
+ headers.Authorization = "Basic " + Buffer.from(parsed.auth).toString("base64");
2947
+ }
2948
+ if (cookies = options.cookies.get(url)) {
2949
+ headers.cookie = cookies;
2950
+ }
2951
+ if (options.body) {
2952
+ if (options.contentType !== false) {
2953
+ headers["Content-Type"] = options.contentType || "application/x-www-form-urlencoded";
2954
+ }
2955
+ if (typeof options.body.pipe === "function") {
2956
+ headers["Transfer-Encoding"] = "chunked";
2957
+ body = options.body;
2958
+ body.on("error", (err) => {
2959
+ if (finished) {
2960
+ return;
2961
+ }
2962
+ finished = true;
2963
+ err.code = errors2.EFETCH;
2964
+ err.sourceUrl = url;
2965
+ fetchRes.emit("error", err);
2966
+ });
2967
+ } else {
2968
+ if (options.body instanceof Buffer) {
2969
+ body = options.body;
2970
+ } else if (typeof options.body === "object") {
2971
+ try {
2972
+ body = Buffer.from(
2973
+ Object.keys(options.body).map((key) => {
2974
+ const value = options.body[key].toString().trim();
2975
+ return encodeURIComponent(key) + "=" + encodeURIComponent(value);
2976
+ }).join("&")
2977
+ );
2978
+ } catch (E) {
2979
+ if (finished) {
2980
+ return;
2981
+ }
2982
+ finished = true;
2983
+ E.code = errors2.EFETCH;
2984
+ E.sourceUrl = url;
2985
+ fetchRes.emit("error", E);
2986
+ return;
2987
+ }
2988
+ } else {
2989
+ body = Buffer.from(options.body.toString().trim());
2990
+ }
2991
+ headers["Content-Type"] = options.contentType || "application/x-www-form-urlencoded";
2992
+ headers["Content-Length"] = body.length;
2993
+ }
2994
+ method = (options.method || "").toString().trim().toUpperCase() || "POST";
2995
+ }
2996
+ let req;
2997
+ const reqOptions = {
2998
+ method,
2999
+ host: parsed.hostname,
3000
+ path: parsed.path,
3001
+ port: parsed.port ? parsed.port : parsed.protocol === "https:" ? 443 : 80,
3002
+ headers,
3003
+ rejectUnauthorized: false,
3004
+ agent: false
3005
+ };
3006
+ if (options.tls) {
3007
+ Object.assign(reqOptions, options.tls);
3008
+ }
3009
+ if (parsed.protocol === "https:" && parsed.hostname && parsed.hostname !== reqOptions.host && !net.isIP(parsed.hostname) && !reqOptions.servername) {
3010
+ reqOptions.servername = parsed.hostname;
3011
+ }
3012
+ try {
3013
+ req = handler.request(reqOptions);
3014
+ } catch (E) {
3015
+ finished = true;
3016
+ setImmediate(() => {
3017
+ E.code = errors2.EFETCH;
3018
+ E.sourceUrl = url;
3019
+ fetchRes.emit("error", E);
3020
+ });
3021
+ return fetchRes;
3022
+ }
3023
+ if (options.timeout) {
3024
+ req.setTimeout(options.timeout, () => {
3025
+ if (finished) {
3026
+ return;
3027
+ }
3028
+ finished = true;
3029
+ req.abort();
3030
+ const err = new Error("Request Timeout");
3031
+ err.code = errors2.EFETCH;
3032
+ err.sourceUrl = url;
3033
+ fetchRes.emit("error", err);
3034
+ });
3035
+ }
3036
+ req.on("error", (err) => {
3037
+ if (finished) {
3038
+ return;
3039
+ }
3040
+ finished = true;
3041
+ err.code = errors2.EFETCH;
3042
+ err.sourceUrl = url;
3043
+ fetchRes.emit("error", err);
3044
+ });
3045
+ req.on("response", (res) => {
3046
+ let inflate;
3047
+ if (finished) {
3048
+ return;
3049
+ }
3050
+ switch (res.headers["content-encoding"]) {
3051
+ case "gzip":
3052
+ case "deflate":
3053
+ inflate = zlib.createUnzip();
3054
+ break;
3055
+ }
3056
+ if (res.headers["set-cookie"]) {
3057
+ [].concat(res.headers["set-cookie"] || []).forEach((cookie) => {
3058
+ options.cookies.set(cookie, url);
3059
+ });
3060
+ }
3061
+ if ([301, 302, 303, 307, 308].includes(res.statusCode) && res.headers.location) {
3062
+ options.redirects++;
3063
+ if (options.redirects > options.maxRedirects) {
3064
+ finished = true;
3065
+ const err = new Error("Maximum redirect count exceeded");
3066
+ err.code = errors2.EFETCH;
3067
+ err.sourceUrl = url;
3068
+ fetchRes.emit("error", err);
3069
+ req.abort();
3070
+ return;
3071
+ }
3072
+ options.method = "GET";
3073
+ options.body = false;
3074
+ return nmfetch(urllib.resolve(url, res.headers.location), options);
3075
+ }
3076
+ fetchRes.statusCode = res.statusCode;
3077
+ fetchRes.headers = res.headers;
3078
+ if (res.statusCode >= 300 && !options.allowErrorResponse) {
3079
+ finished = true;
3080
+ const err = new Error("Invalid status code " + res.statusCode);
3081
+ err.code = errors2.EFETCH;
3082
+ err.sourceUrl = url;
3083
+ fetchRes.emit("error", err);
3084
+ req.abort();
3085
+ return;
3086
+ }
3087
+ res.on("error", (err) => {
3088
+ if (finished) {
3089
+ return;
3090
+ }
3091
+ finished = true;
3092
+ err.code = errors2.EFETCH;
3093
+ err.sourceUrl = url;
3094
+ fetchRes.emit("error", err);
3095
+ req.abort();
3096
+ });
3097
+ if (inflate) {
3098
+ res.pipe(inflate).pipe(fetchRes);
3099
+ inflate.on("error", (err) => {
3100
+ if (finished) {
3101
+ return;
3102
+ }
3103
+ finished = true;
3104
+ err.code = errors2.EFETCH;
3105
+ err.sourceUrl = url;
3106
+ fetchRes.emit("error", err);
3107
+ req.abort();
3108
+ });
3109
+ } else {
3110
+ res.pipe(fetchRes);
3111
+ }
3112
+ });
3113
+ setImmediate(() => {
3114
+ if (body) {
3115
+ try {
3116
+ if (typeof body.pipe === "function") {
3117
+ return body.pipe(req);
3118
+ }
3119
+ req.write(body);
3120
+ } catch (err) {
3121
+ finished = true;
3122
+ err.code = errors2.EFETCH;
3123
+ err.sourceUrl = url;
3124
+ fetchRes.emit("error", err);
3125
+ return;
3126
+ }
3127
+ }
3128
+ req.end();
3129
+ });
3130
+ return fetchRes;
3131
+ }
3132
+ }
3133
+ });
3134
+
3135
+ // node_modules/nodemailer/lib/shared/index.js
3136
+ var require_shared = __commonJS({
3137
+ "node_modules/nodemailer/lib/shared/index.js"(exports, module) {
3138
+ "use strict";
3139
+ var urllib = __require("url");
3140
+ var util = __require("util");
3141
+ var fs = __require("fs");
3142
+ var nmfetch = require_fetch();
3143
+ var dns = __require("dns");
3144
+ var net = __require("net");
3145
+ var os = __require("os");
3146
+ var DNS_TTL = 5 * 60 * 1e3;
3147
+ var CACHE_CLEANUP_INTERVAL = 30 * 1e3;
3148
+ var MAX_CACHE_SIZE = 1e3;
3149
+ var lastCacheCleanup = 0;
3150
+ module.exports._lastCacheCleanup = () => lastCacheCleanup;
3151
+ module.exports._resetCacheCleanup = () => {
3152
+ lastCacheCleanup = 0;
3153
+ };
3154
+ var networkInterfaces;
3155
+ try {
3156
+ networkInterfaces = os.networkInterfaces();
3157
+ } catch (_err) {
3158
+ }
3159
+ module.exports.networkInterfaces = networkInterfaces;
3160
+ var isFamilySupported = (family, allowInternal) => {
3161
+ const ifaces = module.exports.networkInterfaces;
3162
+ if (!ifaces) {
3163
+ return true;
3164
+ }
3165
+ return Object.keys(ifaces).map((key) => ifaces[key]).reduce((acc, val) => acc.concat(val), []).filter((i) => !i.internal || allowInternal).some((i) => i.family === "IPv" + family || i.family === family);
3166
+ };
3167
+ var resolve = (family, hostname, options, callback) => {
3168
+ options = options || {};
3169
+ if (!isFamilySupported(family, options.allowInternalNetworkInterfaces)) {
3170
+ return callback(null, []);
3171
+ }
3172
+ const dnsResolver = dns.Resolver ? new dns.Resolver(options) : dns;
3173
+ dnsResolver["resolve" + family](hostname, (err, addresses) => {
3174
+ if (err) {
3175
+ switch (err.code) {
3176
+ case dns.NODATA:
3177
+ case dns.NOTFOUND:
3178
+ case dns.NOTIMP:
3179
+ case dns.SERVFAIL:
3180
+ case dns.CONNREFUSED:
3181
+ case dns.REFUSED:
3182
+ case "EAI_AGAIN":
3183
+ return callback(null, []);
3184
+ }
3185
+ return callback(err);
3186
+ }
3187
+ return callback(null, Array.isArray(addresses) ? addresses : [].concat(addresses || []));
3188
+ });
3189
+ };
3190
+ var dnsCache = module.exports.dnsCache = /* @__PURE__ */ new Map();
3191
+ var formatDNSValue = (value, extra) => {
3192
+ if (!value) {
3193
+ return Object.assign({}, extra || {});
3194
+ }
3195
+ const addresses = value.addresses || [];
3196
+ const host = addresses.length > 0 ? addresses[Math.floor(Math.random() * addresses.length)] : null;
3197
+ return Object.assign(
3198
+ {
3199
+ servername: value.servername,
3200
+ host,
3201
+ // Include all addresses for connection fallback support
3202
+ _addresses: addresses
3203
+ },
3204
+ extra || {}
3205
+ );
3206
+ };
3207
+ module.exports.resolveHostname = (options, callback) => {
3208
+ options = options || {};
3209
+ if (!options.host && options.servername) {
3210
+ options.host = options.servername;
3211
+ }
3212
+ if (!options.host || net.isIP(options.host)) {
3213
+ const value = {
3214
+ addresses: [options.host],
3215
+ servername: options.servername || false
3216
+ };
3217
+ return callback(
3218
+ null,
3219
+ formatDNSValue(value, {
3220
+ cached: false
3221
+ })
3222
+ );
3223
+ }
3224
+ let cached;
3225
+ if (dnsCache.has(options.host)) {
3226
+ cached = dnsCache.get(options.host);
3227
+ const now = Date.now();
3228
+ if (now - lastCacheCleanup > CACHE_CLEANUP_INTERVAL) {
3229
+ lastCacheCleanup = now;
3230
+ for (const [host, entry] of dnsCache.entries()) {
3231
+ if (entry.expires && entry.expires < now) {
3232
+ dnsCache.delete(host);
3233
+ }
3234
+ }
3235
+ if (dnsCache.size > MAX_CACHE_SIZE) {
3236
+ const toDelete = Math.floor(MAX_CACHE_SIZE * 0.1);
3237
+ const keys = Array.from(dnsCache.keys()).slice(0, toDelete);
3238
+ keys.forEach((key) => dnsCache.delete(key));
3239
+ }
3240
+ }
3241
+ if (!cached.expires || cached.expires >= now) {
3242
+ return callback(
3243
+ null,
3244
+ formatDNSValue(cached.value, {
3245
+ cached: true
3246
+ })
3247
+ );
3248
+ }
3249
+ }
3250
+ let ipv4Addresses = [];
3251
+ let ipv6Addresses = [];
3252
+ let ipv4Error = null;
3253
+ let ipv6Error = null;
3254
+ resolve(4, options.host, options, (err, addresses) => {
3255
+ if (err) {
3256
+ ipv4Error = err;
3257
+ } else {
3258
+ ipv4Addresses = addresses || [];
3259
+ }
3260
+ resolve(6, options.host, options, (err2, addresses2) => {
3261
+ if (err2) {
3262
+ ipv6Error = err2;
3263
+ } else {
3264
+ ipv6Addresses = addresses2 || [];
3265
+ }
3266
+ const allAddresses = ipv4Addresses.concat(ipv6Addresses);
3267
+ if (allAddresses.length) {
3268
+ const value = {
3269
+ addresses: allAddresses,
3270
+ servername: options.servername || options.host
3271
+ };
3272
+ dnsCache.set(options.host, {
3273
+ value,
3274
+ expires: Date.now() + (options.dnsTtl || DNS_TTL)
3275
+ });
3276
+ return callback(
3277
+ null,
3278
+ formatDNSValue(value, {
3279
+ cached: false
3280
+ })
3281
+ );
3282
+ }
3283
+ if (ipv4Error && ipv6Error) {
3284
+ if (cached) {
3285
+ dnsCache.set(options.host, {
3286
+ value: cached.value,
3287
+ expires: Date.now() + (options.dnsTtl || DNS_TTL)
3288
+ });
3289
+ return callback(
3290
+ null,
3291
+ formatDNSValue(cached.value, {
3292
+ cached: true,
3293
+ error: ipv4Error
3294
+ })
3295
+ );
3296
+ }
3297
+ }
3298
+ try {
3299
+ dns.lookup(options.host, { all: true }, (err3, addresses3) => {
3300
+ if (err3) {
3301
+ if (cached) {
3302
+ dnsCache.set(options.host, {
3303
+ value: cached.value,
3304
+ expires: Date.now() + (options.dnsTtl || DNS_TTL)
3305
+ });
3306
+ return callback(
3307
+ null,
3308
+ formatDNSValue(cached.value, {
3309
+ cached: true,
3310
+ error: err3
3311
+ })
3312
+ );
3313
+ }
3314
+ return callback(err3);
3315
+ }
3316
+ const supportedAddresses = addresses3 ? addresses3.filter((addr) => isFamilySupported(addr.family)).map((addr) => addr.address) : [];
3317
+ if (addresses3 && addresses3.length && !supportedAddresses.length) {
3318
+ console.warn(`Failed to resolve IPv${addresses3[0].family} addresses with current network`);
3319
+ }
3320
+ if (!supportedAddresses.length && cached) {
3321
+ return callback(
3322
+ null,
3323
+ formatDNSValue(cached.value, {
3324
+ cached: true
3325
+ })
3326
+ );
3327
+ }
3328
+ const value = {
3329
+ addresses: supportedAddresses.length ? supportedAddresses : [options.host],
3330
+ servername: options.servername || options.host
3331
+ };
3332
+ dnsCache.set(options.host, {
3333
+ value,
3334
+ expires: Date.now() + (options.dnsTtl || DNS_TTL)
3335
+ });
3336
+ return callback(
3337
+ null,
3338
+ formatDNSValue(value, {
3339
+ cached: false
3340
+ })
3341
+ );
3342
+ });
3343
+ } catch (lookupErr) {
3344
+ if (cached) {
3345
+ dnsCache.set(options.host, {
3346
+ value: cached.value,
3347
+ expires: Date.now() + (options.dnsTtl || DNS_TTL)
3348
+ });
3349
+ return callback(
3350
+ null,
3351
+ formatDNSValue(cached.value, {
3352
+ cached: true,
3353
+ error: lookupErr
3354
+ })
3355
+ );
3356
+ }
3357
+ return callback(ipv4Error || ipv6Error || lookupErr);
3358
+ }
3359
+ });
3360
+ });
3361
+ };
3362
+ module.exports.parseConnectionUrl = (str) => {
3363
+ str = str || "";
3364
+ const options = {};
3365
+ const url = urllib.parse(str, true);
3366
+ switch (url.protocol) {
3367
+ case "smtp:":
3368
+ options.secure = false;
3369
+ break;
3370
+ case "smtps:":
3371
+ options.secure = true;
3372
+ break;
3373
+ case "direct:":
3374
+ options.direct = true;
3375
+ break;
3376
+ }
3377
+ if (!isNaN(url.port) && Number(url.port)) {
3378
+ options.port = Number(url.port);
3379
+ }
3380
+ if (url.hostname) {
3381
+ options.host = url.hostname;
3382
+ }
3383
+ if (url.auth) {
3384
+ const auth = url.auth.split(":");
3385
+ options.auth = {
3386
+ user: auth.shift(),
3387
+ pass: auth.join(":")
3388
+ };
3389
+ }
3390
+ Object.keys(url.query || {}).forEach((key) => {
3391
+ let obj = options;
3392
+ let lKey = key;
3393
+ let value = url.query[key];
3394
+ if (!isNaN(value)) {
3395
+ value = Number(value);
3396
+ }
3397
+ switch (value) {
3398
+ case "true":
3399
+ value = true;
3400
+ break;
3401
+ case "false":
3402
+ value = false;
3403
+ break;
3404
+ }
3405
+ if (key.indexOf("tls.") === 0) {
3406
+ lKey = key.substr(4);
3407
+ if (!options.tls) {
3408
+ options.tls = {};
3409
+ }
3410
+ obj = options.tls;
3411
+ } else if (key.indexOf(".") >= 0) {
3412
+ return;
3413
+ }
3414
+ if (!(lKey in obj)) {
3415
+ obj[lKey] = value;
3416
+ }
3417
+ });
3418
+ return options;
3419
+ };
3420
+ module.exports._logFunc = (logger, level, defaults, data, message, ...args) => {
3421
+ const entry = Object.assign({}, defaults || {}, data || {});
3422
+ delete entry.level;
3423
+ logger[level](entry, message, ...args);
3424
+ };
3425
+ module.exports.getLogger = (options, defaults) => {
3426
+ options = options || {};
3427
+ const response = {};
3428
+ const levels = ["trace", "debug", "info", "warn", "error", "fatal"];
3429
+ if (!options.logger) {
3430
+ levels.forEach((level) => {
3431
+ response[level] = () => false;
3432
+ });
3433
+ return response;
3434
+ }
3435
+ const logger = options.logger === true ? createDefaultLogger(levels) : options.logger;
3436
+ levels.forEach((level) => {
3437
+ response[level] = (data, message, ...args) => {
3438
+ module.exports._logFunc(logger, level, defaults, data, message, ...args);
3439
+ };
3440
+ });
3441
+ return response;
3442
+ };
3443
+ module.exports.callbackPromise = (resolve2, reject) => function() {
3444
+ const args = Array.from(arguments);
3445
+ const err = args.shift();
3446
+ if (err) {
3447
+ reject(err);
3448
+ } else {
3449
+ resolve2(...args);
3450
+ }
3451
+ };
3452
+ module.exports.parseDataURI = (uri) => {
3453
+ if (typeof uri !== "string") {
3454
+ return null;
3455
+ }
3456
+ if (!uri.startsWith("data:")) {
3457
+ return null;
3458
+ }
3459
+ const commaPos = uri.indexOf(",");
3460
+ if (commaPos === -1) {
3461
+ return null;
3462
+ }
3463
+ const data = uri.substring(commaPos + 1);
3464
+ const metaStr = uri.substring("data:".length, commaPos);
3465
+ let encoding;
3466
+ const metaEntries = metaStr.split(";");
3467
+ if (metaEntries.length > 0) {
3468
+ const lastEntry = metaEntries[metaEntries.length - 1].toLowerCase().trim();
3469
+ if (["base64", "utf8", "utf-8"].includes(lastEntry) && lastEntry.indexOf("=") === -1) {
3470
+ encoding = lastEntry;
3471
+ metaEntries.pop();
3472
+ }
3473
+ }
3474
+ const contentType = metaEntries.length > 0 ? metaEntries.shift() : "application/octet-stream";
3475
+ const params = {};
3476
+ for (let i = 0; i < metaEntries.length; i++) {
3477
+ const entry = metaEntries[i];
3478
+ const sepPos = entry.indexOf("=");
3479
+ if (sepPos > 0) {
3480
+ const key = entry.substring(0, sepPos).trim();
3481
+ const value = entry.substring(sepPos + 1).trim();
3482
+ if (key) {
3483
+ params[key] = value;
3484
+ }
3485
+ }
3486
+ }
3487
+ let bufferData;
3488
+ try {
3489
+ if (encoding === "base64") {
3490
+ bufferData = Buffer.from(data, "base64");
3491
+ } else {
3492
+ try {
3493
+ bufferData = Buffer.from(decodeURIComponent(data));
3494
+ } catch (_decodeError) {
3495
+ bufferData = Buffer.from(data);
3496
+ }
3497
+ }
3498
+ } catch (_bufferError) {
3499
+ bufferData = Buffer.alloc(0);
3500
+ }
3501
+ return {
3502
+ data: bufferData,
3503
+ encoding: encoding || null,
3504
+ contentType: contentType || "application/octet-stream",
3505
+ params
3506
+ };
3507
+ };
3508
+ module.exports.resolveContent = (data, key, callback) => {
3509
+ let promise;
3510
+ if (!callback) {
3511
+ promise = new Promise((resolve2, reject) => {
3512
+ callback = module.exports.callbackPromise(resolve2, reject);
3513
+ });
3514
+ }
3515
+ let content = data && data[key] && data[key].content || data[key];
3516
+ const encoding = (typeof data[key] === "object" && data[key].encoding || "utf8").toString().toLowerCase().replace(/[-_\s]/g, "");
3517
+ if (!content) {
3518
+ return callback(null, content);
3519
+ }
3520
+ if (typeof content === "object") {
3521
+ if (typeof content.pipe === "function") {
3522
+ return resolveStream(content, (err, value) => {
3523
+ if (err) {
3524
+ return callback(err);
3525
+ }
3526
+ if (data[key].content) {
3527
+ data[key].content = value;
3528
+ } else {
3529
+ data[key] = value;
3530
+ }
3531
+ callback(null, value);
3532
+ });
3533
+ } else if (/^https?:\/\//i.test(content.path || content.href)) {
3534
+ return resolveStream(nmfetch(content.path || content.href), callback);
3535
+ } else if (/^data:/i.test(content.path || content.href)) {
3536
+ const parsedDataUri = module.exports.parseDataURI(content.path || content.href);
3537
+ if (!parsedDataUri || !parsedDataUri.data) {
3538
+ return callback(null, Buffer.from(0));
3539
+ }
3540
+ return callback(null, parsedDataUri.data);
3541
+ } else if (content.path) {
3542
+ return resolveStream(fs.createReadStream(content.path), callback);
3543
+ }
3544
+ }
3545
+ if (typeof data[key].content === "string" && !["utf8", "usascii", "ascii"].includes(encoding)) {
3546
+ content = Buffer.from(data[key].content, encoding);
3547
+ }
3548
+ setImmediate(() => callback(null, content));
3549
+ return promise;
3550
+ };
3551
+ module.exports.assign = function() {
3552
+ const args = Array.from(arguments);
3553
+ const target = args.shift() || {};
3554
+ args.forEach((source) => {
3555
+ Object.keys(source || {}).forEach((key) => {
3556
+ if (["tls", "auth"].includes(key) && source[key] && typeof source[key] === "object") {
3557
+ target[key] = Object.assign(target[key] || {}, source[key]);
3558
+ } else {
3559
+ target[key] = source[key];
3560
+ }
3561
+ });
3562
+ });
3563
+ return target;
3564
+ };
3565
+ module.exports.encodeXText = (str) => {
3566
+ if (!/[^\x21-\x2A\x2C-\x3C\x3E-\x7E]/.test(str)) {
3567
+ return str;
3568
+ }
3569
+ const buf = Buffer.from(str);
3570
+ let result = "";
3571
+ for (let i = 0, len = buf.length; i < len; i++) {
3572
+ const c = buf[i];
3573
+ if (c < 33 || c > 126 || c === 43 || c === 61) {
3574
+ result += "+" + (c < 16 ? "0" : "") + c.toString(16).toUpperCase();
3575
+ } else {
3576
+ result += String.fromCharCode(c);
3577
+ }
3578
+ }
3579
+ return result;
3580
+ };
3581
+ function resolveStream(stream, callback) {
3582
+ let responded = false;
3583
+ const chunks = [];
3584
+ let chunklen = 0;
3585
+ stream.on("error", (err) => {
3586
+ if (responded) {
3587
+ return;
3588
+ }
3589
+ responded = true;
3590
+ callback(err);
3591
+ });
3592
+ stream.on("readable", () => {
3593
+ let chunk;
3594
+ while ((chunk = stream.read()) !== null) {
3595
+ chunks.push(chunk);
3596
+ chunklen += chunk.length;
3597
+ }
3598
+ });
3599
+ stream.on("end", () => {
3600
+ if (responded) {
3601
+ return;
3602
+ }
3603
+ responded = true;
3604
+ let value;
3605
+ try {
3606
+ value = Buffer.concat(chunks, chunklen);
3607
+ } catch (E) {
3608
+ return callback(E);
3609
+ }
3610
+ callback(null, value);
3611
+ });
3612
+ }
3613
+ function createDefaultLogger(levels) {
3614
+ const levelMaxLen = levels.reduce((max, level) => Math.max(max, level.length), 0);
3615
+ const levelNames = /* @__PURE__ */ new Map();
3616
+ levels.forEach((level) => {
3617
+ let levelName = level.toUpperCase();
3618
+ if (levelName.length < levelMaxLen) {
3619
+ levelName += " ".repeat(levelMaxLen - levelName.length);
3620
+ }
3621
+ levelNames.set(level, levelName);
3622
+ });
3623
+ const print = (level, entry, message, ...args) => {
3624
+ let prefix = "";
3625
+ if (entry) {
3626
+ if (entry.tnx === "server") {
3627
+ prefix = "S: ";
3628
+ } else if (entry.tnx === "client") {
3629
+ prefix = "C: ";
3630
+ }
3631
+ if (entry.sid) {
3632
+ prefix = "[" + entry.sid + "] " + prefix;
3633
+ }
3634
+ if (entry.cid) {
3635
+ prefix = "[#" + entry.cid + "] " + prefix;
3636
+ }
3637
+ }
3638
+ message = util.format(message, ...args);
3639
+ message.split(/\r?\n/).forEach((line) => {
3640
+ console.log("[%s] %s %s", (/* @__PURE__ */ new Date()).toISOString().substr(0, 19).replace(/T/, " "), levelNames.get(level), prefix + line);
3641
+ });
3642
+ };
3643
+ const logger = {};
3644
+ levels.forEach((level) => {
3645
+ logger[level] = print.bind(null, level);
3646
+ });
3647
+ return logger;
3648
+ }
3649
+ }
3650
+ });
3651
+
3652
+ // node_modules/smtp-server/lib/smtp-server.js
3653
+ var require_smtp_server = __commonJS({
3654
+ "node_modules/smtp-server/lib/smtp-server.js"(exports, module) {
3655
+ "use strict";
3656
+ var net = __require("net");
3657
+ var tls = __require("tls");
3658
+ var SMTPConnection = require_smtp_connection().SMTPConnection;
3659
+ var tlsOptions = require_tls_options();
3660
+ var EventEmitter = __require("events");
3661
+ var shared = require_shared();
3662
+ var punycode2 = (init_punycode_es6(), __toCommonJS(punycode_es6_exports));
3663
+ var crypto = __require("crypto");
3664
+ var CLOSE_TIMEOUT = 30 * 1e3;
3665
+ var SMTPServer2 = class extends EventEmitter {
3666
+ constructor(options) {
3667
+ super();
3668
+ this.options = options || {};
3669
+ this.updateSecureContext();
3670
+ this.options.disabledCommands = [].concat(this.options.disabledCommands || []).map((command) => (command || "").toString().toUpperCase().trim());
3671
+ this.options.authMethods = [].concat(this.options.authMethods || []).map((method) => (method || "").toString().toUpperCase().trim());
3672
+ if (!this.options.authMethods.length) {
3673
+ this.options.authMethods = ["LOGIN", "PLAIN"];
3674
+ }
3675
+ if (this.options.hideENHANCEDSTATUSCODES === void 0) {
3676
+ this.options.hideENHANCEDSTATUSCODES = true;
3677
+ }
3678
+ if (this.options.hideDSN === void 0) {
3679
+ this.options.hideDSN = true;
3680
+ }
3681
+ if (this.options.hideREQUIRETLS === void 0) {
3682
+ this.options.hideREQUIRETLS = true;
3683
+ }
3684
+ this.logger = shared.getLogger(this.options, {
3685
+ component: this.options.component || "smtp-server"
3686
+ });
3687
+ ["onConnect", "onSecure", "onAuth", "onMailFrom", "onRcptTo", "onData", "onClose"].forEach((handler) => {
3688
+ if (typeof this.options[handler] === "function") {
3689
+ this[handler] = this.options[handler];
3690
+ }
3691
+ });
3692
+ this._closeTimeout = false;
3693
+ this.connections = /* @__PURE__ */ new Set();
3694
+ if (this.options.secure && !this.options.needsUpgrade) {
3695
+ this.server = net.createServer(this.options, (socket) => {
3696
+ this._handleProxy(socket, (err, socketOptions) => {
3697
+ if (err) {
3698
+ }
3699
+ if (this.options.secured) {
3700
+ return this.connect(socket, socketOptions);
3701
+ }
3702
+ this._upgrade(socket, (err2, tlsSocket) => {
3703
+ if (err2) {
3704
+ return this._onError(err2);
3705
+ }
3706
+ this.connect(tlsSocket, socketOptions);
3707
+ });
3708
+ });
3709
+ });
3710
+ } else {
3711
+ this.server = net.createServer(
3712
+ this.options,
3713
+ (socket) => this._handleProxy(socket, (err, socketOptions) => {
3714
+ if (err) {
3715
+ }
3716
+ this.connect(socket, socketOptions);
3717
+ })
3718
+ );
3719
+ }
3720
+ this._setListeners();
3721
+ }
3722
+ connect(socket, socketOptions) {
3723
+ let connection = new SMTPConnection(this, socket, socketOptions);
3724
+ this.connections.add(connection);
3725
+ connection.on("error", (err) => this._onError(err));
3726
+ connection.on("connect", (data) => this._onClientConnect(data));
3727
+ connection.init();
3728
+ }
3729
+ /**
3730
+ * Start listening on selected port and interface
3731
+ */
3732
+ listen(...args) {
3733
+ return this.server.listen(...args);
3734
+ }
3735
+ /**
3736
+ * Closes the server
3737
+ *
3738
+ * @param {Function} callback Callback to run once the server is fully closed
3739
+ */
3740
+ close(callback) {
3741
+ let connections = this.connections.size;
3742
+ let timeout = this.options.closeTimeout || CLOSE_TIMEOUT;
3743
+ this.server.close(() => {
3744
+ clearTimeout(this._closeTimeout);
3745
+ if (typeof callback === "function") {
3746
+ return callback();
3747
+ }
3748
+ });
3749
+ if (connections) {
3750
+ this.logger.info(
3751
+ {
3752
+ tnx: "close"
3753
+ },
3754
+ "Server closing with %s pending connection%s, waiting %s seconds before terminating",
3755
+ connections,
3756
+ connections !== 1 ? "s" : "",
3757
+ timeout / 1e3
3758
+ );
3759
+ }
3760
+ this._closeTimeout = setTimeout(() => {
3761
+ connections = this.connections.size;
3762
+ if (connections) {
3763
+ this.logger.info(
3764
+ {
3765
+ tnx: "close"
3766
+ },
3767
+ "Closing %s pending connection%s to close the server",
3768
+ connections,
3769
+ connections !== 1 ? "s" : ""
3770
+ );
3771
+ this.connections.forEach((connection) => {
3772
+ connection.send(421, "Server shutting down");
3773
+ connection.close();
3774
+ });
3775
+ }
3776
+ if (typeof callback === "function") {
3777
+ const realCallback = callback;
3778
+ callback = null;
3779
+ return realCallback();
3780
+ }
3781
+ }, timeout);
3782
+ this._closeTimeout.unref();
3783
+ }
3784
+ /**
3785
+ * Authentication handler. Override this
3786
+ *
3787
+ * @param {Object} auth Authentication options
3788
+ * @param {Function} callback Callback to run once the user is authenticated
3789
+ */
3790
+ onAuth(auth, session, callback) {
3791
+ if (auth.method === "XOAUTH2") {
3792
+ return callback(null, {
3793
+ data: {
3794
+ status: "401",
3795
+ schemes: "bearer mac",
3796
+ scope: "https://mail.google.com/"
3797
+ }
3798
+ });
3799
+ }
3800
+ if (auth.method === "XCLIENT") {
3801
+ return callback();
3802
+ }
3803
+ return callback(null, {
3804
+ message: "Authentication not implemented"
3805
+ });
3806
+ }
3807
+ onConnect(session, callback) {
3808
+ setImmediate(callback);
3809
+ }
3810
+ onMailFrom(address, session, callback) {
3811
+ setImmediate(callback);
3812
+ }
3813
+ onRcptTo(address, session, callback) {
3814
+ setImmediate(callback);
3815
+ }
3816
+ onSecure(socket, session, callback) {
3817
+ setImmediate(callback);
3818
+ }
3819
+ onData(stream, session, callback) {
3820
+ let chunklen = 0;
3821
+ stream.on("data", (chunk) => {
3822
+ chunklen += chunk.length;
3823
+ });
3824
+ stream.on("end", () => {
3825
+ this.logger.info(
3826
+ {
3827
+ tnx: "message",
3828
+ size: chunklen
3829
+ },
3830
+ "<received %s bytes>",
3831
+ chunklen
3832
+ );
3833
+ callback();
3834
+ });
3835
+ }
3836
+ onClose() {
3837
+ }
3838
+ updateSecureContext(options) {
3839
+ Object.keys(options || {}).forEach((key) => {
3840
+ this.options[key] = options[key];
3841
+ });
3842
+ let defaultTlsOptions = tlsOptions(this.options);
3843
+ this.secureContext = /* @__PURE__ */ new Map();
3844
+ this.secureContext.set("*", tls.createSecureContext(defaultTlsOptions));
3845
+ let ctxMap = this.options.sniOptions || {};
3846
+ if (typeof ctxMap.get === "function") {
3847
+ ctxMap.forEach((ctx, servername) => {
3848
+ this.secureContext.set(this._normalizeHostname(servername), tls.createSecureContext(tlsOptions(ctx)));
3849
+ });
3850
+ } else {
3851
+ Object.keys(ctxMap).forEach((servername) => {
3852
+ this.secureContext.set(this._normalizeHostname(servername), tls.createSecureContext(tlsOptions(ctxMap[servername])));
3853
+ });
3854
+ }
3855
+ if (this.options.secure) {
3856
+ Object.keys(defaultTlsOptions || {}).forEach((key) => {
3857
+ if (!(key in this.options)) {
3858
+ this.options[key] = defaultTlsOptions[key];
3859
+ }
3860
+ });
3861
+ if (typeof this.options.SNICallback !== "function") {
3862
+ this.options.SNICallback = (servername, cb) => {
3863
+ cb(null, this.secureContext.get(servername));
3864
+ };
3865
+ }
3866
+ }
3867
+ }
3868
+ // PRIVATE METHODS
3869
+ /**
3870
+ * Setup server event handlers
3871
+ */
3872
+ _setListeners() {
3873
+ let server = this.server;
3874
+ server.once("listening", (...args) => this._onListening(...args));
3875
+ server.once("close", (...args) => this._onClose(server, ...args));
3876
+ server.on("error", (...args) => this._onError(...args));
3877
+ }
3878
+ /**
3879
+ * Called when server started listening
3880
+ *
3881
+ * @event
3882
+ */
3883
+ _onListening() {
3884
+ let address = this.server.address();
3885
+ if (address === null) {
3886
+ address = { address: null, port: null, family: null };
3887
+ }
3888
+ this.logger.info(
3889
+ //
3890
+ {
3891
+ tnx: "listen",
3892
+ host: address.address,
3893
+ port: address.port,
3894
+ secure: !!this.options.secure,
3895
+ protocol: this.options.lmtp ? "LMTP" : "SMTP"
3896
+ },
3897
+ "%s%s Server listening on %s:%s",
3898
+ this.options.secure ? "Secure " : "",
3899
+ this.options.lmtp ? "LMTP" : "SMTP",
3900
+ address.family === "IPv4" ? address.address : "[" + address.address + "]",
3901
+ address.port
3902
+ );
3903
+ }
3904
+ /**
3905
+ * Called when server is closed
3906
+ *
3907
+ * @event
3908
+ */
3909
+ _onClose(server) {
3910
+ this.logger.info(
3911
+ {
3912
+ tnx: "closed"
3913
+ },
3914
+ (this.options.lmtp ? "LMTP" : "SMTP") + " Server closed"
3915
+ );
3916
+ if (server !== this.server) {
3917
+ return;
3918
+ }
3919
+ this.emit("close");
3920
+ }
3921
+ /**
3922
+ * Called when an error occurs with the server
3923
+ *
3924
+ * @event
3925
+ */
3926
+ _onError(err) {
3927
+ this.emit("error", err);
3928
+ }
3929
+ _handleProxy(socket, callback) {
3930
+ let socketOptions = {
3931
+ id: BigInt("0x" + crypto.randomBytes(10).toString("hex")).toString(32).padStart(16, "0")
3932
+ };
3933
+ if (!this.options.useProxy || Array.isArray(this.options.useProxy) && !this.options.useProxy.includes(socket.remoteAddress) && !this.options.useProxy.includes("*")) {
3934
+ socketOptions.ignore = this.options.ignoredHosts && this.options.ignoredHosts.includes(socket.remoteAddress);
3935
+ return setImmediate(() => callback(null, socketOptions));
3936
+ }
3937
+ let chunks = [];
3938
+ let chunklen = 0;
3939
+ let socketReader = () => {
3940
+ let chunk;
3941
+ while ((chunk = socket.read()) !== null) {
3942
+ for (let i = 0, len = chunk.length; i < len; i++) {
3943
+ let chr = chunk[i];
3944
+ if (chr === 10) {
3945
+ socket.removeListener("readable", socketReader);
3946
+ chunks.push(chunk.slice(0, i + 1));
3947
+ chunklen += i + 1;
3948
+ let remainder = chunk.slice(i + 1);
3949
+ if (remainder.length) {
3950
+ socket.unshift(remainder);
3951
+ }
3952
+ let header = Buffer.concat(chunks, chunklen).toString().trim();
3953
+ let params = (header || "").toString().split(" ");
3954
+ let commandName = params.shift().toUpperCase();
3955
+ if (commandName !== "PROXY") {
3956
+ try {
3957
+ socket.end("* BAD Invalid PROXY header\r\n");
3958
+ } catch {
3959
+ }
3960
+ return;
3961
+ }
3962
+ if (params[1]) {
3963
+ socketOptions.remoteAddress = params[1].trim().toLowerCase();
3964
+ socketOptions.ignore = this.options.ignoredHosts && this.options.ignoredHosts.includes(socketOptions.remoteAddress);
3965
+ try {
3966
+ if (!socketOptions.ignore) {
3967
+ this.logger.info(
3968
+ {
3969
+ tnx: "proxy",
3970
+ cid: socketOptions.id,
3971
+ proxy: params[1].trim().toLowerCase()
3972
+ },
3973
+ "[%s] PROXY from %s through %s (%s)",
3974
+ socketOptions.id,
3975
+ params[1].trim().toLowerCase(),
3976
+ params[2].trim().toLowerCase(),
3977
+ JSON.stringify(params)
3978
+ );
3979
+ }
3980
+ } catch {
3981
+ socket.end("* BAD Invalid PROXY header\r\n");
3982
+ return;
3983
+ }
3984
+ if (params[3]) {
3985
+ socketOptions.remotePort = Number(params[3].trim()) || socketOptions.remotePort;
3986
+ }
3987
+ }
3988
+ return callback(null, socketOptions);
3989
+ }
3990
+ }
3991
+ chunks.push(chunk);
3992
+ chunklen += chunk.length;
3993
+ }
3994
+ };
3995
+ socket.on("readable", socketReader);
3996
+ }
3997
+ /**
3998
+ * Called when a new connection is established. This might not be the same time the socket is opened
3999
+ *
4000
+ * @event
4001
+ */
4002
+ _onClientConnect(data) {
4003
+ this.emit("connect", data);
4004
+ }
4005
+ /**
4006
+ * Normalize hostname
4007
+ *
4008
+ * @event
4009
+ */
4010
+ _normalizeHostname(hostname) {
4011
+ try {
4012
+ hostname = punycode2.toUnicode((hostname || "").toString().trim()).toLowerCase();
4013
+ } catch (E) {
4014
+ this.logger.error(
4015
+ {
4016
+ tnx: "punycode"
4017
+ },
4018
+ 'Failed to process punycode domain "%s". error=%s',
4019
+ hostname,
4020
+ E.message
4021
+ );
4022
+ }
4023
+ return hostname;
4024
+ }
4025
+ _upgrade(socket, callback) {
4026
+ let socketOptions = {
4027
+ secureContext: this.secureContext.get("*"),
4028
+ isServer: true,
4029
+ server: this.server,
4030
+ SNICallback: (servername, cb) => {
4031
+ this.options.SNICallback(this._normalizeHostname(servername), (err, context) => {
4032
+ if (err) {
4033
+ this.logger.error(
4034
+ {
4035
+ tnx: "sni",
4036
+ servername,
4037
+ err
4038
+ },
4039
+ "Failed to fetch SNI context for servername %s",
4040
+ servername
4041
+ );
4042
+ }
4043
+ return cb(null, context || this.secureContext.get("*"));
4044
+ });
4045
+ }
4046
+ };
4047
+ let returned = false;
4048
+ let tlsSocket;
4049
+ let onError = (err) => {
4050
+ if (returned) {
4051
+ return;
4052
+ }
4053
+ returned = true;
4054
+ const meta = {};
4055
+ if (tlsSocket) {
4056
+ meta.tlsProtocol = tlsSocket.getProtocol();
4057
+ }
4058
+ meta.protocol = "smtp";
4059
+ meta.stage = "connect";
4060
+ meta.remoteAddress = socket.remoteAddress;
4061
+ if (err) {
4062
+ err.meta = meta;
4063
+ }
4064
+ if (err && /SSL[23]*_GET_CLIENT_HELLO|ssl[23]*_read_bytes|ssl_bytes_to_cipher_list/i.test(err.message)) {
4065
+ let message = err.message;
4066
+ err.message = "Failed to establish TLS session";
4067
+ err.responseCode = 500;
4068
+ err.code = err.code || "TLSError";
4069
+ meta.message = message;
4070
+ }
4071
+ if (!err || !err.message) {
4072
+ err = new Error("Socket closed while initiating TLS");
4073
+ err.responseCode = 500;
4074
+ err.code = "SocketError";
4075
+ err.report = false;
4076
+ err.meta = meta;
4077
+ }
4078
+ callback(err || new Error("Socket closed unexpectedly"));
4079
+ };
4080
+ socket.once("error", onError);
4081
+ tlsSocket = new tls.TLSSocket(socket, socketOptions);
4082
+ tlsSocket.once("close", onError);
4083
+ tlsSocket.once("error", onError);
4084
+ tlsSocket.once("_tlsError", onError);
4085
+ tlsSocket.once("clientError", onError);
4086
+ tlsSocket.once("tlsClientError", onError);
4087
+ tlsSocket.on("secure", () => {
4088
+ socket.removeListener("error", onError);
4089
+ tlsSocket.removeListener("close", onError);
4090
+ tlsSocket.removeListener("error", onError);
4091
+ tlsSocket.removeListener("_tlsError", onError);
4092
+ tlsSocket.removeListener("clientError", onError);
4093
+ tlsSocket.removeListener("tlsClientError", onError);
4094
+ if (returned) {
4095
+ try {
4096
+ tlsSocket.end();
4097
+ } catch {
4098
+ }
4099
+ return;
4100
+ }
4101
+ returned = true;
4102
+ return callback(null, tlsSocket);
4103
+ });
4104
+ }
4105
+ };
4106
+ module.exports.SMTPServer = SMTPServer2;
4107
+ }
4108
+ });
4109
+
4110
+ // src/smtp-server.ts
4111
+ var import_smtp_server = __toESM(require_smtp_server());
4112
+ var DEFAULT_SMTP_PORT = 2525;
4113
+ var DEFAULT_SMTP_HOST = "127.0.0.1";
4114
+ function resolveSmtpServerConfig(config = {}) {
4115
+ const { port = DEFAULT_SMTP_PORT, host = DEFAULT_SMTP_HOST, label, ...serverOptions } = config;
4116
+ return {
4117
+ ...serverOptions,
4118
+ port,
4119
+ host,
4120
+ label
4121
+ };
4122
+ }
4123
+ function normalizeSmtpServerConfigs(input) {
4124
+ const configs = Array.isArray(input) ? input : input ? [input] : [];
4125
+ return configs.map((config) => resolveSmtpServerConfig(config));
4126
+ }
4127
+ function closeSmtpServer(server) {
4128
+ return new Promise((resolve, reject) => {
4129
+ let settled = false;
4130
+ const finish = (error2) => {
4131
+ if (settled) {
4132
+ return;
4133
+ }
4134
+ settled = true;
4135
+ if (error2) {
4136
+ reject(error2);
4137
+ return;
4138
+ }
4139
+ resolve();
4140
+ };
4141
+ const handleCloseError = (error2) => {
4142
+ const errorCode = error2.code;
4143
+ if (errorCode === "ERR_SERVER_NOT_RUNNING") {
4144
+ finish();
4145
+ return;
4146
+ }
4147
+ finish(error2);
4148
+ };
4149
+ try {
4150
+ server.close(() => finish());
4151
+ } catch (error2) {
4152
+ handleCloseError(error2);
4153
+ }
4154
+ });
4155
+ }
4156
+ function createSmtpServer(config = {}) {
4157
+ const resolvedConfig = resolveSmtpServerConfig(config);
4158
+ const { port, host, label: _label, ...serverOptions } = resolvedConfig;
4159
+ const server = new import_smtp_server.SMTPServer(serverOptions);
4160
+ return {
4161
+ server,
4162
+ config: resolvedConfig,
4163
+ listen(callback) {
4164
+ return callback ? server.listen(port, host, callback) : server.listen(port, host);
4165
+ },
4166
+ address() {
4167
+ return server.server.address();
4168
+ },
4169
+ close() {
4170
+ return closeSmtpServer(server);
4171
+ }
4172
+ };
4173
+ }
4174
+ function startSmtpServer(config = {}) {
4175
+ const handle = createSmtpServer(config);
4176
+ handle.listen();
4177
+ return handle;
4178
+ }
4179
+ var smtp_server_default = {
4180
+ SMTPServer: import_smtp_server.SMTPServer,
4181
+ createSmtpServer,
4182
+ startSmtpServer,
4183
+ DEFAULT_SMTP_HOST,
4184
+ DEFAULT_SMTP_PORT
4185
+ };
4186
+ })();