2018-05-14 23:02:16 +00:00
|
|
|
const SMTPError = require('./error');
|
2018-07-06 20:21:43 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @typedef {import('net').Socket} Socket
|
|
|
|
* @typedef {import('tls').TLSSocket} TLSSocket
|
|
|
|
*/
|
2011-02-23 21:23:37 +00:00
|
|
|
|
2018-05-14 23:02:16 +00:00
|
|
|
class SMTPResponse {
|
2018-06-28 03:10:40 +00:00
|
|
|
/**
|
|
|
|
* @constructor
|
2018-07-06 20:21:43 +00:00
|
|
|
* @param {Socket | TLSSocket} stream the open socket to stream a response from
|
2018-06-28 03:10:40 +00:00
|
|
|
* @param {number} timeout the time to wait (in milliseconds) before closing the socket
|
2018-06-29 03:44:54 +00:00
|
|
|
* @param {function(Error): void} onerror the function to call on error
|
2018-06-28 03:10:40 +00:00
|
|
|
*/
|
2018-05-14 23:02:16 +00:00
|
|
|
constructor(stream, timeout, onerror) {
|
|
|
|
let buffer = '';
|
2011-09-25 21:51:29 +00:00
|
|
|
|
2018-06-28 03:10:40 +00:00
|
|
|
/**
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
2018-05-14 23:02:16 +00:00
|
|
|
const notify = () => {
|
|
|
|
if (buffer.length) {
|
|
|
|
// parse buffer for response codes
|
|
|
|
const line = buffer.replace('\r', '');
|
2018-05-27 04:25:08 +00:00
|
|
|
if (
|
|
|
|
!line
|
|
|
|
.trim()
|
|
|
|
.split(/\n/)
|
|
|
|
.pop()
|
|
|
|
.match(/^(\d{3})\s/)
|
|
|
|
) {
|
2018-05-14 23:02:16 +00:00
|
|
|
return;
|
|
|
|
}
|
2014-07-31 02:01:27 +00:00
|
|
|
|
2018-05-14 23:02:16 +00:00
|
|
|
const match = line ? line.match(/(\d+)\s?(.*)/) : null;
|
2018-05-27 04:25:08 +00:00
|
|
|
const data =
|
|
|
|
match !== null
|
|
|
|
? { code: match[1], message: match[2], data: line }
|
|
|
|
: { code: -1, data: line };
|
2011-02-23 21:23:37 +00:00
|
|
|
|
2018-05-14 23:02:16 +00:00
|
|
|
stream.emit('response', null, data);
|
|
|
|
buffer = '';
|
|
|
|
}
|
|
|
|
};
|
2011-02-23 21:23:37 +00:00
|
|
|
|
2018-06-28 03:10:40 +00:00
|
|
|
/**
|
|
|
|
* @param {Error} err the error object
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
2018-05-27 04:25:08 +00:00
|
|
|
const error = err => {
|
|
|
|
stream.emit(
|
|
|
|
'response',
|
|
|
|
SMTPError('connection encountered an error', SMTPError.ERROR, err)
|
|
|
|
);
|
|
|
|
};
|
2011-02-23 21:23:37 +00:00
|
|
|
|
2018-06-28 03:10:40 +00:00
|
|
|
/**
|
|
|
|
* @param {Error} err the error object
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
2018-05-27 04:25:08 +00:00
|
|
|
const timedout = err => {
|
2018-05-14 23:02:16 +00:00
|
|
|
stream.end();
|
2018-05-27 04:25:08 +00:00
|
|
|
stream.emit(
|
|
|
|
'response',
|
|
|
|
SMTPError(
|
|
|
|
'timedout while connecting to smtp server',
|
|
|
|
SMTPError.TIMEDOUT,
|
|
|
|
err
|
|
|
|
)
|
|
|
|
);
|
|
|
|
};
|
2014-07-30 15:54:34 +00:00
|
|
|
|
2018-06-28 03:10:40 +00:00
|
|
|
/**
|
|
|
|
* @param {string | Buffer} data the data
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
2018-05-27 04:25:08 +00:00
|
|
|
const watch = data => {
|
2018-05-14 23:02:16 +00:00
|
|
|
if (data !== null) {
|
|
|
|
buffer += data.toString();
|
|
|
|
notify();
|
|
|
|
}
|
|
|
|
};
|
2011-02-23 21:23:37 +00:00
|
|
|
|
2018-06-28 03:10:40 +00:00
|
|
|
/**
|
|
|
|
* @param {Error} err the error object
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
2018-05-27 04:25:08 +00:00
|
|
|
const close = err => {
|
|
|
|
stream.emit(
|
|
|
|
'response',
|
|
|
|
SMTPError('connection has closed', SMTPError.CONNECTIONCLOSED, err)
|
|
|
|
);
|
2018-05-14 23:02:16 +00:00
|
|
|
};
|
2011-02-23 21:23:37 +00:00
|
|
|
|
2018-06-28 03:10:40 +00:00
|
|
|
/**
|
|
|
|
* @param {Error} err the error object
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
2018-05-27 04:25:08 +00:00
|
|
|
const end = err => {
|
|
|
|
stream.emit(
|
|
|
|
'response',
|
|
|
|
SMTPError('connection has ended', SMTPError.CONNECTIONENDED, err)
|
|
|
|
);
|
2018-05-14 23:02:16 +00:00
|
|
|
};
|
2011-02-23 21:23:37 +00:00
|
|
|
|
2018-06-28 03:10:40 +00:00
|
|
|
/**
|
|
|
|
* @param {Error} [err] the error object
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
2018-05-27 04:25:08 +00:00
|
|
|
this.stop = err => {
|
2018-05-14 23:02:16 +00:00
|
|
|
stream.removeAllListeners('response');
|
2018-07-06 20:21:43 +00:00
|
|
|
stream.removeListener('data', watch);
|
|
|
|
stream.removeListener('end', end);
|
|
|
|
stream.removeListener('close', close);
|
|
|
|
stream.removeListener('error', error);
|
2011-09-25 21:51:29 +00:00
|
|
|
|
2018-06-28 03:10:40 +00:00
|
|
|
if (err != null && typeof onerror === 'function') {
|
2018-06-04 16:40:23 +00:00
|
|
|
onerror(err);
|
|
|
|
}
|
2018-05-14 23:02:16 +00:00
|
|
|
};
|
2011-02-23 21:23:37 +00:00
|
|
|
|
2018-07-06 20:21:43 +00:00
|
|
|
stream.on('data', watch);
|
|
|
|
stream.on('end', end);
|
|
|
|
stream.on('close', close);
|
|
|
|
stream.on('error', error);
|
|
|
|
stream.setTimeout(timeout, timedout);
|
2018-05-14 23:02:16 +00:00
|
|
|
}
|
|
|
|
}
|
2018-06-29 00:55:20 +00:00
|
|
|
|
2018-06-29 03:25:10 +00:00
|
|
|
exports.SMTPResponse = SMTPResponse;
|
|
|
|
|
2018-06-28 03:10:40 +00:00
|
|
|
/**
|
2018-07-06 20:21:43 +00:00
|
|
|
* @param {Socket | TLSSocket} stream the open socket to stream a response from
|
2018-06-28 03:10:40 +00:00
|
|
|
* @param {number} timeout the time to wait (in milliseconds) before closing the socket
|
2018-06-29 03:44:54 +00:00
|
|
|
* @param {function(Error): void} onerror the function to call on error
|
2018-06-28 03:10:40 +00:00
|
|
|
* @returns {SMTPResponse} the smtp response
|
|
|
|
*/
|
2018-05-27 04:25:08 +00:00
|
|
|
exports.monitor = (stream, timeout, onerror) =>
|
|
|
|
new SMTPResponse(stream, timeout, onerror);
|