1use std::fmt;
12
13use anyhow::{Context as _, Result, bail, format_err};
14use deltachat_contact_tools::{EmailAddress, addr_normalize};
15use serde::{Deserialize, Serialize};
16
17use crate::config::Config;
18use crate::configure::server_params::{ServerParams, expand_param_vector};
19use crate::constants::{DC_LP_AUTH_FLAGS, DC_LP_AUTH_OAUTH2};
20use crate::context::Context;
21use crate::events::EventType;
22use crate::login_param::EnteredLoginParam;
23use crate::net::load_connection_timestamp;
24use crate::provider::{Protocol, Provider, Socket, UsernamePattern, get_provider_by_id};
25use crate::sql::Sql;
26use crate::sync::{RemovedTransportData, SyncData, TransportData};
27
28#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
29pub(crate) enum ConnectionSecurity {
30 Tls,
32
33 Starttls,
35
36 Plain,
38}
39
40impl fmt::Display for ConnectionSecurity {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 match self {
43 Self::Tls => write!(f, "tls")?,
44 Self::Starttls => write!(f, "starttls")?,
45 Self::Plain => write!(f, "plain")?,
46 }
47 Ok(())
48 }
49}
50
51impl TryFrom<Socket> for ConnectionSecurity {
52 type Error = anyhow::Error;
53
54 fn try_from(socket: Socket) -> Result<Self> {
55 match socket {
56 Socket::Automatic => Err(format_err!("Socket security is not configured")),
57 Socket::Ssl => Ok(Self::Tls),
58 Socket::Starttls => Ok(Self::Starttls),
59 Socket::Plain => Ok(Self::Plain),
60 }
61 }
62}
63
64#[derive(
66 Copy, Clone, Debug, Display, FromPrimitive, ToPrimitive, PartialEq, Eq, Serialize, Deserialize,
67)]
68#[repr(u32)]
69#[strum(serialize_all = "snake_case")]
70pub(crate) enum ConfiguredCertificateChecks {
71 OldAutomatic = 0,
83
84 Strict = 1,
86
87 AcceptInvalidCertificates = 2,
90
91 AcceptInvalidCertificates2 = 3,
96
97 Automatic = 4,
101}
102
103#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
104pub(crate) struct ConnectionCandidate {
105 pub host: String,
107
108 pub port: u16,
110
111 pub security: ConnectionSecurity,
113}
114
115impl fmt::Display for ConnectionCandidate {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 write!(f, "{}:{}:{}", &self.host, self.port, self.security)?;
118 Ok(())
119 }
120}
121
122#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
123pub(crate) struct ConfiguredServerLoginParam {
124 pub connection: ConnectionCandidate,
125
126 pub user: String,
128}
129
130impl fmt::Display for ConfiguredServerLoginParam {
131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 write!(f, "{}:{}", self.connection, &self.user)?;
133 Ok(())
134 }
135}
136
137pub(crate) async fn prioritize_server_login_params(
138 sql: &Sql,
139 params: &[ConfiguredServerLoginParam],
140 alpn: &str,
141) -> Result<Vec<ConfiguredServerLoginParam>> {
142 let mut res: Vec<(Option<i64>, ConfiguredServerLoginParam)> = Vec::with_capacity(params.len());
143 for param in params {
144 let timestamp = load_connection_timestamp(
145 sql,
146 alpn,
147 ¶m.connection.host,
148 param.connection.port,
149 None,
150 )
151 .await?;
152 res.push((timestamp, param.clone()));
153 }
154 res.sort_by_key(|(ts, _param)| std::cmp::Reverse(*ts));
155 Ok(res.into_iter().map(|(_ts, param)| param).collect())
156}
157
158#[derive(Debug, Clone, PartialEq, Eq)]
161pub(crate) struct ConfiguredLoginParam {
162 pub addr: String,
164
165 pub imap: Vec<ConfiguredServerLoginParam>,
166
167 pub imap_user: String,
172
173 pub imap_password: String,
174
175 pub smtp: Vec<ConfiguredServerLoginParam>,
176
177 pub smtp_user: String,
182
183 pub smtp_password: String,
184
185 pub provider: Option<&'static Provider>,
186
187 pub certificate_checks: ConfiguredCertificateChecks,
190
191 pub oauth2: bool,
193}
194
195#[derive(Debug, Serialize, Deserialize)]
198pub(crate) struct ConfiguredLoginParamJson {
199 pub addr: String,
200 pub imap: Vec<ConfiguredServerLoginParam>,
201 pub imap_user: String,
202 pub imap_password: String,
203 pub smtp: Vec<ConfiguredServerLoginParam>,
204 pub smtp_user: String,
205 pub smtp_password: String,
206 pub provider_id: Option<String>,
207 pub certificate_checks: ConfiguredCertificateChecks,
208 pub oauth2: bool,
209}
210
211impl fmt::Display for ConfiguredLoginParam {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 let addr = &self.addr;
214 let provider_id = match self.provider {
215 Some(provider) => provider.id,
216 None => "none",
217 };
218 let certificate_checks = self.certificate_checks;
219 write!(f, "{addr} imap:[")?;
220 let mut first = true;
221 for imap in &self.imap {
222 if !first {
223 write!(f, ", ")?;
224 }
225 write!(f, "{imap}")?;
226 first = false;
227 }
228 write!(f, "] smtp:[")?;
229 let mut first = true;
230 for smtp in &self.smtp {
231 if !first {
232 write!(f, ", ")?;
233 }
234 write!(f, "{smtp}")?;
235 first = false;
236 }
237 write!(f, "] provider:{provider_id} cert_{certificate_checks}")?;
238 Ok(())
239 }
240}
241
242impl ConfiguredLoginParam {
243 pub(crate) async fn load(context: &Context) -> Result<Option<(u32, Self)>> {
249 let Some(self_addr) = context.get_config(Config::ConfiguredAddr).await? else {
250 return Ok(None);
251 };
252
253 let Some((id, json)) = context
254 .sql
255 .query_row_optional(
256 "SELECT id, configured_param FROM transports WHERE addr=?",
257 (&self_addr,),
258 |row| {
259 let id: u32 = row.get(0)?;
260 let json: String = row.get(1)?;
261 Ok((id, json))
262 },
263 )
264 .await?
265 else {
266 bail!("Self address {self_addr} doesn't have a corresponding transport");
267 };
268 Ok(Some((id, Self::from_json(&json)?)))
269 }
270
271 pub(crate) async fn load_all(context: &Context) -> Result<Vec<(u32, Self)>> {
276 context
277 .sql
278 .query_map_vec("SELECT id, configured_param FROM transports", (), |row| {
279 let id: u32 = row.get(0)?;
280 let json: String = row.get(1)?;
281 let param = Self::from_json(&json)?;
282 Ok((id, param))
283 })
284 .await
285 }
286
287 pub(crate) async fn load_legacy(context: &Context) -> Result<Option<Self>> {
289 if !context.get_config_bool(Config::Configured).await? {
290 return Ok(None);
291 }
292
293 let addr = context
294 .get_config(Config::ConfiguredAddr)
295 .await?
296 .unwrap_or_default()
297 .trim()
298 .to_string();
299
300 let certificate_checks: ConfiguredCertificateChecks = if let Some(certificate_checks) =
301 context
302 .get_config_parsed::<i32>(Config::ConfiguredImapCertificateChecks)
303 .await?
304 {
305 num_traits::FromPrimitive::from_i32(certificate_checks)
306 .context("Invalid configured_imap_certificate_checks value")?
307 } else {
308 ConfiguredCertificateChecks::OldAutomatic
311 };
312
313 let send_pw = context
314 .get_config(Config::ConfiguredSendPw)
315 .await?
316 .context("SMTP password is not configured")?;
317 let mail_pw = context
318 .get_config(Config::ConfiguredMailPw)
319 .await?
320 .context("IMAP password is not configured")?;
321
322 let server_flags = context
323 .get_config_parsed::<i32>(Config::ConfiguredServerFlags)
324 .await?
325 .unwrap_or_default();
326 let oauth2 = matches!(server_flags & DC_LP_AUTH_FLAGS, DC_LP_AUTH_OAUTH2);
327
328 let provider = context
329 .get_config(Config::ConfiguredProvider)
330 .await?
331 .and_then(|cfg| get_provider_by_id(&cfg));
332
333 let imap;
334 let smtp;
335
336 let mail_user = context
337 .get_config(Config::ConfiguredMailUser)
338 .await?
339 .unwrap_or_default();
340 let send_user = context
341 .get_config(Config::ConfiguredSendUser)
342 .await?
343 .unwrap_or_default();
344
345 if let Some(provider) = provider {
346 let parsed_addr = EmailAddress::new(&addr).context("Bad email-address")?;
347 let addr_localpart = parsed_addr.local;
348
349 if provider.server.is_empty() {
350 let servers = vec![
351 ServerParams {
352 protocol: Protocol::Imap,
353 hostname: context
354 .get_config(Config::ConfiguredMailServer)
355 .await?
356 .unwrap_or_default(),
357 port: context
358 .get_config_parsed::<u16>(Config::ConfiguredMailPort)
359 .await?
360 .unwrap_or_default(),
361 socket: context
362 .get_config_parsed::<i32>(Config::ConfiguredMailSecurity)
363 .await?
364 .and_then(num_traits::FromPrimitive::from_i32)
365 .unwrap_or_default(),
366 username: mail_user.clone(),
367 },
368 ServerParams {
369 protocol: Protocol::Smtp,
370 hostname: context
371 .get_config(Config::ConfiguredSendServer)
372 .await?
373 .unwrap_or_default(),
374 port: context
375 .get_config_parsed::<u16>(Config::ConfiguredSendPort)
376 .await?
377 .unwrap_or_default(),
378 socket: context
379 .get_config_parsed::<i32>(Config::ConfiguredSendSecurity)
380 .await?
381 .and_then(num_traits::FromPrimitive::from_i32)
382 .unwrap_or_default(),
383 username: send_user.clone(),
384 },
385 ];
386 let servers = expand_param_vector(servers, &addr, &parsed_addr.domain);
387 imap = servers
388 .iter()
389 .filter_map(|params| {
390 let Ok(security) = params.socket.try_into() else {
391 return None;
392 };
393 if params.protocol == Protocol::Imap {
394 Some(ConfiguredServerLoginParam {
395 connection: ConnectionCandidate {
396 host: params.hostname.clone(),
397 port: params.port,
398 security,
399 },
400 user: params.username.clone(),
401 })
402 } else {
403 None
404 }
405 })
406 .collect();
407 smtp = servers
408 .iter()
409 .filter_map(|params| {
410 let Ok(security) = params.socket.try_into() else {
411 return None;
412 };
413 if params.protocol == Protocol::Smtp {
414 Some(ConfiguredServerLoginParam {
415 connection: ConnectionCandidate {
416 host: params.hostname.clone(),
417 port: params.port,
418 security,
419 },
420 user: params.username.clone(),
421 })
422 } else {
423 None
424 }
425 })
426 .collect();
427 } else {
428 imap = provider
429 .server
430 .iter()
431 .filter_map(|server| {
432 if server.protocol != Protocol::Imap {
433 return None;
434 }
435
436 let Ok(security) = server.socket.try_into() else {
437 return None;
438 };
439
440 Some(ConfiguredServerLoginParam {
441 connection: ConnectionCandidate {
442 host: server.hostname.to_string(),
443 port: server.port,
444 security,
445 },
446 user: if !mail_user.is_empty() {
447 mail_user.clone()
448 } else {
449 match server.username_pattern {
450 UsernamePattern::Email => addr.to_string(),
451 UsernamePattern::Emaillocalpart => addr_localpart.clone(),
452 }
453 },
454 })
455 })
456 .collect();
457 smtp = provider
458 .server
459 .iter()
460 .filter_map(|server| {
461 if server.protocol != Protocol::Smtp {
462 return None;
463 }
464
465 let Ok(security) = server.socket.try_into() else {
466 return None;
467 };
468
469 Some(ConfiguredServerLoginParam {
470 connection: ConnectionCandidate {
471 host: server.hostname.to_string(),
472 port: server.port,
473 security,
474 },
475 user: if !send_user.is_empty() {
476 send_user.clone()
477 } else {
478 match server.username_pattern {
479 UsernamePattern::Email => addr.to_string(),
480 UsernamePattern::Emaillocalpart => addr_localpart.clone(),
481 }
482 },
483 })
484 })
485 .collect();
486 }
487 } else if let (Some(configured_mail_servers), Some(configured_send_servers)) = (
488 context.get_config(Config::ConfiguredImapServers).await?,
489 context.get_config(Config::ConfiguredSmtpServers).await?,
490 ) {
491 imap = serde_json::from_str(&configured_mail_servers)
492 .context("Failed to parse configured IMAP servers")?;
493 smtp = serde_json::from_str(&configured_send_servers)
494 .context("Failed to parse configured SMTP servers")?;
495 } else {
496 let mail_server = context
498 .get_config(Config::ConfiguredMailServer)
499 .await?
500 .unwrap_or_default();
501 let mail_port = context
502 .get_config_parsed::<u16>(Config::ConfiguredMailPort)
503 .await?
504 .unwrap_or_default();
505
506 let mail_security: Socket = context
507 .get_config_parsed::<i32>(Config::ConfiguredMailSecurity)
508 .await?
509 .and_then(num_traits::FromPrimitive::from_i32)
510 .unwrap_or_default();
511
512 let send_server = context
513 .get_config(Config::ConfiguredSendServer)
514 .await?
515 .context("SMTP server is not configured")?;
516 let send_port = context
517 .get_config_parsed::<u16>(Config::ConfiguredSendPort)
518 .await?
519 .unwrap_or_default();
520 let send_security: Socket = context
521 .get_config_parsed::<i32>(Config::ConfiguredSendSecurity)
522 .await?
523 .and_then(num_traits::FromPrimitive::from_i32)
524 .unwrap_or_default();
525
526 imap = vec![ConfiguredServerLoginParam {
527 connection: ConnectionCandidate {
528 host: mail_server,
529 port: mail_port,
530 security: mail_security.try_into()?,
531 },
532 user: mail_user.clone(),
533 }];
534 smtp = vec![ConfiguredServerLoginParam {
535 connection: ConnectionCandidate {
536 host: send_server,
537 port: send_port,
538 security: send_security.try_into()?,
539 },
540 user: send_user.clone(),
541 }];
542 }
543
544 Ok(Some(ConfiguredLoginParam {
545 addr,
546 imap,
547 imap_user: mail_user,
548 imap_password: mail_pw,
549 smtp,
550 smtp_user: send_user,
551 smtp_password: send_pw,
552 certificate_checks,
553 provider,
554 oauth2,
555 }))
556 }
557
558 pub(crate) async fn save_to_transports_table(
559 self,
560 context: &Context,
561 entered_param: &EnteredLoginParam,
562 timestamp: i64,
563 ) -> Result<()> {
564 save_transport(context, entered_param, &self.into(), timestamp).await?;
565 Ok(())
566 }
567
568 pub(crate) fn from_json(json: &str) -> Result<Self> {
569 let json: ConfiguredLoginParamJson = serde_json::from_str(json)?;
570
571 let provider = json.provider_id.and_then(|id| get_provider_by_id(&id));
572
573 Ok(ConfiguredLoginParam {
574 addr: json.addr,
575 imap: json.imap,
576 imap_user: json.imap_user,
577 imap_password: json.imap_password,
578 smtp: json.smtp,
579 smtp_user: json.smtp_user,
580 smtp_password: json.smtp_password,
581 provider,
582 certificate_checks: json.certificate_checks,
583 oauth2: json.oauth2,
584 })
585 }
586
587 pub(crate) fn into_json(self) -> Result<String> {
588 let json: ConfiguredLoginParamJson = self.into();
589 Ok(serde_json::to_string(&json)?)
590 }
591
592 pub(crate) fn strict_tls(&self, connected_through_proxy: bool) -> bool {
593 let provider_strict_tls = self.provider.map(|provider| provider.opt.strict_tls);
594 match self.certificate_checks {
595 ConfiguredCertificateChecks::OldAutomatic => {
596 provider_strict_tls.unwrap_or(connected_through_proxy)
597 }
598 ConfiguredCertificateChecks::Automatic => provider_strict_tls.unwrap_or(true),
599 ConfiguredCertificateChecks::Strict => true,
600 ConfiguredCertificateChecks::AcceptInvalidCertificates
601 | ConfiguredCertificateChecks::AcceptInvalidCertificates2 => false,
602 }
603 }
604}
605
606impl From<ConfiguredLoginParam> for ConfiguredLoginParamJson {
607 fn from(configured_login_param: ConfiguredLoginParam) -> Self {
608 Self {
609 addr: configured_login_param.addr,
610 imap: configured_login_param.imap,
611 imap_user: configured_login_param.imap_user,
612 imap_password: configured_login_param.imap_password,
613 smtp: configured_login_param.smtp,
614 smtp_user: configured_login_param.smtp_user,
615 smtp_password: configured_login_param.smtp_password,
616 provider_id: configured_login_param.provider.map(|p| p.id.to_string()),
617 certificate_checks: configured_login_param.certificate_checks,
618 oauth2: configured_login_param.oauth2,
619 }
620 }
621}
622
623pub(crate) async fn save_transport(
626 context: &Context,
627 entered_param: &EnteredLoginParam,
628 configured: &ConfiguredLoginParamJson,
629 add_timestamp: i64,
630) -> Result<bool> {
631 let addr = addr_normalize(&configured.addr);
632 let configured_addr = context.get_config(Config::ConfiguredAddr).await?;
633
634 let mut modified = context
635 .sql
636 .execute(
637 "INSERT INTO transports (addr, entered_param, configured_param, add_timestamp)
638 VALUES (?, ?, ?, ?)
639 ON CONFLICT (addr)
640 DO UPDATE SET entered_param=excluded.entered_param,
641 configured_param=excluded.configured_param,
642 add_timestamp=excluded.add_timestamp
643 WHERE entered_param != excluded.entered_param
644 OR configured_param != excluded.configured_param
645 OR add_timestamp < excluded.add_timestamp",
646 (
647 &addr,
648 serde_json::to_string(entered_param)?,
649 serde_json::to_string(configured)?,
650 add_timestamp,
651 ),
652 )
653 .await?
654 > 0;
655
656 if configured_addr.is_none() {
657 context
659 .sql
660 .set_raw_config(Config::ConfiguredAddr.as_ref(), Some(&addr))
661 .await?;
662 modified = true;
663 }
664 Ok(modified)
665}
666
667pub(crate) async fn send_sync_transports(context: &Context) -> Result<()> {
669 info!(context, "Sending transport synchronization message.");
670
671 let transports = context
682 .sql
683 .query_map_vec(
684 "SELECT entered_param, configured_param, add_timestamp
685 FROM transports WHERE id>1",
686 (),
687 |row| {
688 let entered_json: String = row.get(0)?;
689 let entered: EnteredLoginParam = serde_json::from_str(&entered_json)?;
690 let configured_json: String = row.get(1)?;
691 let configured: ConfiguredLoginParamJson = serde_json::from_str(&configured_json)?;
692 let timestamp: i64 = row.get(2)?;
693 Ok(TransportData {
694 configured,
695 entered,
696 timestamp,
697 })
698 },
699 )
700 .await?;
701 let removed_transports = context
702 .sql
703 .query_map_vec(
704 "SELECT addr, remove_timestamp FROM removed_transports",
705 (),
706 |row| {
707 let addr: String = row.get(0)?;
708 let timestamp: i64 = row.get(1)?;
709 Ok(RemovedTransportData { addr, timestamp })
710 },
711 )
712 .await?;
713 context
714 .add_sync_item(SyncData::Transports {
715 transports,
716 removed_transports,
717 })
718 .await?;
719 context.scheduler.interrupt_smtp().await;
720
721 Ok(())
722}
723
724pub(crate) async fn sync_transports(
726 context: &Context,
727 transports: &[TransportData],
728 removed_transports: &[RemovedTransportData],
729) -> Result<()> {
730 let mut modified = false;
731 for TransportData {
732 configured,
733 entered,
734 timestamp,
735 } in transports
736 {
737 modified |= save_transport(context, entered, configured, *timestamp).await?;
738 }
739
740 context
741 .sql
742 .transaction(|transaction| {
743 for RemovedTransportData { addr, timestamp } in removed_transports {
744 modified |= transaction.execute(
745 "DELETE FROM transports
746 WHERE addr=? AND add_timestamp<=?",
747 (addr, timestamp),
748 )? > 0;
749 transaction.execute(
750 "INSERT INTO removed_transports (addr, remove_timestamp)
751 VALUES (?, ?)
752 ON CONFLICT (addr) DO
753 UPDATE SET remove_timestamp = excluded.remove_timestamp
754 WHERE excluded.remove_timestamp > remove_timestamp",
755 (addr, timestamp),
756 )?;
757 }
758 Ok(())
759 })
760 .await?;
761
762 if modified {
763 context.emit_event(EventType::TransportsModified);
764 }
765 Ok(())
766}
767
768pub(crate) async fn add_pseudo_transport(context: &Context, addr: &str) -> Result<()> {
770 context.sql
771 .execute(
772 "INSERT INTO transports (addr, entered_param, configured_param) VALUES (?, ?, ?)",
773 (
774 addr,
775 serde_json::to_string(&EnteredLoginParam::default())?,
776 format!(r#"{{"addr":"{addr}","imap":[],"imap_user":"","imap_password":"","smtp":[],"smtp_user":"","smtp_password":"","certificate_checks":"Automatic","oauth2":false}}"#)
777 ),
778 )
779 .await?;
780 Ok(())
781}
782
783#[cfg(test)]
784mod tests {
785 use super::*;
786 use crate::log::LogExt as _;
787 use crate::provider::get_provider_by_id;
788 use crate::test_utils::TestContext;
789 use crate::tools::time;
790
791 #[test]
792 fn test_configured_certificate_checks_display() {
793 use std::string::ToString;
794
795 assert_eq!(
796 "accept_invalid_certificates".to_string(),
797 ConfiguredCertificateChecks::AcceptInvalidCertificates.to_string()
798 );
799 }
800
801 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
802 async fn test_save_load_login_param() -> Result<()> {
803 let t = TestContext::new().await;
804
805 let param = ConfiguredLoginParam {
806 addr: "alice@example.org".to_string(),
807 imap: vec![ConfiguredServerLoginParam {
808 connection: ConnectionCandidate {
809 host: "imap.example.com".to_string(),
810 port: 123,
811 security: ConnectionSecurity::Starttls,
812 },
813 user: "alice".to_string(),
814 }],
815 imap_user: "".to_string(),
816 imap_password: "foo".to_string(),
817 smtp: vec![ConfiguredServerLoginParam {
818 connection: ConnectionCandidate {
819 host: "smtp.example.com".to_string(),
820 port: 456,
821 security: ConnectionSecurity::Tls,
822 },
823 user: "alice@example.org".to_string(),
824 }],
825 smtp_user: "".to_string(),
826 smtp_password: "bar".to_string(),
827 provider: None,
828 certificate_checks: ConfiguredCertificateChecks::Strict,
829 oauth2: false,
830 };
831
832 param
833 .clone()
834 .save_to_transports_table(&t, &EnteredLoginParam::default(), time())
835 .await?;
836 let expected_param = r#"{"addr":"alice@example.org","imap":[{"connection":{"host":"imap.example.com","port":123,"security":"Starttls"},"user":"alice"}],"imap_user":"","imap_password":"foo","smtp":[{"connection":{"host":"smtp.example.com","port":456,"security":"Tls"},"user":"alice@example.org"}],"smtp_user":"","smtp_password":"bar","provider_id":null,"certificate_checks":"Strict","oauth2":false}"#;
837 assert_eq!(
838 t.sql
839 .query_get_value::<String>("SELECT configured_param FROM transports", ())
840 .await?
841 .unwrap(),
842 expected_param
843 );
844 assert_eq!(t.is_configured().await?, true);
845 let (_transport_id, loaded) = ConfiguredLoginParam::load(&t).await?.unwrap();
846 assert_eq!(param, loaded);
847
848 t.set_config(Config::ConfiguredImapCertificateChecks, Some("999"))
850 .await?;
851 assert!(ConfiguredLoginParam::load(&t).await.is_ok());
852
853 let wrong_param = expected_param.replace("Strict", "Stricct");
855 assert_ne!(expected_param, wrong_param);
856 t.sql
857 .execute("UPDATE transports SET configured_param=?", (wrong_param,))
858 .await?;
859 assert!(ConfiguredLoginParam::load(&t).await.is_err());
860
861 Ok(())
862 }
863
864 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
865 async fn test_posteo_alias() -> Result<()> {
866 let t = TestContext::new().await;
867
868 let user = "alice@posteo.de";
869
870 t.set_config(Config::Configured, Some("1")).await?;
873 t.set_config(Config::ConfiguredProvider, Some("posteo"))
874 .await?;
875 t.sql
876 .set_raw_config(Config::ConfiguredAddr.as_ref(), Some("alice@posteo.at"))
877 .await?;
878 t.set_config(Config::ConfiguredMailServer, Some("posteo.de"))
879 .await?;
880 t.set_config(Config::ConfiguredMailPort, Some("993"))
881 .await?;
882 t.set_config(Config::ConfiguredMailSecurity, Some("1"))
883 .await?; t.set_config(Config::ConfiguredMailUser, Some(user)).await?;
885 t.set_config(Config::ConfiguredMailPw, Some("foobarbaz"))
886 .await?;
887 t.set_config(Config::ConfiguredImapCertificateChecks, Some("1"))
888 .await?; t.set_config(Config::ConfiguredSendServer, Some("posteo.de"))
890 .await?;
891 t.set_config(Config::ConfiguredSendPort, Some("465"))
892 .await?;
893 t.set_config(Config::ConfiguredSendSecurity, Some("1"))
894 .await?; t.set_config(Config::ConfiguredSendUser, Some(user)).await?;
896 t.set_config(Config::ConfiguredSendPw, Some("foobarbaz"))
897 .await?;
898 t.set_config(Config::ConfiguredSmtpCertificateChecks, Some("1"))
899 .await?; t.set_config(Config::ConfiguredServerFlags, Some("0"))
901 .await?;
902
903 let param = ConfiguredLoginParam {
904 addr: "alice@posteo.at".to_string(),
905 imap: vec![
906 ConfiguredServerLoginParam {
907 connection: ConnectionCandidate {
908 host: "posteo.de".to_string(),
909 port: 993,
910 security: ConnectionSecurity::Tls,
911 },
912 user: user.to_string(),
913 },
914 ConfiguredServerLoginParam {
915 connection: ConnectionCandidate {
916 host: "posteo.de".to_string(),
917 port: 143,
918 security: ConnectionSecurity::Starttls,
919 },
920 user: user.to_string(),
921 },
922 ],
923 imap_user: "alice@posteo.de".to_string(),
924 imap_password: "foobarbaz".to_string(),
925 smtp: vec![
926 ConfiguredServerLoginParam {
927 connection: ConnectionCandidate {
928 host: "posteo.de".to_string(),
929 port: 465,
930 security: ConnectionSecurity::Tls,
931 },
932 user: user.to_string(),
933 },
934 ConfiguredServerLoginParam {
935 connection: ConnectionCandidate {
936 host: "posteo.de".to_string(),
937 port: 587,
938 security: ConnectionSecurity::Starttls,
939 },
940 user: user.to_string(),
941 },
942 ],
943 smtp_user: "alice@posteo.de".to_string(),
944 smtp_password: "foobarbaz".to_string(),
945 provider: get_provider_by_id("posteo"),
946 certificate_checks: ConfiguredCertificateChecks::Strict,
947 oauth2: false,
948 };
949
950 let loaded = ConfiguredLoginParam::load_legacy(&t).await?.unwrap();
951 assert_eq!(loaded, param);
952
953 migrate_configured_login_param(&t).await;
954 let (_transport_id, loaded) = ConfiguredLoginParam::load(&t).await?.unwrap();
955 assert_eq!(loaded, param);
956
957 Ok(())
958 }
959
960 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
961 async fn test_empty_server_list_legacy() -> Result<()> {
962 let (domain, provider) = crate::provider::data::PROVIDER_DATA
966 .iter()
967 .find(|(_domain, provider)| provider.server.is_empty())
968 .unwrap();
969
970 let t = TestContext::new().await;
971
972 let addr = format!("alice@{domain}");
973
974 t.set_config(Config::Configured, Some("1")).await?;
975 t.set_config(Config::ConfiguredProvider, Some(provider.id))
976 .await?;
977 t.sql
978 .set_raw_config(Config::ConfiguredAddr.as_ref(), Some(&addr))
979 .await?;
980 t.set_config(Config::ConfiguredMailPw, Some("foobarbaz"))
981 .await?;
982 t.set_config(Config::ConfiguredImapCertificateChecks, Some("1"))
983 .await?; t.set_config(Config::ConfiguredSendPw, Some("foobarbaz"))
985 .await?;
986 t.set_config(Config::ConfiguredSmtpCertificateChecks, Some("1"))
987 .await?; t.set_config(Config::ConfiguredServerFlags, Some("0"))
989 .await?;
990
991 let loaded = ConfiguredLoginParam::load_legacy(&t).await?.unwrap();
992 assert_eq!(loaded.provider, Some(*provider));
993 assert_eq!(loaded.imap.is_empty(), false);
994 assert_eq!(loaded.smtp.is_empty(), false);
995
996 migrate_configured_login_param(&t).await;
997
998 let (_transport_id, loaded) = ConfiguredLoginParam::load(&t).await?.unwrap();
999 assert_eq!(loaded.provider, Some(*provider));
1000 assert_eq!(loaded.imap.is_empty(), false);
1001 assert_eq!(loaded.smtp.is_empty(), false);
1002
1003 Ok(())
1004 }
1005
1006 async fn migrate_configured_login_param(t: &TestContext) {
1007 t.sql.execute("DROP TABLE transports;", ()).await.unwrap();
1008 t.sql.set_raw_config_int("dbversion", 130).await.unwrap();
1009 t.sql.run_migrations(t).await.log_err(t).ok();
1010 }
1011
1012 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1013 async fn test_empty_server_list() -> Result<()> {
1014 let (domain, provider) = crate::provider::data::PROVIDER_DATA
1018 .iter()
1019 .find(|(_domain, provider)| provider.server.is_empty())
1020 .unwrap();
1021
1022 let t = TestContext::new().await;
1023
1024 let addr = format!("alice@{domain}");
1025
1026 ConfiguredLoginParam {
1027 addr: addr.clone(),
1028 imap: vec![ConfiguredServerLoginParam {
1029 connection: ConnectionCandidate {
1030 host: "example.org".to_string(),
1031 port: 100,
1032 security: ConnectionSecurity::Tls,
1033 },
1034 user: addr.clone(),
1035 }],
1036 imap_user: addr.clone(),
1037 imap_password: "foobarbaz".to_string(),
1038 smtp: vec![ConfiguredServerLoginParam {
1039 connection: ConnectionCandidate {
1040 host: "example.org".to_string(),
1041 port: 100,
1042 security: ConnectionSecurity::Tls,
1043 },
1044 user: addr.clone(),
1045 }],
1046 smtp_user: addr.clone(),
1047 smtp_password: "foobarbaz".to_string(),
1048 provider: Some(provider),
1049 certificate_checks: ConfiguredCertificateChecks::Automatic,
1050 oauth2: false,
1051 }
1052 .save_to_transports_table(&t, &EnteredLoginParam::default(), time())
1053 .await?;
1054
1055 let (_transport_id, loaded) = ConfiguredLoginParam::load(&t).await?.unwrap();
1056 assert_eq!(loaded.provider, Some(*provider));
1057 assert_eq!(loaded.imap.is_empty(), false);
1058 assert_eq!(loaded.smtp.is_empty(), false);
1059 assert_eq!(t.get_configured_provider().await?, Some(*provider));
1060
1061 Ok(())
1062 }
1063}