Compare commits

..

No commits in common. "99bdf2fb14c8bde0c61f687ef4a0421a42282968" and "c1c4d0b81b0ddeab7b177deec701afefe6255ae0" have entirely different histories.

7 changed files with 172 additions and 268 deletions

View File

@ -4,18 +4,6 @@ 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/)
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
### Added
- greylist support [#202](https://github.com/eleith/emailjs/issues/202)

View File

@ -50,33 +50,6 @@ 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
```js

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long