Compare commits

...

6 Commits

Author SHA1 Message Date
Zack Schuster 99bdf2fb14 chore: add async/await example to readme 2020-12-01 15:20:43 -08:00
Zack Schuster 3403819397 release: update changelog date 2020-12-01 15:13:43 -08:00
eleith ad2e355286 bump v3.4.0 2020-12-01 13:14:11 -08:00
eleith bc1fcf1f8e
Merge pull request #277 from eleith/v3.4.0
release: update for 3.4.0
2020-12-01 13:11:33 -08:00
Zack Schuster afe2105919 build: update bundles 2020-11-29 21:07:00 -08:00
Zack Schuster f8a691f80a chore: update changelog 2020-11-29 21:06:40 -08:00
7 changed files with 268 additions and 172 deletions

View File

@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [3.4.0] - 2020-12-01
### Added
- `SMTPClient#sendAsync` API [#267](https://github.com/eleith/emailjs/issues/267)
- `isRFC2822Date` API
### Changed
- use `WeakSet` instead of `WeakMap` for greylist tracking
### Fixed
- use camelCase style for internal function names
- use correct types in jsdoc comments
## [3.3.0] - 2020-08-08 ## [3.3.0] - 2020-08-08
### Added ### Added
- greylist support [#202](https://github.com/eleith/emailjs/issues/202) - greylist support [#202](https://github.com/eleith/emailjs/issues/202)

View File

@ -50,6 +50,33 @@ client.send(
); );
``` ```
## EXAMPLE USAGE - using async/await
```js
// assuming top-level await for brevity
import { SMTPClient } from 'emailjs';
const client = new SMTPClient({
user: 'user',
password: 'password',
host: 'smtp.your-email.com',
ssl: true,
});
try {
const message = await client.sendAsync({
text: 'i hope this works',
from: 'you <username@your-email.com>',
to: 'someone <someone@your-email.com>, another <another@your-email.com>',
cc: 'else <else@your-email.com>',
subject: 'testing emailjs',
});
console.log(message);
} catch (err) {
console.error(err);
}
```
## EXAMPLE USAGE - html emails and attachments ## EXAMPLE USAGE - html emails and attachments
```js ```js

View File

@ -1,7 +1,7 @@
{ {
"name": "emailjs", "name": "emailjs",
"description": "send text/html emails and attachments (files, streams and strings) from node.js to any smtp server", "description": "send text/html emails and attachments (files, streams and strings) from node.js to any smtp server",
"version": "3.3.0", "version": "3.4.0",
"author": "eleith", "author": "eleith",
"contributors": [ "contributors": [
"izuzak", "izuzak",

View File

@ -256,6 +256,19 @@ function getRFC2822DateUTC(date = new Date()) {
dates.push('+0000'); dates.push('+0000');
return dates.join(' '); return dates.join(' ');
} }
/**
* RFC 2822 regex
* @see https://tools.ietf.org/html/rfc2822#section-3.3
* @see https://github.com/moment/moment/blob/a831fc7e2694281ce31e4f090bbcf90a690f0277/src/lib/create/from-string.js#L101
*/
const rfc2822re = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/.compile();
/**
* @param {string} [date] a string to check for conformance to the [rfc2822](https://tools.ietf.org/html/rfc2822#section-3.3) standard
* @returns {boolean} the result of the conformance check
*/
function isRFC2822Date(date) {
return rfc2822re.test(date);
}
// adapted from https://github.com/emailjs/emailjs-mime-codec/blob/6909c706b9f09bc0e5c3faf48f723cca53e5b352/src/mimecodec.js // adapted from https://github.com/emailjs/emailjs-mime-codec/blob/6909c706b9f09bc0e5c3faf48f723cca53e5b352/src/mimecodec.js
const encoder = new util.TextEncoder(); const encoder = new util.TextEncoder();
@ -458,7 +471,7 @@ const MIME64CHUNK = (MIMECHUNK * 6);
*/ */
const BUFFERSIZE = (MIMECHUNK * 24 * 7); const BUFFERSIZE = (MIMECHUNK * 24 * 7);
let counter = 0; let counter = 0;
function generate_boundary() { function generateBoundary() {
let text = ''; let text = '';
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'()+_,-./:=?"; const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'()+_,-./:=?";
for (let i = 0; i < 69; i++) { for (let i = 0; i < 69; i++) {
@ -597,7 +610,7 @@ class Message {
} }
/** /**
* @public * @public
* @returns {*} a stream of the current message * @returns {MessageStream} a stream of the current message
*/ */
stream() { stream() {
return new MessageStream(this); return new MessageStream(this);
@ -617,7 +630,7 @@ class Message {
} }
class MessageStream extends stream.Stream { class MessageStream extends stream.Stream {
/** /**
* @param {*} message the message to stream * @param {Message} message the message to stream
*/ */
constructor(message) { constructor(message) {
super(); super();
@ -670,7 +683,7 @@ class MessageStream extends stream.Stream {
* @param {MessageAttachment} [attachment] the attachment whose headers you would like to output * @param {MessageAttachment} [attachment] the attachment whose headers you would like to output
* @returns {void} * @returns {void}
*/ */
const output_attachment_headers = (attachment) => { const outputAttachmentHeaders = (attachment) => {
let data = []; let data = [];
const headers = { const headers = {
'content-type': attachment.type + 'content-type': attachment.type +
@ -702,7 +715,7 @@ class MessageStream extends stream.Stream {
* @param {function(): void} [callback] the function to call after output is finished * @param {function(): void} [callback] the function to call after output is finished
* @returns {void} * @returns {void}
*/ */
const output_base64 = (data, callback) => { const outputBase64 = (data, callback) => {
const loops = Math.ceil(data.length / MIMECHUNK); const loops = Math.ceil(data.length / MIMECHUNK);
let loop = 0; let loop = 0;
while (loop < loops) { while (loop < loops) {
@ -713,7 +726,7 @@ class MessageStream extends stream.Stream {
callback(); callback();
} }
}; };
const output_file = (attachment, next) => { const outputFile = (attachment, next) => {
const chunk = MIME64CHUNK * 16; const chunk = MIME64CHUNK * 16;
const buffer = Buffer.alloc(chunk); const buffer = Buffer.alloc(chunk);
const closed = (fd) => fs.closeSync(fd); const closed = (fd) => fs.closeSync(fd);
@ -739,7 +752,7 @@ class MessageStream extends stream.Stream {
encoding = 'base64'; encoding = 'base64';
} }
// guaranteed to be encoded without padding unless it is our last read // guaranteed to be encoded without padding unless it is our last read
output_base64(buffer.toString(encoding, 0, bytes), () => { outputBase64(buffer.toString(encoding, 0, bytes), () => {
if (bytes == chunk) { if (bytes == chunk) {
// we read a full chunk, there might be more // we read a full chunk, there might be more
fs.read(fd, buffer, 0, chunk, null, read); fs.read(fd, buffer, 0, chunk, null, read);
@ -768,13 +781,13 @@ class MessageStream extends stream.Stream {
* @param {function(): void} callback the function to call after output is finished * @param {function(): void} callback the function to call after output is finished
* @returns {void} * @returns {void}
*/ */
const output_stream = (attachment, callback) => { const outputStream = (attachment, callback) => {
const { stream } = attachment; const { stream } = attachment;
if (stream === null || stream === void 0 ? void 0 : stream.readable) { if (stream === null || stream === void 0 ? void 0 : stream.readable) {
let previous = Buffer.alloc(0); let previous = Buffer.alloc(0);
stream.resume(); stream.resume();
stream.on('end', () => { stream.on('end', () => {
output_base64(previous.toString('base64'), callback); outputBase64(previous.toString('base64'), callback);
this.removeListener('pause', stream.pause); this.removeListener('pause', stream.pause);
this.removeListener('resume', stream.resume); this.removeListener('resume', stream.resume);
this.removeListener('error', stream.resume); this.removeListener('error', stream.resume);
@ -792,7 +805,7 @@ class MessageStream extends stream.Stream {
// copy dangling bytes into previous buffer // copy dangling bytes into previous buffer
buffer.copy(previous, 0, buffer.length - padded); buffer.copy(previous, 0, buffer.length - padded);
} }
output_base64(buffer.toString('base64', 0, buffer.length - padded)); outputBase64(buffer.toString('base64', 0, buffer.length - padded));
}); });
this.on('pause', stream.pause); this.on('pause', stream.pause);
this.on('resume', stream.resume); this.on('resume', stream.resume);
@ -802,13 +815,13 @@ class MessageStream extends stream.Stream {
this.emit('error', { message: 'stream not readable' }); this.emit('error', { message: 'stream not readable' });
} }
}; };
const output_attachment = (attachment, callback) => { const outputAttachment = (attachment, callback) => {
const build = attachment.path const build = attachment.path
? output_file ? outputFile
: attachment.stream : attachment.stream
? output_stream ? outputStream
: output_data; : outputData;
output_attachment_headers(attachment); outputAttachmentHeaders(attachment);
build(attachment, callback); build(attachment, callback);
}; };
/** /**
@ -818,14 +831,14 @@ class MessageStream extends stream.Stream {
* @param {function(): void} callback the function to call if index is greater than upper bound * @param {function(): void} callback the function to call if index is greater than upper bound
* @returns {void} * @returns {void}
*/ */
const output_message = (boundary, list, index, callback) => { const outputMessage = (boundary, list, index, callback) => {
if (index < list.length) { if (index < list.length) {
output(`--${boundary}${CRLF}`); output(`--${boundary}${CRLF}`);
if (list[index].related) { if (list[index].related) {
output_related(list[index], () => output_message(boundary, list, index + 1, callback)); outputRelated(list[index], () => outputMessage(boundary, list, index + 1, callback));
} }
else { else {
output_attachment(list[index], () => output_message(boundary, list, index + 1, callback)); outputAttachment(list[index], () => outputMessage(boundary, list, index + 1, callback));
} }
} }
else { else {
@ -833,17 +846,17 @@ class MessageStream extends stream.Stream {
callback(); callback();
} }
}; };
const output_mixed = () => { const outputMixed = () => {
const boundary = generate_boundary(); const boundary = generateBoundary();
output(`Content-Type: multipart/mixed; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}`); output(`Content-Type: multipart/mixed; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}`);
if (this.message.alternative == null) { if (this.message.alternative == null) {
output_text(this.message); outputText(this.message);
output_message(boundary, this.message.attachments, 0, close); outputMessage(boundary, this.message.attachments, 0, close);
} }
else { else {
output_alternative( outputAlternative(
// typescript bug; should narrow to { alternative: MessageAttachment } // typescript bug; should narrow to { alternative: MessageAttachment }
this.message, () => output_message(boundary, this.message.attachments, 0, close)); this.message, () => outputMessage(boundary, this.message.attachments, 0, close));
} }
}; };
/** /**
@ -851,16 +864,16 @@ class MessageStream extends stream.Stream {
* @param {function(): void} callback the function to call after output is finished * @param {function(): void} callback the function to call after output is finished
* @returns {void} * @returns {void}
*/ */
const output_data = (attachment, callback) => { const outputData = (attachment, callback) => {
var _a, _b; var _a, _b;
output_base64(attachment.encoded outputBase64(attachment.encoded
? (_a = attachment.data) !== null && _a !== void 0 ? _a : '' : Buffer.from((_b = attachment.data) !== null && _b !== void 0 ? _b : '').toString('base64'), callback); ? (_a = attachment.data) !== null && _a !== void 0 ? _a : '' : Buffer.from((_b = attachment.data) !== null && _b !== void 0 ? _b : '').toString('base64'), callback);
}; };
/** /**
* @param {Message} message the message to output * @param {Message} message the message to output
* @returns {void} * @returns {void}
*/ */
const output_text = (message) => { const outputText = (message) => {
let data = []; let data = [];
data = data.concat([ data = data.concat([
'Content-Type:', 'Content-Type:',
@ -878,12 +891,12 @@ class MessageStream extends stream.Stream {
* @param {function(): void} callback the function to call after output is finished * @param {function(): void} callback the function to call after output is finished
* @returns {void} * @returns {void}
*/ */
const output_related = (message, callback) => { const outputRelated = (message, callback) => {
const boundary = generate_boundary(); const boundary = generateBoundary();
output(`Content-Type: multipart/related; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}`); output(`Content-Type: multipart/related; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}`);
output_attachment(message, () => { outputAttachment(message, () => {
var _a; var _a;
output_message(boundary, (_a = message.related) !== null && _a !== void 0 ? _a : [], 0, () => { outputMessage(boundary, (_a = message.related) !== null && _a !== void 0 ? _a : [], 0, () => {
output(`${CRLF}--${boundary}--${CRLF}${CRLF}`); output(`${CRLF}--${boundary}--${CRLF}${CRLF}`);
callback(); callback();
}); });
@ -894,10 +907,10 @@ class MessageStream extends stream.Stream {
* @param {function(): void} callback the function to call after output is finished * @param {function(): void} callback the function to call after output is finished
* @returns {void} * @returns {void}
*/ */
const output_alternative = (message, callback) => { const outputAlternative = (message, callback) => {
const boundary = generate_boundary(); const boundary = generateBoundary();
output(`Content-Type: multipart/alternative; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}`); output(`Content-Type: multipart/alternative; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}`);
output_text(message); outputText(message);
output(`--${boundary}${CRLF}`); output(`--${boundary}${CRLF}`);
/** /**
* @returns {void} * @returns {void}
@ -907,10 +920,10 @@ class MessageStream extends stream.Stream {
callback(); callback();
}; };
if (message.alternative.related) { if (message.alternative.related) {
output_related(message.alternative, finish); outputRelated(message.alternative, finish);
} }
else { else {
output_attachment(message.alternative, finish); outputAttachment(message.alternative, finish);
} }
}; };
const close = (err) => { const close = (err) => {
@ -934,20 +947,20 @@ class MessageStream extends stream.Stream {
/** /**
* @returns {void} * @returns {void}
*/ */
const output_header_data = () => { const outputHeaderData = () => {
if (this.message.attachments.length || this.message.alternative) { if (this.message.attachments.length || this.message.alternative) {
output(`MIME-Version: 1.0${CRLF}`); output(`MIME-Version: 1.0${CRLF}`);
output_mixed(); outputMixed();
} // you only have a text message! } // you only have a text message!
else { else {
output_text(this.message); outputText(this.message);
close(); close();
} }
}; };
/** /**
* @returns {void} * @returns {void}
*/ */
const output_header = () => { const outputHeader = () => {
let data = []; let data = [];
for (const header in this.message.header) { for (const header in this.message.header) {
// do not output BCC in the headers (regex) nor custom Object.prototype functions... // do not output BCC in the headers (regex) nor custom Object.prototype functions...
@ -962,10 +975,10 @@ class MessageStream extends stream.Stream {
} }
} }
output(data.join('')); output(data.join(''));
output_header_data(); outputHeaderData();
}; };
this.once('destroy', close); this.once('destroy', close);
process.nextTick(output_header); process.nextTick(outputHeader);
} }
/** /**
* @public * @public
@ -1136,7 +1149,7 @@ const CRLF$1 = '\r\n';
const GREYLIST_DELAY = 300; const GREYLIST_DELAY = 300;
let DEBUG = 0; let DEBUG = 0;
/** /**
* @param {...any} args the message(s) to log * @param {...any[]} args the message(s) to log
* @returns {void} * @returns {void}
*/ */
const log = (...args) => { const log = (...args) => {
@ -1149,8 +1162,8 @@ const log = (...args) => {
} }
}; };
/** /**
* @param {function(...*): void} callback the function to call * @param {function(...any[]): void} callback the function to call
* @param {...*} args the arguments to apply to the function * @param {...any[]} args the arguments to apply to the function
* @returns {void} * @returns {void}
*/ */
const caller = (callback, ...args) => { const caller = (callback, ...args) => {
@ -1187,7 +1200,7 @@ class SMTPConnection extends events.EventEmitter {
this.host = 'localhost'; this.host = 'localhost';
this.ssl = false; this.ssl = false;
this.tls = false; this.tls = false;
this.greylistResponseTracker = new WeakMap(); this.greylistResponseTracker = new WeakSet();
if (Array.isArray(authentication)) { if (Array.isArray(authentication)) {
this.authentication = authentication; this.authentication = authentication;
} }
@ -1250,7 +1263,7 @@ class SMTPConnection extends events.EventEmitter {
* NOTE: `host` is trimmed before being used to establish a connection; however, the original untrimmed value will still be visible in configuration. * NOTE: `host` is trimmed before being used to establish a connection; however, the original untrimmed value will still be visible in configuration.
* *
* @public * @public
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @param {number} [port] the port to use for the connection * @param {number} [port] the port to use for the connection
* @param {string} [host] the hostname to use for the connection * @param {string} [host] the hostname to use for the connection
* @param {ConnectOptions} [options={}] the options * @param {ConnectOptions} [options={}] the options
@ -1332,7 +1345,7 @@ class SMTPConnection extends events.EventEmitter {
/** /**
* @public * @public
* @param {string} str the string to send * @param {string} str the string to send
* @param {*} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @returns {void} * @returns {void}
*/ */
send(str, callback) { send(str, callback) {
@ -1359,7 +1372,7 @@ class SMTPConnection extends events.EventEmitter {
/** /**
* @public * @public
* @param {string} cmd command to issue * @param {string} cmd command to issue
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @param {(number[] | number)} [codes=[250]] array codes * @param {(number[] | number)} [codes=[250]] array codes
* @returns {void} * @returns {void}
*/ */
@ -1380,8 +1393,8 @@ class SMTPConnection extends events.EventEmitter {
} }
else if ((code === 450 || code === 451) && else if ((code === 450 || code === 451) &&
msg.message.toLowerCase().includes('greylist') && msg.message.toLowerCase().includes('greylist') &&
this.greylistResponseTracker.get(response) === false) { this.greylistResponseTracker.has(response) === false) {
this.greylistResponseTracker.set(response, true); this.greylistResponseTracker.add(response);
setTimeout(() => { setTimeout(() => {
this.send(cmd + CRLF$1, response); this.send(cmd + CRLF$1, response);
}, GREYLIST_DELAY); }, GREYLIST_DELAY);
@ -1393,7 +1406,7 @@ class SMTPConnection extends events.EventEmitter {
} }
} }
}; };
this.greylistResponseTracker.set(response, false); this.greylistResponseTracker.delete(response);
this.send(cmd + CRLF$1, response); this.send(cmd + CRLF$1, response);
} }
/** /**
@ -1406,7 +1419,7 @@ class SMTPConnection extends events.EventEmitter {
* As this command was deprecated by rfc2821, it should only be used for compatibility with non-compliant servers. * As this command was deprecated by rfc2821, it should only be used for compatibility with non-compliant servers.
* @see https://tools.ietf.org/html/rfc2821#appendix-F.3 * @see https://tools.ietf.org/html/rfc2821#appendix-F.3
* *
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @param {string} domain the domain to associate with the 'helo' request * @param {string} domain the domain to associate with the 'helo' request
* @returns {void} * @returns {void}
*/ */
@ -1423,7 +1436,7 @@ class SMTPConnection extends events.EventEmitter {
} }
/** /**
* @public * @public
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @returns {void} * @returns {void}
*/ */
starttls(callback) { starttls(callback) {
@ -1478,7 +1491,7 @@ class SMTPConnection extends events.EventEmitter {
} }
/** /**
* @public * @public
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @param {string} domain the domain to associate with the 'ehlo' request * @param {string} domain the domain to associate with the 'ehlo' request
* @returns {void} * @returns {void}
*/ */
@ -1511,7 +1524,7 @@ class SMTPConnection extends events.EventEmitter {
/** /**
* @public * @public
* @description SMTP 'help' command, returns text from the server * @description SMTP 'help' command, returns text from the server
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @param {string} domain the domain to associate with the 'help' request * @param {string} domain the domain to associate with the 'help' request
* @returns {void} * @returns {void}
*/ */
@ -1520,7 +1533,7 @@ class SMTPConnection extends events.EventEmitter {
} }
/** /**
* @public * @public
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @returns {void} * @returns {void}
*/ */
rset(callback) { rset(callback) {
@ -1528,7 +1541,7 @@ class SMTPConnection extends events.EventEmitter {
} }
/** /**
* @public * @public
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @returns {void} * @returns {void}
*/ */
noop(callback) { noop(callback) {
@ -1536,7 +1549,7 @@ class SMTPConnection extends events.EventEmitter {
} }
/** /**
* @public * @public
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @param {string} from the sender * @param {string} from the sender
* @returns {void} * @returns {void}
*/ */
@ -1545,7 +1558,7 @@ class SMTPConnection extends events.EventEmitter {
} }
/** /**
* @public * @public
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @param {string} to the receiver * @param {string} to the receiver
* @returns {void} * @returns {void}
*/ */
@ -1554,7 +1567,7 @@ class SMTPConnection extends events.EventEmitter {
} }
/** /**
* @public * @public
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @returns {void} * @returns {void}
*/ */
data(callback) { data(callback) {
@ -1562,7 +1575,7 @@ class SMTPConnection extends events.EventEmitter {
} }
/** /**
* @public * @public
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @returns {void} * @returns {void}
*/ */
data_end(callback) { data_end(callback) {
@ -1582,7 +1595,7 @@ class SMTPConnection extends events.EventEmitter {
* @public * @public
* @description SMTP 'verify' command -- checks for address validity. * @description SMTP 'verify' command -- checks for address validity.
* @param {string} address the address to validate * @param {string} address the address to validate
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @returns {void} * @returns {void}
*/ */
verify(address, callback) { verify(address, callback) {
@ -1592,7 +1605,7 @@ class SMTPConnection extends events.EventEmitter {
* @public * @public
* @description SMTP 'expn' command -- expands a mailing list. * @description SMTP 'expn' command -- expands a mailing list.
* @param {string} address the mailing list to expand * @param {string} address the mailing list to expand
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @returns {void} * @returns {void}
*/ */
expn(address, callback) { expn(address, callback) {
@ -1605,7 +1618,7 @@ class SMTPConnection extends events.EventEmitter {
* If there has been no previous EHLO or HELO command self session, self * If there has been no previous EHLO or HELO command self session, self
* method tries ESMTP EHLO first. * method tries ESMTP EHLO first.
* *
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @param {string} [domain] the domain to associate with the command * @param {string} [domain] the domain to associate with the command
* @returns {void} * @returns {void}
*/ */
@ -1633,7 +1646,7 @@ class SMTPConnection extends events.EventEmitter {
* *
* This method will return normally if the authentication was successful. * This method will return normally if the authentication was successful.
* *
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @param {string} [user] the username to authenticate with * @param {string} [user] the username to authenticate with
* @param {string} [password] the password for the authentication * @param {string} [password] the password for the authentication
* @param {{ method: string, domain: string }} [options] login options * @param {{ method: string, domain: string }} [options] login options
@ -1658,7 +1671,7 @@ class SMTPConnection extends events.EventEmitter {
* @param {string} challenge challenge * @param {string} challenge challenge
* @returns {string} base64 cram hash * @returns {string} base64 cram hash
*/ */
const encode_cram_md5 = (challenge) => { const encodeCramMd5 = (challenge) => {
const hmac = crypto.createHmac('md5', login.password()); const hmac = crypto.createHmac('md5', login.password());
hmac.update(Buffer.from(challenge, 'base64').toString('ascii')); hmac.update(Buffer.from(challenge, 'base64').toString('ascii'));
return Buffer.from(`${login.user()} ${hmac.digest('hex')}`).toString('base64'); return Buffer.from(`${login.user()} ${hmac.digest('hex')}`).toString('base64');
@ -1666,12 +1679,12 @@ class SMTPConnection extends events.EventEmitter {
/** /**
* @returns {string} base64 login/password * @returns {string} base64 login/password
*/ */
const encode_plain = () => Buffer.from(`\u0000${login.user()}\u0000${login.password()}`).toString('base64'); const encodePlain = () => Buffer.from(`\u0000${login.user()}\u0000${login.password()}`).toString('base64');
/** /**
* @see https://developers.google.com/gmail/xoauth2_protocol * @see https://developers.google.com/gmail/xoauth2_protocol
* @returns {string} base64 xoauth2 auth token * @returns {string} base64 xoauth2 auth token
*/ */
const encode_xoauth2 = () => Buffer.from(`user=${login.user()}\u0001auth=Bearer ${login.password()}\u0001\u0001`).toString('base64'); const encodeXoauth2 = () => Buffer.from(`user=${login.user()}\u0001auth=Bearer ${login.password()}\u0001\u0001`).toString('base64');
// List of authentication methods we support: from preferred to // List of authentication methods we support: from preferred to
// less preferred methods. // less preferred methods.
if (!method) { if (!method) {
@ -1690,7 +1703,7 @@ class SMTPConnection extends events.EventEmitter {
/** /**
* handle bad responses from command differently * handle bad responses from command differently
* @param {Error} err err * @param {Error} err err
* @param {*} data data * @param {unknown} data data
* @returns {void} * @returns {void}
*/ */
const failed = (err, data) => { const failed = (err, data) => {
@ -1700,7 +1713,7 @@ class SMTPConnection extends events.EventEmitter {
}; };
/** /**
* @param {Error} err err * @param {Error} err err
* @param {*} data data * @param {unknown} data data
* @returns {void} * @returns {void}
*/ */
const response = (err, data) => { const response = (err, data) => {
@ -1714,7 +1727,7 @@ class SMTPConnection extends events.EventEmitter {
}; };
/** /**
* @param {Error} err err * @param {Error} err err
* @param {*} data data * @param {unknown} data data
* @param {string} msg msg * @param {string} msg msg
* @returns {void} * @returns {void}
*/ */
@ -1724,7 +1737,7 @@ class SMTPConnection extends events.EventEmitter {
} }
else { else {
if (method === AUTH_METHODS['CRAM-MD5']) { if (method === AUTH_METHODS['CRAM-MD5']) {
this.command(encode_cram_md5(msg), response, [235, 503]); this.command(encodeCramMd5(msg), response, [235, 503]);
} }
else if (method === AUTH_METHODS.LOGIN) { else if (method === AUTH_METHODS.LOGIN) {
this.command(Buffer.from(login.password()).toString('base64'), response, [235, 503]); this.command(Buffer.from(login.password()).toString('base64'), response, [235, 503]);
@ -1733,11 +1746,11 @@ class SMTPConnection extends events.EventEmitter {
}; };
/** /**
* @param {Error} err err * @param {Error} err err
* @param {*} data data * @param {unknown} data data
* @param {string} msg msg * @param {string} msg msg
* @returns {void} * @returns {void}
*/ */
const attempt_user = (err, data) => { const attemptUser = (err, data) => {
if (err) { if (err) {
failed(err, data); failed(err, data);
} }
@ -1752,18 +1765,16 @@ class SMTPConnection extends events.EventEmitter {
this.command(`AUTH ${AUTH_METHODS['CRAM-MD5']}`, attempt, [334]); this.command(`AUTH ${AUTH_METHODS['CRAM-MD5']}`, attempt, [334]);
break; break;
case AUTH_METHODS.LOGIN: case AUTH_METHODS.LOGIN:
this.command(`AUTH ${AUTH_METHODS.LOGIN}`, attempt_user, [334]); this.command(`AUTH ${AUTH_METHODS.LOGIN}`, attemptUser, [334]);
break; break;
case AUTH_METHODS.PLAIN: case AUTH_METHODS.PLAIN:
this.command(`AUTH ${AUTH_METHODS.PLAIN} ${encode_plain()}`, response, [235, 503]); this.command(`AUTH ${AUTH_METHODS.PLAIN} ${encodePlain()}`, response, [235, 503]);
break; break;
case AUTH_METHODS.XOAUTH2: case AUTH_METHODS.XOAUTH2:
this.command(`AUTH ${AUTH_METHODS.XOAUTH2} ${encode_xoauth2()}`, response, [235, 503]); this.command(`AUTH ${AUTH_METHODS.XOAUTH2} ${encodeXoauth2()}`, response, [235, 503]);
break; break;
default: default:
const msg = 'no form of authorization supported'; caller(callback, SMTPError.create('no form of authorization supported', SMTPErrorStates.AUTHNOTSUPPORTED, null, data));
const err = SMTPError.create(msg, SMTPErrorStates.AUTHNOTSUPPORTED, null, data);
caller(callback, err);
break; break;
} }
}; };
@ -1797,7 +1808,7 @@ class SMTPConnection extends events.EventEmitter {
} }
/** /**
* @public * @public
* @param {function(...*): void} [callback] function to call after response * @param {function(...any[]): void} [callback] function to call after response
* @returns {void} * @returns {void}
*/ */
quit(callback) { quit(callback) {
@ -1853,6 +1864,23 @@ class SMTPClient {
} }
}); });
} }
/**
* @public
* @param {Message} msg the message to send
* @returns {Promise<Message>} a promise that resolves to the fully processed message
*/
sendAsync(msg) {
return new Promise((resolve, reject) => {
this.send(msg, (err, msg) => {
if (err != null) {
reject(err);
}
else {
resolve(msg);
}
});
});
}
/** /**
* @public * @public
* @description Converts a message to the raw object used by the internal stack. * @description Converts a message to the raw object used by the internal stack.
@ -1966,8 +1994,8 @@ class SMTPClient {
} }
/** /**
* @protected * @protected
* @param {*} attachment attachment * @param {MessageAttachment | MessageAttachment[]} attachment attachment
* @returns {*} whether the attachment contains inlined html * @returns {boolean} whether the attachment contains inlined html
*/ */
_containsInlinedHtml(attachment) { _containsInlinedHtml(attachment) {
if (Array.isArray(attachment)) { if (Array.isArray(attachment)) {
@ -2088,6 +2116,7 @@ exports.SMTPState = SMTPState;
exports.addressparser = addressparser; exports.addressparser = addressparser;
exports.getRFC2822Date = getRFC2822Date; exports.getRFC2822Date = getRFC2822Date;
exports.getRFC2822DateUTC = getRFC2822DateUTC; exports.getRFC2822DateUTC = getRFC2822DateUTC;
exports.isRFC2822Date = isRFC2822Date;
exports.mimeEncode = mimeEncode; exports.mimeEncode = mimeEncode;
exports.mimeWordEncode = mimeWordEncode; exports.mimeWordEncode = mimeWordEncode;
//# sourceMappingURL=email.cjs.map //# sourceMappingURL=email.cjs.map

File diff suppressed because one or more lines are too long

View File

@ -252,6 +252,19 @@ function getRFC2822DateUTC(date = new Date()) {
dates.push('+0000'); dates.push('+0000');
return dates.join(' '); return dates.join(' ');
} }
/**
* RFC 2822 regex
* @see https://tools.ietf.org/html/rfc2822#section-3.3
* @see https://github.com/moment/moment/blob/a831fc7e2694281ce31e4f090bbcf90a690f0277/src/lib/create/from-string.js#L101
*/
const rfc2822re = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/.compile();
/**
* @param {string} [date] a string to check for conformance to the [rfc2822](https://tools.ietf.org/html/rfc2822#section-3.3) standard
* @returns {boolean} the result of the conformance check
*/
function isRFC2822Date(date) {
return rfc2822re.test(date);
}
// adapted from https://github.com/emailjs/emailjs-mime-codec/blob/6909c706b9f09bc0e5c3faf48f723cca53e5b352/src/mimecodec.js // adapted from https://github.com/emailjs/emailjs-mime-codec/blob/6909c706b9f09bc0e5c3faf48f723cca53e5b352/src/mimecodec.js
const encoder = new TextEncoder(); const encoder = new TextEncoder();
@ -454,7 +467,7 @@ const MIME64CHUNK = (MIMECHUNK * 6);
*/ */
const BUFFERSIZE = (MIMECHUNK * 24 * 7); const BUFFERSIZE = (MIMECHUNK * 24 * 7);
let counter = 0; let counter = 0;
function generate_boundary() { function generateBoundary() {
let text = ''; let text = '';
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'()+_,-./:=?"; const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'()+_,-./:=?";
for (let i = 0; i < 69; i++) { for (let i = 0; i < 69; i++) {
@ -593,7 +606,7 @@ class Message {
} }
/** /**
* @public * @public
* @returns {*} a stream of the current message * @returns {MessageStream} a stream of the current message
*/ */
stream() { stream() {
return new MessageStream(this); return new MessageStream(this);
@ -613,7 +626,7 @@ class Message {
} }
class MessageStream extends Stream { class MessageStream extends Stream {
/** /**
* @param {*} message the message to stream * @param {Message} message the message to stream
*/ */
constructor(message) { constructor(message) {
super(); super();
@ -666,7 +679,7 @@ class MessageStream extends Stream {
* @param {MessageAttachment} [attachment] the attachment whose headers you would like to output * @param {MessageAttachment} [attachment] the attachment whose headers you would like to output
* @returns {void} * @returns {void}
*/ */
const output_attachment_headers = (attachment) => { const outputAttachmentHeaders = (attachment) => {
let data = []; let data = [];
const headers = { const headers = {
'content-type': attachment.type + 'content-type': attachment.type +
@ -698,7 +711,7 @@ class MessageStream extends Stream {
* @param {function(): void} [callback] the function to call after output is finished * @param {function(): void} [callback] the function to call after output is finished
* @returns {void} * @returns {void}
*/ */
const output_base64 = (data, callback) => { const outputBase64 = (data, callback) => {
const loops = Math.ceil(data.length / MIMECHUNK); const loops = Math.ceil(data.length / MIMECHUNK);
let loop = 0; let loop = 0;
while (loop < loops) { while (loop < loops) {
@ -709,7 +722,7 @@ class MessageStream extends Stream {
callback(); callback();
} }
}; };
const output_file = (attachment, next) => { const outputFile = (attachment, next) => {
const chunk = MIME64CHUNK * 16; const chunk = MIME64CHUNK * 16;
const buffer = Buffer.alloc(chunk); const buffer = Buffer.alloc(chunk);
const closed = (fd) => fs.closeSync(fd); const closed = (fd) => fs.closeSync(fd);
@ -735,7 +748,7 @@ class MessageStream extends Stream {
encoding = 'base64'; encoding = 'base64';
} }
// guaranteed to be encoded without padding unless it is our last read // guaranteed to be encoded without padding unless it is our last read
output_base64(buffer.toString(encoding, 0, bytes), () => { outputBase64(buffer.toString(encoding, 0, bytes), () => {
if (bytes == chunk) { if (bytes == chunk) {
// we read a full chunk, there might be more // we read a full chunk, there might be more
fs.read(fd, buffer, 0, chunk, null, read); fs.read(fd, buffer, 0, chunk, null, read);
@ -764,13 +777,13 @@ class MessageStream extends Stream {
* @param {function(): void} callback the function to call after output is finished * @param {function(): void} callback the function to call after output is finished
* @returns {void} * @returns {void}
*/ */
const output_stream = (attachment, callback) => { const outputStream = (attachment, callback) => {
const { stream } = attachment; const { stream } = attachment;
if (stream === null || stream === void 0 ? void 0 : stream.readable) { if (stream === null || stream === void 0 ? void 0 : stream.readable) {
let previous = Buffer.alloc(0); let previous = Buffer.alloc(0);
stream.resume(); stream.resume();
stream.on('end', () => { stream.on('end', () => {
output_base64(previous.toString('base64'), callback); outputBase64(previous.toString('base64'), callback);
this.removeListener('pause', stream.pause); this.removeListener('pause', stream.pause);
this.removeListener('resume', stream.resume); this.removeListener('resume', stream.resume);
this.removeListener('error', stream.resume); this.removeListener('error', stream.resume);
@ -788,7 +801,7 @@ class MessageStream extends Stream {
// copy dangling bytes into previous buffer // copy dangling bytes into previous buffer
buffer.copy(previous, 0, buffer.length - padded); buffer.copy(previous, 0, buffer.length - padded);
} }
output_base64(buffer.toString('base64', 0, buffer.length - padded)); outputBase64(buffer.toString('base64', 0, buffer.length - padded));
}); });
this.on('pause', stream.pause); this.on('pause', stream.pause);
this.on('resume', stream.resume); this.on('resume', stream.resume);
@ -798,13 +811,13 @@ class MessageStream extends Stream {
this.emit('error', { message: 'stream not readable' }); this.emit('error', { message: 'stream not readable' });
} }
}; };
const output_attachment = (attachment, callback) => { const outputAttachment = (attachment, callback) => {
const build = attachment.path const build = attachment.path
? output_file ? outputFile
: attachment.stream : attachment.stream
? output_stream ? outputStream
: output_data; : outputData;
output_attachment_headers(attachment); outputAttachmentHeaders(attachment);
build(attachment, callback); build(attachment, callback);
}; };
/** /**
@ -814,14 +827,14 @@ class MessageStream extends Stream {
* @param {function(): void} callback the function to call if index is greater than upper bound * @param {function(): void} callback the function to call if index is greater than upper bound
* @returns {void} * @returns {void}
*/ */
const output_message = (boundary, list, index, callback) => { const outputMessage = (boundary, list, index, callback) => {
if (index < list.length) { if (index < list.length) {
output(`--${boundary}${CRLF}`); output(`--${boundary}${CRLF}`);
if (list[index].related) { if (list[index].related) {
output_related(list[index], () => output_message(boundary, list, index + 1, callback)); outputRelated(list[index], () => outputMessage(boundary, list, index + 1, callback));
} }
else { else {
output_attachment(list[index], () => output_message(boundary, list, index + 1, callback)); outputAttachment(list[index], () => outputMessage(boundary, list, index + 1, callback));
} }
} }
else { else {
@ -829,17 +842,17 @@ class MessageStream extends Stream {
callback(); callback();
} }
}; };
const output_mixed = () => { const outputMixed = () => {
const boundary = generate_boundary(); const boundary = generateBoundary();
output(`Content-Type: multipart/mixed; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}`); output(`Content-Type: multipart/mixed; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}`);
if (this.message.alternative == null) { if (this.message.alternative == null) {
output_text(this.message); outputText(this.message);
output_message(boundary, this.message.attachments, 0, close); outputMessage(boundary, this.message.attachments, 0, close);
} }
else { else {
output_alternative( outputAlternative(
// typescript bug; should narrow to { alternative: MessageAttachment } // typescript bug; should narrow to { alternative: MessageAttachment }
this.message, () => output_message(boundary, this.message.attachments, 0, close)); this.message, () => outputMessage(boundary, this.message.attachments, 0, close));
} }
}; };
/** /**
@ -847,16 +860,16 @@ class MessageStream extends Stream {
* @param {function(): void} callback the function to call after output is finished * @param {function(): void} callback the function to call after output is finished
* @returns {void} * @returns {void}
*/ */
const output_data = (attachment, callback) => { const outputData = (attachment, callback) => {
var _a, _b; var _a, _b;
output_base64(attachment.encoded outputBase64(attachment.encoded
? (_a = attachment.data) !== null && _a !== void 0 ? _a : '' : Buffer.from((_b = attachment.data) !== null && _b !== void 0 ? _b : '').toString('base64'), callback); ? (_a = attachment.data) !== null && _a !== void 0 ? _a : '' : Buffer.from((_b = attachment.data) !== null && _b !== void 0 ? _b : '').toString('base64'), callback);
}; };
/** /**
* @param {Message} message the message to output * @param {Message} message the message to output
* @returns {void} * @returns {void}
*/ */
const output_text = (message) => { const outputText = (message) => {
let data = []; let data = [];
data = data.concat([ data = data.concat([
'Content-Type:', 'Content-Type:',
@ -874,12 +887,12 @@ class MessageStream extends Stream {
* @param {function(): void} callback the function to call after output is finished * @param {function(): void} callback the function to call after output is finished
* @returns {void} * @returns {void}
*/ */
const output_related = (message, callback) => { const outputRelated = (message, callback) => {
const boundary = generate_boundary(); const boundary = generateBoundary();
output(`Content-Type: multipart/related; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}`); output(`Content-Type: multipart/related; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}`);
output_attachment(message, () => { outputAttachment(message, () => {
var _a; var _a;
output_message(boundary, (_a = message.related) !== null && _a !== void 0 ? _a : [], 0, () => { outputMessage(boundary, (_a = message.related) !== null && _a !== void 0 ? _a : [], 0, () => {
output(`${CRLF}--${boundary}--${CRLF}${CRLF}`); output(`${CRLF}--${boundary}--${CRLF}${CRLF}`);
callback(); callback();
}); });
@ -890,10 +903,10 @@ class MessageStream extends Stream {
* @param {function(): void} callback the function to call after output is finished * @param {function(): void} callback the function to call after output is finished
* @returns {void} * @returns {void}
*/ */
const output_alternative = (message, callback) => { const outputAlternative = (message, callback) => {
const boundary = generate_boundary(); const boundary = generateBoundary();
output(`Content-Type: multipart/alternative; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}`); output(`Content-Type: multipart/alternative; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}`);
output_text(message); outputText(message);
output(`--${boundary}${CRLF}`); output(`--${boundary}${CRLF}`);
/** /**
* @returns {void} * @returns {void}
@ -903,10 +916,10 @@ class MessageStream extends Stream {
callback(); callback();
}; };
if (message.alternative.related) { if (message.alternative.related) {
output_related(message.alternative, finish); outputRelated(message.alternative, finish);
} }
else { else {
output_attachment(message.alternative, finish); outputAttachment(message.alternative, finish);
} }
}; };
const close = (err) => { const close = (err) => {
@ -930,20 +943,20 @@ class MessageStream extends Stream {
/** /**
* @returns {void} * @returns {void}
*/ */
const output_header_data = () => { const outputHeaderData = () => {
if (this.message.attachments.length || this.message.alternative) { if (this.message.attachments.length || this.message.alternative) {
output(`MIME-Version: 1.0${CRLF}`); output(`MIME-Version: 1.0${CRLF}`);
output_mixed(); outputMixed();
} // you only have a text message! } // you only have a text message!
else { else {
output_text(this.message); outputText(this.message);
close(); close();
} }
}; };
/** /**
* @returns {void} * @returns {void}
*/ */
const output_header = () => { const outputHeader = () => {
let data = []; let data = [];
for (const header in this.message.header) { for (const header in this.message.header) {
// do not output BCC in the headers (regex) nor custom Object.prototype functions... // do not output BCC in the headers (regex) nor custom Object.prototype functions...
@ -958,10 +971,10 @@ class MessageStream extends Stream {
} }
} }
output(data.join('')); output(data.join(''));
output_header_data(); outputHeaderData();
}; };
this.once('destroy', close); this.once('destroy', close);
process.nextTick(output_header); process.nextTick(outputHeader);
} }
/** /**
* @public * @public
@ -1132,7 +1145,7 @@ const CRLF$1 = '\r\n';
const GREYLIST_DELAY = 300; const GREYLIST_DELAY = 300;
let DEBUG = 0; let DEBUG = 0;
/** /**
* @param {...any} args the message(s) to log * @param {...any[]} args the message(s) to log
* @returns {void} * @returns {void}
*/ */
const log = (...args) => { const log = (...args) => {
@ -1145,8 +1158,8 @@ const log = (...args) => {
} }
}; };
/** /**
* @param {function(...*): void} callback the function to call * @param {function(...any[]): void} callback the function to call
* @param {...*} args the arguments to apply to the function * @param {...any[]} args the arguments to apply to the function
* @returns {void} * @returns {void}
*/ */
const caller = (callback, ...args) => { const caller = (callback, ...args) => {
@ -1183,7 +1196,7 @@ class SMTPConnection extends EventEmitter {
this.host = 'localhost'; this.host = 'localhost';
this.ssl = false; this.ssl = false;
this.tls = false; this.tls = false;
this.greylistResponseTracker = new WeakMap(); this.greylistResponseTracker = new WeakSet();
if (Array.isArray(authentication)) { if (Array.isArray(authentication)) {
this.authentication = authentication; this.authentication = authentication;
} }
@ -1246,7 +1259,7 @@ class SMTPConnection extends EventEmitter {
* NOTE: `host` is trimmed before being used to establish a connection; however, the original untrimmed value will still be visible in configuration. * NOTE: `host` is trimmed before being used to establish a connection; however, the original untrimmed value will still be visible in configuration.
* *
* @public * @public
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @param {number} [port] the port to use for the connection * @param {number} [port] the port to use for the connection
* @param {string} [host] the hostname to use for the connection * @param {string} [host] the hostname to use for the connection
* @param {ConnectOptions} [options={}] the options * @param {ConnectOptions} [options={}] the options
@ -1328,7 +1341,7 @@ class SMTPConnection extends EventEmitter {
/** /**
* @public * @public
* @param {string} str the string to send * @param {string} str the string to send
* @param {*} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @returns {void} * @returns {void}
*/ */
send(str, callback) { send(str, callback) {
@ -1355,7 +1368,7 @@ class SMTPConnection extends EventEmitter {
/** /**
* @public * @public
* @param {string} cmd command to issue * @param {string} cmd command to issue
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @param {(number[] | number)} [codes=[250]] array codes * @param {(number[] | number)} [codes=[250]] array codes
* @returns {void} * @returns {void}
*/ */
@ -1376,8 +1389,8 @@ class SMTPConnection extends EventEmitter {
} }
else if ((code === 450 || code === 451) && else if ((code === 450 || code === 451) &&
msg.message.toLowerCase().includes('greylist') && msg.message.toLowerCase().includes('greylist') &&
this.greylistResponseTracker.get(response) === false) { this.greylistResponseTracker.has(response) === false) {
this.greylistResponseTracker.set(response, true); this.greylistResponseTracker.add(response);
setTimeout(() => { setTimeout(() => {
this.send(cmd + CRLF$1, response); this.send(cmd + CRLF$1, response);
}, GREYLIST_DELAY); }, GREYLIST_DELAY);
@ -1389,7 +1402,7 @@ class SMTPConnection extends EventEmitter {
} }
} }
}; };
this.greylistResponseTracker.set(response, false); this.greylistResponseTracker.delete(response);
this.send(cmd + CRLF$1, response); this.send(cmd + CRLF$1, response);
} }
/** /**
@ -1402,7 +1415,7 @@ class SMTPConnection extends EventEmitter {
* As this command was deprecated by rfc2821, it should only be used for compatibility with non-compliant servers. * As this command was deprecated by rfc2821, it should only be used for compatibility with non-compliant servers.
* @see https://tools.ietf.org/html/rfc2821#appendix-F.3 * @see https://tools.ietf.org/html/rfc2821#appendix-F.3
* *
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @param {string} domain the domain to associate with the 'helo' request * @param {string} domain the domain to associate with the 'helo' request
* @returns {void} * @returns {void}
*/ */
@ -1419,7 +1432,7 @@ class SMTPConnection extends EventEmitter {
} }
/** /**
* @public * @public
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @returns {void} * @returns {void}
*/ */
starttls(callback) { starttls(callback) {
@ -1474,7 +1487,7 @@ class SMTPConnection extends EventEmitter {
} }
/** /**
* @public * @public
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @param {string} domain the domain to associate with the 'ehlo' request * @param {string} domain the domain to associate with the 'ehlo' request
* @returns {void} * @returns {void}
*/ */
@ -1507,7 +1520,7 @@ class SMTPConnection extends EventEmitter {
/** /**
* @public * @public
* @description SMTP 'help' command, returns text from the server * @description SMTP 'help' command, returns text from the server
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @param {string} domain the domain to associate with the 'help' request * @param {string} domain the domain to associate with the 'help' request
* @returns {void} * @returns {void}
*/ */
@ -1516,7 +1529,7 @@ class SMTPConnection extends EventEmitter {
} }
/** /**
* @public * @public
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @returns {void} * @returns {void}
*/ */
rset(callback) { rset(callback) {
@ -1524,7 +1537,7 @@ class SMTPConnection extends EventEmitter {
} }
/** /**
* @public * @public
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @returns {void} * @returns {void}
*/ */
noop(callback) { noop(callback) {
@ -1532,7 +1545,7 @@ class SMTPConnection extends EventEmitter {
} }
/** /**
* @public * @public
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @param {string} from the sender * @param {string} from the sender
* @returns {void} * @returns {void}
*/ */
@ -1541,7 +1554,7 @@ class SMTPConnection extends EventEmitter {
} }
/** /**
* @public * @public
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @param {string} to the receiver * @param {string} to the receiver
* @returns {void} * @returns {void}
*/ */
@ -1550,7 +1563,7 @@ class SMTPConnection extends EventEmitter {
} }
/** /**
* @public * @public
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @returns {void} * @returns {void}
*/ */
data(callback) { data(callback) {
@ -1558,7 +1571,7 @@ class SMTPConnection extends EventEmitter {
} }
/** /**
* @public * @public
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @returns {void} * @returns {void}
*/ */
data_end(callback) { data_end(callback) {
@ -1578,7 +1591,7 @@ class SMTPConnection extends EventEmitter {
* @public * @public
* @description SMTP 'verify' command -- checks for address validity. * @description SMTP 'verify' command -- checks for address validity.
* @param {string} address the address to validate * @param {string} address the address to validate
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @returns {void} * @returns {void}
*/ */
verify(address, callback) { verify(address, callback) {
@ -1588,7 +1601,7 @@ class SMTPConnection extends EventEmitter {
* @public * @public
* @description SMTP 'expn' command -- expands a mailing list. * @description SMTP 'expn' command -- expands a mailing list.
* @param {string} address the mailing list to expand * @param {string} address the mailing list to expand
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @returns {void} * @returns {void}
*/ */
expn(address, callback) { expn(address, callback) {
@ -1601,7 +1614,7 @@ class SMTPConnection extends EventEmitter {
* If there has been no previous EHLO or HELO command self session, self * If there has been no previous EHLO or HELO command self session, self
* method tries ESMTP EHLO first. * method tries ESMTP EHLO first.
* *
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @param {string} [domain] the domain to associate with the command * @param {string} [domain] the domain to associate with the command
* @returns {void} * @returns {void}
*/ */
@ -1629,7 +1642,7 @@ class SMTPConnection extends EventEmitter {
* *
* This method will return normally if the authentication was successful. * This method will return normally if the authentication was successful.
* *
* @param {function(...*): void} callback function to call after response * @param {function(...any[]): void} callback function to call after response
* @param {string} [user] the username to authenticate with * @param {string} [user] the username to authenticate with
* @param {string} [password] the password for the authentication * @param {string} [password] the password for the authentication
* @param {{ method: string, domain: string }} [options] login options * @param {{ method: string, domain: string }} [options] login options
@ -1654,7 +1667,7 @@ class SMTPConnection extends EventEmitter {
* @param {string} challenge challenge * @param {string} challenge challenge
* @returns {string} base64 cram hash * @returns {string} base64 cram hash
*/ */
const encode_cram_md5 = (challenge) => { const encodeCramMd5 = (challenge) => {
const hmac = createHmac('md5', login.password()); const hmac = createHmac('md5', login.password());
hmac.update(Buffer.from(challenge, 'base64').toString('ascii')); hmac.update(Buffer.from(challenge, 'base64').toString('ascii'));
return Buffer.from(`${login.user()} ${hmac.digest('hex')}`).toString('base64'); return Buffer.from(`${login.user()} ${hmac.digest('hex')}`).toString('base64');
@ -1662,12 +1675,12 @@ class SMTPConnection extends EventEmitter {
/** /**
* @returns {string} base64 login/password * @returns {string} base64 login/password
*/ */
const encode_plain = () => Buffer.from(`\u0000${login.user()}\u0000${login.password()}`).toString('base64'); const encodePlain = () => Buffer.from(`\u0000${login.user()}\u0000${login.password()}`).toString('base64');
/** /**
* @see https://developers.google.com/gmail/xoauth2_protocol * @see https://developers.google.com/gmail/xoauth2_protocol
* @returns {string} base64 xoauth2 auth token * @returns {string} base64 xoauth2 auth token
*/ */
const encode_xoauth2 = () => Buffer.from(`user=${login.user()}\u0001auth=Bearer ${login.password()}\u0001\u0001`).toString('base64'); const encodeXoauth2 = () => Buffer.from(`user=${login.user()}\u0001auth=Bearer ${login.password()}\u0001\u0001`).toString('base64');
// List of authentication methods we support: from preferred to // List of authentication methods we support: from preferred to
// less preferred methods. // less preferred methods.
if (!method) { if (!method) {
@ -1686,7 +1699,7 @@ class SMTPConnection extends EventEmitter {
/** /**
* handle bad responses from command differently * handle bad responses from command differently
* @param {Error} err err * @param {Error} err err
* @param {*} data data * @param {unknown} data data
* @returns {void} * @returns {void}
*/ */
const failed = (err, data) => { const failed = (err, data) => {
@ -1696,7 +1709,7 @@ class SMTPConnection extends EventEmitter {
}; };
/** /**
* @param {Error} err err * @param {Error} err err
* @param {*} data data * @param {unknown} data data
* @returns {void} * @returns {void}
*/ */
const response = (err, data) => { const response = (err, data) => {
@ -1710,7 +1723,7 @@ class SMTPConnection extends EventEmitter {
}; };
/** /**
* @param {Error} err err * @param {Error} err err
* @param {*} data data * @param {unknown} data data
* @param {string} msg msg * @param {string} msg msg
* @returns {void} * @returns {void}
*/ */
@ -1720,7 +1733,7 @@ class SMTPConnection extends EventEmitter {
} }
else { else {
if (method === AUTH_METHODS['CRAM-MD5']) { if (method === AUTH_METHODS['CRAM-MD5']) {
this.command(encode_cram_md5(msg), response, [235, 503]); this.command(encodeCramMd5(msg), response, [235, 503]);
} }
else if (method === AUTH_METHODS.LOGIN) { else if (method === AUTH_METHODS.LOGIN) {
this.command(Buffer.from(login.password()).toString('base64'), response, [235, 503]); this.command(Buffer.from(login.password()).toString('base64'), response, [235, 503]);
@ -1729,11 +1742,11 @@ class SMTPConnection extends EventEmitter {
}; };
/** /**
* @param {Error} err err * @param {Error} err err
* @param {*} data data * @param {unknown} data data
* @param {string} msg msg * @param {string} msg msg
* @returns {void} * @returns {void}
*/ */
const attempt_user = (err, data) => { const attemptUser = (err, data) => {
if (err) { if (err) {
failed(err, data); failed(err, data);
} }
@ -1748,18 +1761,16 @@ class SMTPConnection extends EventEmitter {
this.command(`AUTH ${AUTH_METHODS['CRAM-MD5']}`, attempt, [334]); this.command(`AUTH ${AUTH_METHODS['CRAM-MD5']}`, attempt, [334]);
break; break;
case AUTH_METHODS.LOGIN: case AUTH_METHODS.LOGIN:
this.command(`AUTH ${AUTH_METHODS.LOGIN}`, attempt_user, [334]); this.command(`AUTH ${AUTH_METHODS.LOGIN}`, attemptUser, [334]);
break; break;
case AUTH_METHODS.PLAIN: case AUTH_METHODS.PLAIN:
this.command(`AUTH ${AUTH_METHODS.PLAIN} ${encode_plain()}`, response, [235, 503]); this.command(`AUTH ${AUTH_METHODS.PLAIN} ${encodePlain()}`, response, [235, 503]);
break; break;
case AUTH_METHODS.XOAUTH2: case AUTH_METHODS.XOAUTH2:
this.command(`AUTH ${AUTH_METHODS.XOAUTH2} ${encode_xoauth2()}`, response, [235, 503]); this.command(`AUTH ${AUTH_METHODS.XOAUTH2} ${encodeXoauth2()}`, response, [235, 503]);
break; break;
default: default:
const msg = 'no form of authorization supported'; caller(callback, SMTPError.create('no form of authorization supported', SMTPErrorStates.AUTHNOTSUPPORTED, null, data));
const err = SMTPError.create(msg, SMTPErrorStates.AUTHNOTSUPPORTED, null, data);
caller(callback, err);
break; break;
} }
}; };
@ -1793,7 +1804,7 @@ class SMTPConnection extends EventEmitter {
} }
/** /**
* @public * @public
* @param {function(...*): void} [callback] function to call after response * @param {function(...any[]): void} [callback] function to call after response
* @returns {void} * @returns {void}
*/ */
quit(callback) { quit(callback) {
@ -1849,6 +1860,23 @@ class SMTPClient {
} }
}); });
} }
/**
* @public
* @param {Message} msg the message to send
* @returns {Promise<Message>} a promise that resolves to the fully processed message
*/
sendAsync(msg) {
return new Promise((resolve, reject) => {
this.send(msg, (err, msg) => {
if (err != null) {
reject(err);
}
else {
resolve(msg);
}
});
});
}
/** /**
* @public * @public
* @description Converts a message to the raw object used by the internal stack. * @description Converts a message to the raw object used by the internal stack.
@ -1962,8 +1990,8 @@ class SMTPClient {
} }
/** /**
* @protected * @protected
* @param {*} attachment attachment * @param {MessageAttachment | MessageAttachment[]} attachment attachment
* @returns {*} whether the attachment contains inlined html * @returns {boolean} whether the attachment contains inlined html
*/ */
_containsInlinedHtml(attachment) { _containsInlinedHtml(attachment) {
if (Array.isArray(attachment)) { if (Array.isArray(attachment)) {
@ -2069,5 +2097,5 @@ class SMTPClient {
} }
} }
export { AUTH_METHODS, BUFFERSIZE, DEFAULT_TIMEOUT, MIME64CHUNK, MIMECHUNK, Message, SMTPClient, SMTPConnection, SMTPError, SMTPErrorStates, SMTPResponseMonitor, SMTPState, addressparser, getRFC2822Date, getRFC2822DateUTC, mimeEncode, mimeWordEncode }; export { AUTH_METHODS, BUFFERSIZE, DEFAULT_TIMEOUT, MIME64CHUNK, MIMECHUNK, Message, SMTPClient, SMTPConnection, SMTPError, SMTPErrorStates, SMTPResponseMonitor, SMTPState, addressparser, getRFC2822Date, getRFC2822DateUTC, isRFC2822Date, mimeEncode, mimeWordEncode };
//# sourceMappingURL=email.mjs.map //# sourceMappingURL=email.mjs.map

File diff suppressed because one or more lines are too long