You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
6.4 KiB
211 lines
6.4 KiB
'use strict';
|
|
|
|
const spawn = require('child_process').spawn;
|
|
const packageData = require('../../package.json');
|
|
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 Sendmail
|
|
*/
|
|
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;
|
|
|
|
const hasInvalidAddresses = []
|
|
.concat(envelope.from || [])
|
|
.concat(envelope.to || [])
|
|
.some(addr => /^-/.test(addr));
|
|
if (hasInvalidAddresses) {
|
|
return done(new Error('Can not send mail. Invalid envelope addresses.'));
|
|
}
|
|
|
|
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(', ')
|
|
);
|
|
|
|
let sourceStream = mail.message.createReadStream();
|
|
sourceStream.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.pipe(sendmail.stdin);
|
|
} else {
|
|
return callback(new Error('sendmail was not found'));
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = SendmailTransport;
|