@@ -15,6 +15,11 @@ use tracing::{debug, error, trace};
15
15
pub mod ipv4;
16
16
pub mod template;
17
17
18
+ pub enum EmailLanguage {
19
+ Nl ,
20
+ En ,
21
+ }
22
+
18
23
#[ derive( Debug , Error ) ]
19
24
pub enum SendError {
20
25
#[ error( "Failed to parse email address" ) ]
@@ -37,31 +42,39 @@ pub struct Attachment {
37
42
pub mime : String ,
38
43
}
39
44
40
- pub async fn send_email (
45
+ pub struct TreasurerEmailData < ' a > {
46
+ pub to : & ' a str ,
47
+ pub body : & ' a str ,
48
+ pub reply_to_name : & ' a str ,
49
+ pub reply_to_email : & ' a str ,
50
+ pub commission : & ' a str ,
51
+ pub attachments : Vec < Attachment > ,
52
+ }
53
+
54
+ pub async fn send_treasurer_email (
41
55
smtp_config : & SmtpConfig ,
42
56
local_addr4 : Ipv4Addr ,
43
- to : & str ,
44
- body : String ,
45
- reply_to_name : String ,
46
- reply_to_email : & str ,
47
- commission : & str ,
48
- attachments : Vec < Attachment > ,
57
+ data : TreasurerEmailData < ' _ > ,
49
58
) -> Result < ( ) , SendError > {
50
- let mb_to = Mailbox :: from_str ( to) ?;
59
+ let mb_to = Mailbox :: from_str ( data . to ) ?;
51
60
52
61
let mb_from = Mailbox :: new (
53
62
Some ( smtp_config. from_name . clone ( ) ) ,
54
63
Address :: from_str ( & smtp_config. from_email ) ?,
55
64
) ;
56
65
57
- let mb_reply_to = Mailbox :: new ( Some ( reply_to_name) , Address :: from_str ( & reply_to_email) ?) ;
66
+ let mb_reply_to = Mailbox :: new (
67
+ Some ( data. reply_to_name . to_string ( ) ) ,
68
+ Address :: from_str ( data. reply_to_email ) ?,
69
+ ) ;
58
70
59
71
let msg = Message :: builder ( )
60
72
. reply_to ( mb_reply_to)
61
73
. from ( mb_from)
62
74
. to ( mb_to)
63
75
. subject ( format ! (
64
- "[DigiDecs] Nieuwe declaratie: {commission} ({})" ,
76
+ "[DigiDecs] Nieuwe declaratie: {} ({})" ,
77
+ data. commission,
65
78
rand:: thread_rng( )
66
79
. sample_iter( rand:: distributions:: Alphanumeric )
67
80
. take( 6 )
@@ -70,9 +83,9 @@ pub async fn send_email(
70
83
) ) ;
71
84
72
85
let mut mp = MultiPart :: mixed ( ) . build ( ) ;
73
- mp = mp. singlepart ( SinglePart :: html ( body) ) ;
86
+ mp = mp. singlepart ( SinglePart :: html ( data . body . to_string ( ) ) ) ;
74
87
75
- for att in attachments {
88
+ for att in data . attachments {
76
89
mp = mp. singlepart (
77
90
lettre:: message:: Attachment :: new ( att. name )
78
91
. body ( att. content , ContentType :: parse ( & att. mime ) ?) ,
@@ -81,24 +94,71 @@ pub async fn send_email(
81
94
82
95
let msg = msg. multipart ( mp) ?;
83
96
97
+ let mut conn = smtp_connect ( smtp_config, local_addr4) . await ?;
98
+
99
+ trace ! ( "Sending email" ) ;
100
+ conn. send ( msg. envelope ( ) , & msg. formatted ( ) ) . await ?;
101
+
102
+ Ok ( ( ) )
103
+ }
104
+
105
+ pub async fn send_submitter_email (
106
+ smtp_config : & SmtpConfig ,
107
+ local_addr4 : Ipv4Addr ,
108
+ to_email : & str ,
109
+ body : String ,
110
+ name : & str ,
111
+ locale : & EmailLanguage ,
112
+ ) -> Result < ( ) , SendError > {
113
+ let mb_to = Mailbox :: new ( Some ( name. to_string ( ) ) , Address :: from_str ( to_email) ?) ;
114
+
115
+ let mb_from = Mailbox :: new (
116
+ Some ( smtp_config. from_name . clone ( ) ) ,
117
+ Address :: from_str ( & smtp_config. from_email ) ?,
118
+ ) ;
119
+
120
+ let msg = Message :: builder ( )
121
+ . to ( mb_to)
122
+ . from ( mb_from)
123
+ . subject ( submitter_subject ( locale) )
124
+ . singlepart ( SinglePart :: html ( body) ) ?;
125
+
126
+ let mut conn = smtp_connect ( smtp_config, local_addr4) . await ?;
127
+ trace ! ( "Sending email" ) ;
128
+ conn. send ( msg. envelope ( ) , & msg. formatted ( ) ) . await ?;
129
+
130
+ Ok ( ( ) )
131
+ }
132
+
133
+ fn submitter_subject ( locale : & EmailLanguage ) -> String {
134
+ match locale {
135
+ EmailLanguage :: Nl => "Je DigiDecs is ontvangen!" . into ( ) ,
136
+ EmailLanguage :: En => "Your DigiDecs has been received!" . into ( ) ,
137
+ }
138
+ }
139
+
140
+ async fn smtp_connect (
141
+ config : & SmtpConfig ,
142
+ bind_addr : Ipv4Addr ,
143
+ ) -> Result < AsyncSmtpConnection , SendError > {
84
144
let client_id =
85
- ClientId :: Domain ( get_ehlo_domain ( & smtp_config . from_email ) . ok_or ( SendError :: EmailParse ) ?) ;
145
+ ClientId :: Domain ( get_ehlo_domain ( & config . from_email ) . ok_or ( SendError :: EmailParse ) ?) ;
86
146
87
147
trace ! ( "Opening SMTP connection" ) ;
88
148
let mut conn = AsyncSmtpConnection :: connect_tokio1 (
89
- ( smtp_config . smtp_relay . as_str ( ) , 587 ) ,
149
+ ( config . smtp_relay . as_str ( ) , 587 ) ,
90
150
Some ( Duration :: from_secs ( 3 ) ) ,
91
151
& client_id,
92
152
// We cannot do STARTTLS (which uses port 465, which is blocked by Hetzner), so use port 587
93
153
// Port 587 starts out with regular SMTP commands, after the EHLO we upgrade to STARTTLS
94
154
None ,
95
- Some ( IpAddr :: V4 ( local_addr4 ) ) ,
155
+ Some ( IpAddr :: V4 ( bind_addr ) ) ,
96
156
)
97
157
. await ?;
98
158
99
159
if conn. can_starttls ( ) {
100
160
conn. starttls (
101
- TlsParameters :: new_rustls ( smtp_config . smtp_relay . as_str ( ) . into ( ) ) ?,
161
+ TlsParameters :: new_rustls ( config . smtp_relay . as_str ( ) . into ( ) ) ?,
102
162
& client_id,
103
163
)
104
164
. await ?;
@@ -107,15 +167,11 @@ pub async fn send_email(
107
167
trace ! ( "Checking SMTP connection" ) ;
108
168
if conn. test_connected ( ) . await {
109
169
debug ! ( "SMTP connection OK" ) ;
170
+ Ok ( conn)
110
171
} else {
111
172
error ! ( "Could not connect to server (SMTP)" ) ;
112
- return Err ( SendError :: Connect ) ;
173
+ Err ( SendError :: Connect )
113
174
}
114
-
115
- trace ! ( "Sending email" ) ;
116
- conn. send ( msg. envelope ( ) , & msg. formatted ( ) ) . await ?;
117
-
118
- Ok ( ( ) )
119
175
}
120
176
121
177
fn get_ehlo_domain ( email : & str ) -> Option < String > {
0 commit comments