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 provider_id = self.provider.map(|provider| provider.id);
813 let configured_addr = context.get_config(Config::ConfiguredAddr).await?;
814 if let Some(configured_addr) = &configured_addr {
815 ensure!(
816 addr_cmp(configured_addr, &addr),
817 "Adding a second transport is not supported right now."
818 );
819 }
820 context
821 .sql
822 .execute(
823 "INSERT INTO transports (addr, entered_param, configured_param)
824 VALUES (?, ?, ?)
825 ON CONFLICT (addr)
826 DO UPDATE SET entered_param=excluded.entered_param, configured_param=excluded.configured_param",
827 (
828 self.addr.clone(),
829 serde_json::to_string(entered_param)?,
830 self.into_json()?,
831 ),
832 )
833 .await?;
834 if configured_addr.is_none() {
835 context
837 .sql
838 .set_raw_config(Config::ConfiguredProvider.as_ref(), provider_id)
839 .await?;
840 context
841 .sql
842 .set_raw_config(Config::ConfiguredAddr.as_ref(), Some(&addr))
843 .await?;
844 }
845 Ok(())
846 }
847
848 pub(crate) fn from_json(json: &str) -> Result<Self> {
849 let json: ConfiguredLoginParamJson = serde_json::from_str(json)?;
850
851 let provider = json.provider_id.and_then(|id| get_provider_by_id(&id));
852
853 Ok(ConfiguredLoginParam {
854 addr: json.addr,
855 imap: json.imap,
856 imap_user: json.imap_user,
857 imap_password: json.imap_password,
858 smtp: json.smtp,
859 smtp_user: json.smtp_user,
860 smtp_password: json.smtp_password,
861 provider,
862 certificate_checks: json.certificate_checks,
863 oauth2: json.oauth2,
864 })
865 }
866
867 pub(crate) fn into_json(self) -> Result<String> {
868 let json = ConfiguredLoginParamJson {
869 addr: self.addr,
870 imap: self.imap,
871 imap_user: self.imap_user,
872 imap_password: self.imap_password,
873 smtp: self.smtp,
874 smtp_user: self.smtp_user,
875 smtp_password: self.smtp_password,
876 provider_id: self.provider.map(|p| p.id.to_string()),
877 certificate_checks: self.certificate_checks,
878 oauth2: self.oauth2,
879 };
880 Ok(serde_json::to_string(&json)?)
881 }
882
883 pub(crate) fn strict_tls(&self, connected_through_proxy: bool) -> bool {
884 let provider_strict_tls = self.provider.map(|provider| provider.opt.strict_tls);
885 match self.certificate_checks {
886 ConfiguredCertificateChecks::OldAutomatic => {
887 provider_strict_tls.unwrap_or(connected_through_proxy)
888 }
889 ConfiguredCertificateChecks::Automatic => provider_strict_tls.unwrap_or(true),
890 ConfiguredCertificateChecks::Strict => true,
891 ConfiguredCertificateChecks::AcceptInvalidCertificates
892 | ConfiguredCertificateChecks::AcceptInvalidCertificates2 => false,
893 }
894 }
895}
896
897#[cfg(test)]
898mod tests {
899 use super::*;
900 use crate::log::LogExt as _;
901 use crate::provider::get_provider_by_id;
902 use crate::test_utils::TestContext;
903 use pretty_assertions::assert_eq;
904
905 #[test]
906 fn test_certificate_checks_display() {
907 use std::string::ToString;
908
909 assert_eq!(
910 "accept_invalid_certificates".to_string(),
911 EnteredCertificateChecks::AcceptInvalidCertificates.to_string()
912 );
913
914 assert_eq!(
915 "accept_invalid_certificates".to_string(),
916 ConfiguredCertificateChecks::AcceptInvalidCertificates.to_string()
917 );
918 }
919
920 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
921 async fn test_entered_login_param() -> Result<()> {
922 let t = &TestContext::new().await;
923
924 t.set_config(Config::Addr, Some("alice@example.org"))
925 .await?;
926 t.set_config(Config::MailPw, Some("foobarbaz")).await?;
927
928 let param = EnteredLoginParam::load(t).await?;
929 assert_eq!(param.addr, "alice@example.org");
930 assert_eq!(
931 param.certificate_checks,
932 EnteredCertificateChecks::Automatic
933 );
934
935 t.set_config(Config::ImapCertificateChecks, Some("1"))
936 .await?;
937 let param = EnteredLoginParam::load(t).await?;
938 assert_eq!(param.certificate_checks, EnteredCertificateChecks::Strict);
939
940 t.set_config(Config::ImapCertificateChecks, Some("999"))
942 .await?;
943 assert!(EnteredLoginParam::load(t).await.is_err());
944
945 Ok(())
946 }
947
948 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
949 async fn test_save_entered_login_param() -> Result<()> {
950 let t = TestContext::new().await;
951 let param = EnteredLoginParam {
952 addr: "alice@example.org".to_string(),
953 imap: EnteredServerLoginParam {
954 server: "".to_string(),
955 port: 0,
956 security: Socket::Starttls,
957 user: "".to_string(),
958 password: "foobar".to_string(),
959 },
960 smtp: EnteredServerLoginParam {
961 server: "".to_string(),
962 port: 2947,
963 security: Socket::default(),
964 user: "".to_string(),
965 password: "".to_string(),
966 },
967 certificate_checks: Default::default(),
968 oauth2: false,
969 };
970 param.save(&t).await?;
971 assert_eq!(
972 t.get_config(Config::Addr).await?.unwrap(),
973 "alice@example.org"
974 );
975 assert_eq!(t.get_config(Config::MailPw).await?.unwrap(), "foobar");
976 assert_eq!(t.get_config(Config::SendPw).await?, None);
977 assert_eq!(t.get_config_int(Config::SendPort).await?, 2947);
978
979 assert_eq!(EnteredLoginParam::load(&t).await?, param);
980
981 Ok(())
982 }
983
984 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
985 async fn test_save_load_login_param() -> Result<()> {
986 let t = TestContext::new().await;
987
988 let param = ConfiguredLoginParam {
989 addr: "alice@example.org".to_string(),
990 imap: vec![ConfiguredServerLoginParam {
991 connection: ConnectionCandidate {
992 host: "imap.example.com".to_string(),
993 port: 123,
994 security: ConnectionSecurity::Starttls,
995 },
996 user: "alice".to_string(),
997 }],
998 imap_user: "".to_string(),
999 imap_password: "foo".to_string(),
1000 smtp: vec![ConfiguredServerLoginParam {
1001 connection: ConnectionCandidate {
1002 host: "smtp.example.com".to_string(),
1003 port: 456,
1004 security: ConnectionSecurity::Tls,
1005 },
1006 user: "alice@example.org".to_string(),
1007 }],
1008 smtp_user: "".to_string(),
1009 smtp_password: "bar".to_string(),
1010 provider: None,
1011 certificate_checks: ConfiguredCertificateChecks::Strict,
1012 oauth2: false,
1013 };
1014
1015 param
1016 .clone()
1017 .save_to_transports_table(&t, &EnteredLoginParam::default())
1018 .await?;
1019 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}"#;
1020 assert_eq!(
1021 t.sql
1022 .query_get_value::<String>("SELECT configured_param FROM transports", ())
1023 .await?
1024 .unwrap(),
1025 expected_param
1026 );
1027 assert_eq!(t.is_configured().await?, true);
1028 let loaded = ConfiguredLoginParam::load(&t).await?.unwrap();
1029 assert_eq!(param, loaded);
1030
1031 t.set_config(Config::ConfiguredImapCertificateChecks, Some("999"))
1033 .await?;
1034 assert!(ConfiguredLoginParam::load(&t).await.is_ok());
1035
1036 let wrong_param = expected_param.replace("Strict", "Stricct");
1038 assert_ne!(expected_param, wrong_param);
1039 t.sql
1040 .execute("UPDATE transports SET configured_param=?", (wrong_param,))
1041 .await?;
1042 assert!(ConfiguredLoginParam::load(&t).await.is_err());
1043
1044 Ok(())
1045 }
1046
1047 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1048 async fn test_posteo_alias() -> Result<()> {
1049 let t = TestContext::new().await;
1050
1051 let user = "alice@posteo.de";
1052
1053 t.set_config(Config::Configured, Some("1")).await?;
1056 t.set_config(Config::ConfiguredProvider, Some("posteo"))
1057 .await?;
1058 t.sql
1059 .set_raw_config(Config::ConfiguredAddr.as_ref(), Some("alice@posteo.at"))
1060 .await?;
1061 t.set_config(Config::ConfiguredMailServer, Some("posteo.de"))
1062 .await?;
1063 t.set_config(Config::ConfiguredMailPort, Some("993"))
1064 .await?;
1065 t.set_config(Config::ConfiguredMailSecurity, Some("1"))
1066 .await?; t.set_config(Config::ConfiguredMailUser, Some(user)).await?;
1068 t.set_config(Config::ConfiguredMailPw, Some("foobarbaz"))
1069 .await?;
1070 t.set_config(Config::ConfiguredImapCertificateChecks, Some("1"))
1071 .await?; t.set_config(Config::ConfiguredSendServer, Some("posteo.de"))
1073 .await?;
1074 t.set_config(Config::ConfiguredSendPort, Some("465"))
1075 .await?;
1076 t.set_config(Config::ConfiguredSendSecurity, Some("1"))
1077 .await?; t.set_config(Config::ConfiguredSendUser, Some(user)).await?;
1079 t.set_config(Config::ConfiguredSendPw, Some("foobarbaz"))
1080 .await?;
1081 t.set_config(Config::ConfiguredSmtpCertificateChecks, Some("1"))
1082 .await?; t.set_config(Config::ConfiguredServerFlags, Some("0"))
1084 .await?;
1085
1086 let param = ConfiguredLoginParam {
1087 addr: "alice@posteo.at".to_string(),
1088 imap: vec![
1089 ConfiguredServerLoginParam {
1090 connection: ConnectionCandidate {
1091 host: "posteo.de".to_string(),
1092 port: 993,
1093 security: ConnectionSecurity::Tls,
1094 },
1095 user: user.to_string(),
1096 },
1097 ConfiguredServerLoginParam {
1098 connection: ConnectionCandidate {
1099 host: "posteo.de".to_string(),
1100 port: 143,
1101 security: ConnectionSecurity::Starttls,
1102 },
1103 user: user.to_string(),
1104 },
1105 ],
1106 imap_user: "alice@posteo.de".to_string(),
1107 imap_password: "foobarbaz".to_string(),
1108 smtp: vec![
1109 ConfiguredServerLoginParam {
1110 connection: ConnectionCandidate {
1111 host: "posteo.de".to_string(),
1112 port: 465,
1113 security: ConnectionSecurity::Tls,
1114 },
1115 user: user.to_string(),
1116 },
1117 ConfiguredServerLoginParam {
1118 connection: ConnectionCandidate {
1119 host: "posteo.de".to_string(),
1120 port: 587,
1121 security: ConnectionSecurity::Starttls,
1122 },
1123 user: user.to_string(),
1124 },
1125 ],
1126 smtp_user: "alice@posteo.de".to_string(),
1127 smtp_password: "foobarbaz".to_string(),
1128 provider: get_provider_by_id("posteo"),
1129 certificate_checks: ConfiguredCertificateChecks::Strict,
1130 oauth2: false,
1131 };
1132
1133 let loaded = ConfiguredLoginParam::load_legacy(&t).await?.unwrap();
1134 assert_eq!(loaded, param);
1135
1136 migrate_configured_login_param(&t).await;
1137 let loaded = ConfiguredLoginParam::load(&t).await?.unwrap();
1138 assert_eq!(loaded, param);
1139
1140 Ok(())
1141 }
1142
1143 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1144 async fn test_empty_server_list_legacy() -> Result<()> {
1145 let (domain, provider) = crate::provider::data::PROVIDER_DATA
1149 .iter()
1150 .find(|(_domain, provider)| provider.server.is_empty())
1151 .unwrap();
1152
1153 let t = TestContext::new().await;
1154
1155 let addr = format!("alice@{domain}");
1156
1157 t.set_config(Config::Configured, Some("1")).await?;
1158 t.set_config(Config::ConfiguredProvider, Some(provider.id))
1159 .await?;
1160 t.sql
1161 .set_raw_config(Config::ConfiguredAddr.as_ref(), Some(&addr))
1162 .await?;
1163 t.set_config(Config::ConfiguredMailPw, Some("foobarbaz"))
1164 .await?;
1165 t.set_config(Config::ConfiguredImapCertificateChecks, Some("1"))
1166 .await?; t.set_config(Config::ConfiguredSendPw, Some("foobarbaz"))
1168 .await?;
1169 t.set_config(Config::ConfiguredSmtpCertificateChecks, Some("1"))
1170 .await?; t.set_config(Config::ConfiguredServerFlags, Some("0"))
1172 .await?;
1173
1174 let loaded = ConfiguredLoginParam::load_legacy(&t).await?.unwrap();
1175 assert_eq!(loaded.provider, Some(*provider));
1176 assert_eq!(loaded.imap.is_empty(), false);
1177 assert_eq!(loaded.smtp.is_empty(), false);
1178
1179 migrate_configured_login_param(&t).await;
1180
1181 let loaded = ConfiguredLoginParam::load(&t).await?.unwrap();
1182 assert_eq!(loaded.provider, Some(*provider));
1183 assert_eq!(loaded.imap.is_empty(), false);
1184 assert_eq!(loaded.smtp.is_empty(), false);
1185
1186 Ok(())
1187 }
1188
1189 async fn migrate_configured_login_param(t: &TestContext) {
1190 t.sql.execute("DROP TABLE transports;", ()).await.unwrap();
1191 t.sql.set_raw_config_int("dbversion", 130).await.unwrap();
1192 t.sql.run_migrations(t).await.log_err(t).ok();
1193 }
1194
1195 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1196 async fn test_empty_server_list() -> Result<()> {
1197 let (domain, provider) = crate::provider::data::PROVIDER_DATA
1201 .iter()
1202 .find(|(_domain, provider)| provider.server.is_empty())
1203 .unwrap();
1204
1205 let t = TestContext::new().await;
1206
1207 let addr = format!("alice@{domain}");
1208
1209 ConfiguredLoginParam {
1210 addr: addr.clone(),
1211 imap: vec![ConfiguredServerLoginParam {
1212 connection: ConnectionCandidate {
1213 host: "example.org".to_string(),
1214 port: 100,
1215 security: ConnectionSecurity::Tls,
1216 },
1217 user: addr.clone(),
1218 }],
1219 imap_user: addr.clone(),
1220 imap_password: "foobarbaz".to_string(),
1221 smtp: vec![ConfiguredServerLoginParam {
1222 connection: ConnectionCandidate {
1223 host: "example.org".to_string(),
1224 port: 100,
1225 security: ConnectionSecurity::Tls,
1226 },
1227 user: addr.clone(),
1228 }],
1229 smtp_user: addr.clone(),
1230 smtp_password: "foobarbaz".to_string(),
1231 provider: Some(provider),
1232 certificate_checks: ConfiguredCertificateChecks::Automatic,
1233 oauth2: false,
1234 }
1235 .save_to_transports_table(&t, &EnteredLoginParam::default())
1236 .await?;
1237
1238 let loaded = ConfiguredLoginParam::load(&t).await?.unwrap();
1239 assert_eq!(loaded.provider, Some(*provider));
1240 assert_eq!(loaded.imap.is_empty(), false);
1241 assert_eq!(loaded.smtp.is_empty(), false);
1242 assert_eq!(t.get_configured_provider().await?, Some(*provider));
1243
1244 Ok(())
1245 }
1246}