-
Notifications
You must be signed in to change notification settings - Fork 1
/
smtp.js
106 lines (100 loc) · 3.19 KB
/
smtp.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
const cache = require('memory-cache');
const SMTPServer = require('smtp-server').SMTPServer;
const MailParser = require('mailparser').MailParser;
const bind = process.env.SMSMTP_BIND || '127.0.0.1';
const port = process.env.SMSMTP_PORT || 2525;
const host = process.env.SMSMTP_HOST || 'localhost';
const ttl = Math.max(0, 1000 * +(process.env.SMSMTP_TTL || '300'));
const token_min = 8;
const token_max = 32;
const token_pat = /^[0-9a-z]/;
const checkToken = function(token) {
'use strict';
if (!token) {
return 'missing';
}
token = '' + token;
const len = token.length;
if (token_min > len || token_max < len) {
return 'must have ' + token_min + ' to ' + token_max + 'characters';
}
if (!token_pat.test(token)) {
return 'must contain 0-9 a-z only';
}
return false;
};
var server = new SMTPServer({
logger: true,
banner: 'SMSMTP single mail local SMTP testing',
requireAuthentication: true,
hideSTARTTLS: true,
size: 1 * 1024 * 1024, // 1 MB
useXClient: true,
useXForward: false,
onAuth: function (auth, session, callback) {
'use strict';
var username = auth && auth.username;
var tokenInvalid = checkToken(username);
if (tokenInvalid) {
return callback(new Error('username ' + tokenInvalid));
}
return callback(null, {
user: {
username: auth.username
}
});
},
onConnect: function (session, callback) {
'use strict';
if (session.remoteAddress !== '127.0.0.1') {
return callback(new Error('Only connections from localhost allowed'));
}
return callback(); // Accept the connection
},
onData: function (stream, session, callback) {
'use strict';
var mailparser = session.mailparser;
const user = session.user;
const username = user && user.username || 'unknown';
const qMessage = 'Message queued, see: https://' + host + '/' + username + '/';
var firstRecipient;
if (!session.mailparser) {
mailparser = new MailParser();
session.mailparser = mailparser;
const mailparserOnEnd = function (email) {
email.attachments = (email.attachments || []).map(function(attachment){
var b = Buffer.from(attachment.content);
attachment.content = b.toString('base64');
return attachment;
});
(email.to || []).forEach(function(recipient) {
if (!firstRecipient) {
firstRecipient = recipient.address;
}
cache.put(username + '/' + recipient.address, email, ttl);
});
// accept the message once the mail parser is through
callback(null, qMessage + (firstRecipient || ''));
};
mailparser.on('end', mailparserOnEnd);
}
const streamOnEnd = function () {
var err;
if (stream.sizeExceeded) {
err = new Error('Error: message exceeds fixed maximum message size 1 MB');
err.responseCode = 552;
callback(err);
}
};
stream.on('end', streamOnEnd);
stream.pipe(mailparser);
}
});
server.on('error', function (err) {
'use strict';
console.log('Error occurred');
console.log(err);
});
// start listening
server.listen(port, bind);
console.log('SMSMTP SMTP backend (' + host + ') listening on: ' + bind + ':' + port);