1#[doc(inline)]
7use super::{Error, Result};
8#[cfg(feature = "oauth2")]
9use crate::account::config::oauth2::OAuth2Config;
10use crate::{account::config::passwd::PasswordConfig, tls::Encryption};
11
12#[derive(Clone, Debug, Default, Eq, PartialEq)]
16#[cfg_attr(
17 feature = "derive",
18 derive(serde::Serialize, serde::Deserialize),
19 serde(rename_all = "kebab-case")
20)]
21pub struct ImapConfig {
22 pub host: String,
24
25 pub port: u16,
27
28 pub encryption: Option<Encryption>,
32
33 pub login: String,
38
39 pub auth: ImapAuthConfig,
44
45 pub extensions: Option<ImapExtensionsConfig>,
47
48 pub watch: Option<ImapWatchConfig>,
53
54 pub clients_pool_size: Option<u8>,
59}
60
61impl ImapConfig {
62 pub fn clients_pool_size(&self) -> u8 {
63 self.clients_pool_size.unwrap_or(1)
64 }
65
66 pub fn send_id_after_auth(&self) -> bool {
67 self.extensions
68 .as_ref()
69 .and_then(|ext| ext.id.as_ref())
70 .and_then(|id| id.send_after_auth)
71 .unwrap_or_default()
72 }
73
74 pub fn is_encryption_enabled(&self) -> bool {
76 matches!(
77 self.encryption.as_ref(),
78 None | Some(Encryption::Tls(_)) | Some(Encryption::StartTls(_))
79 )
80 }
81
82 pub fn is_start_tls_encryption_enabled(&self) -> bool {
84 matches!(self.encryption.as_ref(), Some(Encryption::StartTls(_)))
85 }
86
87 pub fn is_encryption_disabled(&self) -> bool {
89 matches!(self.encryption.as_ref(), Some(Encryption::None))
90 }
91
92 pub async fn build_credentials(&self) -> Result<String> {
97 self.auth.build_credentials().await
98 }
99
100 pub fn find_watch_timeout(&self) -> Option<u64> {
102 self.watch.as_ref().and_then(|c| c.find_timeout())
103 }
104}
105
106#[cfg(feature = "sync")]
107impl crate::sync::hash::SyncHash for ImapConfig {
108 fn sync_hash(&self, state: &mut std::hash::DefaultHasher) {
109 use std::hash::Hash;
110
111 Hash::hash(&self.host, state);
112 Hash::hash(&self.port, state);
113 Hash::hash(&self.login, state);
114 }
115}
116
117#[derive(Clone, Debug, Eq, PartialEq)]
121#[cfg_attr(
122 feature = "derive",
123 derive(serde::Serialize, serde::Deserialize),
124 serde(rename_all = "lowercase"),
125 serde(tag = "type"),
126 serde(from = "ImapAuthConfigDerive")
127)]
128pub enum ImapAuthConfig {
129 Password(PasswordConfig),
131 #[cfg(feature = "oauth2")]
133 OAuth2(OAuth2Config),
134}
135
136impl ImapAuthConfig {
137 pub async fn reset(&self) -> Result<()> {
139 match self {
140 ImapAuthConfig::Password(config) => {
141 config.reset().await.map_err(Error::ResetPasswordError)
142 }
143 #[cfg(feature = "oauth2")]
144 ImapAuthConfig::OAuth2(config) => {
145 config.reset().await.map_err(Error::ResetOAuthSecretsError)
146 }
147 }
148 }
149
150 pub async fn build_credentials(&self) -> Result<String> {
155 match self {
156 ImapAuthConfig::Password(passwd) => {
157 let passwd = passwd.get().await.map_err(Error::GetPasswdImapError)?;
158 let passwd = passwd
159 .lines()
160 .next()
161 .ok_or(Error::GetPasswdEmptyImapError)?;
162 Ok(passwd.to_owned())
163 }
164 #[cfg(feature = "oauth2")]
165 ImapAuthConfig::OAuth2(oauth2) => Ok(oauth2
166 .access_token()
167 .await
168 .map_err(Error::AccessTokenNotAvailable)?),
169 }
170 }
171
172 #[cfg(feature = "keyring")]
173 pub fn replace_empty_secrets(&mut self, name: impl AsRef<str>) -> Result<()> {
174 let name = name.as_ref();
175
176 match self {
177 Self::Password(secret) => {
178 secret
179 .replace_with_keyring_if_empty(format!("{name}-imap-passwd"))
180 .map_err(Error::ReplacingUnidentifiedFailed)?;
181 }
182 #[cfg(feature = "oauth2")]
183 Self::OAuth2(config) => {
184 if let Some(secret) = config.client_secret.as_mut() {
185 secret
186 .replace_with_keyring_if_empty(format!("{name}-imap-oauth2-client-secret"))
187 .map_err(Error::ReplacingUnidentifiedFailed)?;
188 }
189
190 config
191 .access_token
192 .replace_with_keyring_if_empty(format!("{name}-imap-oauth2-access-token"))
193 .map_err(Error::ReplacingUnidentifiedFailed)?;
194 config
195 .refresh_token
196 .replace_with_keyring_if_empty(format!("{name}-imap-oauth2-refresh-token"))
197 .map_err(Error::ReplacingUnidentifiedFailed)?;
198 }
199 }
200
201 Ok(())
202 }
203}
204
205impl Default for ImapAuthConfig {
206 fn default() -> Self {
207 Self::Password(Default::default())
208 }
209}
210
211#[cfg(feature = "derive")]
212#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
213#[serde(rename_all = "lowercase", tag = "type")]
214pub enum ImapAuthConfigDerive {
215 Password(PasswordConfig),
216 #[cfg(feature = "oauth2")]
217 OAuth2(OAuth2Config),
218 #[cfg(not(feature = "oauth2"))]
219 #[serde(skip_serializing, deserialize_with = "missing_oauth2_feature")]
220 OAuth2,
221}
222
223#[cfg(all(feature = "derive", not(feature = "oauth2")))]
224fn missing_oauth2_feature<'de, D>(_: D) -> std::result::Result<(), D::Error>
225where
226 D: serde::Deserializer<'de>,
227{
228 Err(serde::de::Error::custom("missing `oauth2` cargo feature"))
229}
230
231#[cfg(feature = "derive")]
232impl From<ImapAuthConfigDerive> for ImapAuthConfig {
233 fn from(config: ImapAuthConfigDerive) -> Self {
234 match config {
235 ImapAuthConfigDerive::Password(config) => Self::Password(config),
236 #[cfg(feature = "oauth2")]
237 ImapAuthConfigDerive::OAuth2(config) => Self::OAuth2(config),
238 #[cfg(not(feature = "oauth2"))]
239 ImapAuthConfigDerive::OAuth2 => unreachable!(),
240 }
241 }
242}
243
244#[derive(Clone, Debug, Eq, PartialEq)]
249#[cfg_attr(
250 feature = "derive",
251 derive(serde::Serialize, serde::Deserialize),
252 serde(rename_all = "kebab-case")
253)]
254pub struct ImapWatchConfig {
255 timeout: Option<u64>,
260}
261
262impl ImapWatchConfig {
263 pub fn find_timeout(&self) -> Option<u64> {
265 self.timeout
266 }
267}
268
269#[derive(Clone, Debug, Default, Eq, PartialEq)]
271#[cfg_attr(
272 feature = "derive",
273 derive(serde::Serialize, serde::Deserialize),
274 serde(rename_all = "kebab-case")
275)]
276pub struct ImapExtensionsConfig {
277 id: Option<ImapIdExtensionConfig>,
278}
279
280#[derive(Clone, Debug, Default, Eq, PartialEq)]
284#[cfg_attr(
285 feature = "derive",
286 derive(serde::Serialize, serde::Deserialize),
287 serde(rename_all = "kebab-case")
288)]
289pub struct ImapIdExtensionConfig {
290 send_after_auth: Option<bool>,
293}