mirror of https://github.com/eleith/emailjs.git
smtp/connection: implement greylist support (#265)
* smtp/connection: implement greylist support * smtp/connection: use weakmap to track if response has handled greylist * smtp/connection: fix style nits * test/client: fix greylist unit * readme: note greylist support * smtp/connection: accept code 451 for greylist resend * smtp/connection: reorder greylist conditionals in response callback * test/client: only respond once to greylist
This commit is contained in:
parent
b1b6fad17b
commit
b1a101d976
|
@ -15,6 +15,7 @@ send emails, html and attachments (files, streams and strings) from node.js to a
|
|||
- attachments can be added as strings, streams or file paths
|
||||
- supports utf-8 headers and body
|
||||
- built-in type declarations
|
||||
- automatically handles [greylisting](http://projects.puremagic.com/greylisting/whitepaper.html)
|
||||
|
||||
## REQUIRES
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ const SMTP_PORT = 25 as const;
|
|||
const SMTP_SSL_PORT = 465 as const;
|
||||
const SMTP_TLS_PORT = 587 as const;
|
||||
const CRLF = '\r\n' as const;
|
||||
const GREYLIST_DELAY = 300 as const;
|
||||
|
||||
let DEBUG: 0 | 1 = 0;
|
||||
|
||||
|
@ -115,6 +116,11 @@ export class SMTPConnection extends EventEmitter {
|
|||
protected tls: boolean | SMTPSocketOptions = false;
|
||||
protected port: number;
|
||||
|
||||
private greylistResponseTracker = new WeakMap<
|
||||
(...rest: any[]) => void,
|
||||
boolean
|
||||
>();
|
||||
|
||||
/**
|
||||
* SMTP class written using python's (2.7) smtplib.py as a base.
|
||||
*
|
||||
|
@ -393,8 +399,18 @@ export class SMTPConnection extends EventEmitter {
|
|||
if (err) {
|
||||
caller(callback, err);
|
||||
} else {
|
||||
if (codesArray.indexOf(Number(msg.code)) !== -1) {
|
||||
const code = Number(msg.code);
|
||||
if (codesArray.indexOf(code) !== -1) {
|
||||
caller(callback, err, msg.data, msg.message);
|
||||
} else if (
|
||||
(code === 450 || code === 451) &&
|
||||
msg.message.toLowerCase().includes('greylist') &&
|
||||
this.greylistResponseTracker.get(response) === false
|
||||
) {
|
||||
this.greylistResponseTracker.set(response, true);
|
||||
setTimeout(() => {
|
||||
this.send(cmd + CRLF, response);
|
||||
}, GREYLIST_DELAY);
|
||||
} else {
|
||||
const suffix = msg.message ? `: ${msg.message}` : '';
|
||||
const errorMessage = `bad response on command '${
|
||||
|
@ -413,6 +429,7 @@ export class SMTPConnection extends EventEmitter {
|
|||
}
|
||||
};
|
||||
|
||||
this.greylistResponseTracker.set(response, false);
|
||||
this.send(cmd + CRLF, response);
|
||||
}
|
||||
|
||||
|
|
|
@ -213,3 +213,80 @@ test('client constructor throws if `password` supplied without `user`', (t) => {
|
|||
>)
|
||||
);
|
||||
});
|
||||
|
||||
test.cb('client supports greylisting', (t) => {
|
||||
t.plan(2);
|
||||
|
||||
const msg = {
|
||||
subject: 'this is a test TEXT message from emailjs',
|
||||
from: 'piglet@gmail.com',
|
||||
bcc: 'pooh@gmail.com',
|
||||
text: "It is hard to be brave when you're only a Very Small Animal.",
|
||||
};
|
||||
|
||||
const { onRcptTo } = server;
|
||||
server.onRcptTo = (_address, _session, callback) => {
|
||||
server.onRcptTo = (a, s, cb) => {
|
||||
t.pass();
|
||||
onRcptTo(a, s, cb);
|
||||
};
|
||||
|
||||
const err = new Error('greylist');
|
||||
((err as never) as { responseCode: number }).responseCode = 450;
|
||||
callback(err);
|
||||
};
|
||||
|
||||
client.send(new Message(msg), (err) => {
|
||||
if (err) {
|
||||
t.fail();
|
||||
}
|
||||
t.pass();
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test.cb('client only responds once to greylisting', (t) => {
|
||||
t.plan(3);
|
||||
|
||||
const msg = {
|
||||
subject: 'this is a test TEXT message from emailjs',
|
||||
from: 'piglet@gmail.com',
|
||||
bcc: 'pooh@gmail.com',
|
||||
text: "It is hard to be brave when you're only a Very Small Animal.",
|
||||
};
|
||||
|
||||
const greylistPort = 2527;
|
||||
const greylistClient = new SMTPClient({
|
||||
port: greylistPort,
|
||||
user: 'pooh',
|
||||
password: 'honey',
|
||||
ssl: true,
|
||||
});
|
||||
const greylistServer = new SMTPServer({
|
||||
secure: true,
|
||||
onRcptTo(_address, _session, callback) {
|
||||
t.pass();
|
||||
const err = new Error('greylist');
|
||||
((err as never) as { responseCode: number }).responseCode = 450;
|
||||
callback(err);
|
||||
},
|
||||
onAuth(auth, _session, callback) {
|
||||
if (auth.username === 'pooh' && auth.password === 'honey') {
|
||||
callback(null, { user: 'pooh' });
|
||||
} else {
|
||||
return callback(new Error('invalid user / pass'));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
greylistServer.listen(greylistPort, () => {
|
||||
greylistClient.send(new Message(msg), (err) => {
|
||||
if (err) {
|
||||
t.pass();
|
||||
t.end();
|
||||
} else {
|
||||
t.fail();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue