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
59impl EnteredCertificateChecks {
60 pub(crate) fn accept_invalid_certificates(self) -> bool {
61 matches!(
62 self,
63 Self::AcceptInvalidCertificates | Self::AcceptInvalidCertificates2
64 )
65 }
66}
67
68#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
70pub struct EnteredImapLoginParam {
71 pub server: String,
73
74 pub port: u16,
78
79 #[serde(default)]
83 pub folder: String,
84
85 pub security: Socket,
87
88 pub user: String,
92
93 pub password: String,
95}
96
97#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
99pub struct EnteredSmtpLoginParam {
100 pub server: String,
102
103 pub port: u16,
107
108 pub security: Socket,
110
111 pub user: String,
115
116 pub password: String,
118}
119
120#[derive(Debug)]
122pub struct TransportListEntry {
123 pub param: EnteredLoginParam,
125 pub is_unpublished: bool,
128}
129
130#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
132pub struct EnteredLoginParam {
133 pub addr: String,
135
136 pub imap: EnteredImapLoginParam,
138
139 pub smtp: EnteredSmtpLoginParam,
141
142 pub certificate_checks: EnteredCertificateChecks,
145
146 pub oauth2: bool,
148}
149
150impl EnteredLoginParam {
151 pub(crate) async fn load_legacy(context: &Context) -> Result<Self> {
156 let addr = context
157 .get_config(Config::Addr)
158 .await?
159 .unwrap_or_default()
160 .trim()
161 .to_string();
162
163 let mail_server = context
164 .get_config(Config::MailServer)
165 .await?
166 .unwrap_or_default();
167 let mail_port = context
168 .get_config_parsed::<u16>(Config::MailPort)
169 .await?
170 .unwrap_or_default();
171
172 let mail_folder = String::new();
174
175 let mail_security = context
176 .get_config_parsed::<i32>(Config::MailSecurity)
177 .await?
178 .and_then(num_traits::FromPrimitive::from_i32)
179 .unwrap_or_default();
180 let mail_user = context
181 .get_config(Config::MailUser)
182 .await?
183 .unwrap_or_default();
184 let mail_pw = context
185 .get_config(Config::MailPw)
186 .await?
187 .unwrap_or_default();
188
189 let certificate_checks = if let Some(certificate_checks) = context
194 .get_config_parsed::<i32>(Config::ImapCertificateChecks)
195 .await?
196 {
197 num_traits::FromPrimitive::from_i32(certificate_checks)
198 .context("Unknown imap_certificate_checks value")?
199 } else {
200 Default::default()
201 };
202
203 let send_server = context
204 .get_config(Config::SendServer)
205 .await?
206 .unwrap_or_default();
207 let send_port = context
208 .get_config_parsed::<u16>(Config::SendPort)
209 .await?
210 .unwrap_or_default();
211 let send_security = context
212 .get_config_parsed::<i32>(Config::SendSecurity)
213 .await?
214 .and_then(num_traits::FromPrimitive::from_i32)
215 .unwrap_or_default();
216 let send_user = context
217 .get_config(Config::SendUser)
218 .await?
219 .unwrap_or_default();
220 let send_pw = context
221 .get_config(Config::SendPw)
222 .await?
223 .unwrap_or_default();
224
225 let server_flags = context
226 .get_config_parsed::<i32>(Config::ServerFlags)
227 .await?
228 .unwrap_or_default();
229 let oauth2 = matches!(server_flags & DC_LP_AUTH_FLAGS, DC_LP_AUTH_OAUTH2);
230
231 Ok(EnteredLoginParam {
232 addr,
233 imap: EnteredImapLoginParam {
234 server: mail_server,
235 port: mail_port,
236 folder: mail_folder,
237 security: mail_security,
238 user: mail_user,
239 password: mail_pw,
240 },
241 smtp: EnteredSmtpLoginParam {
242 server: send_server,
243 port: send_port,
244 security: send_security,
245 user: send_user,
246 password: send_pw,
247 },
248 certificate_checks,
249 oauth2,
250 })
251 }
252
253 pub(crate) async fn save_legacy(&self, context: &Context) -> Result<()> {
259 context.set_config(Config::Addr, Some(&self.addr)).await?;
260
261 context
262 .set_config(Config::MailServer, self.imap.server.to_option())
263 .await?;
264 context
265 .set_config(Config::MailPort, self.imap.port.to_option().as_deref())
266 .await?;
267 context
268 .set_config(
269 Config::MailSecurity,
270 self.imap.security.to_i32().to_option().as_deref(),
271 )
272 .await?;
273 context
274 .set_config(Config::MailUser, self.imap.user.to_option())
275 .await?;
276 context
277 .set_config(Config::MailPw, self.imap.password.to_option())
278 .await?;
279
280 context
281 .set_config(Config::SendServer, self.smtp.server.to_option())
282 .await?;
283 context
284 .set_config(Config::SendPort, self.smtp.port.to_option().as_deref())
285 .await?;
286 context
287 .set_config(
288 Config::SendSecurity,
289 self.smtp.security.to_i32().to_option().as_deref(),
290 )
291 .await?;
292 context
293 .set_config(Config::SendUser, self.smtp.user.to_option())
294 .await?;
295 context
296 .set_config(Config::SendPw, self.smtp.password.to_option())
297 .await?;
298
299 context
300 .set_config(
301 Config::ImapCertificateChecks,
302 self.certificate_checks.to_i32().to_option().as_deref(),
303 )
304 .await?;
305
306 let server_flags = if self.oauth2 {
307 Some(DC_LP_AUTH_OAUTH2.to_string())
308 } else {
309 None
310 };
311 context
312 .set_config(Config::ServerFlags, server_flags.as_deref())
313 .await?;
314
315 Ok(())
316 }
317}
318
319impl fmt::Display for EnteredLoginParam {
320 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321 let unset = "0";
322 let pw = "***";
323
324 write!(
325 f,
326 "{} imap:{}:{}:{}:{}:{}:{} smtp:{}:{}:{}:{}:{}:{} cert_{}",
327 unset_empty(&self.addr),
328 unset_empty(&self.imap.user),
329 if !self.imap.password.is_empty() {
330 pw
331 } else {
332 unset
333 },
334 unset_empty(&self.imap.server),
335 self.imap.port,
336 self.imap.security,
337 if self.oauth2 { "OAUTH2" } else { "AUTH_NORMAL" },
338 unset_empty(&self.smtp.user),
339 if !self.smtp.password.is_empty() {
340 pw
341 } else {
342 unset
343 },
344 unset_empty(&self.smtp.server),
345 self.smtp.port,
346 self.smtp.security,
347 if self.oauth2 { "OAUTH2" } else { "AUTH_NORMAL" },
348 self.certificate_checks
349 )
350 }
351}
352
353fn unset_empty(s: &str) -> &str {
354 if s.is_empty() { "unset" } else { s }
355}
356
357#[cfg(test)]
358mod tests {
359 use super::*;
360 use crate::test_utils::TestContext;
361 use pretty_assertions::assert_eq;
362
363 #[test]
364 fn test_entered_certificate_checks_display() {
365 use std::string::ToString;
366
367 assert_eq!(
368 "accept_invalid_certificates".to_string(),
369 EnteredCertificateChecks::AcceptInvalidCertificates.to_string()
370 );
371 }
372
373 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
374 async fn test_entered_login_param() -> Result<()> {
375 let t = &TestContext::new().await;
376
377 t.set_config(Config::Addr, Some("alice@example.org"))
378 .await?;
379 t.set_config(Config::MailPw, Some("foobarbaz")).await?;
380
381 let param = EnteredLoginParam::load_legacy(t).await?;
382 assert_eq!(param.addr, "alice@example.org");
383 assert_eq!(
384 param.certificate_checks,
385 EnteredCertificateChecks::Automatic
386 );
387
388 t.set_config(Config::ImapCertificateChecks, Some("1"))
389 .await?;
390 let param = EnteredLoginParam::load_legacy(t).await?;
391 assert_eq!(param.certificate_checks, EnteredCertificateChecks::Strict);
392
393 t.set_config(Config::ImapCertificateChecks, Some("999"))
395 .await?;
396 assert!(EnteredLoginParam::load_legacy(t).await.is_err());
397
398 Ok(())
399 }
400
401 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
402 async fn test_save_entered_login_param() -> Result<()> {
403 let t = TestContext::new().await;
404 let param = EnteredLoginParam {
405 addr: "alice@example.org".to_string(),
406 imap: EnteredImapLoginParam {
407 server: "".to_string(),
408 port: 0,
409 folder: "".to_string(),
410 security: Socket::Starttls,
411 user: "".to_string(),
412 password: "foobar".to_string(),
413 },
414 smtp: EnteredSmtpLoginParam {
415 server: "".to_string(),
416 port: 2947,
417 security: Socket::default(),
418 user: "".to_string(),
419 password: "".to_string(),
420 },
421 certificate_checks: Default::default(),
422 oauth2: false,
423 };
424 param.save_legacy(&t).await?;
425 assert_eq!(
426 t.get_config(Config::Addr).await?.unwrap(),
427 "alice@example.org"
428 );
429 assert_eq!(t.get_config(Config::MailPw).await?.unwrap(), "foobar");
430 assert_eq!(t.get_config(Config::SendPw).await?, None);
431 assert_eq!(t.get_config_int(Config::SendPort).await?, 2947);
432
433 assert_eq!(EnteredLoginParam::load_legacy(&t).await?, param);
434
435 Ok(())
436 }
437}