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