emailjs/smtp/response.ts

113 lines
2.2 KiB
TypeScript

import { SMTPError, SMTPErrorStates } from './error.js';
import type { Socket } from 'net';
import type { TLSSocket } from 'tls';
export class SMTPResponseMonitor {
public readonly stop: (err?: Error) => void;
constructor(
stream: Socket | TLSSocket,
timeout: number,
onerror: (err: Error) => void
) {
let buffer = '';
const notify = () => {
if (buffer.length) {
// parse buffer for response codes
const line = buffer.replace('\r', '');
if (
!(
line
.trim()
.split(/\n/)
.pop()
?.match(/^(\d{3})\s/) ?? false
)
) {
return;
}
const match = line ? line.match(/(\d+)\s?(.*)/) : null;
const data =
match !== null
? { code: match[1], message: match[2], data: line }
: { code: -1, data: line };
stream.emit('response', null, data);
buffer = '';
}
};
const error = (err: Error) => {
stream.emit(
'response',
SMTPError.create(
'connection encountered an error',
SMTPErrorStates.ERROR,
err
)
);
};
const timedout = (err?: Error) => {
stream.end();
stream.emit(
'response',
SMTPError.create(
'timedout while connecting to smtp server',
SMTPErrorStates.TIMEDOUT,
err
)
);
};
const watch = (data: string | Buffer) => {
if (data !== null) {
buffer += data.toString();
notify();
}
};
const close = (err: Error) => {
stream.emit(
'response',
SMTPError.create(
'connection has closed',
SMTPErrorStates.CONNECTIONCLOSED,
err
)
);
};
const end = (err: Error) => {
stream.emit(
'response',
SMTPError.create(
'connection has ended',
SMTPErrorStates.CONNECTIONENDED,
err
)
);
};
this.stop = (err) => {
stream.removeAllListeners('response');
stream.removeListener('data', watch);
stream.removeListener('end', end);
stream.removeListener('close', close);
stream.removeListener('error', error);
if (err != null && typeof onerror === 'function') {
onerror(err);
}
};
stream.on('data', watch);
stream.on('end', end);
stream.on('close', close);
stream.on('error', error);
stream.setTimeout(timeout, timedout);
}
}