1use std::fmt;
4
5use anyhow::{bail, ensure, format_err, Context as _, Result};
6use deltachat_contact_tools::{addr_cmp, addr_normalize, EmailAddress};
7use num_traits::ToPrimitive as _;
8use serde::{Deserialize, Serialize};
9
10use crate::config::Config;
11use crate::configure::server_params::{expand_param_vector, ServerParams};
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::{get_provider_by_id, Protocol, Provider, UsernamePattern};
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() {
334 "unset"
335 } else {
336 s
337 }
338}
339
340#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
341pub(crate) struct ConnectionCandidate {
342 pub host: String,
344
345 pub port: u16,
347
348 pub security: ConnectionSecurity,
350}
351
352impl fmt::Display for ConnectionCandidate {
353 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354 write!(f, "{}:{}:{}", &self.host, self.port, self.security)?;
355 Ok(())
356 }
357}
358
359#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
360pub(crate) enum ConnectionSecurity {
361 Tls,
363
364 Starttls,
366
367 Plain,
369}
370
371impl fmt::Display for ConnectionSecurity {
372 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373 match self {
374 Self::Tls => write!(f, "tls")?,
375 Self::Starttls => write!(f, "starttls")?,
376 Self::Plain => write!(f, "plain")?,
377 }
378 Ok(())
379 }
380}
381
382impl TryFrom<Socket> for ConnectionSecurity {
383 type Error = anyhow::Error;
384
385 fn try_from(socket: Socket) -> Result<Self> {
386 match socket {
387 Socket::Automatic => Err(format_err!("Socket security is not configured")),
388 Socket::Ssl => Ok(Self::Tls),
389 Socket::Starttls => Ok(Self::Starttls),
390 Socket::Plain => Ok(Self::Plain),
391 }
392 }
393}
394
395#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
396pub(crate) struct ConfiguredServerLoginParam {
397 pub connection: ConnectionCandidate,
398
399 pub user: String,
401}
402
403impl fmt::Display for ConfiguredServerLoginParam {
404 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
405 write!(f, "{}:{}", self.connection, &self.user)?;
406 Ok(())
407 }
408}
409
410pub(crate) async fn prioritize_server_login_params(
411 sql: &Sql,
412 params: &[ConfiguredServerLoginParam],
413 alpn: &str,
414) -> Result<Vec<ConfiguredServerLoginParam>> {
415 let mut res: Vec<(Option<i64>, ConfiguredServerLoginParam)> = Vec::with_capacity(params.len());
416 for param in params {
417 let timestamp = load_connection_timestamp(
418 sql,
419 alpn,
420 ¶m.connection.host,
421 param.connection.port,
422 None,
423 )
424 .await?;
425 res.push((timestamp, param.clone()));
426 }
427 res.sort_by_key(|(ts, _param)| std::cmp::Reverse(*ts));
428 Ok(res.into_iter().map(|(_ts, param)| param).collect())
429}
430
431#[derive(Debug, Clone, PartialEq, Eq)]
434pub(crate) struct ConfiguredLoginParam {
435 pub addr: String,
437
438 pub imap: Vec<ConfiguredServerLoginParam>,
439
440 pub imap_user: String,
445
446 pub imap_password: String,
447
448 pub smtp: Vec<ConfiguredServerLoginParam>,
449
450 pub smtp_user: String,
455
456 pub smtp_password: String,
457
458 pub provider: Option<&'static Provider>,
459
460 pub certificate_checks: ConfiguredCertificateChecks,
463
464 pub oauth2: bool,
466}
467
468#[derive(Debug, Serialize, Deserialize)]
471struct ConfiguredLoginParamJson {
472 pub addr: String,
473 pub imap: Vec<ConfiguredServerLoginParam>,
474 pub imap_user: String,
475 pub imap_password: String,
476 pub smtp: Vec<ConfiguredServerLoginParam>,
477 pub smtp_user: String,
478 pub smtp_password: String,
479 pub provider_id: Option<String>,
480 pub certificate_checks: ConfiguredCertificateChecks,
481 pub oauth2: bool,
482}
483
484impl fmt::Display for ConfiguredLoginParam {
485 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
486 let addr = &self.addr;
487 let provider_id = match self.provider {
488 Some(provider) => provider.id,
489 None => "none",
490 };
491 let certificate_checks = self.certificate_checks;
492 write!(f, "{addr} imap:[")?;
493 let mut first = true;
494 for imap in &self.imap {
495 if !first {
496 write!(f, ", ")?;
497 }
498 write!(f, "{imap}")?;
499 first = false;
500 }
501 write!(f, "] smtp:[")?;
502 let mut first = true;
503 for smtp in &self.smtp {
504 if !first {
505 write!(f, ", ")?;
506 }
507 write!(f, "{smtp}")?;
508 first = false;
509 }
510 write!(f, "] provider:{provider_id} cert_{certificate_checks}")?;
511 Ok(())
512 }
513}
514
515impl ConfiguredLoginParam {
516 pub(crate) async fn load(context: &Context) -> Result<Option<Self>> {
520 let Some(self_addr) = context.get_config(Config::ConfiguredAddr).await? else {
521 return Ok(None);
522 };
523
524 let json: Option<String> = context
525 .sql
526 .query_get_value(
527 "SELECT configured_param FROM transports WHERE addr=?",
528 (&self_addr,),
529 )
530 .await?;
531 if let Some(json) = json {
532 Ok(Some(Self::from_json(&json)?))
533 } else {
534 bail!("Self address {self_addr} doesn't have a corresponding transport");
535 }
536 }
537
538 pub(crate) async fn load_legacy(context: &Context) -> Result<Option<Self>> {
540 if !context.get_config_bool(Config::Configured).await? {
541 return Ok(None);
542 }
543
544 let addr = context
545 .get_config(Config::ConfiguredAddr)
546 .await?
547 .unwrap_or_default()
548 .trim()
549 .to_string();
550
551 let certificate_checks: ConfiguredCertificateChecks = if let Some(certificate_checks) =
552 context
553 .get_config_parsed::<i32>(Config::ConfiguredImapCertificateChecks)
554 .await?
555 {
556 num_traits::FromPrimitive::from_i32(certificate_checks)
557 .context("Invalid configured_imap_certificate_checks value")?
558 } else {
559 ConfiguredCertificateChecks::OldAutomatic
562 };
563
564 let send_pw = context
565 .get_config(Config::ConfiguredSendPw)
566 .await?
567 .context("SMTP password is not configured")?;
568 let mail_pw = context
569 .get_config(Config::ConfiguredMailPw)
570 .await?
571 .context("IMAP password is not configured")?;
572
573 let server_flags = context
574 .get_config_parsed::<i32>(Config::ConfiguredServerFlags)
575 .await?
576 .unwrap_or_default();
577 let oauth2 = matches!(server_flags & DC_LP_AUTH_FLAGS, DC_LP_AUTH_OAUTH2);
578
579 let provider = context.get_configured_provider().await?;
580
581 let imap;
582 let smtp;
583
584 let mail_user = context
585 .get_config(Config::ConfiguredMailUser)
586 .await?
587 .unwrap_or_default();
588 let send_user = context
589 .get_config(Config::ConfiguredSendUser)
590 .await?
591 .unwrap_or_default();
592
593 if let Some(provider) = provider {
594 let parsed_addr = EmailAddress::new(&addr).context("Bad email-address")?;
595 let addr_localpart = parsed_addr.local;
596
597 if provider.server.is_empty() {
598 let servers = vec![
599 ServerParams {
600 protocol: Protocol::Imap,
601 hostname: context
602 .get_config(Config::ConfiguredMailServer)
603 .await?
604 .unwrap_or_default(),
605 port: context
606 .get_config_parsed::<u16>(Config::ConfiguredMailPort)
607 .await?
608 .unwrap_or_default(),
609 socket: context
610 .get_config_parsed::<i32>(Config::ConfiguredMailSecurity)
611 .await?
612 .and_then(num_traits::FromPrimitive::from_i32)
613 .unwrap_or_default(),
614 username: mail_user.clone(),
615 },
616 ServerParams {
617 protocol: Protocol::Smtp,
618 hostname: context
619 .get_config(Config::ConfiguredSendServer)
620 .await?
621 .unwrap_or_default(),
622 port: context
623 .get_config_parsed::<u16>(Config::ConfiguredSendPort)
624 .await?
625 .unwrap_or_default(),
626 socket: context
627 .get_config_parsed::<i32>(Config::ConfiguredSendSecurity)
628 .await?
629 .and_then(num_traits::FromPrimitive::from_i32)
630 .unwrap_or_default(),
631 username: send_user.clone(),
632 },
633 ];
634 let servers = expand_param_vector(servers, &addr, &parsed_addr.domain);
635 imap = servers
636 .iter()
637 .filter_map(|params| {
638 let Ok(security) = params.socket.try_into() else {
639 return None;
640 };
641 if params.protocol == Protocol::Imap {
642 Some(ConfiguredServerLoginParam {
643 connection: ConnectionCandidate {
644 host: params.hostname.clone(),
645 port: params.port,
646 security,
647 },
648 user: params.username.clone(),
649 })
650 } else {
651 None
652 }
653 })
654 .collect();
655 smtp = servers
656 .iter()
657 .filter_map(|params| {
658 let Ok(security) = params.socket.try_into() else {
659 return None;
660 };
661 if params.protocol == Protocol::Smtp {
662 Some(ConfiguredServerLoginParam {
663 connection: ConnectionCandidate {
664 host: params.hostname.clone(),
665 port: params.port,
666 security,
667 },
668 user: params.username.clone(),
669 })
670 } else {
671 None
672 }
673 })
674 .collect();
675 } else {
676 imap = provider
677 .server
678 .iter()
679 .filter_map(|server| {
680 if server.protocol != Protocol::Imap {
681 return None;
682 }
683
684 let Ok(security) = server.socket.try_into() else {
685 return None;
686 };
687
688 Some(ConfiguredServerLoginParam {
689 connection: ConnectionCandidate {
690 host: server.hostname.to_string(),
691 port: server.port,
692 security,
693 },
694 user: if !mail_user.is_empty() {
695 mail_user.clone()
696 } else {
697 match server.username_pattern {
698 UsernamePattern::Email => addr.to_string(),
699 UsernamePattern::Emaillocalpart => addr_localpart.clone(),
700 }
701 },
702 })
703 })
704 .collect();
705 smtp = provider
706 .server
707 .iter()
708 .filter_map(|server| {
709 if server.protocol != Protocol::Smtp {
710 return None;
711 }
712
713 let Ok(security) = server.socket.try_into() else {
714 return None;
715 };
716
717 Some(ConfiguredServerLoginParam {
718 connection: ConnectionCandidate {
719 host: server.hostname.to_string(),
720 port: server.port,
721 security,
722 },
723 user: if !send_user.is_empty() {
724 send_user.clone()
725 } else {
726 match server.username_pattern {
727 UsernamePattern::Email => addr.to_string(),
728 UsernamePattern::Emaillocalpart => addr_localpart.clone(),
729 }
730 },
731 })
732 })
733 .collect();
734 }
735 } else if let (Some(configured_mail_servers), Some(configured_send_servers)) = (
736 context.get_config(Config::ConfiguredImapServers).await?,
737 context.get_config(Config::ConfiguredSmtpServers).await?,
738 ) {
739 imap = serde_json::from_str(&configured_mail_servers)
740 .context("Failed to parse configured IMAP servers")?;
741 smtp = serde_json::from_str(&configured_send_servers)
742 .context("Failed to parse configured SMTP servers")?;
743 } else {
744 let mail_server = context
746 .get_config(Config::ConfiguredMailServer)
747 .await?
748 .unwrap_or_default();
749 let mail_port = context
750 .get_config_parsed::<u16>(Config::ConfiguredMailPort)
751 .await?
752 .unwrap_or_default();
753
754 let mail_security: Socket = context
755 .get_config_parsed::<i32>(Config::ConfiguredMailSecurity)
756 .await?
757 .and_then(num_traits::FromPrimitive::from_i32)
758 .unwrap_or_default();
759
760 let send_server = context
761 .get_config(Config::ConfiguredSendServer)
762 .await?
763 .context("SMTP server is not configured")?;
764 let send_port = context
765 .get_config_parsed::<u16>(Config::ConfiguredSendPort)
766 .await?
767 .unwrap_or_default();
768 let send_security: Socket = context
769 .get_config_parsed::<i32>(Config::ConfiguredSendSecurity)
770 .await?
771 .and_then(num_traits::FromPrimitive::from_i32)
772 .unwrap_or_default();
773
774 imap = vec![ConfiguredServerLoginParam {
775 connection: ConnectionCandidate {
776 host: mail_server,
777 port: mail_port,
778 security: mail_security.try_into()?,
779 },
780 user: mail_user.clone(),
781 }];
782 smtp = vec![ConfiguredServerLoginParam {
783 connection: ConnectionCandidate {
784 host: send_server,
785 port: send_port,
786 security: send_security.try_into()?,
787 },
788 user: send_user.clone(),
789 }];
790 }
791
792 Ok(Some(ConfiguredLoginParam {
793 addr,
794 imap,
795 imap_user: mail_user,
796 imap_password: mail_pw,
797 smtp,
798 smtp_user: send_user,
799 smtp_password: send_pw,
800 certificate_checks,
801 provider,
802 oauth2,
803 }))
804 }
805
806 pub(crate) async fn save_to_transports_table(
807 self,
808 context: &Context,
809 entered_param: &EnteredLoginParam,
810 ) -> Result<()> {
811 let addr = addr_normalize(&self.addr);
812 let configured_addr = context.get_config(Config::ConfiguredAddr).await?;
813 if let Some(configured_addr) = configured_addr {
814 ensure!(
815 addr_cmp(&configured_addr, &addr,),
816 "Adding a second transport is not supported right now."
817 );
818 }
819 context
820 .sql
821 .set_raw_config(
822 Config::ConfiguredProvider.as_ref(),
823 self.provider.map(|provider| provider.id),
824 )
825 .await?;
826 context
827 .sql
828 .execute(
829 "INSERT INTO transports (addr, entered_param, configured_param)
830 VALUES (?, ?, ?)
831 ON CONFLICT (addr)
832 DO UPDATE SET entered_param=excluded.entered_param, configured_param=excluded.configured_param",
833 (
834 self.addr.clone(),
835 serde_json::to_string(entered_param)?,
836 self.into_json()?,
837 ),
838 )
839 .await?;
840 context
841 .sql
842 .set_raw_config(Config::ConfiguredAddr.as_ref(), Some(&addr))
843 .await?;
844 Ok(())
845 }
846
847 pub(crate) fn from_json(json: &str) -> Result<Self> {
848 let json: ConfiguredLoginParamJson = serde_json::from_str(json)?;
849
850 let provider = json.provider_id.and_then(|id| get_provider_by_id(&id));
851
852 Ok(ConfiguredLoginParam {
853 addr: json.addr,
854 imap: json.imap,
855 imap_user: json.imap_user,
856 imap_password: json.imap_password,
857 smtp: json.smtp,
858 smtp_user: json.smtp_user,
859 smtp_password: json.smtp_password,
860 provider,
861 certificate_checks: json.certificate_checks,
862 oauth2: json.oauth2,
863 })
864 }
865
866 pub(crate) fn into_json(self) -> Result<String> {
867 let json = ConfiguredLoginParamJson {
868 addr: self.addr,
869 imap: self.imap,
870 imap_user: self.imap_user,
871 imap_password: self.imap_password,
872 smtp: self.smtp,
873 smtp_user: self.smtp_user,
874 smtp_password: self.smtp_password,
875 provider_id: self.provider.map(|p| p.id.to_string()),
876 certificate_checks: self.certificate_checks,
877 oauth2: self.oauth2,
878 };
879 Ok(serde_json::to_string(&json)?)
880 }
881
882 pub(crate) fn strict_tls(&self, connected_through_proxy: bool) -> bool {
883 let provider_strict_tls = self.provider.map(|provider| provider.opt.strict_tls);
884 match self.certificate_checks {
885 ConfiguredCertificateChecks::OldAutomatic => {
886 provider_strict_tls.unwrap_or(connected_through_proxy)
887 }
888 ConfiguredCertificateChecks::Automatic => provider_strict_tls.unwrap_or(true),
889 ConfiguredCertificateChecks::Strict => true,
890 ConfiguredCertificateChecks::AcceptInvalidCertificates
891 | ConfiguredCertificateChecks::AcceptInvalidCertificates2 => false,
892 }
893 }
894}
895
896#[cfg(test)]
897mod tests {
898 use super::*;
899 use crate::log::LogExt as _;
900 use crate::provider::get_provider_by_id;
901 use crate::test_utils::TestContext;
902 use pretty_assertions::assert_eq;
903
904 #[test]
905 fn test_certificate_checks_display() {
906 use std::string::ToString;
907
908 assert_eq!(
909 "accept_invalid_certificates".to_string(),
910 EnteredCertificateChecks::AcceptInvalidCertificates.to_string()
911 );
912
913 assert_eq!(
914 "accept_invalid_certificates".to_string(),
915 ConfiguredCertificateChecks::AcceptInvalidCertificates.to_string()
916 );
917 }
918
919 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
920 async fn test_entered_login_param() -> Result<()> {
921 let t = &TestContext::new().await;
922
923 t.set_config(Config::Addr, Some("alice@example.org"))
924 .await?;
925 t.set_config(Config::MailPw, Some("foobarbaz")).await?;
926
927 let param = EnteredLoginParam::load(t).await?;
928 assert_eq!(param.addr, "alice@example.org");
929 assert_eq!(
930 param.certificate_checks,
931 EnteredCertificateChecks::Automatic
932 );
933
934 t.set_config(Config::ImapCertificateChecks, Some("1"))
935 .await?;
936 let param = EnteredLoginParam::load(t).await?;
937 assert_eq!(param.certificate_checks, EnteredCertificateChecks::Strict);
938
939 t.set_config(Config::ImapCertificateChecks, Some("999"))
941 .await?;
942 assert!(EnteredLoginParam::load(t).await.is_err());
943
944 Ok(())
945 }
946
947 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
948 async fn test_save_entered_login_param() -> Result<()> {
949 let t = TestContext::new().await;
950 let param = EnteredLoginParam {
951 addr: "alice@example.org".to_string(),
952 imap: EnteredServerLoginParam {
953 server: "".to_string(),
954 port: 0,
955 security: Socket::Starttls,
956 user: "".to_string(),
957 password: "foobar".to_string(),
958 },
959 smtp: EnteredServerLoginParam {
960 server: "".to_string(),
961 port: 2947,
962 security: Socket::default(),
963 user: "".to_string(),
964 password: "".to_string(),
965 },
966 certificate_checks: Default::default(),
967 oauth2: false,
968 };
969 param.save(&t).await?;
970 assert_eq!(
971 t.get_config(Config::Addr).await?.unwrap(),
972 "alice@example.org"
973 );
974 assert_eq!(t.get_config(Config::MailPw).await?.unwrap(), "foobar");
975 assert_eq!(t.get_config(Config::SendPw).await?, None);
976 assert_eq!(t.get_config_int(Config::SendPort).await?, 2947);
977
978 assert_eq!(EnteredLoginParam::load(&t).await?, param);
979
980 Ok(())
981 }
982
983 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
984 async fn test_save_load_login_param() -> Result<()> {
985 let t = TestContext::new().await;
986
987 let param = ConfiguredLoginParam {
988 addr: "alice@example.org".to_string(),
989 imap: vec![ConfiguredServerLoginParam {
990 connection: ConnectionCandidate {
991 host: "imap.example.com".to_string(),
992 port: 123,
993 security: ConnectionSecurity::Starttls,
994 },
995 user: "alice".to_string(),
996 }],
997 imap_user: "".to_string(),
998 imap_password: "foo".to_string(),
999 smtp: vec![ConfiguredServerLoginParam {
1000 connection: ConnectionCandidate {
1001 host: "smtp.example.com".to_string(),
1002 port: 456,
1003 security: ConnectionSecurity::Tls,
1004 },
1005 user: "alice@example.org".to_string(),
1006 }],
1007 smtp_user: "".to_string(),
1008 smtp_password: "bar".to_string(),
1009 provider: None,
1010 certificate_checks: ConfiguredCertificateChecks::Strict,
1011 oauth2: false,
1012 };
1013
1014 param
1015 .clone()
1016 .save_to_transports_table(&t, &EnteredLoginParam::default())
1017 .await?;
1018 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}"#;
1019 assert_eq!(
1020 t.sql
1021 .query_get_value::<String>("SELECT configured_param FROM transports", ())
1022 .await?
1023 .unwrap(),
1024 expected_param
1025 );
1026 assert_eq!(t.is_configured().await?, true);
1027 let loaded = ConfiguredLoginParam::load(&t).await?.unwrap();
1028 assert_eq!(param, loaded);
1029
1030 t.set_config(Config::ConfiguredImapCertificateChecks, Some("999"))
1032 .await?;
1033 assert!(ConfiguredLoginParam::load(&t).await.is_ok());
1034
1035 let wrong_param = expected_param.replace("Strict", "Stricct");
1037 assert_ne!(expected_param, wrong_param);
1038 t.sql
1039 .execute("UPDATE transports SET configured_param=?", (wrong_param,))
1040 .await?;
1041 assert!(ConfiguredLoginParam::load(&t).await.is_err());
1042
1043 Ok(())
1044 }
1045
1046 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1047 async fn test_posteo_alias() -> Result<()> {
1048 let t = TestContext::new().await;
1049
1050 let user = "alice@posteo.de";
1051
1052 t.set_config(Config::Configured, Some("1")).await?;
1055 t.set_config(Config::ConfiguredProvider, Some("posteo"))
1056 .await?;
1057 t.sql
1058 .set_raw_config(Config::ConfiguredAddr.as_ref(), Some("alice@posteo.at"))
1059 .await?;
1060 t.set_config(Config::ConfiguredMailServer, Some("posteo.de"))
1061 .await?;
1062 t.set_config(Config::ConfiguredMailPort, Some("993"))
1063 .await?;
1064 t.set_config(Config::ConfiguredMailSecurity, Some("1"))
1065 .await?; t.set_config(Config::ConfiguredMailUser, Some(user)).await?;
1067 t.set_config(Config::ConfiguredMailPw, Some("foobarbaz"))
1068 .await?;
1069 t.set_config(Config::ConfiguredImapCertificateChecks, Some("1"))
1070 .await?; t.set_config(Config::ConfiguredSendServer, Some("posteo.de"))
1072 .await?;
1073 t.set_config(Config::ConfiguredSendPort, Some("465"))
1074 .await?;
1075 t.set_config(Config::ConfiguredSendSecurity, Some("1"))
1076 .await?; t.set_config(Config::ConfiguredSendUser, Some(user)).await?;
1078 t.set_config(Config::ConfiguredSendPw, Some("foobarbaz"))
1079 .await?;
1080 t.set_config(Config::ConfiguredSmtpCertificateChecks, Some("1"))
1081 .await?; t.set_config(Config::ConfiguredServerFlags, Some("0"))
1083 .await?;
1084
1085 let param = ConfiguredLoginParam {
1086 addr: "alice@posteo.at".to_string(),
1087 imap: vec![
1088 ConfiguredServerLoginParam {
1089 connection: ConnectionCandidate {
1090 host: "posteo.de".to_string(),
1091 port: 993,
1092 security: ConnectionSecurity::Tls,
1093 },
1094 user: user.to_string(),
1095 },
1096 ConfiguredServerLoginParam {
1097 connection: ConnectionCandidate {
1098 host: "posteo.de".to_string(),
1099 port: 143,
1100 security: ConnectionSecurity::Starttls,
1101 },
1102 user: user.to_string(),
1103 },
1104 ],
1105 imap_user: "alice@posteo.de".to_string(),
1106 imap_password: "foobarbaz".to_string(),
1107 smtp: vec![
1108 ConfiguredServerLoginParam {
1109 connection: ConnectionCandidate {
1110 host: "posteo.de".to_string(),
1111 port: 465,
1112 security: ConnectionSecurity::Tls,
1113 },
1114 user: user.to_string(),
1115 },
1116 ConfiguredServerLoginParam {
1117 connection: ConnectionCandidate {
1118 host: "posteo.de".to_string(),
1119 port: 587,
1120 security: ConnectionSecurity::Starttls,
1121 },
1122 user: user.to_string(),
1123 },
1124 ],
1125 smtp_user: "alice@posteo.de".to_string(),
1126 smtp_password: "foobarbaz".to_string(),
1127 provider: get_provider_by_id("posteo"),
1128 certificate_checks: ConfiguredCertificateChecks::Strict,
1129 oauth2: false,
1130 };
1131
1132 let loaded = ConfiguredLoginParam::load_legacy(&t).await?.unwrap();
1133 assert_eq!(loaded, param);
1134
1135 migrate_configured_login_param(&t).await;
1136 let loaded = ConfiguredLoginParam::load(&t).await?.unwrap();
1137 assert_eq!(loaded, param);
1138
1139 Ok(())
1140 }
1141
1142 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1143 async fn test_empty_server_list_legacy() -> Result<()> {
1144 let (domain, provider) = crate::provider::data::PROVIDER_DATA
1148 .iter()
1149 .find(|(_domain, provider)| provider.server.is_empty())
1150 .unwrap();
1151
1152 let t = TestContext::new().await;
1153
1154 let addr = format!("alice@{domain}");
1155
1156 t.set_config(Config::Configured, Some("1")).await?;
1157 t.set_config(Config::ConfiguredProvider, Some(provider.id))
1158 .await?;
1159 t.sql
1160 .set_raw_config(Config::ConfiguredAddr.as_ref(), Some(&addr))
1161 .await?;
1162 t.set_config(Config::ConfiguredMailPw, Some("foobarbaz"))
1163 .await?;
1164 t.set_config(Config::ConfiguredImapCertificateChecks, Some("1"))
1165 .await?; t.set_config(Config::ConfiguredSendPw, Some("foobarbaz"))
1167 .await?;
1168 t.set_config(Config::ConfiguredSmtpCertificateChecks, Some("1"))
1169 .await?; t.set_config(Config::ConfiguredServerFlags, Some("0"))
1171 .await?;
1172
1173 let loaded = ConfiguredLoginParam::load_legacy(&t).await?.unwrap();
1174 assert_eq!(loaded.provider, Some(*provider));
1175 assert_eq!(loaded.imap.is_empty(), false);
1176 assert_eq!(loaded.smtp.is_empty(), false);
1177
1178 migrate_configured_login_param(&t).await;
1179
1180 let loaded = ConfiguredLoginParam::load(&t).await?.unwrap();
1181 assert_eq!(loaded.provider, Some(*provider));
1182 assert_eq!(loaded.imap.is_empty(), false);
1183 assert_eq!(loaded.smtp.is_empty(), false);
1184
1185 Ok(())
1186 }
1187
1188 async fn migrate_configured_login_param(t: &TestContext) {
1189 t.sql.execute("DROP TABLE transports;", ()).await.unwrap();
1190 t.sql.set_raw_config_int("dbversion", 130).await.unwrap();
1191 t.sql.run_migrations(t).await.log_err(t).ok();
1192 }
1193
1194 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1195 async fn test_empty_server_list() -> Result<()> {
1196 let (domain, provider) = crate::provider::data::PROVIDER_DATA
1200 .iter()
1201 .find(|(_domain, provider)| provider.server.is_empty())
1202 .unwrap();
1203
1204 let t = TestContext::new().await;
1205
1206 let addr = format!("alice@{domain}");
1207
1208 ConfiguredLoginParam {
1209 addr: addr.clone(),
1210 imap: vec![ConfiguredServerLoginParam {
1211 connection: ConnectionCandidate {
1212 host: "example.org".to_string(),
1213 port: 100,
1214 security: ConnectionSecurity::Tls,
1215 },
1216 user: addr.clone(),
1217 }],
1218 imap_user: addr.clone(),
1219 imap_password: "foobarbaz".to_string(),
1220 smtp: vec![ConfiguredServerLoginParam {
1221 connection: ConnectionCandidate {
1222 host: "example.org".to_string(),
1223 port: 100,
1224 security: ConnectionSecurity::Tls,
1225 },
1226 user: addr.clone(),
1227 }],
1228 smtp_user: addr.clone(),
1229 smtp_password: "foobarbaz".to_string(),
1230 provider: Some(provider),
1231 certificate_checks: ConfiguredCertificateChecks::Automatic,
1232 oauth2: false,
1233 }
1234 .save_to_transports_table(&t, &EnteredLoginParam::default())
1235 .await?;
1236
1237 let loaded = ConfiguredLoginParam::load(&t).await?.unwrap();
1238 assert_eq!(loaded.provider, Some(*provider));
1239 assert_eq!(loaded.imap.is_empty(), false);
1240 assert_eq!(loaded.smtp.is_empty(), false);
1241 assert_eq!(t.get_configured_provider().await?, Some(*provider));
1242
1243 Ok(())
1244 }
1245}