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