@@ -27,7 +27,7 @@ use rusoto_core::Region;
27
27
use rusoto_ses:: {
28
28
Body , Content , Destination , Message , SendEmailRequest , Ses , SesClient ,
29
29
} ;
30
- use serde:: Serialize ;
30
+ use serde:: { Deserialize , Serialize } ;
31
31
use serde_json:: Value ;
32
32
use simple_logger:: SimpleLogger ;
33
33
use std:: collections:: HashMap ;
@@ -81,6 +81,54 @@ impl fmt::Display for LambdaResponse {
81
81
}
82
82
}
83
83
84
+ #[ derive( Debug , Clone , Serialize , Deserialize , Default ) ]
85
+ pub struct EmailReceiptNotification {
86
+ #[ serde( rename = "notificationType" ) ]
87
+ notification_type : String ,
88
+ mail : Mail ,
89
+ receipt : Receipt ,
90
+ content : String ,
91
+ // #[serde(flatten)]
92
+ // other: HashMap<String, Value>,
93
+ }
94
+
95
+ #[ derive( Debug , Clone , Serialize , Deserialize , Default ) ]
96
+ pub struct Mail {
97
+ timestamp : String ,
98
+ source : String ,
99
+ #[ serde( rename = "messageId" ) ]
100
+ message_id : String ,
101
+ destination : Vec < String > ,
102
+ commonHeaders : CommonHeaders ,
103
+ #[ serde( flatten) ]
104
+ other : HashMap < String , Value > ,
105
+ }
106
+
107
+ #[ derive( Debug , Clone , Serialize , Deserialize , Default ) ]
108
+ pub struct CommonHeaders {
109
+ // replyTo: Vec<String>,
110
+ subject : String ,
111
+ #[ serde( rename = "returnPath" ) ]
112
+ return_path : String ,
113
+ #[ serde( flatten) ]
114
+ other : HashMap < String , Value > ,
115
+ }
116
+
117
+ #[ derive( Debug , Clone , Serialize , Deserialize , Default ) ]
118
+ pub struct Receipt {
119
+ #[ serde( rename = "spamVerdict" ) ]
120
+ spam_verdict : Verdict ,
121
+ #[ serde( rename = "virusVerdict" ) ]
122
+ virus_verdict : Verdict ,
123
+ #[ serde( flatten) ]
124
+ other : HashMap < String , Value > ,
125
+ }
126
+
127
+ #[ derive( Debug , Clone , Serialize , Deserialize , Default ) ]
128
+ pub struct Verdict {
129
+ status : String ,
130
+ }
131
+
84
132
/// PrivatEmail_Handler: processes incoming messages from SNS
85
133
/// and forwards to the appropriate recipient email
86
134
pub ( crate ) async fn privatemail_handler (
@@ -101,36 +149,28 @@ pub(crate) async fn privatemail_handler(
101
149
let sns_payload = event[ "Records" ] [ 0 ] [ "Sns" ] . as_object ( ) . unwrap ( ) ;
102
150
info ! ( "Raw Email Info: {:?}" , sns_payload) ;
103
151
104
- let sns_message: HashMap < String , Value > =
105
- serde_json:: from_str ( sns_payload[ "Message" ] . as_str ( ) . unwrap ( ) ) . unwrap ( ) ;
152
+ let sns_message: EmailReceiptNotification =
153
+ serde_json:: from_str ( sns_payload[ "Message" ] . as_str ( ) . unwrap ( ) ) ? ;
106
154
info ! ( "Parsed SES Message: {:#?}" , sns_message) ;
107
- info ! ( "Parsed SES Message content: {:#?}" , sns_message[ "content" ] ) ;
108
- info ! ( "Is String: {}" , sns_message[ "content" ] . is_string( ) ) ;
155
+ info ! ( "Parsed SES Message Mail: {:#?}" , sns_message. mail) ;
156
+ info ! ( "Parsed SES Message Receipt: {:#?}" , sns_message. receipt) ;
157
+ info ! ( "Parsed SES Message content: {:#?}" , sns_message. content) ;
109
158
110
159
// skip spam messages
111
- let spam_verdict: & str = sns_message. get ( "receipt" ) . unwrap ( ) [ "spamVerdict" ]
112
- [ "status" ]
113
- . as_str ( )
114
- . unwrap_or_default ( ) ;
115
- let virus_verdict: & str = sns_message. get ( "receipt" ) . unwrap ( ) [ "virus" ]
116
- [ "status" ]
117
- . as_str ( )
118
- . unwrap_or_default ( ) ;
119
- if spam_verdict == "FAIL" || virus_verdict == "FAIL" {
160
+ if sns_message. receipt . spam_verdict . status == "FAIL"
161
+ || sns_message. receipt . virus_verdict . status == "FAIL"
162
+ {
120
163
warn ! ( "Message contains spam or virus, skipping!" ) ;
121
164
process:: exit ( 200 ) ;
165
+ // Ok(LambdaResponse(200, "message skipped"))
122
166
}
123
-
124
167
// Rewrite Email From header to contain sender's name with forwarder's email address
125
- let original_sender: Vec < String > = serde_json:: from_value (
126
- sns_message[ "mail" ] [ "commonHeaders" ] [ "replyTo" ] . clone ( ) ,
127
- ) ?;
128
- let subject: String = serde_json:: from_value (
129
- sns_message[ "mail" ] [ "commonHeaders" ] [ "subject" ] . clone ( ) ,
130
- ) ?;
168
+ let original_sender: String = sns_message. mail . commonHeaders . return_path ;
131
169
170
+ let subject: String = sns_message. mail . commonHeaders . subject ;
132
171
// <<<< The bug is extracting the email from the JSON <<<< //
133
- let mail_content: String = sns_message[ "content" ] . to_string ( ) . clone ( ) ;
172
+
173
+ let mail_content: String = sns_message. content ;
134
174
135
175
info ! ( "sender: {:#?}" , original_sender) ;
136
176
info ! ( "Subject: {:#?}" , subject) ;
@@ -157,7 +197,7 @@ pub(crate) async fn privatemail_handler(
157
197
data : subject,
158
198
} ,
159
199
} ,
160
- reply_to_addresses : Some ( original_sender) ,
200
+ reply_to_addresses : Some ( vec ! [ original_sender] ) ,
161
201
return_path : None ,
162
202
return_path_arn : None ,
163
203
source : email_config. from_email . to_string ( ) ,
@@ -202,9 +242,10 @@ mod tests {
202
242
#[ tokio:: test]
203
243
// #[ignore = "skipping integration because because of IAM requirements"]
204
244
async fn handler_handles ( ) {
205
- env
:: set_var ( "TO_EMAIL" , "[email protected] " ) ;
206
- env:: set_var ( "FROM_EMAIL" , "achu@fufu.soup " ) ;
245
+ env
:: set_var ( "TO_EMAIL" , "[email protected] " ) ;
246
+ env:: set_var ( "FROM_EMAIL" , "achu@fufu.africa " ) ;
207
247
let test_event = read_test_event ( ) ;
248
+
208
249
assert_eq ! (
209
250
privatemail_handler( test_event, Context :: default ( ) )
210
251
. await
0 commit comments