@@ -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.
3877var defaultConfig = {
397879+ 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 / ^ s u b j e c t : [ \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 ( ) ,
0 commit comments