mirror of
https://github.com/eleith/emailjs.git
synced 2024-07-02 11:08:51 +00:00
smtp: repair differences with pre-conversion code
This commit is contained in:
parent
70620710bc
commit
1727412b98
|
@ -18,51 +18,16 @@ export interface MessageStack {
|
|||
export class Client {
|
||||
public smtp: SMTP;
|
||||
public queue: MessageStack[] = [];
|
||||
public timer: any;
|
||||
public sending: boolean;
|
||||
public ready: boolean;
|
||||
public timer: NodeJS.Timer | null = null;
|
||||
public sending = false;
|
||||
public ready = false;
|
||||
|
||||
/**
|
||||
* @typedef {Object} MessageStack
|
||||
*
|
||||
* @typedef {Object} SMTPSocketOptions
|
||||
* @property {string} key
|
||||
* @property {string} ca
|
||||
* @property {string} cert
|
||||
*
|
||||
* @typedef {Object} SMTPOptions
|
||||
* @property {number} [timeout]
|
||||
* @property {string} [user]
|
||||
* @property {string} [password]
|
||||
* @property {string} [domain]
|
||||
* @property {string} [host]
|
||||
* @property {number} [port]
|
||||
* @property {boolean|SMTPSocketOptions} [ssl]
|
||||
* @property {boolean|SMTPSocketOptions} [tls]
|
||||
* @property {string[]} [authentication]
|
||||
* @property {function(...any): void} [logger]
|
||||
*
|
||||
* @constructor
|
||||
* @param {SMTPOptions} server smtp options
|
||||
* @param {*} server smtp options
|
||||
*/
|
||||
constructor(server: Partial<import('./smtp').SMTPOptions>) {
|
||||
this.smtp = new SMTP(server);
|
||||
//this.smtp.debug(1);
|
||||
|
||||
/**
|
||||
* @type {NodeJS.Timer}
|
||||
*/
|
||||
this.timer = null;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.sending = false;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.ready = false;
|
||||
}
|
||||
|
||||
send(msg: Message, callback: (err: Error, msg: Message) => void): void {
|
||||
|
@ -107,7 +72,7 @@ export class Client {
|
|||
this.queue.push(stack);
|
||||
this._poll();
|
||||
} else {
|
||||
callback(new Error(why), /** @type {MessageStack} */ msg);
|
||||
callback(new Error(why), msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -117,13 +82,15 @@ export class Client {
|
|||
* @returns {void}
|
||||
*/
|
||||
_poll(): void {
|
||||
clearTimeout(this.timer);
|
||||
if (this.timer != null) {
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
|
||||
if (this.queue.length > 0) {
|
||||
if (this.smtp.state == SMTPState.NOTCONNECTED) {
|
||||
if (this.smtp.state() == SMTPState.NOTCONNECTED) {
|
||||
this._connect(this.queue[0]);
|
||||
} else if (
|
||||
this.smtp.state == SMTPState.CONNECTED &&
|
||||
this.smtp.state() == SMTPState.CONNECTED &&
|
||||
!this.sending &&
|
||||
this.ready
|
||||
) {
|
||||
|
@ -132,7 +99,7 @@ export class Client {
|
|||
}
|
||||
// wait around 1 seconds in case something does come in,
|
||||
// otherwise close out SMTP connection if still open
|
||||
else if (this.smtp.state == SMTPState.CONNECTED) {
|
||||
else if (this.smtp.state() == SMTPState.CONNECTED) {
|
||||
this.timer = setTimeout(() => this.smtp.quit(), 1000);
|
||||
}
|
||||
}
|
||||
|
@ -162,10 +129,10 @@ export class Client {
|
|||
}
|
||||
};
|
||||
|
||||
if (!this.smtp.isAuthorized) {
|
||||
this.smtp.login(begin);
|
||||
} else {
|
||||
if (this.smtp.authorized()) {
|
||||
this.smtp.ehlo_or_helo_if_needed(begin);
|
||||
} else {
|
||||
this.smtp.login(begin);
|
||||
}
|
||||
} else {
|
||||
stack.callback(err, stack.message);
|
||||
|
@ -189,16 +156,16 @@ export class Client {
|
|||
return !!(
|
||||
msg.from &&
|
||||
(msg.to || msg.cc || msg.bcc) &&
|
||||
(msg.text != null || this._containsInlinedHtml(msg.attachment))
|
||||
(msg.text !== undefined || this._containsInlinedHtml(msg.attachment))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {*} attachment attachment
|
||||
* @returns {boolean} does contain
|
||||
* @returns {*} whether the attachment contains inlined html
|
||||
*/
|
||||
_containsInlinedHtml(attachment: any): boolean {
|
||||
_containsInlinedHtml(attachment: any) {
|
||||
if (Array.isArray(attachment)) {
|
||||
return attachment.some((att) => {
|
||||
return this._isAttachmentInlinedHtml(att);
|
||||
|
@ -211,9 +178,9 @@ export class Client {
|
|||
/**
|
||||
* @private
|
||||
* @param {*} attachment attachment
|
||||
* @returns {boolean} is inlined
|
||||
* @returns {boolean} whether the attachment is inlined html
|
||||
*/
|
||||
_isAttachmentInlinedHtml(attachment: any): boolean {
|
||||
_isAttachmentInlinedHtml(attachment: any) {
|
||||
return (
|
||||
attachment &&
|
||||
(attachment.data || attachment.path) &&
|
||||
|
@ -267,9 +234,12 @@ export class Client {
|
|||
throw new TypeError('stack.to must be array');
|
||||
}
|
||||
|
||||
const { address: to } = stack.to.shift() ?? {};
|
||||
const to = stack.to.shift()?.address;
|
||||
this.smtp.rcpt(
|
||||
this._sendsmtp(stack, stack.to.length ? this._sendrcpt : this._senddata),
|
||||
this._sendsmtp(
|
||||
stack,
|
||||
stack.to.length > 0 ? this._sendrcpt : this._senddata
|
||||
),
|
||||
`<${to}>`
|
||||
);
|
||||
}
|
||||
|
|
175
smtp/message.ts
175
smtp/message.ts
|
@ -10,22 +10,22 @@ import { getRFC2822Date } from './date';
|
|||
|
||||
type Indexed = import('@ledge/types').Indexed;
|
||||
|
||||
const CRLF = '\r\n';
|
||||
const CRLF = '\r\n' as const;
|
||||
|
||||
/**
|
||||
* MIME standard wants 76 char chunks when sending out.
|
||||
*/
|
||||
export const MIMECHUNK: 76 = 76;
|
||||
export const MIMECHUNK = 76 as const;
|
||||
|
||||
/**
|
||||
* meets both base64 and mime divisibility
|
||||
*/
|
||||
export const MIME64CHUNK: 456 = (MIMECHUNK * 6) as 456;
|
||||
export const MIME64CHUNK = (MIMECHUNK * 6) as 456;
|
||||
|
||||
/**
|
||||
* size of the message stream buffer
|
||||
*/
|
||||
export const BUFFERSIZE: 12768 = (MIMECHUNK * 24 * 7) as 12768;
|
||||
export const BUFFERSIZE = (MIMECHUNK * 24 * 7) as 12768;
|
||||
|
||||
export interface MessageAttachmentHeaders extends Indexed {
|
||||
'content-type'?: string;
|
||||
|
@ -34,7 +34,7 @@ export interface MessageAttachmentHeaders extends Indexed {
|
|||
}
|
||||
|
||||
export interface AlternateMessageAttachment extends Indexed {
|
||||
headers: MessageAttachmentHeaders;
|
||||
headers?: MessageAttachmentHeaders;
|
||||
inline: boolean;
|
||||
alternative?: MessageAttachment;
|
||||
related?: MessageAttachment[];
|
||||
|
@ -97,35 +97,33 @@ function convertDashDelimitedTextToSnakeCase(text: string) {
|
|||
export class Message {
|
||||
attachments: any[] = [];
|
||||
alternative: AlternateMessageAttachment | null = null;
|
||||
header: Partial<MessageHeaders>;
|
||||
content: string;
|
||||
header: Partial<MessageHeaders> = {
|
||||
'message-id': `<${new Date().getTime()}.${counter++}.${
|
||||
process.pid
|
||||
}@${hostname()}>`,
|
||||
date: getRFC2822Date(),
|
||||
};
|
||||
content = 'text/plain; charset=utf-8';
|
||||
text: any;
|
||||
|
||||
constructor(headers: Partial<MessageHeaders>) {
|
||||
this.header = {
|
||||
'message-id': `<${new Date().getTime()}.${counter++}.${
|
||||
process.pid
|
||||
}@${hostname()}>`,
|
||||
date: getRFC2822Date(),
|
||||
};
|
||||
|
||||
this.content = 'text/plain; charset=utf-8';
|
||||
for (const header in headers) {
|
||||
// allow user to override default content-type to override charset or send a single non-text message
|
||||
if (/^content-type$/i.test(header)) {
|
||||
this.content = headers[header];
|
||||
} else if (header === 'text') {
|
||||
this.text = headers[header];
|
||||
} else if (header === 'attachment') {
|
||||
} else if (
|
||||
header === 'attachment' &&
|
||||
typeof headers[header] === 'object'
|
||||
) {
|
||||
const attachment = headers[header];
|
||||
if (attachment != null) {
|
||||
if (Array.isArray(attachment)) {
|
||||
for (let i = 0; i < attachment.length; i++) {
|
||||
this.attach(attachment[i]);
|
||||
}
|
||||
} else {
|
||||
this.attach(attachment);
|
||||
if (Array.isArray(attachment)) {
|
||||
for (let i = 0; i < attachment.length; i++) {
|
||||
this.attach(attachment[i]);
|
||||
}
|
||||
} else if (attachment != null) {
|
||||
this.attach(attachment);
|
||||
}
|
||||
} else if (header === 'subject') {
|
||||
this.header.subject = mimeWordEncode(headers.subject);
|
||||
|
@ -166,11 +164,10 @@ export class Message {
|
|||
* @param {string} [charset='utf-8'] the charset to encode as
|
||||
* @returns {Message} the current Message instance
|
||||
*/
|
||||
attach_alternative(html: string, charset = 'utf-8'): Message {
|
||||
attach_alternative(html: string, charset: string): Message {
|
||||
this.alternative = {
|
||||
headers: {},
|
||||
data: html,
|
||||
charset,
|
||||
charset: charset || 'utf-8',
|
||||
type: 'text/html',
|
||||
inline: true,
|
||||
};
|
||||
|
@ -182,7 +179,7 @@ export class Message {
|
|||
* @param {function(boolean, string): void} callback This callback is displayed as part of the Requester class.
|
||||
* @returns {void}
|
||||
*/
|
||||
valid(callback: (arg0: boolean, arg1?: string) => void): void {
|
||||
valid(callback: (arg0: boolean, arg1?: string) => void) {
|
||||
if (!this.header.from) {
|
||||
callback(false, 'message does not have a valid sender');
|
||||
}
|
||||
|
@ -213,10 +210,9 @@ export class Message {
|
|||
}
|
||||
|
||||
/**
|
||||
* returns a stream of the current message
|
||||
* @returns {MessageStream} a stream of the current message
|
||||
* @returns {*} a stream of the current message
|
||||
*/
|
||||
stream(): MessageStream {
|
||||
stream() {
|
||||
return new MessageStream(this);
|
||||
}
|
||||
|
||||
|
@ -224,7 +220,7 @@ export class Message {
|
|||
* @param {function(Error, string): void} callback the function to call with the error and buffer
|
||||
* @returns {void}
|
||||
*/
|
||||
read(callback: (arg0: Error, arg1: string) => void): void {
|
||||
read(callback: (err: Error, buffer: string) => void) {
|
||||
let buffer = '';
|
||||
const str = this.stream();
|
||||
str.on('data', (data) => (buffer += data));
|
||||
|
@ -234,46 +230,18 @@ export class Message {
|
|||
}
|
||||
|
||||
class MessageStream extends Stream {
|
||||
message: Message;
|
||||
readable: boolean;
|
||||
paused: boolean;
|
||||
buffer: Buffer | null;
|
||||
bufferIndex: number;
|
||||
readable = true;
|
||||
paused = false;
|
||||
buffer: Buffer | null = Buffer.alloc(MIMECHUNK * 24 * 7);
|
||||
bufferIndex = 0;
|
||||
|
||||
/**
|
||||
* @param {Message} message the message to stream
|
||||
* @param {*} message the message to stream
|
||||
*/
|
||||
constructor(message: Message) {
|
||||
constructor(private message: Message) {
|
||||
super();
|
||||
|
||||
/**
|
||||
* @type {Message}
|
||||
*/
|
||||
this.message = message;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.readable = true;
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.paused = false;
|
||||
|
||||
/**
|
||||
* @type {Buffer}
|
||||
*/
|
||||
this.buffer = Buffer.alloc(MIMECHUNK * 24 * 7);
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
this.bufferIndex = 0;
|
||||
|
||||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
const output_mixed = (): void => {
|
||||
const output_mixed = () => {
|
||||
const boundary = generate_boundary();
|
||||
output(
|
||||
`Content-Type: multipart/mixed; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}`
|
||||
|
@ -303,7 +271,7 @@ class MessageStream extends Stream {
|
|||
list: MessageAttachment[],
|
||||
index: number,
|
||||
callback: () => void
|
||||
): void => {
|
||||
) => {
|
||||
if (index < list.length) {
|
||||
output(`--${boundary}${CRLF}`);
|
||||
if (list[index].related) {
|
||||
|
@ -327,7 +295,7 @@ class MessageStream extends Stream {
|
|||
*/
|
||||
const output_attachment_headers = (
|
||||
attachment: MessageAttachment | AlternateMessageAttachment
|
||||
): void => {
|
||||
) => {
|
||||
let data: string[] = [];
|
||||
const headers: Partial<MessageHeaders> = {
|
||||
'content-type':
|
||||
|
@ -341,8 +309,10 @@ class MessageStream extends Stream {
|
|||
};
|
||||
|
||||
// allow sender to override default headers
|
||||
for (const header in attachment.headers || {}) {
|
||||
headers[header.toLowerCase()] = attachment.headers[header];
|
||||
if (attachment.headers != null) {
|
||||
for (const header in attachment.headers) {
|
||||
headers[header.toLowerCase()] = attachment.headers[header];
|
||||
}
|
||||
}
|
||||
|
||||
for (const header in headers) {
|
||||
|
@ -360,7 +330,7 @@ class MessageStream extends Stream {
|
|||
const output_attachment = (
|
||||
attachment: MessageAttachment | AlternateMessageAttachment,
|
||||
callback: () => void
|
||||
): void => {
|
||||
) => {
|
||||
const build = attachment.path
|
||||
? output_file
|
||||
: attachment.stream
|
||||
|
@ -378,7 +348,7 @@ class MessageStream extends Stream {
|
|||
const output_data = (
|
||||
attachment: MessageAttachment | AlternateMessageAttachment,
|
||||
callback: () => void
|
||||
): void => {
|
||||
) => {
|
||||
output_base64(
|
||||
attachment.encoded
|
||||
? attachment.data
|
||||
|
@ -390,7 +360,7 @@ class MessageStream extends Stream {
|
|||
const output_file = (
|
||||
attachment: MessageAttachment | AlternateMessageAttachment,
|
||||
next: (err: NodeJS.ErrnoException) => void
|
||||
): void => {
|
||||
) => {
|
||||
const chunk = MIME64CHUNK * 16;
|
||||
const buffer = Buffer.alloc(chunk);
|
||||
const closed = (fd: number) => fs.closeSync(fd);
|
||||
|
@ -400,7 +370,7 @@ class MessageStream extends Stream {
|
|||
* @param {number} fd the file descriptor
|
||||
* @returns {void}
|
||||
*/
|
||||
const opened = (err: Error, fd: number): void => {
|
||||
const opened = (err: Error, fd: number) => {
|
||||
if (!err) {
|
||||
const read = (err: Error, bytes: number) => {
|
||||
if (!err && this.readable) {
|
||||
|
@ -452,13 +422,13 @@ class MessageStream extends Stream {
|
|||
const output_stream = (
|
||||
attachment: MessageAttachment | AlternateMessageAttachment,
|
||||
callback: () => void
|
||||
): void => {
|
||||
) => {
|
||||
if (attachment.stream.readable) {
|
||||
let previous = Buffer.alloc(0);
|
||||
|
||||
attachment.stream.resume();
|
||||
|
||||
(attachment as MessageAttachment).on('end', () => {
|
||||
(attachment as MessageAttachment).stream.on('end', () => {
|
||||
output_base64(previous.toString('base64'), callback);
|
||||
this.removeListener('pause', attachment.stream.pause);
|
||||
this.removeListener('resume', attachment.stream.resume);
|
||||
|
@ -497,7 +467,7 @@ class MessageStream extends Stream {
|
|||
* @param {function(): void} [callback] the function to call after output is finished
|
||||
* @returns {void}
|
||||
*/
|
||||
const output_base64 = (data: string, callback?: () => void): void => {
|
||||
const output_base64 = (data: string, callback?: () => void) => {
|
||||
const loops = Math.ceil(data.length / MIMECHUNK);
|
||||
let loop = 0;
|
||||
while (loop < loops) {
|
||||
|
@ -513,7 +483,7 @@ class MessageStream extends Stream {
|
|||
* @param {Message} message the message to output
|
||||
* @returns {void}
|
||||
*/
|
||||
const output_text = (message: Message): void => {
|
||||
const output_text = (message: Message) => {
|
||||
let data: string[] = [];
|
||||
|
||||
data = data.concat([
|
||||
|
@ -537,7 +507,7 @@ class MessageStream extends Stream {
|
|||
const output_alternative = (
|
||||
message: Message & { alternative: AlternateMessageAttachment },
|
||||
callback: () => void
|
||||
): void => {
|
||||
) => {
|
||||
const boundary = generate_boundary();
|
||||
output(
|
||||
`Content-Type: multipart/alternative; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}`
|
||||
|
@ -548,7 +518,7 @@ class MessageStream extends Stream {
|
|||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
const finish = (): void => {
|
||||
const finish = () => {
|
||||
output([CRLF, '--', boundary, '--', CRLF, CRLF].join(''));
|
||||
callback();
|
||||
};
|
||||
|
@ -568,7 +538,7 @@ class MessageStream extends Stream {
|
|||
const output_related = (
|
||||
message: AlternateMessageAttachment,
|
||||
callback: () => void
|
||||
): void => {
|
||||
) => {
|
||||
const boundary = generate_boundary();
|
||||
output(
|
||||
`Content-Type: multipart/related; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}`
|
||||
|
@ -584,7 +554,7 @@ class MessageStream extends Stream {
|
|||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
const output_header_data = (): void => {
|
||||
const output_header_data = () => {
|
||||
if (this.message.attachments.length || this.message.alternative) {
|
||||
output(`MIME-Version: 1.0${CRLF}`);
|
||||
output_mixed();
|
||||
|
@ -598,14 +568,15 @@ class MessageStream extends Stream {
|
|||
/**
|
||||
* @returns {void}
|
||||
*/
|
||||
const output_header = (): void => {
|
||||
const output_header = () => {
|
||||
let data: string[] = [];
|
||||
|
||||
for (const header in this.message.header) {
|
||||
// do not output BCC in the headers (regex) nor custom Object.prototype functions...
|
||||
if (
|
||||
!/bcc/i.test(header) &&
|
||||
Object.prototype.hasOwnProperty.call(this.message.header, header)
|
||||
// eslint-disable-next-line no-prototype-builtins
|
||||
this.message.header.hasOwnProperty(header)
|
||||
) {
|
||||
data = data.concat([
|
||||
convertDashDelimitedTextToSnakeCase(header),
|
||||
|
@ -626,11 +597,7 @@ class MessageStream extends Stream {
|
|||
* @param {any[]} [args] array of arguments to pass to the callback
|
||||
* @returns {void}
|
||||
*/
|
||||
const output = (
|
||||
data: string,
|
||||
callback?: (...args: any[]) => void,
|
||||
args: any[] = []
|
||||
) => {
|
||||
const output = (data: string) => {
|
||||
// can we buffer the data?
|
||||
if (this.buffer != null) {
|
||||
const bytes = Buffer.byteLength(data);
|
||||
|
@ -638,9 +605,6 @@ class MessageStream extends Stream {
|
|||
if (bytes + this.bufferIndex < this.buffer.length) {
|
||||
this.buffer.write(data, this.bufferIndex);
|
||||
this.bufferIndex += bytes;
|
||||
if (callback) {
|
||||
callback.apply(null, args);
|
||||
}
|
||||
}
|
||||
// we can't buffer the data, so ship it out!
|
||||
else if (bytes > this.buffer.length) {
|
||||
|
@ -673,24 +637,15 @@ class MessageStream extends Stream {
|
|||
);
|
||||
this.buffer.write(data, 0);
|
||||
this.bufferIndex = bytes;
|
||||
// we could get paused after emitting data...
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
if (this.paused) {
|
||||
this.once('resume', () => callback.apply(null, args));
|
||||
} else {
|
||||
callback.apply(null, args);
|
||||
}
|
||||
}
|
||||
} // we can't empty out the buffer, so let's wait till we resume before adding to it
|
||||
else {
|
||||
this.once('resume', () => output(data, callback, args));
|
||||
} else {
|
||||
// we can't empty out the buffer, so let's wait till we resume before adding to it
|
||||
this.once('resume', () => output(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const close = (err?: any): void => {
|
||||
const close = (err?: any) => {
|
||||
if (err) {
|
||||
this.emit('error', err);
|
||||
} else {
|
||||
|
@ -718,7 +673,7 @@ class MessageStream extends Stream {
|
|||
* pause the stream
|
||||
* @returns {void}
|
||||
*/
|
||||
pause(): void {
|
||||
pause() {
|
||||
this.paused = true;
|
||||
this.emit('pause');
|
||||
}
|
||||
|
@ -727,7 +682,7 @@ class MessageStream extends Stream {
|
|||
* resume the stream
|
||||
* @returns {void}
|
||||
*/
|
||||
resume(): void {
|
||||
resume() {
|
||||
this.paused = false;
|
||||
this.emit('resume');
|
||||
}
|
||||
|
@ -736,7 +691,7 @@ class MessageStream extends Stream {
|
|||
* destroy the stream
|
||||
* @returns {void}
|
||||
*/
|
||||
destroy(): void {
|
||||
destroy() {
|
||||
this.emit(
|
||||
'destroy',
|
||||
this.bufferIndex > 0 ? { message: 'message stream destroyed' } : null
|
||||
|
@ -747,7 +702,7 @@ class MessageStream extends Stream {
|
|||
* destroy the stream at first opportunity
|
||||
* @returns {void}
|
||||
*/
|
||||
destroySoon(): void {
|
||||
destroySoon() {
|
||||
this.emit('destroy');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@ export class SMTPResponse {
|
|||
) {
|
||||
const watch = (data: Parameters<SMTPResponse['watch']>[0]) =>
|
||||
this.watch(data);
|
||||
const end = () => this.end();
|
||||
const close = () => this.close();
|
||||
const end = (err: Error) => this.end(err);
|
||||
const close = (err: Error) => this.close(err);
|
||||
const error = (data: Parameters<SMTPResponse['error']>[0]) =>
|
||||
this.error(data);
|
||||
const timedout = (data: Parameters<SMTPResponse['timedout']>[0]) =>
|
||||
|
@ -43,12 +43,13 @@ export class SMTPResponse {
|
|||
// parse buffer for response codes
|
||||
const line = this.buffer.replace('\r', '');
|
||||
if (
|
||||
!line
|
||||
.trim()
|
||||
.split(/\n/)
|
||||
.pop()
|
||||
?.match(/^(\d{3})\s/) ??
|
||||
false
|
||||
!(
|
||||
line
|
||||
.trim()
|
||||
.split(/\n/)
|
||||
.pop()
|
||||
?.match(/^(\d{3})\s/) ?? false
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
@ -75,13 +76,6 @@ export class SMTPResponse {
|
|||
);
|
||||
}
|
||||
|
||||
protected watch(data: string | Buffer) {
|
||||
if (data !== null) {
|
||||
this.buffer += data.toString();
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
protected timedout(err: Error) {
|
||||
this.stream.end();
|
||||
this.stream.emit(
|
||||
|
@ -94,17 +88,32 @@ export class SMTPResponse {
|
|||
);
|
||||
}
|
||||
|
||||
protected close() {
|
||||
protected watch(data: string | Buffer) {
|
||||
if (data !== null) {
|
||||
this.buffer += data.toString();
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
protected close(err: Error) {
|
||||
this.stream.emit(
|
||||
'response',
|
||||
makeSMTPError('connection has closed', SMTPErrorStates.CONNECTIONCLOSED)
|
||||
makeSMTPError(
|
||||
'connection has closed',
|
||||
SMTPErrorStates.CONNECTIONCLOSED,
|
||||
err
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
protected end() {
|
||||
protected end(err: Error) {
|
||||
this.stream.emit(
|
||||
'response',
|
||||
makeSMTPError('connection has ended', SMTPErrorStates.CONNECTIONENDED)
|
||||
makeSMTPError(
|
||||
'connection has ended',
|
||||
SMTPErrorStates.CONNECTIONENDED,
|
||||
err
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
87
smtp/smtp.ts
87
smtp/smtp.ts
|
@ -8,10 +8,6 @@ import { SMTPResponse } from './response';
|
|||
import { makeSMTPError, SMTPErrorStates } from './error';
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
/**
|
||||
* @readonly
|
||||
* @enum
|
||||
*/
|
||||
export enum AUTH_METHODS {
|
||||
PLAIN = 'PLAIN',
|
||||
CRAM_MD5 = 'CRAM-MD5',
|
||||
|
@ -19,10 +15,6 @@ export enum AUTH_METHODS {
|
|||
XOAUTH2 = 'XOAUTH2',
|
||||
}
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
* @enum
|
||||
*/
|
||||
export enum SMTPState {
|
||||
NOTCONNECTED = 0,
|
||||
CONNECTING = 1,
|
||||
|
@ -30,39 +22,11 @@ export enum SMTPState {
|
|||
}
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
* @type {5000}
|
||||
*/
|
||||
export const DEFAULT_TIMEOUT: 5000 = 5000;
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
* @type {25}
|
||||
*/
|
||||
const SMTP_PORT: 25 = 25;
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
* @type {465}
|
||||
*/
|
||||
const SMTP_SSL_PORT: 465 = 465;
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
* @type {587}
|
||||
*/
|
||||
const SMTP_TLS_PORT: 587 = 587;
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
* @type {'\r\n'}
|
||||
*/
|
||||
const CRLF: '\r\n' = '\r\n';
|
||||
|
||||
/**
|
||||
* @type {0 | 1}
|
||||
*/
|
||||
export const DEFAULT_TIMEOUT = 5000 as const;
|
||||
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;
|
||||
let DEBUG: 0 | 1 = 0;
|
||||
|
||||
/**
|
||||
|
@ -118,34 +82,28 @@ export interface ConnectOptions {
|
|||
}
|
||||
|
||||
export class SMTP extends EventEmitter {
|
||||
private _state: 0 | 1 | 2 = SMTPState.NOTCONNECTED;
|
||||
private _state: SMTPState = SMTPState.NOTCONNECTED;
|
||||
private _isAuthorized = false;
|
||||
private _isSecure = false;
|
||||
private _user?: string = '';
|
||||
private _password?: string = '';
|
||||
private _timeout: number = DEFAULT_TIMEOUT;
|
||||
public timeout: number = DEFAULT_TIMEOUT;
|
||||
|
||||
public set debug(level: 0 | 1) {
|
||||
DEBUG = level;
|
||||
}
|
||||
|
||||
public get state() {
|
||||
public state() {
|
||||
return this._state;
|
||||
}
|
||||
|
||||
public get timeout() {
|
||||
return this._timeout;
|
||||
}
|
||||
public user: () => string;
|
||||
public password: () => string;
|
||||
|
||||
public get user() {
|
||||
return this._user;
|
||||
}
|
||||
|
||||
public get password() {
|
||||
return this._password;
|
||||
}
|
||||
|
||||
public get isAuthorized() {
|
||||
/**
|
||||
* @returns {boolean} whether or not the instance is authorized
|
||||
*/
|
||||
public authorized() {
|
||||
return this._isAuthorized;
|
||||
}
|
||||
|
||||
|
@ -177,9 +135,6 @@ export class SMTP extends EventEmitter {
|
|||
}: Partial<SMTPOptions> = {}) {
|
||||
super();
|
||||
|
||||
this._user = user;
|
||||
this._password = password;
|
||||
|
||||
this.authentication = Array.isArray(authentication)
|
||||
? authentication
|
||||
: [
|
||||
|
@ -190,7 +145,7 @@ export class SMTP extends EventEmitter {
|
|||
];
|
||||
|
||||
if (typeof timeout === 'number') {
|
||||
this._timeout = timeout;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
if (typeof domain === 'string') {
|
||||
|
@ -230,6 +185,10 @@ export class SMTP extends EventEmitter {
|
|||
}
|
||||
|
||||
this._isAuthorized = user && password ? false : true;
|
||||
|
||||
// keep these strings hidden when quicky debugging/logging
|
||||
this.user = () => user as string;
|
||||
this.password = () => password as string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -351,7 +310,7 @@ export class SMTP extends EventEmitter {
|
|||
this.sock.connect(this.port, this.host, connectedErrBack);
|
||||
}
|
||||
|
||||
this.monitor = new SMTPResponse(this.sock, this._timeout, () =>
|
||||
this.monitor = new SMTPResponse(this.sock, this.timeout, () =>
|
||||
this.close(true)
|
||||
);
|
||||
this.sock.once('response', response);
|
||||
|
@ -483,7 +442,7 @@ export class SMTP extends EventEmitter {
|
|||
this._isSecure = true;
|
||||
this.sock = secureSocket;
|
||||
|
||||
new SMTPResponse(this.sock, this._timeout, () => this.close(true));
|
||||
new SMTPResponse(this.sock, this.timeout, () => this.close(true));
|
||||
caller(callback, msg.data);
|
||||
}
|
||||
};
|
||||
|
@ -689,8 +648,8 @@ export class SMTP extends EventEmitter {
|
|||
options: { method?: string; domain?: string } = {}
|
||||
): void {
|
||||
const login = {
|
||||
user: () => user || this.user || '',
|
||||
password: () => password || this.password || '',
|
||||
user: (user?.length ?? 0) > 0 ? () => user : this.user,
|
||||
password: (password?.length ?? 0) > 0 ? () => password : this.password,
|
||||
method: options && options.method ? options.method.toUpperCase() : '',
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user