total5 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/mail.js ADDED
@@ -0,0 +1,922 @@
1
+ // Total.js SMTP sender
2
+ // The MIT License
3
+ // Copyright 2016-2023 (c) Peter Širka <petersirka@gmail.com>
4
+
5
+ const CRLF = '\r\n';
6
+ const REG_ESMTP = /\besmtp\b/i;
7
+ const REG_STATE = /\d+/;
8
+ const REG_WINLINE = /\r\n/g;
9
+ const REG_NEWLINE = /\n/g;
10
+ const REG_AUTH = /(AUTH LOGIN|AUTH PLAIN|PLAIN LOGIN|XOAUTH2|XOAUTH)/i;
11
+ const REG_TLS = /TLS/;
12
+ const REG_STARTTLS = /STARTTLS/;
13
+ const ATTACHMENT = { encoding: 'base64' };
14
+
15
+ var INDEXATTACHMENT = 0;
16
+
17
+ const CRLF_BUFFER = Buffer.from(CRLF);
18
+ const CONCAT = [null, null];
19
+
20
+ var Mailer = {};
21
+ Mailer.debug = false;
22
+ Mailer.Message = Message;
23
+ Mailer.Mail = Message;
24
+ Mailer.connections = {};
25
+ Mailer.create = (subject, body) => new Message(subject, body);
26
+ F.TUtils.EventEmitter2.extend(Mailer);
27
+
28
+ function Message(subject, body) {
29
+ var self = this;
30
+ self.subject = subject || '';
31
+ self.body = body || '';
32
+ self.type = 'html';
33
+ self.files;
34
+ self.email_to = [];
35
+ self.email_reply;
36
+ self.email_cc;
37
+ self.email_bcc;
38
+ self.email_from = '';
39
+ self.closed = false;
40
+ self.tls = false;
41
+ self.$callback;
42
+
43
+ // t.headers;
44
+ // t.$unsubscribe;
45
+ }
46
+
47
+ Message.prototype.preview = function(val) {
48
+ this.$preview = val;
49
+ return this;
50
+ };
51
+
52
+ Message.prototype.unsubscribe = function(url) {
53
+ var tmp = url.substring(0, 6);
54
+ this.$unsubscribe = url ? (tmp === 'http:/' || tmp === 'https:' ? '<' + url + '>' : '<mailto:' + url + '>') : null;
55
+ return this;
56
+ };
57
+
58
+ Message.prototype.callback = function(fn) {
59
+ this.$callback = fn;
60
+ return this;
61
+ };
62
+
63
+ Message.prototype.sender = Message.prototype.from = function(email) {
64
+ this.email_from = email;
65
+ return this;
66
+ };
67
+
68
+ Message.prototype.high = function() {
69
+ this.$priority = 1;
70
+ return this;
71
+ };
72
+
73
+ Message.prototype.low = function() {
74
+ this.$priority = 5;
75
+ return this;
76
+ };
77
+
78
+ Message.prototype.confidential = function() {
79
+ this.$confidential = true;
80
+ return this;
81
+ };
82
+
83
+ Message.prototype.to = function(value, clear) {
84
+ var self = this;
85
+ if (clear)
86
+ self.email_to.length = 0;
87
+ self.email_to.push(value);
88
+ return self;
89
+ };
90
+
91
+ Message.prototype.cc = function(value, clear) {
92
+ var self = this;
93
+ if (clear || !self.email_cc)
94
+ self.email_cc = [];
95
+ self.email_cc.push(value);
96
+ return self;
97
+ };
98
+
99
+ Message.prototype.bcc = function(value, clear) {
100
+ var self = this;
101
+ if (clear || !self.email_bcc)
102
+ self.email_bcc = [];
103
+ self.email_bcc.push(value);
104
+ return self;
105
+ };
106
+
107
+ Message.prototype.reply = function(value, clear) {
108
+ var self = this;
109
+ if (clear || !self.email_reply)
110
+ self.email_reply = [];
111
+ self.email_reply.push(value);
112
+ return self;
113
+ };
114
+
115
+ Message.prototype.attachment = function(filename, name, contentid) {
116
+
117
+ var self = this;
118
+ var type;
119
+ var ext;
120
+
121
+ if (name) {
122
+ ext = F.TUtils.getExtension(name);
123
+ type = F.TUtils.contentTypes[ext];
124
+ }
125
+
126
+ var obj = {};
127
+ obj.name = name;
128
+ obj.filename = filename;
129
+ obj.type = type;
130
+ obj.ext = ext;
131
+
132
+ if (contentid) {
133
+ obj.disposition = 'inline';
134
+ obj.contentid = contentid;
135
+ }
136
+
137
+ if (!self.attachments)
138
+ self.attachments = [];
139
+
140
+ self.attachments.push(obj);
141
+ return self;
142
+ };
143
+
144
+ Message.prototype.attachmentfs = function(storage, id, name, contentid) {
145
+
146
+ var self = this;
147
+ var ext;
148
+ var type;
149
+
150
+ if (name) {
151
+ ext = F.TUtils.getExtension(name);
152
+ type = F.TUtils.contentTypes[ext];
153
+ }
154
+
155
+ var obj = {};
156
+ obj.storage = storage;
157
+ obj.name = name;
158
+ obj.filename = id;
159
+ obj.type = type;
160
+ obj.ext = ext;
161
+
162
+ if (contentid) {
163
+ obj.disposition = 'inline';
164
+ obj.contentid = contentid;
165
+ }
166
+
167
+ if (!self.attachments)
168
+ self.attachments = [];
169
+
170
+ self.attachments.push(obj);
171
+ return self;
172
+ };
173
+
174
+ Message.prototype.manually = function() {
175
+ this.$sending && clearImmediate(this.$sending);
176
+ return this;
177
+ };
178
+
179
+ Message.prototype.send2 = function(callback) {
180
+
181
+ var self = this;
182
+
183
+ if (F.config.$tapi && F.config.$tapimail) {
184
+
185
+ var data = {};
186
+
187
+ data.to = [];
188
+
189
+ for (let m of self.email_to)
190
+ data.to.push(m);
191
+
192
+ if (self.email_cc && self.email_cc.length) {
193
+ data.cc = [];
194
+ for (let m of self.email_cc)
195
+ data.cc.push(m);
196
+ }
197
+
198
+ if (self.email_bcc && self.email_bcc.length) {
199
+ data.bcc = [];
200
+ for (let m of self.email_bcc)
201
+ data.bcc.push(m);
202
+ }
203
+
204
+ if (self.email_reply && self.email_reply.length) {
205
+ data.reply = [];
206
+ for (let m of self.email_reply)
207
+ data.reply.push(m);
208
+ }
209
+
210
+ data.from = self.email_from;
211
+ data.subject = self.subject;
212
+ data.type = self.type;
213
+ data.body = self.body;
214
+ data.priority = self.$priotity;
215
+ data.unsubscribe = self.$unsubscribe;
216
+ data.confidential = self.$confidential;
217
+
218
+ F.api('TAPI/mail', data).callback(callback || NOOP);
219
+ return;
220
+ }
221
+
222
+ for (let key in F.temporary.smtp) {
223
+ if (!F.config.smtp || F.config.smtp.server !== key)
224
+ Mailer.destroy(F.temporary.smtp[key]);
225
+ }
226
+
227
+ Mailer.send(F.config.smtp, self, callback);
228
+ };
229
+
230
+ Message.prototype.send = function(options, callback) {
231
+ var self = this;
232
+ self.$callback2 = callback;
233
+ Mailer.send(options, self, callback);
234
+ return self;
235
+ };
236
+
237
+ Mailer.tls = function(obj, opt) {
238
+
239
+ var self = this;
240
+
241
+ obj.tls = true;
242
+ obj.socket.removeAllListeners();
243
+
244
+ var tlsoptions = { socket: obj.socket, host: obj.socket.$host, ciphers: 'SSLv3' };
245
+
246
+ for (let key in opt.tls)
247
+ tlsoptions[key] = opt.tls[key];
248
+
249
+ obj.socket2 = F.Tls.connect(tlsoptions, () => self.$send(obj, opt, true));
250
+
251
+ obj.socket2.on('error', function(err) {
252
+ Mailer.destroy(obj);
253
+ self.closed = true;
254
+ self.callback && self.callback(err);
255
+ self.callback = null;
256
+ if (obj.try || err.stack.indexOf('ECONNRESET') !== -1)
257
+ return;
258
+ Mailer.$events.error && Mailer.emit('error', err, obj);
259
+ });
260
+
261
+ obj.socket2.on('clientError', function(err) {
262
+ Mailer.destroy(obj);
263
+ self.callback && self.callback(err);
264
+ self.callback = null;
265
+ Mailer.$events.error && !obj.try && Mailer.emit('error', err, obj);
266
+ });
267
+
268
+ obj.socket2.on('connect', () => !opt.secure && self.$send(obj, opt));
269
+ };
270
+
271
+ Mailer.destroy = function(obj) {
272
+
273
+ var self = this;
274
+
275
+ if (obj.destroyed)
276
+ return self;
277
+
278
+ obj.destroyed = true;
279
+ obj.closed = true;
280
+
281
+ if (obj.socket) {
282
+ obj.socket.removeAllListeners();
283
+ obj.socket.end();
284
+ obj.socket.destroy();
285
+ obj.socket = null;
286
+ }
287
+
288
+ if (obj.socket2) {
289
+ obj.socket2.removeAllListeners();
290
+ obj.socket2.end();
291
+ obj.socket2.destroy();
292
+ obj.socket2 = null;
293
+ }
294
+
295
+ if (obj === F.temporary.smtp[obj.server])
296
+ delete F.temporary.smtp[obj.server];
297
+
298
+ delete self.connections[obj.id];
299
+ return self;
300
+ };
301
+
302
+ Mailer.$writeattachment = function(obj) {
303
+
304
+ var attachment = obj.files ? obj.files.shift() : false;
305
+ if (!attachment) {
306
+
307
+ Mailer.$writeline(obj, '--' + obj.boundary + '--', '', '.');
308
+
309
+ if (obj.callback) {
310
+ obj.callback(null, obj.instance);
311
+ obj.callback = null;
312
+ }
313
+
314
+ if (obj.messagecallback) {
315
+ obj.messagecallback(null, obj.instance);
316
+ obj.messagecallback = null;
317
+ }
318
+
319
+ if (obj.messagecallback2) {
320
+ obj.messagecallback2(null, obj.instance);
321
+ obj.messagecallback2 = null;
322
+ }
323
+
324
+ return this;
325
+ }
326
+
327
+ var stream;
328
+
329
+ if (attachment.storage) {
330
+ FILESTORAGE(attachment.storage).readbase64(attachment.filename, function(err, meta) {
331
+ if (err) {
332
+ F.error(err, 'Mail.filestorage()', attachment.filename);
333
+ Mailer.$writeattachment(obj);
334
+ } else {
335
+
336
+ if (!attachment.name) {
337
+ attachment.name = meta.name;
338
+ attachment.type = meta.type;
339
+ attachment.extension = meta.ext;
340
+ }
341
+
342
+ writeattachemnt_stream(attachment, obj, meta.stream);
343
+ }
344
+ });
345
+ } else {
346
+ F.stats.performance.open++;
347
+ stream = F.Fs.createReadStream(attachment.filename, ATTACHMENT);
348
+ writeattachemnt_stream(attachment, obj, stream);
349
+ }
350
+
351
+ return this;
352
+ };
353
+
354
+ function writeattachemnt_stream(attachment, obj, stream) {
355
+
356
+ var name = attachment.name;
357
+ var isCalendar = attachment.extension === 'ics';
358
+ var message = [];
359
+
360
+ message.push('--' + obj.boundary);
361
+
362
+ if (!isCalendar) {
363
+ if (attachment.contentid) {
364
+ message.push('Content-Disposition: inline; filename="' + name + '"');
365
+ message.push('Content-ID: <' + attachment.contentid + '>');
366
+ } else
367
+ message.push('Content-Disposition: attachment; filename="' + name + '"');
368
+ }
369
+
370
+ message.push('Content-Type: ' + attachment.type + ';' + (isCalendar ? ' charset="utf-8"; method=REQUEST' : ''));
371
+ message.push('Content-Transfer-Encoding: base64');
372
+ message.push(CRLF);
373
+ Mailer.$writeline(obj, message.join(CRLF));
374
+
375
+ stream.$mailerdata = obj;
376
+ stream.on('data', writeattachmentbytes);
377
+
378
+ F.cleanup(stream, function() {
379
+ Mailer.$writeline(obj, CRLF);
380
+ Mailer.$writeattachment(obj);
381
+ });
382
+ }
383
+
384
+ function writeattachmentbytes(chunk) {
385
+
386
+ var length = chunk.length;
387
+ var count = 0;
388
+ var beg = 0;
389
+
390
+ while (count < length) {
391
+
392
+ count += 68;
393
+
394
+ if (count > length)
395
+ count = length;
396
+
397
+ Mailer.$writeline(this.$mailerdata, chunk.slice(beg, count).toString('base64'));
398
+ beg = count;
399
+ }
400
+ }
401
+
402
+ Mailer.try = function(options, callback) {
403
+ var self = this;
404
+ if (callback)
405
+ self.send(options, undefined, callback);
406
+ else
407
+ return new Promise((resolve, reject) => self.send(options, undefined, err => err ? reject(err) : resolve()));
408
+ };
409
+
410
+ Mailer.send2 = function(messages, callback) {
411
+ return this.send(F.config.smtp, messages, callback);
412
+ };
413
+
414
+ Mailer.send = function(opt, messages, callback) {
415
+
416
+ var cache = opt.keepalive;
417
+ var cached = cache ? F.temporary.smtp[opt.server] : null;
418
+
419
+ if (cached) {
420
+ if (messages instanceof Array) {
421
+ var count = messages.length;
422
+ F.stats.performance.mail += count;
423
+ for (var i = 0; i < count; i++)
424
+ cached.messages.push(messages[i]);
425
+ } else if (messages) {
426
+ F.stats.performance.mail++;
427
+ cached.messages.push(messages);
428
+ }
429
+ cached.trytosend();
430
+ return;
431
+ }
432
+
433
+ var self = this;
434
+ var id = F.TUtils.guid();
435
+
436
+ self.connections[id] = {};
437
+ var obj = self.connections[id];
438
+
439
+ obj.id = id;
440
+ obj.buffer = [];
441
+ obj.try = messages === undefined;
442
+ obj.messages = obj.try ? F.EMPTYARRAY : messages instanceof Array ? messages : [messages];
443
+
444
+ F.stats.performance.mail += obj.messages.length;
445
+
446
+ obj.callback = callback;
447
+ obj.closed = false;
448
+ obj.message = null;
449
+ obj.attachments = null;
450
+ obj.count = 0;
451
+ obj.socket;
452
+ obj.tls = false;
453
+ obj.date = new Date();
454
+
455
+ if (opt.secure && !opt.port)
456
+ opt.port = 465;
457
+
458
+ if (!opt.server) {
459
+ var err = new Error('No SMTP server configuration.');
460
+ callback && callback(err);
461
+ F.error(err, 'mail_smtp');
462
+ return self;
463
+ }
464
+
465
+ if (opt.secure) {
466
+ let internal = F.TUtils.copy(opt);
467
+ internal.host = opt.server;
468
+ obj.socket = F.Tls.connect(internal, () => Mailer.$send(obj, opt));
469
+ } else
470
+ obj.socket = F.Net.createConnection(opt.port, opt.server);
471
+
472
+ if (cache) {
473
+ obj.trytosend = function() {
474
+ if (!obj.sending && obj.messages && obj.messages.length) {
475
+ obj.sending = true;
476
+ obj.buffer = [];
477
+ Mailer.$writemessage(obj, obj.buffer);
478
+ Mailer.$writeline(obj, obj.buffer.shift());
479
+ }
480
+ };
481
+ obj.TS = NOW.add(cache === true ? '10 minutes' : typeof(cache) === 'number' ? (cache + ' minutes') : cache);
482
+ F.temporary.smtp[opt.server] = obj;
483
+ }
484
+
485
+ obj.cached = cache;
486
+ obj.smtp = opt;
487
+ obj.socket.$host = opt.server;
488
+ obj.host = opt.server.substring(opt.server.lastIndexOf('.', opt.server.lastIndexOf('.') - 1) + 1);
489
+
490
+ obj.socket.on('error', function(err) {
491
+
492
+ Mailer.destroy(obj);
493
+
494
+ var is = obj.callback ? true : false;
495
+
496
+ obj.callback && obj.callback(err);
497
+ obj.callback = null;
498
+
499
+ if (obj.try || err.stack.indexOf('ECONNRESET') !== -1)
500
+ return;
501
+
502
+ if (!obj.try && !is)
503
+ F.error(err, 'mail_smtp', opt.server);
504
+
505
+ if (obj === F.temporary.smtp[opt.server])
506
+ delete F.temporary.smtp[opt.server];
507
+
508
+ Mailer.$events.error && Mailer.emit('error', err, obj);
509
+ });
510
+
511
+ obj.socket.on('clientError', function(err) {
512
+
513
+ Mailer.destroy(obj);
514
+
515
+ if (!obj.try && !obj.callback)
516
+ F.error(err, 'mail_smtp', opt.server);
517
+
518
+ obj.callback && obj.callback(err);
519
+ obj.callback = null;
520
+
521
+ if (obj === F.temporary.smtp[opt.server])
522
+ delete F.temporary.smtp[opt.server];
523
+
524
+ if (Mailer.$events.error && !obj.try)
525
+ Mailer.emit('error', err, obj);
526
+
527
+ });
528
+
529
+ if (!cache) {
530
+ obj.socket.setTimeout(opt.timeout || 60000, function() {
531
+ var err = F.TUtils.httpstatus(408);
532
+ Mailer.destroy(obj);
533
+
534
+ if (!obj.try && !obj.callback)
535
+ F.error(err, 'mail_smtp', opt.server);
536
+
537
+ obj.callback && obj.callback(err);
538
+ obj.callback = null;
539
+
540
+ if (obj === F.temporary.smtp[opt.server])
541
+ delete F.temporary.smtp[opt.server];
542
+
543
+ if (Mailer.$events.error && !obj.try)
544
+ Mailer.emit('error', err, obj);
545
+ });
546
+ }
547
+
548
+ obj.sending = true;
549
+ obj.socket.on('connect', () => !opt.secure && Mailer.$send(obj, opt));
550
+ };
551
+
552
+ Mailer.$writemessage = function(obj, buffer) {
553
+
554
+ var self = this;
555
+ var msg = obj.messages.shift();
556
+ var message = [];
557
+
558
+ F.stats.other.mail++;
559
+ F.$events.$mail && F.emit('$mail', msg);
560
+
561
+ var dt = obj.date.getTime();
562
+
563
+ obj.boundary = '--total5' + dt;
564
+ obj.files = msg.files;
565
+ obj.count++;
566
+
567
+ message.push('MIME-Version: 1.0');
568
+ buffer.push('MAIL FROM: <' + msg.email_from + '>');
569
+ message.push('Message-ID: <total5X' + dt.toString(36) + 'X' + (INDEXATTACHMENT++) + 'X' + (INDEXATTACHMENT) + '>');
570
+
571
+ self.$priority && message.push('X-Priority: ' + self.$priority);
572
+ self.$confidential && message.push('Sensitivity: Company-Confidential');
573
+
574
+ message.push('From: <' + msg.email_from + '>');
575
+
576
+ if (msg.headers) {
577
+ for (let key in msg.headers)
578
+ message.push(key + ': ' + msg.headers[key]);
579
+ }
580
+
581
+ var builder = '';
582
+ var mail;
583
+
584
+ if (msg.email_to.length) {
585
+ for (let item of msg.email_to) {
586
+ mail = '<' + item + '>';
587
+ buffer.push('RCPT TO: ' + mail);
588
+ builder += (builder ? ', ' : '') + mail;
589
+ }
590
+ message.push('To: ' + builder);
591
+ builder = '';
592
+ }
593
+
594
+ if (msg.email_cc) {
595
+ for (let item of msg.email_cc) {
596
+ mail = '<' + item + '>';
597
+ buffer.push('RCPT TO: ' + mail);
598
+ builder += (builder ? ', ' : '') + mail;
599
+ }
600
+ message.push('Cc: ' + builder);
601
+ builder = '';
602
+ }
603
+
604
+ if (msg.email_bcc) {
605
+ for (let item of msg.email_bcc)
606
+ buffer.push('RCPT TO: <' + item + '>');
607
+ }
608
+
609
+ buffer.push('DATA');
610
+ buffer.push('');
611
+
612
+ message.push('Date: ' + obj.date.toUTCString());
613
+ message.push('Subject: ' + unicode_encode(msg.subject));
614
+
615
+ if (msg.$unsubscribe) {
616
+ message.push('List-Unsubscribe: ' + msg.$unsubscribe);
617
+ message.push('List-Unsubscribe-Post: List-Unsubscribe=One-Click');
618
+ }
619
+
620
+ if (msg.email_reply) {
621
+ for (let item of msg.email_reply)
622
+ builder += (builder !== '' ? ', ' : '') + '<' + item + '>';
623
+ message.push('Reply-To: ' + builder);
624
+ builder = '';
625
+ }
626
+
627
+ message.push('Content-Type: multipart/mixed; boundary="' + obj.boundary + '"');
628
+ message.push('');
629
+ message.push('--' + obj.boundary);
630
+ message.push('Content-Type: text/' + msg.type + '; charset="utf-8"');
631
+ message.push('Content-Transfer-Encoding: base64');
632
+ message.push('');
633
+ message.push(prepareBASE64(Buffer.from(msg.body.replace(REG_WINLINE, '\n').replace(REG_NEWLINE, CRLF)).toString('base64')));
634
+
635
+ // if (msg.type === 'html' && msg.$preview) {
636
+ // message.push('--' + obj.boundary);
637
+ // message.push('Content-Type: text/plain; charset="utf-8"; format="fixed"');
638
+ // message.push('Content-Transfer-Encoding: base64');
639
+ // message.push('');
640
+ // message.push(prepareBASE64(Buffer.from(msg.$preview.replace(REG_WINLINE, '\n').replace(REG_NEWLINE, CRLF)).toString('base64')));
641
+ // }
642
+
643
+ obj.message = message.join(CRLF);
644
+ obj.messagecallback = msg.$callback;
645
+ obj.messagecallback2 = msg.$callback2;
646
+ obj.instance = msg;
647
+
648
+ message = null;
649
+ return self;
650
+ };
651
+
652
+ Mailer.$writeline = function(obj) {
653
+
654
+ if (obj.closed)
655
+ return false;
656
+
657
+ var socket = obj.socket2 ? obj.socket2 : obj.socket;
658
+
659
+ for (var i = 1; i < arguments.length; i++) {
660
+ var line = arguments[i];
661
+ if (line) {
662
+ Mailer.debug && console.log('SEND', line);
663
+ socket.write(line + CRLF);
664
+ }
665
+ }
666
+
667
+ return true;
668
+ };
669
+
670
+ Mailer.$send = function(obj, options, autosend) {
671
+
672
+ var self = this;
673
+ var isAuthorized = false;
674
+ var isAuthorization = false;
675
+ var command = '';
676
+ var auth = [];
677
+ var socket = obj.socket2 ? obj.socket2 : obj.socket;
678
+ var host = obj.host;
679
+ var line = null;
680
+ var isAttach = !options.tls || (obj.tls && options.tls);
681
+
682
+ isAttach && Mailer.$events.send && Mailer.emit('send', obj);
683
+ socket.setEncoding('utf8');
684
+
685
+ socket.on('end', function() {
686
+ Mailer.destroy(obj);
687
+ obj.callback && obj.callback();
688
+ obj.callback = null;
689
+ if (obj.cached)
690
+ delete F.temporary.smtp[obj.server];
691
+ line = null;
692
+ });
693
+
694
+ socket.on('data', function(data) {
695
+
696
+ if (obj.closed)
697
+ return;
698
+
699
+ while (true) {
700
+
701
+ var index = data.indexOf(CRLF_BUFFER);
702
+ if (index === -1) {
703
+ if (line) {
704
+ CONCAT[0] = line;
705
+ CONCAT[1] = data;
706
+ line = Buffer.concat(CONCAT);
707
+ } else
708
+ line = data;
709
+ break;
710
+ }
711
+
712
+ var tmp = data.slice(0, index).toString('utf8');
713
+ data = data.slice(index + CRLF_BUFFER.length);
714
+ tmp && socket && socket.emit('line', tmp);
715
+ }
716
+ });
717
+
718
+ socket.on('line', function(line) {
719
+
720
+ line = line.toUpperCase();
721
+ Mailer.debug && console.log('<---', line);
722
+
723
+ var code = +line.match(REG_STATE)[0];
724
+ if (code === 250 && !isAuthorization) {
725
+ if (REG_AUTH.test(line) && (options.user && (options.password || options.token))) {
726
+ isAuthorization = true;
727
+ if (options.token && line.indexOf('XOAUTH2') !== -1) {
728
+ auth.push('AUTH XOAUTH2');
729
+ auth.push(Buffer.from('user=' + options.user + '\1auth=Bearer ' + options.token + '\1\1').toString('base64'));
730
+ } else if (line.lastIndexOf('XOAUTH') === -1) {
731
+ auth.push('AUTH LOGIN');
732
+ auth.push(Buffer.from(options.user).toString('base64'));
733
+ auth.push(Buffer.from(options.password).toString('base64'));
734
+ } else
735
+ auth.push('AUTH PLAIN ' + Buffer.from('\0'+ options.user + '\0' + options.password).toString('base64'));
736
+ }
737
+ }
738
+
739
+ // help
740
+ if (line.substring(3, 4) === '-')
741
+ return;
742
+
743
+ if (!isAuthorized && isAuthorization) {
744
+ isAuthorized = true;
745
+ code = 334;
746
+ }
747
+
748
+ switch (code) {
749
+ case 220:
750
+
751
+ if (obj.isTLS || REG_TLS.test(line)) {
752
+ Mailer.tls(obj, options);
753
+ } else {
754
+ obj.secured = REG_ESMTP.test(line);
755
+ command = options.heloid ? options.heloid : (obj.isTLS || (options.user && options.password) || obj.secured ? 'EHLO' : 'HELO');
756
+ Mailer.$writeline(obj, command + ' ' + host);
757
+ }
758
+
759
+ return;
760
+
761
+ case 250: // OPERATION
762
+ case 251: // FORWARD
763
+ case 235: // VERIFY
764
+ case 999: // Total.js again
765
+
766
+ if (obj.secured && !obj.isTLS && !obj.logged && obj.smtp.user && obj.smtp.password) {
767
+ // maybe TLS
768
+ obj.isTLS = true;
769
+ Mailer.$writeline(obj, 'STARTTLS');
770
+ return;
771
+ }
772
+
773
+ Mailer.$writeline(obj, obj.buffer.shift());
774
+
775
+ if (obj.buffer.length)
776
+ return;
777
+
778
+ // NEW MESSAGE
779
+ if (obj.messages.length) {
780
+ obj.buffer = [];
781
+ Mailer.$writemessage(obj, obj.buffer);
782
+ Mailer.$writeline(obj, obj.buffer.shift());
783
+ } else {
784
+
785
+ obj.sending = false;
786
+
787
+ // end
788
+ if (obj.cached)
789
+ obj.trytosend();
790
+ else
791
+ Mailer.$writeline(obj, 'QUIT');
792
+ }
793
+
794
+ return;
795
+
796
+ case 221: // BYE
797
+
798
+ if (!obj.cached)
799
+ Mailer.destroy(obj);
800
+
801
+ obj.callback && obj.callback(null, obj.try ? true : obj.count);
802
+ obj.callback = null;
803
+
804
+ return;
805
+
806
+ case 334: // LOGIN
807
+
808
+ if (!self.tls && !obj.isTLS && options.tls) {
809
+ obj.isTLS = true;
810
+ Mailer.$writeline(obj, 'STARTTLS');
811
+ return;
812
+ }
813
+
814
+ var value = auth.shift();
815
+ if (value) {
816
+ obj.logged = true;
817
+ Mailer.$writeline(obj, value);
818
+ } else {
819
+ var err = new Error('Forbidden.');
820
+ Mailer.destroy(obj);
821
+ obj.callback && obj.callback(err);
822
+ obj.callback = null;
823
+ Mailer.$events.error && !obj.try && Mailer.emit('error', err, obj);
824
+ }
825
+
826
+ return;
827
+
828
+ case 354:
829
+ Mailer.$writeline(obj, obj.message);
830
+ Mailer.$writeattachment(obj);
831
+ obj.message = null;
832
+ return;
833
+
834
+ default:
835
+
836
+ if (code < 400)
837
+ return;
838
+
839
+ if (!obj.isTLS && code === 530 && REG_STARTTLS.test(line)) {
840
+ obj.isTLS = true;
841
+ Mailer.$writeline(obj, 'STARTTLS');
842
+ return;
843
+ }
844
+
845
+ var err = line;
846
+
847
+ Mailer.$events.error && !obj.try && Mailer.emit('error', err, obj);
848
+
849
+ if (obj.messagecallback) {
850
+ obj.messagecallback(err, obj.instance);
851
+ obj.messagecallback = null;
852
+ }
853
+
854
+ if (obj.messagecallback2) {
855
+ obj.messagecallback2(err, obj.instance);
856
+ obj.messagecallback2 = null;
857
+ }
858
+
859
+ if (obj.messages.length) {
860
+
861
+ F.error(err, 'SMTP error');
862
+
863
+ // a problem
864
+ obj.buffer = [];
865
+ obj.count--;
866
+ socket.emit('line', '999 TRY NEXT MESSAGE');
867
+
868
+ } else {
869
+ Mailer.destroy(obj);
870
+ obj.callback && obj.callback(err);
871
+ obj.callback = null;
872
+ }
873
+
874
+ return;
875
+ }
876
+ });
877
+
878
+ autosend && self.$writeline(obj, 'EHLO ' + host);
879
+ };
880
+
881
+ Mailer.restart = function() {
882
+ var self = this;
883
+ self.off();
884
+ self.debug = false;
885
+ INDEXATTACHMENT = 0;
886
+ };
887
+
888
+ // Split Base64 to lines with 68 characters
889
+ function prepareBASE64(value) {
890
+
891
+ var index = 0;
892
+ var output = '';
893
+ var length = value.length;
894
+
895
+ while (index < length) {
896
+ var max = index + 68;
897
+ if (max > length)
898
+ max = length;
899
+ output += value.substring(index, max) + CRLF;
900
+ index = max;
901
+ }
902
+
903
+ return output;
904
+ }
905
+
906
+ function unicode_encode(val) {
907
+ return val ? '=?utf-8?B?' + Buffer.from(val.toString()).toString('base64') + '?=' : '';
908
+ }
909
+
910
+ // ======================================================
911
+ // EXPORTS
912
+ // ======================================================
913
+
914
+ exports.Mailer = Mailer;
915
+ exports.Message = Message;
916
+ exports.refresh = function() {
917
+ for (let key in F.temporary.smtp) {
918
+ let conn = F.temporary.smtp[key];
919
+ if (conn.TS < NOW && (!conn.messages || !conn.messages.length))
920
+ Mailer.destroy(F.temporary.smtp[key]);
921
+ }
922
+ };