Skip to content

Commit 58bddc6

Browse files
authored
Merge pull request #1 from ThomasTJdev/Send-response-to-550-and-552
Send response to 550 and 552
2 parents 0e58e8c + e027a30 commit 58bddc6

File tree

4 files changed

+310
-21
lines changed

4 files changed

+310
-21
lines changed

index.js

Lines changed: 152 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,49 @@ console.log("AWS Lambda SES Forwarder // @arithmetric // Version 5.0.0");
77
// Configure the S3 bucket and key prefix for stored raw emails, and the
88
// mapping of email addresses to forward from and to.
99
//
10+
// IAM access:
11+
// {
12+
// "Version": "2020-01-01",
13+
// "Statement": [
14+
// {
15+
// "Effect": "Allow",
16+
// "Action": [
17+
// "logs:CreateLogGroup",
18+
// "logs:CreateLogStream",
19+
// "logs:PutLogEvents"
20+
// ],
21+
// "Resource": "arn:aws:logs:*:*:*"
22+
// },
23+
// {
24+
// "Effect": "Allow",
25+
// "Action": [
26+
// "ses:SendRawEmail",
27+
// "ses:SendEmail"
28+
// ],
29+
// "Resource": "*"
30+
// },
31+
// {
32+
// "Effect": "Allow",
33+
// "Action": [
34+
// "s3:GetObject",
35+
// "s3:PutObject"
36+
// ],
37+
// "Resource": "arn:aws:s3:::s3-bucket-name/emailsPrefix/*"
38+
// }
39+
// ]
40+
// }
41+
//
1042
// Expected keys/values:
1143
//
1244
// - fromEmail: Forwarded emails will come from this verified address
1345
//
46+
// - notifyEmail: This will be used to notify the sender, if the mail could not
47+
// be delivered.
48+
//
49+
// - notify550: Enables auto response when email address was not found.
50+
//
51+
// - notify552: Enables auto response if mail is larger than 10 MB.
52+
//
1453
// - subjectPrefix: Forwarded emails subject will contain this prefix
1554
//
1655
// - emailBucket: S3 bucket name where SES stores emails.
@@ -37,6 +76,9 @@ console.log("AWS Lambda SES Forwarder // @arithmetric // Version 5.0.0");
3776
// To match all email addresses matching no other mapping, use "@" as a key.
3877
var defaultConfig = {
3978
fromEmail: "[email protected]",
79+
notifyEmail: "[email protected]",
80+
notify550: true,
81+
notify552: true,
4082
subjectPrefix: "",
4183
emailBucket: "s3-bucket-name",
4284
emailKeyPrefix: "emailsPrefix/",
@@ -138,7 +180,11 @@ exports.transformRecipients = function(data) {
138180
"original destinations: " + data.originalRecipients.join(", "),
139181
level: "info"
140182
});
141-
return data.callback();
183+
if (data.config.notify550) {
184+
data.smtpErr = "550";
185+
} else {
186+
return data.callback();
187+
}
142188
}
143189

144190
data.recipients = newRecipients;
@@ -195,6 +241,23 @@ exports.fetchMessage = function(data) {
195241
return reject(
196242
new Error("Error: Failed to load message body from S3."));
197243
}
244+
// Check content lenght (SES hardcoded 10 MB - 10_000_000 bytes)
245+
if (result.ContentLength >= 10000000) {
246+
if (data.config.notify552) {
247+
data.smtpErr = "552",
248+
data.log({
249+
level: "info",
250+
message: "ContentLength > 10 MB, size = " + result.ContentLength + " bytes (SMTP 552)"
251+
});
252+
} else {
253+
data.log({
254+
level: "error",
255+
message: "ContentLength > 10 MB, size = " + result.ContentLength + " bytes (SMTP 552)"
256+
});
257+
return reject(
258+
new Error("Error: Mail size exceeds 10 MB."));
259+
}
260+
}
198261
data.emailData = result.Body.toString();
199262
return resolve(data);
200263
});
@@ -221,6 +284,7 @@ exports.processMessage = function(data) {
221284
var from = match && match[1] ? match[1] : '';
222285
if (from) {
223286
header = header + 'Reply-To: ' + from;
287+
data.from = from;
224288
data.log({
225289
level: "info",
226290
message: "Added Reply-To address of: " + from
@@ -256,6 +320,7 @@ exports.processMessage = function(data) {
256320
header = header.replace(
257321
/^subject:[\t ]?(.*)/mgi,
258322
function(match, subject) {
323+
data.subject = subject;
259324
return 'Subject: ' + data.config.subjectPrefix + subject;
260325
});
261326
}
@@ -299,30 +364,93 @@ exports.sendMessage = function(data) {
299364
Data: data.emailData
300365
}
301366
};
302-
data.log({
303-
level: "info",
304-
message: "sendMessage: Sending email via SES. Original recipients: " +
305-
data.originalRecipients.join(", ") + ". Transformed recipients: " +
306-
data.recipients.join(", ") + "."
307-
});
367+
368+
// Format params if we are bouncing
369+
if (data.smtpErr != "" && data.config.notifyEmail != "") {
370+
var bounceData = "";
371+
switch (data.smtpErr) {
372+
case '552':
373+
bounceData = "Your email was rejected. Please ensure that the size of your mail is less than 10 MB.\n\n" + "SMTP Reply Code = 552, SMTP Status Code = 5.3.4";
374+
break;
375+
case '550':
376+
bounceData = "Your email was rejected. The email address was not found. Please check the receiving email address.\n\n" + "SMTP Reply Code = 550, SMTP Status Code = 5.1.1";
377+
break;
378+
default:
379+
bounceData = "Unknown error"
380+
}
381+
382+
params = {
383+
Destination: {
384+
ToAddresses: data.from.split()
385+
},
386+
Source: data.config.notifyEmail,
387+
Message: {
388+
Subject: {
389+
Data: "Delivery Status Notification (Failure)"
390+
},
391+
Body: {
392+
Text: {
393+
Data: "An error occurred while trying to deliver the mail to the following recipients: " + data.originalRecipients + "\n\n" + bounceData
394+
}
395+
}
396+
}
397+
};
398+
399+
data.log({
400+
level: "info",
401+
message: "sendMessage: Sending bounce email via SES. Recipients: " +
402+
data.originalRecipients + ". Bounce code: " + data.smtpErr
403+
});
404+
} else {
405+
data.log({
406+
level: "info",
407+
message: "sendMessage: Sending email via SES. Original recipients: " +
408+
data.originalRecipients.join(", ") + ". Transformed recipients: " +
409+
data.recipients.join(", ") + "."
410+
});
411+
}
412+
308413
return new Promise(function(resolve, reject) {
309-
data.ses.sendRawEmail(params, function(err, result) {
310-
if (err) {
414+
if (data.smtpErr != "" && data.config.notifyEmail != "") {
415+
// If bounce
416+
data.ses.sendEmail(params, function(err, result) {
417+
if (err) {
418+
data.log({
419+
level: "error",
420+
message: "sendRawEmail() data.smtpErr returned error.",
421+
error: err,
422+
stack: err.stack
423+
});
424+
return reject(new Error('Error: Email sending failed.'));
425+
}
311426
data.log({
312-
level: "error",
313-
message: "sendRawEmail() returned error.",
314-
error: err,
315-
stack: err.stack
427+
level: "info",
428+
message: "sendRawEmail() data.smtpErr successful.",
429+
result: result
316430
});
317-
return reject(new Error('Error: Email sending failed.'));
318-
}
319-
data.log({
320-
level: "info",
321-
message: "sendRawEmail() successful.",
322-
result: result
431+
resolve(data);
323432
});
324-
resolve(data);
325-
});
433+
434+
} else {
435+
// If OK
436+
data.ses.sendRawEmail(params, function(err, result) {
437+
if (err) {
438+
data.log({
439+
level: "error",
440+
message: "sendRawEmail() returned error.",
441+
error: err,
442+
stack: err.stack
443+
});
444+
return reject(new Error('Error: Email sending failed.'));
445+
}
446+
data.log({
447+
level: "info",
448+
message: "sendRawEmail() successful.",
449+
result: result
450+
});
451+
resolve(data);
452+
});
453+
}
326454
});
327455
};
328456

@@ -349,6 +477,9 @@ exports.handler = function(event, context, callback, overrides) {
349477
event: event,
350478
callback: callback,
351479
context: context,
480+
from: "",
481+
subject: "",
482+
smtpErr: "",
352483
config: overrides && overrides.config ? overrides.config : defaultConfig,
353484
log: overrides && overrides.log ? overrides.log : console.log,
354485
ses: overrides && overrides.ses ? overrides.ses : new AWS.SES(),

test/fetchMessage.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,5 +97,77 @@ describe('index.js', function() {
9797
done();
9898
});
9999
});
100+
101+
it('should enable smtp error 552',
102+
function(done) {
103+
var data = {
104+
config: {
105+
notifyEmail: "[email protected]",
106+
notify552: true,
107+
emailBucket: "bucket",
108+
emailKeyPrefix: "prefix/"
109+
},
110+
context: {
111+
fail: function() {
112+
assert.ok(false, 'context.fail() was called');
113+
done();
114+
}
115+
},
116+
email: {
117+
messageId: "abc"
118+
},
119+
log: console.log,
120+
s3: {
121+
copyObject: function(options, callback) {
122+
callback(null);
123+
},
124+
getObject: function(options, callback) {
125+
callback(null, {Body: "email data", ContentLength: 20000000});
126+
}
127+
}
128+
};
129+
index.fetchMessage(data)
130+
.then(function(data) {
131+
assert.equal(data.smtpErr,
132+
"552",
133+
"fetchMessage returned email data");
134+
done();
135+
});
136+
});
137+
138+
it('should fail due to mail size exceeds 10MB',
139+
function(done) {
140+
var data = {
141+
config: {
142+
notifyEmail: "[email protected]",
143+
notify552: false,
144+
emailBucket: "bucket",
145+
emailKeyPrefix: "prefix/"
146+
},
147+
context: {
148+
fail: function() {
149+
assert.ok(false, 'context.fail() was called');
150+
done();
151+
}
152+
},
153+
email: {
154+
messageId: "abc"
155+
},
156+
log: console.log,
157+
s3: {
158+
copyObject: function(options, callback) {
159+
callback(null);
160+
},
161+
getObject: function(options, callback) {
162+
callback(null, {Body: "email data", ContentLength: 20000000});
163+
}
164+
}
165+
};
166+
index.fetchMessage(data)
167+
.catch(function(err) {
168+
assert.ok(err, "fetchMessage aborted operation");
169+
done();
170+
});
171+
});
100172
});
101173
});

test/sendMessage.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,67 @@ describe('index.js', function() {
5656
done();
5757
});
5858
});
59+
60+
it('should invoke the AWS SES SDK to send the bounce message',
61+
function(done) {
62+
var data = {
63+
config: {
64+
notifyEmail: "[email protected]",
65+
notify550: true,
66+
notify552: true,
67+
},
68+
smtpErr: "552",
69+
recipients: [
70+
71+
],
72+
originalRecipients: [
73+
74+
],
75+
emailData: "message data",
76+
context: {},
77+
log: console.log,
78+
ses: {
79+
sendEmail: function(options, callback) {
80+
callback(null, {status: "ok"});
81+
}
82+
}
83+
};
84+
index.sendMessage(data)
85+
.then(function() {
86+
assert.ok(true, "sendMessage returned successfully");
87+
done();
88+
});
89+
});
90+
91+
it('should result in failure if the AWS SES SDK cannot send the bounce message',
92+
function(done) {
93+
var data = {
94+
config: {
95+
notifyEmail: "[email protected]",
96+
notify550: true,
97+
notify552: true,
98+
},
99+
smtpErr: "550",
100+
recipients: [
101+
102+
],
103+
originalRecipients: [
104+
105+
],
106+
emailData: "message data",
107+
context: {},
108+
log: console.log,
109+
ses: {
110+
sendEmail: function(options, callback) {
111+
callback(true);
112+
}
113+
}
114+
};
115+
index.sendMessage(data)
116+
.catch(function(err) {
117+
assert.ok(err, "sendMessage bounce msg aborted operation");
118+
done();
119+
});
120+
});
59121
});
60122
});

0 commit comments

Comments
 (0)