1use std::fmt;
4
5use anyhow::{Context as _, Result, bail, ensure, format_err};
6use deltachat_contact_tools::{EmailAddress, addr_cmp, addr_normalize};
7use num_traits::ToPrimitive as _;
8use serde::{Deserialize, Serialize};
9
10use crate::config::Config;
11use crate::configure::server_params::{ServerParams, expand_param_vector};
12use crate::constants::{DC_LP_AUTH_FLAGS, DC_LP_AUTH_OAUTH2};
13use crate::context::Context;
14use crate::net::load_connection_timestamp;
15pub use crate::net::proxy::ProxyConfig;
16pub use crate::provider::Socket;
17use crate::provider::{Protocol, Provider, UsernamePattern, get_provider_by_id};
18use crate::sql::Sql;
19use crate::tools::ToOption;
20
21#[derive(
25 Copy,
26 Clone,
27 Debug,
28 Default,
29 Display,
30 FromPrimitive,
31 ToPrimitive,
32 PartialEq,
33 Eq,
34 Serialize,
35 Deserialize,
36)]
37#[repr(u32)]
38#[strum(serialize_all = "snake_case")]
39pub enum EnteredCertificateChecks {
40 #[default]
44 Automatic = 0,
45
46 Strict = 1,
48
49 AcceptInvalidCertificates = 2,
52
53 AcceptInvalidCertificates2 = 3,
56}
57
58#[derive(
60 Copy, Clone, Debug, Display, FromPrimitive, ToPrimitive, PartialEq, Eq, Serialize, Deserialize,
61)]
62#[repr(u32)]
63#[strum(serialize_all = "snake_case")]
64pub(crate) enum ConfiguredCertificateChecks {
65 OldAutomatic = 0,
77
78 Strict = 1,
80
81 AcceptInvalidCertificates = 2,
84
85 AcceptInvalidCertificates2 = 3,
90
91 Automatic = 4,
95}
96
97#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
99pub struct EnteredServerLoginParam {
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, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
122pub struct EnteredLoginParam {
123 pub addr: String,
125
126 pub imap: EnteredServerLoginParam,
128
129 pub smtp: EnteredServerLoginParam,
131
132 pub certificate_checks: EnteredCertificateChecks,
135
136 pub oauth2: bool,
138}
139
140impl EnteredLoginParam {
141 pub(crate) async fn load(context: &Context) -> Result<Self> {
143 let addr = context
144 .get_config(Config::Addr)
145 .await?
146 .unwrap_or_default()
147 .trim()
148 .to_string();
149
150 let mail_server = context
151 .get_config(Config::MailServer)
152 .await?
153 .unwrap_or_default();
154 let mail_port = context
155 .get_config_parsed::<u16>(Config::MailPort)
156 .await?
157 .unwrap_or_default();
158 let mail_security = context
159 .get_config_parsed::<i32>(Config::MailSecurity)
160 .await?
161 .and_then(num_traits::FromPrimitive::from_i32)
162 .unwrap_or_default();
163 let mail_user = context
164 .get_config(Config::MailUser)
165 .await?
166 .unwrap_or_default();
167 let mail_pw = context
168 .get_config(Config::MailPw)
169 .await?
170 .unwrap_or_default();
171
172 let certificate_checks = if let Some(certificate_checks) = context
177 .get_config_parsed::<i32>(Config::ImapCertificateChecks)
178 .await?
179 {
180 num_traits::FromPrimitive::from_i32(certificate_checks)
181 .context("Unknown imap_certificate_checks value")?
182 } else {
183 Default::default()
184 };
185
186 let send_server = context
187 .get_config(Config::SendServer)
188 .await?
189 .unwrap_or_default();
190 let send_port = context
191 .get_config_parsed::<u16>(Config::SendPort)
192 .await?
193 .unwrap_or_default();
194 let send_security = context
195 .get_config_parsed::<i32>(Config::SendSecurity)
196 .await?
197 .and_then(num_traits::FromPrimitive::from_i32)
198 .unwrap_or_default();
199 let send_user = context
200 .get_config(Config::SendUser)
201 .await?
202 .unwrap_or_default();
203 let send_pw = context
204 .get_config(Config::SendPw)
205 .await?
206 .unwrap_or_default();
207
208 let server_flags = context
209 .get_config_parsed::<i32>(Config::ServerFlags)
210 .await?
211 .unwrap_or_default();
212 let oauth2 = matches!(server_flags & DC_LP_AUTH_FLAGS, DC_LP_AUTH_OAUTH2);
213
214 Ok(EnteredLoginParam {
215 addr,
216 imap: EnteredServerLoginParam {
217 server: mail_server,
218 port: mail_port,
219 security: mail_security,
220 user: mail_user,
221 password: mail_pw,
222 },
223 smtp: EnteredServerLoginParam {
224 server: send_server,
225 port: send_port,
226 security: send_security,
227 user: send_user,
228 password: send_pw,
229 },
230 certificate_checks,
231 oauth2,
232 })
233 }
234
235 pub(crate) async fn save(&self, context: &Context) -> Result<()> {
238 context.set_config(Config::Addr, Some(&self.addr)).await?;
239
240 context
241 .set_config(Config::MailServer, self.imap.server.to_option())
242 .await?;
243 context
244 .set_config(Config::MailPort, self.imap.port.to_option().as_deref())
245 .await?;
246 context
247 .set_config(
248 Config::MailSecurity,
249 self.imap.security.to_i32().to_option().as_deref(),
250 )
251 .await?;
252 context
253 .set_config(Config::MailUser, self.imap.user.to_option())
254 .await?;
255 context
256 .set_config(Config::MailPw, self.imap.password.to_option())
257 .await?;
258
259 context
260 .set_config(Config::SendServer, self.smtp.server.to_option())
261 .await?;
262 context
263 .set_config(Config::SendPort, self.smtp.port.to_option().as_deref())
264 .await?;
265 context
266 .set_config(
267 Config::SendSecurity,
268 self.smtp.security.to_i32().to_option().as_deref(),
269 )
270 .await?;
271 context
272 .set_config(Config::SendUser, self.smtp.user.to_option())
273 .await?;
274 context
275 .set_config(Config::SendPw, self.smtp.password.to_option())
276 .await?;
277
278 context
279 .set_config(
280 Config::ImapCertificateChecks,
281 self.certificate_checks.to_i32().to_option().as_deref(),
282 )
283 .await?;
284
285 let server_flags = if self.oauth2 {
286 Some(DC_LP_AUTH_OAUTH2.to_string())
287 } else {
288 None
289 };
290 context
291 .set_config(Config::ServerFlags, server_flags.as_deref())
292 .await?;
293
294 Ok(())
295 }
296}
297
298impl fmt::Display for EnteredLoginParam {
299 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
300 let unset = "0";
301 let pw = "***";
302
303 write!(
304 f,
305 "{} imap:{}:{}:{}:{}:{}:{} smtp:{}:{}:{}:{}:{}:{} cert_{}",
306 unset_empty(&self.addr),
307 unset_empty(&self.imap.user),
308 if !self.imap.password.is_empty() {
309 pw
310 } else {
311 unset
312 },
313 unset_empty(&self.imap.server),
314 self.imap.port,
315 self.imap.security,
316 if self.oauth2 { "OAUTH2" } else { "AUTH_NORMAL" },
317 unset_empty(&self.smtp.user),
318 if !self.smtp.password.is_empty() {
319 pw
320 } else {
321 unset
322 },
323 unset_empty(&self.smtp.server),
324 self.smtp.port,
325 self.smtp.security,
326 if self.oauth2 { "OAUTH2" } else { "AUTH_NORMAL" },
327 self.certificate_checks
328 )
329 }
330}
331
332fn unset_empty(s: &str) -> &str {
333 if s.is_empty() { "unset" } else { s }
334}
335
336#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
337pub(crate) struct ConnectionCandidate {
338 pub host: String,
340
341 pub port: u16,
343
344 pub security: ConnectionSecurity,
346}
347
348impl fmt::Display for ConnectionCandidate {
349 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350 write!(f, "{}:{}:{}", &self.host, self.port, self.security)?;
351 Ok(())
352 }
353}
354
355#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
356pub(crate) enum ConnectionSecurity {
357 Tls,
359
360 Starttls,
362
363 Plain,
365}
366
367impl fmt::Display for ConnectionSecurity {
368 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
369 match self {
370 Self::Tls => write!(f, "tls")?,
371 Self::Starttls => write!(f, "starttls")?,
372 Self::Plain => write!(f, "plain")?,
373 }
374 Ok(())
375 }
376}
377
378impl TryFrom<Socket> for ConnectionSecurity {
379 type Error = anyhow::Error;
380
381 fn try_from(socket: Socket) -> Result<Self> {
382 match socket {
383 Socket::Automatic => Err(format_err!("Socket security is not configured")),
384 Socket::Ssl => Ok(Self::Tls),
385 Socket::Starttls => Ok(Self::Starttls),
386 Socket::Plain => Ok(Self::Plain),
387 }
388 }
389}
390
391#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
392pub(crate) struct ConfiguredServerLoginParam {
393 pub connection: ConnectionCandidate,
394
395 pub user: String,
397}
398
399impl fmt::Display for ConfiguredServerLoginParam {
400 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
401 write!(f, "{}:{}", self.connection, &self.user)?;
402 Ok(())
403 }
404}
405
406pub(crate) async fn prioritize_server_login_params(
407 sql: &Sql,
408 params: &[ConfiguredServerLoginParam],
409 alpn: &str,
410) -> Result<Vec<ConfiguredServerLoginParam>> {
411 let mut res: Vec<(Option<i64>, ConfiguredServerLoginParam)> = Vec::with_capacity(params.len());
412 for param in params {
413 let timestamp = load_connection_timestamp(
414 sql,
415 alpn,
416 ¶m.connection.host,
417 param.connection.port,
418 None,
419 )
420 .await?;
421 res.push((timestamp, param.clone()));
422 }
423 res.sort_by_key(|(ts, _param)| std::cmp::Reverse(*ts));
424 Ok(res.into_iter().map(|(_ts, param)| param).collect())
425}
426
427#[derive(Debug, Clone, PartialEq, Eq)]
430pub(crate) struct ConfiguredLoginParam {
431 pub addr: String,
433
434 pub imap: Vec<ConfiguredServerLoginParam>,
435
436 pub imap_user: String,
441
442 pub imap_password: String,
443
444 pub smtp: Vec<ConfiguredServerLoginParam>,
445
446 pub smtp_user: String,
451
452 pub smtp_password: String,
453
454 pub provider: Option<&'static Provider>,
455
456 pub certificate_checks: ConfiguredCertificateChecks,
459
460 pub oauth2: bool,
462}
463
464#[derive(Debug, Serialize, Deserialize)]
467struct ConfiguredLoginParamJson {
468 pub addr: String,
469 pub imap: Vec<ConfiguredServerLoginParam>,
470 pub imap_user: String,
471 pub imap_password: String,
472 pub smtp: Vec<ConfiguredServerLoginParam>,
473 pub smtp_user: String,
474 pub smtp_password: String,
475 pub provider_id: Option<String>,
476 pub certificate_checks: ConfiguredCertificateChecks,
477 pub oauth2: bool,
478}
479
480impl fmt::Display for ConfiguredLoginParam {
481 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
482 let addr = &self.addr;
483 let provider_id = match self.provider {
484 Some(provider) => provider.id,
485 None => "none",
486 };
487 let certificate_checks = self.certificate_checks;
488 write!(f, "{addr} imap:[")?;
489 let mut first = true;
490 for imap in &self.imap {
491 if !first {
492 write!(f, ", ")?;
493 }
494 write!(f, "{imap}")?;
495 first = false;
496 }
497 write!(f, "] smtp:[")?;
498 let mut first = true;
499 for smtp in &self.smtp {
500 if !first {
501 write!(f, ", ")?;
502 }
503 write!(f, "{smtp}")?;
504 first = false;
505 }
506 write!(f, "] provider:{provider_id} cert_{certificate_checks}")?;
507 Ok(())
508 }
509}
510
511impl ConfiguredLoginParam {
512 pub(crate) async fn load(context: &Context) -> Result<Option<Self>> {
516 let Some(self_addr) = context.get_config(Config::ConfiguredAddr).await? else {
517 return Ok(None);
518 };
519
520 let json: Option<String> = context
521 .sql
522 .query_get_value(
523 "SELECT configured_param FROM transports WHERE addr=?",
524 (&self_addr,),
525 )
526 .await?;
527 if let Some(json) = json {
528 Ok(Some(Self::from_json(&json)?))
529 } else {
530 bail!("Self address {self_addr} doesn't have a corresponding transport");
531 }
532 }
533
534 pub(crate) async fn load_legacy(context: &Context) -> Result<Option<Self>> {
536 if !context.get_config_bool(Config::Configured).await? {
537 return Ok(None);
538 }
539
540 let addr = context
541 .get_config(Config::ConfiguredAddr)
542 .await?
543 .unwrap_or_default()
544 .trim()
545 .to_string();
546
547 let certificate_checks: ConfiguredCertificateChecks = if let Some(certificate_checks) =
548 context
549 .get_config_parsed::<i32>(Config::ConfiguredImapCertificateChecks)
550 .await?
551 {
552 num_traits::FromPrimitive::from_i32(certificate_checks)
553 .context("Invalid configured_imap_certificate_checks value")?
554 } else {
555 ConfiguredCertificateChecks::OldAutomatic
558 };
559
560 let send_pw = context
561 .get_config(Config::ConfiguredSendPw)
562 .await?
563 .context("SMTP password is not configured")?;
564 let mail_pw = context
565 .get_config(Config::ConfiguredMailPw)
566 .await?
567 .context("IMAP password is not configured")?;
568
569 let server_flags = context
570 .get_config_parsed::<i32>(Config::ConfiguredServerFlags)
571 .await?
572 .unwrap_or_default();
573 let oauth2 = matches!(server_flags & DC_LP_AUTH_FLAGS, DC_LP_AUTH_OAUTH2);
574
575 let provider = context.get_configured_provider().await?;
576
577 let imap;
578 let smtp;
579
580 let mail_user = context
581 .get_config(Config::ConfiguredMailUser)
582 .await?
583 .unwrap_or_default();
584 let send_user = context
585 .get_config(Config::ConfiguredSendUser)
586 .await?
587 .unwrap_or_default();
588
589 if let Some(provider) = provider {
590 let parsed_addr = EmailAddress::new(&addr).context("Bad email-address")?;
591 let addr_localpart = parsed_addr.local;
592
593 if provider.server.is_empty() {
594 let servers = vec![
595 ServerParams {
596 protocol: Protocol::Imap,
597 hostname: context
598 .get_config(Config::ConfiguredMailServer)
599 .await?
600 .unwrap_or_default(),
601 port: context
602 .get_config_parsed::<u16>(Config::ConfiguredMailPort)
603 .await?
604 .unwrap_or_default(),
605 socket: context
606 .get_config_parsed::<i32>(Config::ConfiguredMailSecurity)
607 .await?
608 .and_then(num_traits::FromPrimitive::from_i32)
609 .unwrap_or_default(),
610 username: mail_user.clone(),
611 },
612 ServerParams {
613 protocol: Protocol::Smtp,
614 hostname: context
615 .get_config(Config::ConfiguredSendServer)
616 .await?
617 .unwrap_or_default(),
618 port: context
619 .get_config_parsed::<u16>(Config::ConfiguredSendPort)
620 .await?
621 .unwrap_or_default(),
622 socket: context
623 .get_config_parsed::<i32>(Config::ConfiguredSendSecurity)
624 .await?
625 .and_then(num_traits::FromPrimitive::from_i32)
626 .unwrap_or_default(),
627 username: send_user.clone(),
628 },
629 ];
630 let servers = expand_param_vector(servers, &addr, &parsed_addr.domain);
631 imap = servers
632 .iter()
633 .filter_map(|params| {
634 let Ok(security) = params.socket.try_into() else {
635 return None;
636 };
637 if params.protocol == Protocol::Imap {
638 Some(ConfiguredServerLoginParam {
639 connection: ConnectionCandidate {
640 host: params.hostname.clone(),
641 port: params.port,
642 security,
643 },
644 user: params.username.clone(),
645 })
646 } else {
647 None
648 }
649 })
650 .collect();
651 smtp = servers
652 .iter()
653 .filter_map(|params| {
654 let Ok(security) = params.socket.try_into() else {
655 return None;
656 };
657 if params.protocol == Protocol::Smtp {
658 Some(ConfiguredServerLoginParam {
659 connection: ConnectionCandidate {
660 host: params.hostname.clone(),
661 port: params.port,
662 security,
663 },
664 user: params.username.clone(),
665 })
666 } else {
667 None
668 }
669 })
670 .collect();
671 } else {
672 imap = provider
673 .server
674 .iter()
675 .filter_map(|server| {
676 if server.protocol != Protocol::Imap {
677 return None;
678 }
679
680 let Ok(security) = server.socket.try_into() else {
681 return None;
682 };
683
684 Some(ConfiguredServerLoginParam {
685 connection: ConnectionCandidate {
686 host: server.hostname.to_string(),
687 port: server.port,
688 security,
689 },
690 user: if !mail_user.is_empty() {
691 mail_user.clone()
692 } else {
693 match server.username_pattern {
694 UsernamePattern::Email => addr.to_string(),
695 UsernamePattern::Emaillocalpart => addr_localpart.clone(),
696 }
697 },
698 })
699 })
700 .collect();
701 smtp = provider
702 .server
703 .iter()
704 .filter_map(|server| {
705 if server.protocol != Protocol::Smtp {
706 return None;
707 }
708
709 let Ok(security) = server.socket.try_into() else {
710 return None;
711 };
712
713 Some(ConfiguredServerLoginParam {
714 connection: ConnectionCandidate {
715 host: server.hostname.to_string(),
716 port: server.port,
717 security,
718 },
719 user: if !send_user.is_empty() {
720 send_user.clone()
721 } else {
722 match server.username_pattern {
723 UsernamePattern::Email => addr.to_string(),
724 UsernamePattern::Emaillocalpart => addr_localpart.clone(),
725 }
726 },
727 })
728 })
729 .collect();
730 }
731 } else if let (Some(configured_mail_servers), Some(configured_send_servers)) = (
732 context.get_config(Config::ConfiguredImapServers).await?,
733 context.get_config(Config::ConfiguredSmtpServers).await?,
734 ) {
735 imap = serde_json::from_str(&configured_mail_servers)
736 .context("Failed to parse configured IMAP servers")?;
737 smtp = serde_json::from_str(&configured_send_servers)
738 .context("Failed to parse configured SMTP servers")?;
739 } else {
740 let mail_server = context
742 .get_config(Config::ConfiguredMailServer)
743 .await?
744 .unwrap_or_default();
745 let mail_port = context
746 .get_config_parsed::<u16>(Config::ConfiguredMailPort)
747 .await?
748 .unwrap_or_default();
749
750 let mail_security: Socket = context
751 .get_config_parsed::<i32>(Config::ConfiguredMailSecurity)
752 .await?
753 .and_then(num_traits::FromPrimitive::from_i32)
754 .unwrap_or_default();
755
756 let send_server = context
757 .get_config(Config::ConfiguredSendServer)
758 .await?
759 .context("SMTP server is not configured")?;
760 let send_port = context
761 .get_config_parsed::<u16>(Config::ConfiguredSendPort)
762 .await?
763 .unwrap_or_default();
764 let send_security: Socket = context
765 .get_config_parsed::<i32>(Config::ConfiguredSendSecurity)
766 .await?
767 .and_then(num_traits::FromPrimitive::from_i32)
768 .unwrap_or_default();
769
770 imap = vec![ConfiguredServerLoginParam {
771 connection: ConnectionCandidate {
772 host: mail_server,
773 port: mail_port,
774 security: mail_security.try_into()?,
775 },
776 user: mail_user.clone(),
777 }];
778 smtp = vec![ConfiguredServerLoginParam {
779 connection: ConnectionCandidate {
780 host: send_server,
781 port: send_port,
782 security: send_security.try_into()?,
783 },
784 user: send_user.clone(),
785 }];
786 }
787
788 Ok(Some(ConfiguredLoginParam {
789 addr,
790 imap,
791 imap_user: mail_user,
792 imap_password: mail_pw,
793 smtp,
794 smtp_user: send_user,
795 smtp_password: send_pw,
796 certificate_checks,
797 provider,
798 oauth2,
799 }))
800 }
801
802 pub(crate) async fn save_to_transports_table(
803 self,
804 context: &Context,
805 entered_param: &EnteredLoginParam,
806 ) -> Result<()> {
807 let addr = addr_normalize(&self.addr);
808 let provider_id = self.provider.map(|provider| provider.id);
809 let configured_addr = context.get_config(Config::ConfiguredAddr).await?;
810 if let Some(configured_addr) = &configured_addr {
811 ensure!(
812 addr_cmp(configured_addr, &addr),
813 "Adding a second transport is not supported right now."
814 );
815 }
816 context
817 .sql
818 .execute(
819 "INSERT INTO transports (addr, entered_param, configured_param)
820 VALUES (?, ?, ?)
821 ON CONFLICT (addr)
822 DO UPDATE SET entered_param=excluded.entered_param, configured_param=excluded.configured_param",
823 (
824 self.addr.clone(),
825 serde_json::to_string(entered_param)?,
826 self.into_json()?,
827 ),
828 )
829 .await?;
830 if configured_addr.is_none() {
831 context
833 .sql
834 .set_raw_config(Config::ConfiguredProvider.as_ref(), provider_id)
835 .await?;
836 context
837 .sql
838 .set_raw_config(Config::ConfiguredAddr.as_ref(), Some(&addr))
839 .await?;
840 }
841 Ok(())
842 }
843
844 pub(crate) fn from_json(json: &str) -> Result<Self> {
845 let json: ConfiguredLoginParamJson = serde_json::from_str(json)?;
846
847 let provider = json.provider_id.and_then(|id| get_provider_by_id(&id));
848
849 Ok(ConfiguredLoginParam {
850 addr: json.addr,
851 imap: json.imap,
852 imap_user: json.imap_user,
853 imap_password: json.imap_password,
854 smtp: json.smtp,
855 smtp_user: json.smtp_user,
856 smtp_password: json.smtp_password,
857 provider,
858 certificate_checks: json.certificate_checks,
859 oauth2: json.oauth2,
860 })
861 }
862
863 pub(crate) fn into_json(self) -> Result<String> {
864 let json = ConfiguredLoginParamJson {
865 addr: self.addr,
866 imap: self.imap,
867 imap_user: self.imap_user,
868 imap_password: self.imap_password,
869 smtp: self.smtp,
870 smtp_user: self.smtp_user,
871 smtp_password: self.smtp_password,
872 provider_id: self.provider.map(|p| p.id.to_string()),
873 certificate_checks: self.certificate_checks,
874 oauth2: self.oauth2,
875 };
876 Ok(serde_json::to_string(&json)?)
877 }
878
879 pub(crate) fn strict_tls(&self, connected_through_proxy: bool) -> bool {
880 let provider_strict_tls = self.provider.map(|provider| provider.opt.strict_tls);
881 match self.certificate_checks {
882 ConfiguredCertificateChecks::OldAutomatic => {
883 provider_strict_tls.unwrap_or(connected_through_proxy)
884 }
885 ConfiguredCertificateChecks::Automatic => provider_strict_tls.unwrap_or(true),
886 ConfiguredCertificateChecks::Strict => true,
887 ConfiguredCertificateChecks::AcceptInvalidCertificates
888 | ConfiguredCertificateChecks::AcceptInvalidCertificates2 => false,
889 }
890 }
891}
892
893#[cfg(test)]
894mod tests {
895 use super::*;
896 use crate::log::LogExt as _;
897 use crate::provider::get_provider_by_id;
898 use crate::test_utils::TestContext;
899 use pretty_assertions::assert_eq;
900
901 #[test]
902 fn test_certificate_checks_display() {
903 use std::string::ToString;
904
905 assert_eq!(
906 "accept_invalid_certificates".to_string(),
907 EnteredCertificateChecks::AcceptInvalidCertificates.to_string()
908 );
909
910 assert_eq!(
911 "accept_invalid_certificates".to_string(),
912 ConfiguredCertificateChecks::AcceptInvalidCertificates.to_string()
913 );
914 }
915
916 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
917 async fn test_entered_login_param() -> Result<()> {
918 let t = &TestContext::new().await;
919
920 t.set_config(Config::Addr, Some("alice@example.org"))
921 .await?;
922 t.set_config(Config::MailPw, Some("foobarbaz")).await?;
923
924 let param = EnteredLoginParam::load(t).await?;
925 assert_eq!(param.addr, "alice@example.org");
926 assert_eq!(
927 param.certificate_checks,
928 EnteredCertificateChecks::Automatic
929 );
930
931 t.set_config(Config::ImapCertificateChecks, Some("1"))
932 .await?;
933 let param = EnteredLoginParam::load(t).await?;
934 assert_eq!(param.certificate_checks, EnteredCertificateChecks::Strict);
935
936 t.set_config(Config::ImapCertificateChecks, Some("999"))
938 .await?;
939 assert!(EnteredLoginParam::load(t).await.is_err());
940
941 Ok(())
942 }
943
944 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
945 async fn test_save_entered_login_param() -> Result<()> {
946 let t = TestContext::new().await;
947 let param = EnteredLoginParam {
948 addr: "alice@example.org".to_string(),
949 imap: EnteredServerLoginParam {
950 server: "".to_string(),
951 port: 0,
952 security: Socket::Starttls,
953 user: "".to_string(),
954 password: "foobar".to_string(),
955 },
956 smtp: EnteredServerLoginParam {
957 server: "".to_string(),
958 port: 2947,
959 security: Socket::default(),
960 user: "".to_string(),
961 password: "".to_string(),
962 },
963 certificate_checks: Default::default(),
964 oauth2: false,
965 };
966 param.save(&t).await?;
967 assert_eq!(
968 t.get_config(Config::Addr).await?.unwrap(),
969 "alice@example.org"
970 );
971 assert_eq!(t.get_config(Config::MailPw).await?.unwrap(), "foobar");
972 assert_eq!(t.get_config(Config::SendPw).await?, None);
973 assert_eq!(t.get_config_int(Config::SendPort).await?, 2947);
974
975 assert_eq!(EnteredLoginParam::load(&t).await?, param);
976
977 Ok(())
978 }
979
980 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
981 async fn test_save_load_login_param() -> Result<()> {
982 let t = TestContext::new().await;
983
984 let param = ConfiguredLoginParam {
985 addr: "alice@example.org".to_string(),
986 imap: vec![ConfiguredServerLoginParam {
987 connection: ConnectionCandidate {
988 host: "imap.example.com".to_string(),
989 port: 123,
990 security: ConnectionSecurity::Starttls,
991 },
992 user: "alice".to_string(),
993 }],
994 imap_user: "".to_string(),
995 imap_password: "foo".to_string(),
996 smtp: vec![ConfiguredServerLoginParam {
997 connection: ConnectionCandidate {
998 host: "smtp.example.com".to_string(),
999 port: 456,
1000 security: ConnectionSecurity::Tls,
1001 },
1002 user: "alice@example.org".to_string(),
1003 }],
1004 smtp_user: "".to_string(),
1005 smtp_password: "bar".to_string(),
1006 provider: None,
1007 certificate_checks: ConfiguredCertificateChecks::Strict,
1008 oauth2: false,
1009 };
1010
1011 param
1012 .clone()
1013 .save_to_transports_table(&t, &EnteredLoginParam::default())
1014 .await?;
1015 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}"#;
1016 assert_eq!(
1017 t.sql
1018 .query_get_value::<String>("SELECT configured_param FROM transports", ())
1019 .await?
1020 .unwrap(),
1021 expected_param
1022 );
1023 assert_eq!(t.is_configured().await?, true);
1024 let loaded = ConfiguredLoginParam::load(&t).await?.unwrap();
1025 assert_eq!(param, loaded);
1026
1027 t.set_config(Config::ConfiguredImapCertificateChecks, Some("999"))
1029 .await?;
1030 assert!(ConfiguredLoginParam::load(&t).await.is_ok());
1031
1032 let wrong_param = expected_param.replace("Strict", "Stricct");
1034 assert_ne!(expected_param, wrong_param);
1035 t.sql
1036 .execute("UPDATE transports SET configured_param=?", (wrong_param,))
1037 .await?;
1038 assert!(ConfiguredLoginParam::load(&t).await.is_err());
1039
1040 Ok(())
1041 }
1042
1043 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1044 async fn test_posteo_alias() -> Result<()> {
1045 let t = TestContext::new().await;
1046
1047 let user = "alice@posteo.de";
1048
1049 t.set_config(Config::Configured, Some("1")).await?;
1052 t.set_config(Config::ConfiguredProvider, Some("posteo"))
1053 .await?;
1054 t.sql
1055 .set_raw_config(Config::ConfiguredAddr.as_ref(), Some("alice@posteo.at"))
1056 .await?;
1057 t.set_config(Config::ConfiguredMailServer, Some("posteo.de"))
1058 .await?;
1059 t.set_config(Config::ConfiguredMailPort, Some("993"))
1060 .await?;
1061 t.set_config(Config::ConfiguredMailSecurity, Some("1"))
1062 .await?; t.set_config(Config::ConfiguredMailUser, Some(user)).await?;
1064 t.set_config(Config::ConfiguredMailPw, Some("foobarbaz"))
1065 .await?;
1066 t.set_config(Config::ConfiguredImapCertificateChecks, Some("1"))
1067 .await?; t.set_config(Config::ConfiguredSendServer, Some("posteo.de"))
1069 .await?;
1070 t.set_config(Config::ConfiguredSendPort, Some("465"))
1071 .await?;
1072 t.set_config(Config::ConfiguredSendSecurity, Some("1"))
1073 .await?; t.set_config(Config::ConfiguredSendUser, Some(user)).await?;
1075 t.set_config(Config::ConfiguredSendPw, Some("foobarbaz"))
1076 .await?;
1077 t.set_config(Config::ConfiguredSmtpCertificateChecks, Some("1"))
1078 .await?; t.set_config(Config::ConfiguredServerFlags, Some("0"))
1080 .await?;
1081
1082 let param = ConfiguredLoginParam {
1083 addr: "alice@posteo.at".to_string(),
1084 imap: vec![
1085 ConfiguredServerLoginParam {
1086 connection: ConnectionCandidate {
1087 host: "posteo.de".to_string(),
1088 port: 993,
1089 security: ConnectionSecurity::Tls,
1090 },
1091 user: user.to_string(),
1092 },
1093 ConfiguredServerLoginParam {
1094 connection: ConnectionCandidate {
1095 host: "posteo.de".to_string(),
1096 port: 143,
1097 security: ConnectionSecurity::Starttls,
1098 },
1099 user: user.to_string(),
1100 },
1101 ],
1102 imap_user: "alice@posteo.de".to_string(),
1103 imap_password: "foobarbaz".to_string(),
1104 smtp: vec![
1105 ConfiguredServerLoginParam {
1106 connection: ConnectionCandidate {
1107 host: "posteo.de".to_string(),
1108 port: 465,
1109 security: ConnectionSecurity::Tls,
1110 },
1111 user: user.to_string(),
1112 },
1113 ConfiguredServerLoginParam {
1114 connection: ConnectionCandidate {
1115 host: "posteo.de".to_string(),
1116 port: 587,
1117 security: ConnectionSecurity::Starttls,
1118 },
1119 user: user.to_string(),
1120 },
1121 ],
1122 smtp_user: "alice@posteo.de".to_string(),
1123 smtp_password: "foobarbaz".to_string(),
1124 provider: get_provider_by_id("posteo"),
1125 certificate_checks: ConfiguredCertificateChecks::Strict,
1126 oauth2: false,
1127 };
1128
1129 let loaded = ConfiguredLoginParam::load_legacy(&t).await?.unwrap();
1130 assert_eq!(loaded, param);
1131
1132 migrate_configured_login_param(&t).await;
1133 let loaded = ConfiguredLoginParam::load(&t).await?.unwrap();
1134 assert_eq!(loaded, param);
1135
1136 Ok(())
1137 }
1138
1139 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1140 async fn test_empty_server_list_legacy() -> Result<()> {
1141 let (domain, provider) = crate::provider::data::PROVIDER_DATA
1145 .iter()
1146 .find(|(_domain, provider)| provider.server.is_empty())
1147 .unwrap();
1148
1149 let t = TestContext::new().await;
1150
1151 let addr = format!("alice@{domain}");
1152
1153 t.set_config(Config::Configured, Some("1")).await?;
1154 t.set_config(Config::ConfiguredProvider, Some(provider.id))
1155 .await?;
1156 t.sql
1157 .set_raw_config(Config::ConfiguredAddr.as_ref(), Some(&addr))
1158 .await?;
1159 t.set_config(Config::ConfiguredMailPw, Some("foobarbaz"))
1160 .await?;
1161 t.set_config(Config::ConfiguredImapCertificateChecks, Some("1"))
1162 .await?; t.set_config(Config::ConfiguredSendPw, Some("foobarbaz"))
1164 .await?;
1165 t.set_config(Config::ConfiguredSmtpCertificateChecks, Some("1"))
1166 .await?; t.set_config(Config::ConfiguredServerFlags, Some("0"))
1168 .await?;
1169
1170 let loaded = ConfiguredLoginParam::load_legacy(&t).await?.unwrap();
1171 assert_eq!(loaded.provider, Some(*provider));
1172 assert_eq!(loaded.imap.is_empty(), false);
1173 assert_eq!(loaded.smtp.is_empty(), false);
1174
1175 migrate_configured_login_param(&t).await;
1176
1177 let loaded = ConfiguredLoginParam::load(&t).await?.unwrap();
1178 assert_eq!(loaded.provider, Some(*provider));
1179 assert_eq!(loaded.imap.is_empty(), false);
1180 assert_eq!(loaded.smtp.is_empty(), false);
1181
1182 Ok(())
1183 }
1184
1185 async fn migrate_configured_login_param(t: &TestContext) {
1186 t.sql.execute("DROP TABLE transports;", ()).await.unwrap();
1187 t.sql.set_raw_config_int("dbversion", 130).await.unwrap();
1188 t.sql.run_migrations(t).await.log_err(t).ok();
1189 }
1190
1191 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1192 async fn test_empty_server_list() -> Result<()> {
1193 let (domain, provider) = crate::provider::data::PROVIDER_DATA
1197 .iter()
1198 .find(|(_domain, provider)| provider.server.is_empty())
1199 .unwrap();
1200
1201 let t = TestContext::new().await;
1202
1203 let addr = format!("alice@{domain}");
1204
1205 ConfiguredLoginParam {
1206 addr: addr.clone(),
1207 imap: vec![ConfiguredServerLoginParam {
1208 connection: ConnectionCandidate {
1209 host: "example.org".to_string(),
1210 port: 100,
1211 security: ConnectionSecurity::Tls,
1212 },
1213 user: addr.clone(),
1214 }],
1215 imap_user: addr.clone(),
1216 imap_password: "foobarbaz".to_string(),
1217 smtp: vec![ConfiguredServerLoginParam {
1218 connection: ConnectionCandidate {
1219 host: "example.org".to_string(),
1220 port: 100,
1221 security: ConnectionSecurity::Tls,
1222 },
1223 user: addr.clone(),
1224 }],
1225 smtp_user: addr.clone(),
1226 smtp_password: "foobarbaz".to_string(),
1227 provider: Some(provider),
1228 certificate_checks: ConfiguredCertificateChecks::Automatic,
1229 oauth2: false,
1230 }
1231 .save_to_transports_table(&t, &EnteredLoginParam::default())
1232 .await?;
1233
1234 let loaded = ConfiguredLoginParam::load(&t).await?.unwrap();
1235 assert_eq!(loaded.provider, Some(*provider));
1236 assert_eq!(loaded.imap.is_empty(), false);
1237 assert_eq!(loaded.smtp.is_empty(), false);
1238 assert_eq!(t.get_configured_provider().await?, Some(*provider));
1239
1240 Ok(())
1241 }
1242}