1
0
mirror of https://github.com/eleith/emailjs.git synced 2024-07-03 11:38:50 +00:00
emailjs/smtp/client.js

196 lines
4.4 KiB
JavaScript
Raw Normal View History

2018-06-25 02:42:29 +00:00
const { SMTP, state } = require('./smtp');
const { Message, create } = require('./message');
const addressparser = require('addressparser');
class Client {
2018-05-27 04:25:08 +00:00
constructor(server) {
2018-06-25 02:42:29 +00:00
this.smtp = new SMTP(server);
2018-05-27 04:25:08 +00:00
//this.smtp.debug(1);
this.queue = [];
this.timer = null;
this.sending = false;
this.ready = false;
}
2018-05-27 04:25:08 +00:00
_poll() {
clearTimeout(this.timer);
2018-05-27 04:25:08 +00:00
if (this.queue.length) {
2018-06-25 02:42:29 +00:00
if (this.smtp.state() == state.NOTCONNECTED) {
this._connect(this.queue[0]);
} else if (
2018-06-25 02:42:29 +00:00
this.smtp.state() == state.CONNECTED &&
2018-05-27 04:25:08 +00:00
!this.sending &&
this.ready
) {
this._sendmail(this.queue.shift());
}
2018-05-27 04:25:08 +00:00
}
2018-06-04 16:40:23 +00:00
// wait around 1 seconds in case something does come in,
// otherwise close out SMTP connection if still open
2018-06-25 02:42:29 +00:00
else if (this.smtp.state() == state.CONNECTED) {
2018-05-27 04:25:08 +00:00
this.timer = setTimeout(() => this.smtp.quit(), 1000);
2018-06-04 16:40:23 +00:00
}
2018-05-27 04:25:08 +00:00
}
_connect(stack) {
const connect = err => {
if (!err) {
const begin = err => {
if (!err) {
this.ready = true;
this._poll();
} else {
stack.callback(err, stack.message);
// clear out the queue so all callbacks can be called with the same error message
this.queue.shift();
this._poll();
}
};
2018-06-04 16:40:23 +00:00
if (!this.smtp.authorized()) {
this.smtp.login(begin);
} else {
this.smtp.ehlo_or_helo_if_needed(begin);
}
2018-05-27 04:25:08 +00:00
} else {
stack.callback(err, stack.message);
// clear out the queue so all callbacks can be called with the same error message
this.queue.shift();
this._poll();
}
};
this.ready = false;
this.smtp.connect(connect);
}
send(msg, callback) {
if (
2018-06-25 02:42:29 +00:00
!(msg instanceof Message) &&
2018-05-27 04:25:08 +00:00
msg.from &&
(msg.to || msg.cc || msg.bcc) &&
(msg.text !== undefined || this._containsInlinedHtml(msg.attachment))
2018-06-04 16:40:23 +00:00
) {
2018-06-25 02:42:29 +00:00
msg = create(msg);
2018-06-04 16:40:23 +00:00
}
2018-05-27 04:25:08 +00:00
2018-06-25 02:42:29 +00:00
if (msg instanceof Message) {
2018-05-27 04:25:08 +00:00
msg.valid((valid, why) => {
if (valid) {
const stack = {
message: msg,
to: addressparser(msg.header.to),
from: addressparser(msg.header.from)[0].address,
callback: (callback || function() {}).bind(this),
};
2018-06-04 16:40:23 +00:00
if (msg.header.cc) {
2018-05-27 04:25:08 +00:00
stack.to = stack.to.concat(addressparser(msg.header.cc));
2018-06-04 16:40:23 +00:00
}
2018-05-27 04:25:08 +00:00
2018-06-04 16:40:23 +00:00
if (msg.header.bcc) {
2018-05-27 04:25:08 +00:00
stack.to = stack.to.concat(addressparser(msg.header.bcc));
2018-06-04 16:40:23 +00:00
}
2018-05-27 04:25:08 +00:00
if (
msg.header['return-path'] &&
addressparser(msg.header['return-path']).length
2018-06-04 16:40:23 +00:00
) {
2018-05-27 04:25:08 +00:00
stack.returnPath = addressparser(
msg.header['return-path']
)[0].address;
2018-06-04 16:40:23 +00:00
}
2018-05-27 04:25:08 +00:00
this.queue.push(stack);
this._poll();
} else {
callback(new Error(why), msg);
}
2018-05-27 04:25:08 +00:00
});
} else {
callback(new Error('message is not a valid Message instance'), msg);
2018-05-27 04:25:08 +00:00
}
}
_containsInlinedHtml(attachment) {
if (Array.isArray(attachment)) {
return attachment.some(() => {
return att => {
return this._isAttachmentInlinedHtml(att);
};
});
} else {
return this._isAttachmentInlinedHtml(attachment);
}
}
_isAttachmentInlinedHtml(attachment) {
return (
attachment &&
(attachment.data || attachment.path) &&
attachment.alternative === true
);
}
_sendsmtp(stack, next) {
return err => {
if (!err && next) {
next.apply(this, [stack]);
} else {
// if we snag on SMTP commands, call done, passing the error
// but first reset SMTP state so queue can continue polling
this.smtp.rset(() => this._senddone(err, stack));
}
};
}
_sendmail(stack) {
2018-05-27 04:25:08 +00:00
const from = stack.returnPath || stack.from;
this.sending = true;
this.smtp.mail(this._sendsmtp(stack, this._sendrcpt), '<' + from + '>');
}
_sendrcpt(stack) {
2018-05-27 04:25:08 +00:00
const to = stack.to.shift().address;
this.smtp.rcpt(
this._sendsmtp(stack, stack.to.length ? this._sendrcpt : this._senddata),
'<' + to + '>'
);
}
_senddata(stack) {
2018-05-27 04:25:08 +00:00
this.smtp.data(this._sendsmtp(stack, this._sendmessage));
}
2018-05-27 04:25:08 +00:00
_sendmessage(stack) {
const stream = stack.message.stream();
2018-05-27 04:25:08 +00:00
stream.on('data', data => this.smtp.message(data));
stream.on('end', () => {
this.smtp.data_end(
this._sendsmtp(stack, () => this._senddone(null, stack))
);
});
// there is no way to cancel a message while in the DATA portion,
// so we have to close the socket to prevent a bad email from going out
2018-05-27 04:25:08 +00:00
stream.on('error', err => {
this.smtp.close();
this._senddone(err, stack);
});
}
_senddone(err, stack) {
2018-05-27 04:25:08 +00:00
this.sending = false;
stack.callback(err, stack.message);
this._poll();
}
}
2011-02-23 21:23:37 +00:00
exports.Client = Client;
exports.connect = server => new Client(server);