pimalaya_cli/wizard/
imap.rs1use core::fmt;
4
5use secrecy::SecretString;
6
7use crate::{
8 prompt::{self, PromptResult},
9 wizard::keyring::{self, SecretChoice},
10};
11
12#[derive(Clone, Debug)]
14pub struct WizardImapConfig {
15 pub host: String,
17 pub port: u16,
19 pub encryption: Encryption,
21 pub login: String,
23 pub auth: ImapAuth,
25}
26
27#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
29pub enum Encryption {
30 #[default]
32 Tls,
33 StartTls,
35 None,
37}
38
39impl fmt::Display for Encryption {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match self {
42 Self::Tls => f.write_str("Always (TLS)"),
43 Self::StartTls => f.write_str("Opportunistic (STARTTLS)"),
44 Self::None => f.write_str("None (insecure)"),
45 }
46 }
47}
48
49#[derive(Clone, Debug)]
51pub enum ImapAuth {
52 Password(ImapSecret),
54}
55
56#[derive(Clone, Debug)]
58pub enum ImapSecret {
59 Raw(SecretString),
61 Command(Vec<String>),
64 Shell(String),
67}
68
69const ENCRYPTIONS: [Encryption; 3] = [Encryption::Tls, Encryption::StartTls, Encryption::None];
70
71pub fn run(
74 account_name: impl AsRef<str>,
75 local_part: impl AsRef<str>,
76 domain: impl AsRef<str>,
77 defaults: Option<&WizardImapConfig>,
78) -> PromptResult<WizardImapConfig> {
79 let account_name = account_name.as_ref();
80 let local_part = local_part.as_ref();
81 let domain = domain.as_ref();
82
83 let default_host = defaults
84 .map(|c| c.host.clone())
85 .unwrap_or_else(|| format!("imap.{domain}"));
86
87 let host = prompt::text("IMAP hostname:", Some(&default_host))?;
88
89 let default_encryption = defaults.map(|c| c.encryption).unwrap_or_default();
90
91 let encryption = prompt::item("IMAP encryption:", ENCRYPTIONS, Some(default_encryption))?;
92
93 let default_port = if encryption == default_encryption {
94 defaults
95 .map(|c| c.port)
96 .unwrap_or_else(|| default_port(encryption))
97 } else {
98 default_port(encryption)
99 };
100
101 let port = prompt::u16("IMAP port:", Some(default_port))?;
102
103 let default_login = defaults
104 .map(|c| c.login.clone())
105 .unwrap_or_else(|| format!("{local_part}@{domain}"));
106
107 let login = prompt::text("IMAP login:", Some(&default_login))?;
108
109 let auth = {
110 let key = format!("{account_name}-imap");
111 let secret = keyring::prompt_secret("IMAP password", &key)?;
112 ImapAuth::Password(match secret {
113 SecretChoice::Command(argv) => ImapSecret::Command(argv),
114 SecretChoice::Shell(line) => ImapSecret::Shell(line),
115 SecretChoice::Raw(secret) => ImapSecret::Raw(secret),
116 })
117 };
118
119 Ok(WizardImapConfig {
120 host,
121 port,
122 encryption,
123 login,
124 auth,
125 })
126}
127
128fn default_port(encryption: Encryption) -> u16 {
129 match encryption {
130 Encryption::Tls => 993,
131 Encryption::StartTls | Encryption::None => 143,
132 }
133}