Skip to content

RFC: Add Support for SMTP Server #96

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 97 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ console.log("AWS Lambda SES Forwarder // @arithmetric // Version 3.1.0");
// name part of an email address before the "at" symbol (i.e. `@example.com`).
// The key must be lowercase.
var defaultConfig = {
// Uncomment this if you want to use smtp instead of SES
//smtp_info: {
// region: "aws-region",
// // The aws secret contains the
// // smtp server info.
// secretName: "name of aws secret",
// // The AWS Secret should look like.
// // {
// // "server": "smtp.server",
// // "username": "username",
// // "password": "secret password",
// // "port": "port-number"
// // }
//},
fromEmail: "[email protected]",
subjectPrefix: "",
emailBucket: "s3-bucket-name",
Expand Down Expand Up @@ -226,6 +240,78 @@ exports.processMessage = function(data, next) {
next(null, data);
};

/**
* Send email using an SMTP Server drop in replacement for ses.sendRawMessage
* more or less.
*
* @param {object} config - The config object to get the smtp info out of.
* @param {object} params - params includes destination, source, and raw message
* @param {function} callback - Callback function invoked as (error, data).
*/
exports.sendSMTPEmail = function (config, params, callback) {
var secretsmanager = new AWS.SecretsManager({region: config.smtp_info.region });
secretsmanager.getSecretValue({SecretId: config.smtp_info.secretName},
function(err, data) {
if (err) {
callback(err, null);
} else if (! ('SecretString' in data )) {
console.log({ level: 'debug', message: 'no secret string.'});
callback(new Error('No Secret String'), null);
} else {
var smtp_info = JSON.parse(data.SecretString);

console.log({level: 'debug', message:'host=' + smtp_info.server +
' port=' + smtp_info.port +
' username=' + smtp_info.username});

var smtp_client = require('smtp-client');
var mail_client = new smtp_client.SMTPClient({
host: smtp_info.server,
port: parseInt(smtp_info.port, 10),
secure: true,
timeout: 60000
});

var done = false;
var sendit = async function(smtp_info, params) {
await mail_client.connect();
//await mail_client.secure({timeout: 30000});
// runs EHLO command or HELO as a fallback
await mail_client.greet({hostname: smtp_info.server});
// authenticates a user
await mail_client.authPlain({username: smtp_info.username,
password: smtp_info.password});
// runs MAIL FROM command
await mail_client.mail({from: params.Source});
// runs RCPT TO command (run this multiple times to add more recii)
console.log({level: 'debug', message: 'Destinations = ' + params.Destinations});
if (! params.Destinations instanceof Array) {
params.Destinations = [ params.Destinations];
}
for (var i = 0; i < params.Destinations.length; i++) {
var email = params.Destinations[i];
console.log({level: 'debug', message:'did to: ' + email});
await mail_client.rcpt({to: email});
}

// runs DATA command and streams email source
await mail_client.data(params.RawMessage.Data);
// runs QUIT command
await mail_client.quit();
};
sendit(smtp_info, params).catch(function(err) {
done = true;
console.log({level: 'error', message: 'send smtp ' + err + " " + err.stack});
callback(err, null);
}).then(function(response) {
if (!done) {
callback(null, response);
}
});
}
});
};

/**
* Send email using the SES sendRawEmail command.
*
Expand All @@ -243,7 +329,7 @@ exports.sendMessage = function(data, next) {
data.log({level: "info", message: "sendMessage: Sending email via SES. " +
"Original recipients: " + data.originalRecipients.join(", ") +
". Transformed recipients: " + data.recipients.join(", ") + "."});
data.ses.sendRawEmail(params, function(err, result) {
data.ses.sendRawEmail(data.config, params, function(err, result) {
if (err) {
data.log({level: "error", message: "sendRawEmail() returned error.",
error: err, stack: err.stack});
Expand Down Expand Up @@ -291,9 +377,18 @@ exports.handler = function(event, context, overrides) {
context: context,
config: overrides && overrides.config ? overrides.config : defaultConfig,
log: overrides && overrides.log ? overrides.log : console.log,
ses: overrides && overrides.ses ? overrides.ses : new AWS.SES(),
s3: overrides && overrides.s3 ? overrides.s3 : new AWS.S3()
};
data['ses'] = overrides && overrides.ses ? overrides.ses : (
'smtp_info' in data ['config'] ? {
sendRawEmail: function(config, params, callback) {
exports.sendSMTPEmail(config, params, callback);
}
} : {
sendRawEmail: function(config, params, callback) {
new AWS.SES().sendRawEmail(params, callback);
}});

var nextStep = function(err, data) {
if (err) {
data.log({level: "error", message: "Step (index " + (currentStep - 1) +
Expand Down