The following links were used as reference for setting everything up:
- https://github.com/arithmetric/aws-lambda-ses-forwarder/blob/master/index.js
- https://devlog.rolandow.com/2016/02/forward-incoming-mail-using-amazon-services-and-lambda/
- https://jeremyfelt.com/2016/05/22/send-receive-email-domain-postmark-amazons-ses-s3-lambda-services/?source=post_page-----f88b9ffdb60a----------------------
The configuration for the lambda function used is as follows:
var AWS = require('aws-sdk');
var forwardFrom = '<[email protected]>';
var forwardTo = [
{
source: '[email protected]',
destinations: ['[email protected]']
},
{
source: '[email protected]',
destinations: ['[email protected]']
},
{
source: '[email protected]',
destinations: ['[email protected]', '[email protected]']
}
];
var defaultTo = '[email protected]';
exports.handler = function(event, context) {
function fetchMessage(data) {
return new Promise(function(resolve, reject) {
const s3 = new AWS.S3({ signatureVersion: 'v4' });
s3.copyObject(
{
Bucket: 'emails-example.com',
CopySource: 'emails-example.com/' + data.mail.messageId,
Key: data.mail.messageId,
ACL: 'private',
ContentType: 'text/plain',
StorageClass: 'STANDARD'
},
function(err) {
if (err) {
console.error(err);
return reject(
new Error('Error: Could not make readable copy of email.')
);
}
// Load the raw email from S3
s3.getObject(
{
Bucket: 'emails-example.com',
Key: data.mail.messageId
},
function(err, result) {
if (err) {
console.error(err);
return reject(
new Error('Error: Failed to load message body from S3.')
);
}
return resolve(result.Body.toString());
}
);
}
);
});
}
function escapeHTML(s) {
return s
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/</g, '<')
.replace(/>/g, '>');
}
async function main() {
console.log('JSON.stringify(event)');
console.log(JSON.stringify(event));
var msgInfo = event.Records[0].ses;
console.log('msgInfo', JSON.stringify(msgInfo));
// construct "to" list
const toList = [];
for (const dest of msgInfo.mail.destination) {
console.log('dest', dest);
const filteredForwardAddresses = forwardTo.filter(
mapping => mapping.source === dest
);
console.log('filteredForwardAddresses', filteredForwardAddresses);
for (const mapping of filteredForwardAddresses) {
console.log('mapping', mapping);
for (const destination of mapping.destinations) {
console.log('destination', destination);
toList.push(destination);
}
}
}
console.log('toList', toList);
if (toList.length === 0) {
console.log('No matching forward addresses found.');
toList.push(defaultTo);
}
// don't process spam messages
if (
msgInfo.receipt.spamVerdict.status === 'FAIL' ||
msgInfo.receipt.virusVerdict.status === 'FAIL'
) {
console.log('Message is spam or contains virus, ignoring.');
context.succeed();
}
console.log('here');
var email = await fetchMessage(msgInfo);
console.log('Source email');
console.log(email);
var headers =
'From: ' +
msgInfo.mail.commonHeaders.returnPath +
' ' +
forwardFrom +
'\r\n';
headers += 'Reply-To: ' + msgInfo.mail.commonHeaders.from[0] + '\r\n';
headers += 'X-Original-To: ' + msgInfo.mail.commonHeaders.to[0] + '\r\n';
headers += 'To: ' + toList.join(', ') + '\r\n';
headers +=
'Subject: ' +
msgInfo.mail.commonHeaders.subject +
' -- From: ' +
msgInfo.mail.commonHeaders.from[0] +
'\r\n';
if (email) {
var res;
res = email.match(/Content-Type:.+\s*boundary.*/);
if (res) {
headers += res[0] + '\r\n';
davidwhynot;
} else {
res = email.match(/^Content-Type:(.*)/m);
if (res) {
headers += res[0] + '\r\n';
}
}
res = email.match(/^Content-Transfer-Encoding:(.*)/m);
if (res) {
headers += res[0] + '\r\n';
}
res = email.match(/^MIME-Version:(.*)/m);
if (res) {
headers += res[0] + '\r\n';
}
var splitEmail = email.split('\r\n\r\n');
splitEmail.shift();
console.log('JSON.stringify(splitEmail)');
console.log(JSON.stringify(splitEmail));
var additionalInfoText =
'To: ' +
msgInfo.mail.commonHeaders.to[0] +
' From: ' +
msgInfo.mail.commonHeaders.from[0] +
'\r\n';
var additionalInfoHTML =
'<div>To: ' +
escapeHTML(msgInfo.mail.commonHeaders.to[0]) +
' From: ' +
escapeHTML(msgInfo.mail.commonHeaders.from[0]) +
'</div><div><br></div>\r\n';
var matchingLinesPlain = splitEmail.filter(line =>
line.match(/Content-Type: text\/plain/)
);
for (const line of matchingLinesPlain) {
splitEmail.splice(splitEmail.indexOf(line) + 1, 0, additionalInfoText);
}
console.log('matchingLinesPlain', matchingLinesPlain);
var matchingLinesHTML = splitEmail.filter(line =>
line.match(/Content-Type: text\/html/)
);
console.log('matchingLinesHTML', matchingLinesPlain);
for (const line of matchingLinesHTML) {
splitEmail.splice(splitEmail.indexOf(line) + 1, 0, additionalInfoHTML);
}
console.log('JSON.stringify(splitEmail) (after join)');
console.log(JSON.stringify(splitEmail));
email = headers + '\r\n' + splitEmail.join('\r\n\r\n');
} else {
email = headers + '\r\n' + 'Empty email';
}
console.log('email');
console.log(email);
new AWS.SES().sendRawEmail(
{
RawMessage: { Data: email }
},
function(err, data) {
if (err) context.fail(err);
else {
console.log('Sent with MessageId: ' + data.MessageId);
context.succeed();
}
}
);
}
main();
};