blob: d18ce65508fb4d5b62868d07eb29171b3cd4a82d [file] [log] [blame]
'use strict';
const spawn = require('child_process').spawn;
const packageData = require('../../package.json');
const LeWindows = require('./le-windows');
const LeUnix = require('./le-unix');
const shared = require('../shared');
/**
* Generates a Transport object for Sendmail
*
* Possible options can be the following:
*
* * **path** optional path to sendmail binary
* * **newline** either 'windows' or 'unix'
* * **args** an array of arguments for the sendmail binary
*
* @constructor
* @param {Object} optional config parameter for the AWS Sendmail service
*/
class SendmailTransport {
constructor(options) {
options = options || {};
// use a reference to spawn for mocking purposes
this._spawn = spawn;
this.options = options || {};
this.name = 'Sendmail';
this.version = packageData.version;
this.path = 'sendmail';
this.args = false;
this.winbreak = false;
this.logger = shared.getLogger(this.options, {
component: this.options.component || 'sendmail'
});
if (options) {
if (typeof options === 'string') {
this.path = options;
} else if (typeof options === 'object') {
if (options.path) {
this.path = options.path;
}
if (Array.isArray(options.args)) {
this.args = options.args;
}
this.winbreak = ['win', 'windows', 'dos', '\r\n'].includes((options.newline || '').toString().toLowerCase());
}
}
}
/**
* <p>Compiles a mailcomposer message and forwards it to handler that sends it.</p>
*
* @param {Object} emailMessage MailComposer object
* @param {Function} callback Callback function to run when the sending is completed
*/
send(mail, done) {
// Sendmail strips this header line by itself
mail.message.keepBcc = true;
let envelope = mail.data.envelope || mail.message.getEnvelope();
let messageId = mail.message.messageId();
let args;
let sendmail;
let returned;
let transform;
if (this.args) {
// force -i to keep single dots
args = ['-i'].concat(this.args).concat(envelope.to);
} else {
args = ['-i'].concat(envelope.from ? ['-f', envelope.from] : []).concat(envelope.to);
}
let callback = err => {
if (returned) {
// ignore any additional responses, already done
return;
}
returned = true;
if (typeof done === 'function') {
if (err) {
return done(err);
} else {
return done(null, {
envelope: mail.data.envelope || mail.message.getEnvelope(),
messageId,
response: 'Messages queued for delivery'
});
}
}
};
try {
sendmail = this._spawn(this.path, args);
} catch (E) {
this.logger.error({
err: E,
tnx: 'spawn',
messageId
}, 'Error occurred while spawning sendmail. %s', E.message);
return callback(E);
}
if (sendmail) {
sendmail.on('error', err => {
this.logger.error({
err,
tnx: 'spawn',
messageId
}, 'Error occurred when sending message %s. %s', messageId, err.message);
callback(err);
});
sendmail.once('exit', code => {
if (!code) {
return callback();
}
let err;
if (code === 127) {
err = new Error('Sendmail command not found, process exited with code ' + code);
} else {
err = new Error('Sendmail exited with code ' + code);
}
this.logger.error({
err,
tnx: 'stdin',
messageId
}, 'Error sending message %s to sendmail. %s', messageId, err.message);
callback(err);
});
sendmail.once('close', callback);
sendmail.stdin.on('error', err => {
this.logger.error({
err,
tnx: 'stdin',
messageId
}, 'Error occurred when piping message %s to sendmail. %s', messageId, err.message);
callback(err);
});
let recipients = [].concat(envelope.to || []);
if (recipients.length > 3) {
recipients.push('...and ' + recipients.splice(2).length + ' more');
}
this.logger.info({
tnx: 'send',
messageId
}, 'Sending message %s to <%s>', messageId, recipients.join(', '));
transform = this.winbreak ? new LeWindows() : new LeUnix();
let sourceStream = mail.message.createReadStream();
transform.once('error', err => {
this.logger.error({
err,
tnx: 'stdin',
messageId
}, 'Error occurred when generating message %s. %s', messageId, err.message);
sendmail.kill('SIGINT'); // do not deliver the message
callback(err);
});
sourceStream.once('error', err => transform.emit('error', err));
sourceStream.pipe(transform).pipe(sendmail.stdin);
} else {
return callback(new Error('sendmail was not found'));
}
}
}
module.exports = SendmailTransport;