1use std::fmt;
10
11use anyhow::{Context as _, Result};
12use num_traits::ToPrimitive as _;
13use serde::{Deserialize, Serialize};
14
15use crate::config::Config;
16use crate::constants::{DC_LP_AUTH_FLAGS, DC_LP_AUTH_OAUTH2};
17use crate::context::Context;
18pub use crate::net::proxy::ProxyConfig;
19pub use crate::provider::Socket;
20use crate::tools::ToOption;
21
22#[derive(
26 Copy,
27 Clone,
28 Debug,
29 Default,
30 Display,
31 FromPrimitive,
32 ToPrimitive,
33 PartialEq,
34 Eq,
35 Serialize,
36 Deserialize,
37)]
38#[repr(u32)]
39#[strum(serialize_all = "snake_case")]
40pub enum EnteredCertificateChecks {
41 #[default]
45 Automatic = 0,
46
47 Strict = 1,
49
50 AcceptInvalidCertificates = 2,
53
54 AcceptInvalidCertificates2 = 3,
57}
58
59#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
61pub struct EnteredServerLoginParam {
62 pub server: String,
64
65 pub port: u16,
69
70 pub security: Socket,
72
73 pub user: String,
77
78 pub password: String,
80}
81
82#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
84pub struct EnteredLoginParam {
85 pub addr: String,
87
88 pub imap: EnteredServerLoginParam,
90
91 pub smtp: EnteredServerLoginParam,
93
94 pub certificate_checks: EnteredCertificateChecks,
97
98 pub oauth2: bool,
100}
101
102impl EnteredLoginParam {
103 pub(crate) async fn load(context: &Context) -> Result<Self> {
105 let addr = context
106 .get_config(Config::Addr)
107 .await?
108 .unwrap_or_default()
109 .trim()
110 .to_string();
111
112 let mail_server = context
113 .get_config(Config::MailServer)
114 .await?
115 .unwrap_or_default();
116 let mail_port = context
117 .get_config_parsed::<u16>(Config::MailPort)
118 .await?
119 .unwrap_or_default();
120 let mail_security = context
121 .get_config_parsed::<i32>(Config::MailSecurity)
122 .await?
123 .and_then(num_traits::FromPrimitive::from_i32)
124 .unwrap_or_default();
125 let mail_user = context
126 .get_config(Config::MailUser)
127 .await?
128 .unwrap_or_default();
129 let mail_pw = context
130 .get_config(Config::MailPw)
131 .await?
132 .unwrap_or_default();
133
134 let certificate_checks = if let Some(certificate_checks) = context
139 .get_config_parsed::<i32>(Config::ImapCertificateChecks)
140 .await?
141 {
142 num_traits::FromPrimitive::from_i32(certificate_checks)
143 .context("Unknown imap_certificate_checks value")?
144 } else {
145 Default::default()
146 };
147
148 let send_server = context
149 .get_config(Config::SendServer)
150 .await?
151 .unwrap_or_default();
152 let send_port = context
153 .get_config_parsed::<u16>(Config::SendPort)
154 .await?
155 .unwrap_or_default();
156 let send_security = context
157 .get_config_parsed::<i32>(Config::SendSecurity)
158 .await?
159 .and_then(num_traits::FromPrimitive::from_i32)
160 .unwrap_or_default();
161 let send_user = context
162 .get_config(Config::SendUser)
163 .await?
164 .unwrap_or_default();
165 let send_pw = context
166 .get_config(Config::SendPw)
167 .await?
168 .unwrap_or_default();
169
170 let server_flags = context
171 .get_config_parsed::<i32>(Config::ServerFlags)
172 .await?
173 .unwrap_or_default();
174 let oauth2 = matches!(server_flags & DC_LP_AUTH_FLAGS, DC_LP_AUTH_OAUTH2);
175
176 Ok(EnteredLoginParam {
177 addr,
178 imap: EnteredServerLoginParam {
179 server: mail_server,
180 port: mail_port,
181 security: mail_security,
182 user: mail_user,
183 password: mail_pw,
184 },
185 smtp: EnteredServerLoginParam {
186 server: send_server,
187 port: send_port,
188 security: send_security,
189 user: send_user,
190 password: send_pw,
191 },
192 certificate_checks,
193 oauth2,
194 })
195 }
196
197 pub(crate) async fn save(&self, context: &Context) -> Result<()> {
200 context.set_config(Config::Addr, Some(&self.addr)).await?;
201
202 context
203 .set_config(Config::MailServer, self.imap.server.to_option())
204 .await?;
205 context
206 .set_config(Config::MailPort, self.imap.port.to_option().as_deref())
207 .await?;
208 context
209 .set_config(
210 Config::MailSecurity,
211 self.imap.security.to_i32().to_option().as_deref(),
212 )
213 .await?;
214 context
215 .set_config(Config::MailUser, self.imap.user.to_option())
216 .await?;
217 context
218 .set_config(Config::MailPw, self.imap.password.to_option())
219 .await?;
220
221 context
222 .set_config(Config::SendServer, self.smtp.server.to_option())
223 .await?;
224 context
225 .set_config(Config::SendPort, self.smtp.port.to_option().as_deref())
226 .await?;
227 context
228 .set_config(
229 Config::SendSecurity,
230 self.smtp.security.to_i32().to_option().as_deref(),
231 )
232 .await?;
233 context
234 .set_config(Config::SendUser, self.smtp.user.to_option())
235 .await?;
236 context
237 .set_config(Config::SendPw, self.smtp.password.to_option())
238 .await?;
239
240 context
241 .set_config(
242 Config::ImapCertificateChecks,
243 self.certificate_checks.to_i32().to_option().as_deref(),
244 )
245 .await?;
246
247 let server_flags = if self.oauth2 {
248 Some(DC_LP_AUTH_OAUTH2.to_string())
249 } else {
250 None
251 };
252 context
253 .set_config(Config::ServerFlags, server_flags.as_deref())
254 .await?;
255
256 Ok(())
257 }
258}
259
260impl fmt::Display for EnteredLoginParam {
261 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262 let unset = "0";
263 let pw = "***";
264
265 write!(
266 f,
267 "{} imap:{}:{}:{}:{}:{}:{} smtp:{}:{}:{}:{}:{}:{} cert_{}",
268 unset_empty(&self.addr),
269 unset_empty(&self.imap.user),
270 if !self.imap.password.is_empty() {
271 pw
272 } else {
273 unset
274 },
275 unset_empty(&self.imap.server),
276 self.imap.port,
277 self.imap.security,
278 if self.oauth2 { "OAUTH2" } else { "AUTH_NORMAL" },
279 unset_empty(&self.smtp.user),
280 if !self.smtp.password.is_empty() {
281 pw
282 } else {
283 unset
284 },
285 unset_empty(&self.smtp.server),
286 self.smtp.port,
287 self.smtp.security,
288 if self.oauth2 { "OAUTH2" } else { "AUTH_NORMAL" },
289 self.certificate_checks
290 )
291 }
292}
293
294fn unset_empty(s: &str) -> &str {
295 if s.is_empty() { "unset" } else { s }
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301 use crate::test_utils::TestContext;
302 use pretty_assertions::assert_eq;
303
304 #[test]
305 fn test_entered_certificate_checks_display() {
306 use std::string::ToString;
307
308 assert_eq!(
309 "accept_invalid_certificates".to_string(),
310 EnteredCertificateChecks::AcceptInvalidCertificates.to_string()
311 );
312 }
313
314 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
315 async fn test_entered_login_param() -> Result<()> {
316 let t = &TestContext::new().await;
317
318 t.set_config(Config::Addr, Some("alice@example.org"))
319 .await?;
320 t.set_config(Config::MailPw, Some("foobarbaz")).await?;
321
322 let param = EnteredLoginParam::load(t).await?;
323 assert_eq!(param.addr, "alice@example.org");
324 assert_eq!(
325 param.certificate_checks,
326 EnteredCertificateChecks::Automatic
327 );
328
329 t.set_config(Config::ImapCertificateChecks, Some("1"))
330 .await?;
331 let param = EnteredLoginParam::load(t).await?;
332 assert_eq!(param.certificate_checks, EnteredCertificateChecks::Strict);
333
334 t.set_config(Config::ImapCertificateChecks, Some("999"))
336 .await?;
337 assert!(EnteredLoginParam::load(t).await.is_err());
338
339 Ok(())
340 }
341
342 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
343 async fn test_save_entered_login_param() -> Result<()> {
344 let t = TestContext::new().await;
345 let param = EnteredLoginParam {
346 addr: "alice@example.org".to_string(),
347 imap: EnteredServerLoginParam {
348 server: "".to_string(),
349 port: 0,
350 security: Socket::Starttls,
351 user: "".to_string(),
352 password: "foobar".to_string(),
353 },
354 smtp: EnteredServerLoginParam {
355 server: "".to_string(),
356 port: 2947,
357 security: Socket::default(),
358 user: "".to_string(),
359 password: "".to_string(),
360 },
361 certificate_checks: Default::default(),
362 oauth2: false,
363 };
364 param.save(&t).await?;
365 assert_eq!(
366 t.get_config(Config::Addr).await?.unwrap(),
367 "alice@example.org"
368 );
369 assert_eq!(t.get_config(Config::MailPw).await?.unwrap(), "foobar");
370 assert_eq!(t.get_config(Config::SendPw).await?, None);
371 assert_eq!(t.get_config_int(Config::SendPort).await?, 2947);
372
373 assert_eq!(EnteredLoginParam::load(&t).await?, param);
374
375 Ok(())
376 }
377}