diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 2391ce6d9..5163e1776 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -337,12 +337,16 @@ pub unsafe extern "C" fn dc_get_config( return "".strdup(); } let ffi_context = &*context; - match config::Config::from_str(&to_string_lossy(key)) { + match config::Config::from_key_str(&to_string_lossy(key)) { Ok(key) => ffi_context .with_inner(|ctx| ctx.get_config(key).unwrap_or_default().strdup()) .unwrap_or_else(|_| "".strdup()), - Err(_) => { - ffi_context.error("dc_get_config(): invalid key"); + Err(err) => { + ffi_context.error(&format!( + "dc_get_config(): invalid key {} ({})", + &to_string_lossy(key), + err + )); "".strdup() } } diff --git a/src/config.rs b/src/config.rs index 759dc354a..71a967be5 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,162 +1,523 @@ //! # Key-value configuration management -use strum::{EnumProperty, IntoEnumIterator}; -use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty, EnumString}; +use std::str::FromStr; + +use strum::IntoEnumIterator; +use strum_macros::{AsRefStr, Display, EnumDiscriminants, EnumIter, EnumString}; use crate::blob::BlobObject; use crate::constants::DC_VERSION_STR; use crate::context::Context; -use crate::dc_tools::*; use crate::error::Error; use crate::job::*; use crate::stock::StockMessage; -/// The available configuration keys. +/// The available configuration items. +/// +/// There is also an enum called `ConfigKey` which has the same enum +/// variants but does not carry any data. #[derive( - Debug, Clone, Copy, PartialEq, Eq, Display, EnumString, AsRefStr, EnumIter, EnumProperty, + Debug, + Clone, + Display, + PartialEq, + Eq, + EnumDiscriminants, + EnumProperty, + EnumString, + EnumIter, + AsRefStr, )] #[strum(serialize_all = "snake_case")] -pub enum Config { - Addr, - MailServer, - MailUser, - MailPw, - MailPort, - ImapCertificateChecks, - SendServer, - SendUser, - SendPw, - SendPort, - SmtpCertificateChecks, - ServerFlags, - #[strum(props(default = "INBOX"))] - ImapFolder, - Displayname, - Selfstatus, - Selfavatar, - #[strum(props(default = "0"))] - BccSelf, - #[strum(props(default = "1"))] - E2eeEnabled, - #[strum(props(default = "1"))] - MdnsEnabled, - #[strum(props(default = "1"))] - InboxWatch, - #[strum(props(default = "1"))] - SentboxWatch, - #[strum(props(default = "1"))] - MvboxWatch, - #[strum(props(default = "1"))] - MvboxMove, - #[strum(props(default = "0"))] // also change ShowEmails.default() on changes - ShowEmails, - SaveMimeHeaders, - ConfiguredAddr, - ConfiguredMailServer, - ConfiguredMailUser, - ConfiguredMailPw, - ConfiguredMailPort, - ConfiguredMailSecurity, - ConfiguredImapCertificateChecks, - ConfiguredSendServer, - ConfiguredSendUser, - ConfiguredSendPw, - ConfiguredSendPort, - ConfiguredSmtpCertificateChecks, - ConfiguredServerFlags, - ConfiguredSendSecurity, - ConfiguredE2EEEnabled, - Configured, - // Deprecated - #[strum(serialize = "sys.version")] - SysVersion, - #[strum(serialize = "sys.msgsize_max_recommended")] - SysMsgsizeMaxRecommended, +#[strum_discriminants( + name(ConfigKey), + derive(Display, EnumString), + strum(serialize_all = "snake_case") +)] +pub enum ConfigItem { + Addr(String), + BccSelf(String), + Configured(String), + ConfiguredAddr(String), + ConfiguredE2EEEnabled(String), + ConfiguredImapCertificateChecks(String), + ConfiguredMailPort(String), + ConfiguredMailPw(String), + ConfiguredMailSecurity(String), + ConfiguredMailServer(String), + ConfiguredMailUser(String), + ConfiguredSendPort(String), + ConfiguredSendPw(String), + ConfiguredSendSecurity(String), + ConfiguredSendServer(String), + ConfiguredSendUser(String), + ConfiguredServerFlags(String), + ConfiguredSmtpCertificateChecks(String), + Displayname(String), + E2eeEnabled(String), + ImapCertificateChecks(String), + ImapFolder(String), + InboxWatch(String), + MailPort(String), + MailPw(String), + MailServer(String), + MailUser(String), + MdnsEnabled(String), + MvboxMove(String), + MvboxWatch(String), + SaveMimeHeaders(String), + Selfavatar(String), + Selfstatus(String), + SendPort(String), + SendPw(String), + SendServer(String), + SendUser(String), + SentboxWatch(String), + ServerFlags(String), + ShowEmails(String), + SmtpCertificateChecks(String), #[strum(serialize = "sys.config_keys")] - SysConfigKeys, + SysConfigKeys(String), + #[strum(serialize = "sys.msgsize_max_recommended")] + SysMsgsizeMaxRecommended(String), + #[strum(serialize = "sys.version")] + SysVersion(String), } -impl Context { - /// Get a configuration key. Returns `None` if no value is set, and no default value found. - pub fn get_config(&self, key: Config) -> Option { - let value = match key { - Config::Selfavatar => { - let rel_path = self.sql.get_raw_config(self, key); - rel_path.map(|p| dc_get_abs_path(self, &p).to_string_lossy().into_owned()) - } - Config::SysVersion => Some((&*DC_VERSION_STR).clone()), - Config::SysMsgsizeMaxRecommended => Some(format!("{}", 24 * 1024 * 1024 / 4 * 3)), - Config::SysConfigKeys => Some(get_config_keys_string()), - _ => self.sql.get_raw_config(self, key), - }; +// Transitional: support the old name for ConfigKey. +pub type Config = ConfigKey; - if value.is_some() { - return value; - } - - // Default values - match key { - Config::Selfstatus => Some(self.stock_str(StockMessage::StatusLine).into_owned()), - _ => key.get_str("default").map(|s| s.to_string()), +impl ConfigKey { + /// Creates a [ConfigKey] from a string. + /// + /// Use this rather than [ConfigKey::from_str]. + pub fn from_key_str(s: &str) -> Result { + match s { + "sys.config_keys" => Ok(ConfigKey::SysConfigKeys), + "sys.msgsize_max_recommended" => Ok(ConfigKey::SysMsgsizeMaxRecommended), + "sys.version" => Ok(ConfigKey::SysVersion), + _ => ConfigKey::from_str(s), } } + /// Default values for configuration options. + /// + /// These are returned in case there is no value stored in the + /// database. + fn default_item(&self, context: &Context) -> Option { + match self { + Self::BccSelf => Some(ConfigItem::BccSelf(String::from("1"))), + Self::E2eeEnabled => Some(ConfigItem::E2eeEnabled(String::from("1"))), + Self::ImapFolder => Some(ConfigItem::ImapFolder(String::from("INBOX"))), + Self::InboxWatch => Some(ConfigItem::InboxWatch(String::from("1"))), + Self::MdnsEnabled => Some(ConfigItem::MdnsEnabled(String::from("1"))), + Self::MvboxMove => Some(ConfigItem::MvboxMove(String::from("1"))), + Self::MvboxWatch => Some(ConfigItem::MvboxWatch(String::from("1"))), + Self::Selfstatus => Some(ConfigItem::Selfstatus( + context.stock_str(StockMessage::StatusLine).into_owned(), + )), + Self::SentboxWatch => Some(ConfigItem::SentboxWatch(String::from("1"))), + Self::ShowEmails => Some(ConfigItem::ShowEmails(String::from("0"))), + Self::SysConfigKeys => Some(ConfigItem::SysConfigKeys(get_config_keys_string())), + Self::SysMsgsizeMaxRecommended => Some(ConfigItem::SysMsgsizeMaxRecommended(format!( + "{}", + 24 * 1024 * 1024 / 4 * 3 + ))), + Self::SysVersion => Some(ConfigItem::SysVersion((&*DC_VERSION_STR).clone())), + _ => None, + } + } +} + +impl rusqlite::types::ToSql for ConfigItem { + fn to_sql(&self) -> rusqlite::Result { + let sql_obj = match self { + ConfigItem::Selfavatar(value) => { + let rel_path = std::fs::canonicalize(value) + .map_err(|err| rusqlite::Error::ToSqlConversionFailure(Box::new(err)))?; + rusqlite::types::Value::Text(rel_path.to_string_lossy().into_owned()) + } + ConfigItem::Addr(value) + | ConfigItem::BccSelf(value) + | ConfigItem::Configured(value) + | ConfigItem::ConfiguredAddr(value) + | ConfigItem::ConfiguredE2EEEnabled(value) + | ConfigItem::ConfiguredImapCertificateChecks(value) + | ConfigItem::ConfiguredMailPort(value) + | ConfigItem::ConfiguredMailPw(value) + | ConfigItem::ConfiguredMailSecurity(value) + | ConfigItem::ConfiguredMailServer(value) + | ConfigItem::ConfiguredMailUser(value) + | ConfigItem::ConfiguredSendPort(value) + | ConfigItem::ConfiguredSendPw(value) + | ConfigItem::ConfiguredSendSecurity(value) + | ConfigItem::ConfiguredSendServer(value) + | ConfigItem::ConfiguredSendUser(value) + | ConfigItem::ConfiguredServerFlags(value) + | ConfigItem::ConfiguredSmtpCertificateChecks(value) + | ConfigItem::Displayname(value) + | ConfigItem::E2eeEnabled(value) + | ConfigItem::ImapCertificateChecks(value) + | ConfigItem::ImapFolder(value) + | ConfigItem::InboxWatch(value) + | ConfigItem::MailPort(value) + | ConfigItem::MailPw(value) + | ConfigItem::MailServer(value) + | ConfigItem::MailUser(value) + | ConfigItem::MdnsEnabled(value) + | ConfigItem::MvboxMove(value) + | ConfigItem::MvboxWatch(value) + | ConfigItem::SaveMimeHeaders(value) + | ConfigItem::Selfstatus(value) + | ConfigItem::SendPort(value) + | ConfigItem::SendPw(value) + | ConfigItem::SendServer(value) + | ConfigItem::SendUser(value) + | ConfigItem::SentboxWatch(value) + | ConfigItem::ServerFlags(value) + | ConfigItem::ShowEmails(value) + | ConfigItem::SmtpCertificateChecks(value) + | ConfigItem::SysConfigKeys(value) + | ConfigItem::SysMsgsizeMaxRecommended(value) + | ConfigItem::SysVersion(value) => rusqlite::types::Value::Text(value.to_string()), + }; + let out = rusqlite::types::ToSqlOutput::Owned(sql_obj); + Ok(out) + } +} + +impl Context { + /// Gets a configuration option. + /// + /// If there is no value set in the database returns the default + /// value from [ConfigKey::default_item]. + pub fn get_config_item(&self, key: ConfigKey) -> Option { + let res = self.sql.query_row( + "SELECT value FROM config WHERE keyname=?;", + params![key.to_string()], + |row| { + row.get_raw_checked(0) + .map(|valref| self.sql_raw_to_config_item(key, valref)) + }, + ); + match res { + Ok(item) => item, + Err(Error::Sql(rusqlite::Error::QueryReturnedNoRows)) => key.default_item(self), + Err(err) => { + warn!(self, "get_config: Failed SQL query: {}", err); + None + } + } + } + + // This is effectively the FromSql impl for ConfigItem, but we + // need to know the ConfigKey which the trait does not give us. + fn sql_raw_to_config_item( + &self, + key: ConfigKey, + raw: rusqlite::types::ValueRef, + ) -> Option { + let to_string = |raw: rusqlite::types::ValueRef| -> Option { + raw.as_str() + .map_err(|err| { + warn!(self, "ConfigItem {}; not a string: {}", key, err); + }) + .map(|s| s.to_string()) + .ok() + }; + match key { + ConfigKey::Addr => to_string(raw).map(|val| ConfigItem::Addr(val)), + ConfigKey::BccSelf => to_string(raw).map(|val| ConfigItem::BccSelf(val)), + ConfigKey::Configured => to_string(raw).map(|val| ConfigItem::Configured(val)), + ConfigKey::ConfiguredAddr => to_string(raw).map(|val| ConfigItem::ConfiguredAddr(val)), + ConfigKey::ConfiguredE2EEEnabled => { + to_string(raw).map(|val| ConfigItem::ConfiguredE2EEEnabled(val)) + } + ConfigKey::ConfiguredImapCertificateChecks => { + to_string(raw).map(|val| ConfigItem::ConfiguredImapCertificateChecks(val)) + } + ConfigKey::ConfiguredMailPort => { + to_string(raw).map(|val| ConfigItem::ConfiguredMailPort(val)) + } + ConfigKey::ConfiguredMailPw => { + to_string(raw).map(|val| ConfigItem::ConfiguredMailPw(val)) + } + ConfigKey::ConfiguredMailSecurity => { + to_string(raw).map(|val| ConfigItem::ConfiguredMailSecurity(val)) + } + ConfigKey::ConfiguredMailServer => { + to_string(raw).map(|val| ConfigItem::ConfiguredMailServer(val)) + } + ConfigKey::ConfiguredMailUser => { + to_string(raw).map(|val| ConfigItem::ConfiguredMailUser(val)) + } + ConfigKey::ConfiguredSendPort => { + to_string(raw).map(|val| ConfigItem::ConfiguredSendPort(val)) + } + ConfigKey::ConfiguredSendPw => { + to_string(raw).map(|val| ConfigItem::ConfiguredSendPw(val)) + } + ConfigKey::ConfiguredSendSecurity => { + to_string(raw).map(|val| ConfigItem::ConfiguredSendSecurity(val)) + } + ConfigKey::ConfiguredSendServer => { + to_string(raw).map(|val| ConfigItem::ConfiguredSendServer(val)) + } + ConfigKey::ConfiguredSendUser => { + to_string(raw).map(|val| ConfigItem::ConfiguredSendUser(val)) + } + ConfigKey::ConfiguredServerFlags => { + to_string(raw).map(|val| ConfigItem::ConfiguredServerFlags(val)) + } + ConfigKey::ConfiguredSmtpCertificateChecks => { + to_string(raw).map(|val| ConfigItem::ConfiguredSmtpCertificateChecks(val)) + } + ConfigKey::Displayname => to_string(raw).map(|val| ConfigItem::Displayname(val)), + ConfigKey::E2eeEnabled => to_string(raw).map(|val| ConfigItem::E2eeEnabled(val)), + ConfigKey::ImapCertificateChecks => { + to_string(raw).map(|val| ConfigItem::ImapCertificateChecks(val)) + } + ConfigKey::ImapFolder => to_string(raw).map(|val| ConfigItem::ImapFolder(val)), + ConfigKey::InboxWatch => to_string(raw).map(|val| ConfigItem::InboxWatch(val)), + ConfigKey::MailPort => to_string(raw).map(|val| ConfigItem::MailPort(val)), + ConfigKey::MailPw => to_string(raw).map(|val| ConfigItem::MailPw(val)), + ConfigKey::MailServer => to_string(raw).map(|val| ConfigItem::MailServer(val)), + ConfigKey::MailUser => to_string(raw).map(|val| ConfigItem::MailUser(val)), + ConfigKey::MdnsEnabled => to_string(raw).map(|val| ConfigItem::MdnsEnabled(val)), + ConfigKey::MvboxMove => to_string(raw).map(|val| ConfigItem::MvboxMove(val)), + ConfigKey::MvboxWatch => to_string(raw).map(|val| ConfigItem::MvboxWatch(val)), + ConfigKey::SaveMimeHeaders => { + to_string(raw).map(|val| ConfigItem::SaveMimeHeaders(val)) + } + ConfigKey::Selfavatar => to_string(raw).map(|val| ConfigItem::Selfavatar(val)), + ConfigKey::Selfstatus => to_string(raw).map(|val| ConfigItem::Selfstatus(val)), + ConfigKey::SendPort => to_string(raw).map(|val| ConfigItem::SendPort(val)), + ConfigKey::SendPw => to_string(raw).map(|val| ConfigItem::SendPw(val)), + ConfigKey::SendServer => to_string(raw).map(|val| ConfigItem::SendServer(val)), + ConfigKey::SendUser => to_string(raw).map(|val| ConfigItem::SendUser(val)), + ConfigKey::SentboxWatch => to_string(raw).map(|val| ConfigItem::SentboxWatch(val)), + ConfigKey::ServerFlags => to_string(raw).map(|val| ConfigItem::ServerFlags(val)), + ConfigKey::ShowEmails => to_string(raw).map(|val| ConfigItem::ShowEmails(val)), + ConfigKey::SmtpCertificateChecks => { + to_string(raw).map(|val| ConfigItem::SmtpCertificateChecks(val)) + } + ConfigKey::SysConfigKeys => to_string(raw).map(|val| ConfigItem::SysConfigKeys(val)), + ConfigKey::SysMsgsizeMaxRecommended => { + to_string(raw).map(|val| ConfigItem::SysMsgsizeMaxRecommended(val)) + } + ConfigKey::SysVersion => to_string(raw).map(|val| ConfigItem::SysVersion(val)), + } + } + + /// Stores a configuration item in the database. + /// + /// # Errors + /// + /// You can not store any of the [ConfigItem::SysVersion], + /// [ConfigItem::SysMsgsizemaxrecommended] or + /// [ConfigItem::SysConfigkeys] variants. + pub fn set_config_item(&self, item: ConfigItem) -> Result { + match item { + ConfigItem::SysConfigKeys(_) + | ConfigItem::SysMsgsizeMaxRecommended(_) + | ConfigItem::SysVersion(_) => bail!("Can not set config item {}", item), + _ => (), + } + // Would prefer to use INSERT OR REPLACE but this needs a + // uniqueness constraint on the keyname column which does not + // yet exist. + if self.sql.exists( + "SELECT value FROM config WHERE keyname=?;", + params![item.to_string()], + )? { + self.sql.execute( + "UPDATE config SET value=? WHERE keyname=?", + params![item, item.to_string()], + )?; + } else { + self.sql.execute( + "INSERT INTO config (keyname, value) VALUES (?, ?)", + params![item.to_string(), item], + )?; + } + match item { + ConfigItem::InboxWatch(_) => interrupt_inbox_idle(self, true), + ConfigItem::SentboxWatch(_) => interrupt_sentbox_idle(self), + ConfigItem::MvboxWatch(_) => interrupt_mvbox_idle(self), + _ => (), + }; + Ok(item) + } + + /// Deletes a configuration option. + /// + /// Returns `true` if the option was deleted, `false` if it wasn't + /// present in the first place. + pub fn del_config_item(&self, key: ConfigKey) -> Result { + match self.sql.execute( + "DELETE FROM config WHERE keyname=?;", + params![key.to_string()], + ) { + Ok(0) => Ok(false), + Ok(_) => Ok(true), + Err(err) => Err(err), + } + } + + /// Transitional: migrate to get_config_item. + /// + /// This will migrate to the FFI once nothing in the core uses + /// this anymore. + pub fn get_config(&self, key: Config) -> Option { + if let Some(item) = self.get_config_item(key) { + let value = match item { + ConfigItem::Addr(value) + | ConfigItem::BccSelf(value) + | ConfigItem::Configured(value) + | ConfigItem::ConfiguredAddr(value) + | ConfigItem::ConfiguredE2EEEnabled(value) + | ConfigItem::ConfiguredImapCertificateChecks(value) + | ConfigItem::ConfiguredMailPort(value) + | ConfigItem::ConfiguredMailPw(value) + | ConfigItem::ConfiguredMailSecurity(value) + | ConfigItem::ConfiguredMailServer(value) + | ConfigItem::ConfiguredMailUser(value) + | ConfigItem::ConfiguredSendPort(value) + | ConfigItem::ConfiguredSendPw(value) + | ConfigItem::ConfiguredSendSecurity(value) + | ConfigItem::ConfiguredSendServer(value) + | ConfigItem::ConfiguredSendUser(value) + | ConfigItem::ConfiguredServerFlags(value) + | ConfigItem::ConfiguredSmtpCertificateChecks(value) + | ConfigItem::Displayname(value) + | ConfigItem::E2eeEnabled(value) + | ConfigItem::ImapCertificateChecks(value) + | ConfigItem::ImapFolder(value) + | ConfigItem::InboxWatch(value) + | ConfigItem::MailPort(value) + | ConfigItem::MailPw(value) + | ConfigItem::MailServer(value) + | ConfigItem::MailUser(value) + | ConfigItem::MdnsEnabled(value) + | ConfigItem::MvboxMove(value) + | ConfigItem::MvboxWatch(value) + | ConfigItem::SaveMimeHeaders(value) + | ConfigItem::Selfavatar(value) + | ConfigItem::Selfstatus(value) + | ConfigItem::SendPort(value) + | ConfigItem::SendPw(value) + | ConfigItem::SendServer(value) + | ConfigItem::SendUser(value) + | ConfigItem::SentboxWatch(value) + | ConfigItem::ServerFlags(value) + | ConfigItem::ShowEmails(value) + | ConfigItem::SmtpCertificateChecks(value) + | ConfigItem::SysConfigKeys(value) + | ConfigItem::SysMsgsizeMaxRecommended(value) + | ConfigItem::SysVersion(value) => value, + }; + Some(value) + } else { + None + } + } + + /// Transitional: migrate to get_config_item. pub fn get_config_int(&self, key: Config) -> i32 { self.get_config(key) .and_then(|s| s.parse().ok()) .unwrap_or_default() } + /// Transitional: migrate to get_config_item. pub fn get_config_bool(&self, key: Config) -> bool { self.get_config_int(key) != 0 } /// Set the given config key. - /// If `None` is passed as a value the value is cleared and set to the default if there is one. + /// + /// Transitional: migrate to set_config_item/del_config_item. + /// + /// If `None` is passed as a value the value is cleared and set to + /// the default if there is one. pub fn set_config(&self, key: Config, value: Option<&str>) -> Result<(), Error> { - match key { - Config::Selfavatar if value.is_some() => { - let blob = BlobObject::create_from_path(&self, value.unwrap())?; - self.sql.set_raw_config(self, key, Some(blob.as_name())) - } - Config::InboxWatch => { - let ret = self.sql.set_raw_config(self, key, value); - interrupt_inbox_idle(self, true); - ret - } - Config::SentboxWatch => { - let ret = self.sql.set_raw_config(self, key, value); - interrupt_sentbox_idle(self); - ret - } - Config::MvboxWatch => { - let ret = self.sql.set_raw_config(self, key, value); - interrupt_mvbox_idle(self); - ret - } + let maybe_val = match key { Config::Selfstatus => { let def = self.stock_str(StockMessage::StatusLine); - let val = if value.is_none() || value.unwrap() == def { + if value.is_none() || value.unwrap() == def { None } else { value - }; - - self.sql.set_raw_config(self, key, val) + } } - _ => self.sql.set_raw_config(self, key, value), + _ => value, + }; + if let Some(val) = maybe_val { + let v = val.to_string(); + let item = match key { + ConfigKey::Addr => ConfigItem::Addr(v), + ConfigKey::BccSelf => ConfigItem::BccSelf(v), + ConfigKey::Configured => ConfigItem::Configured(v), + ConfigKey::ConfiguredAddr => ConfigItem::ConfiguredAddr(v), + ConfigKey::ConfiguredE2EEEnabled => ConfigItem::ConfiguredE2EEEnabled(v), + ConfigKey::ConfiguredImapCertificateChecks => { + ConfigItem::ConfiguredImapCertificateChecks(v) + } + ConfigKey::ConfiguredMailPort => ConfigItem::ConfiguredMailPort(v), + ConfigKey::ConfiguredMailPw => ConfigItem::ConfiguredMailPw(v), + ConfigKey::ConfiguredMailSecurity => ConfigItem::ConfiguredMailSecurity(v), + ConfigKey::ConfiguredMailServer => ConfigItem::ConfiguredMailServer(v), + ConfigKey::ConfiguredMailUser => ConfigItem::ConfiguredMailUser(v), + ConfigKey::ConfiguredSendPort => ConfigItem::ConfiguredSendPort(v), + ConfigKey::ConfiguredSendPw => ConfigItem::ConfiguredSendPw(v), + ConfigKey::ConfiguredSendSecurity => ConfigItem::ConfiguredSendSecurity(v), + ConfigKey::ConfiguredSendServer => ConfigItem::ConfiguredSendServer(v), + ConfigKey::ConfiguredSendUser => ConfigItem::ConfiguredSendUser(v), + ConfigKey::ConfiguredServerFlags => ConfigItem::ConfiguredServerFlags(v), + ConfigKey::ConfiguredSmtpCertificateChecks => { + ConfigItem::ConfiguredSmtpCertificateChecks(v) + } + ConfigKey::Displayname => ConfigItem::Displayname(v), + ConfigKey::E2eeEnabled => ConfigItem::E2eeEnabled(v), + ConfigKey::ImapCertificateChecks => ConfigItem::ImapCertificateChecks(v), + ConfigKey::ImapFolder => ConfigItem::ImapFolder(v), + ConfigKey::InboxWatch => ConfigItem::InboxWatch(v), + ConfigKey::MailPort => ConfigItem::MailPort(v), + ConfigKey::MailPw => ConfigItem::MailPw(v), + ConfigKey::MailServer => ConfigItem::MailServer(v), + ConfigKey::MailUser => ConfigItem::MailUser(v), + ConfigKey::MdnsEnabled => ConfigItem::MdnsEnabled(v), + ConfigKey::MvboxMove => ConfigItem::MvboxMove(v), + ConfigKey::MvboxWatch => ConfigItem::MvboxWatch(v), + ConfigKey::SaveMimeHeaders => ConfigItem::SaveMimeHeaders(v), + ConfigKey::Selfavatar => ConfigItem::Selfavatar(v), + ConfigKey::Selfstatus => ConfigItem::Selfstatus(v), + ConfigKey::SendPort => ConfigItem::SendPort(v), + ConfigKey::SendPw => ConfigItem::SendPw(v), + ConfigKey::SendServer => ConfigItem::SendServer(v), + ConfigKey::SendUser => ConfigItem::SendUser(v), + ConfigKey::SentboxWatch => ConfigItem::SentboxWatch(v), + ConfigKey::ServerFlags => ConfigItem::ServerFlags(v), + ConfigKey::ShowEmails => ConfigItem::ShowEmails(v), + ConfigKey::SmtpCertificateChecks => ConfigItem::SmtpCertificateChecks(v), + ConfigKey::SysConfigKeys => ConfigItem::SysConfigKeys(v), + ConfigKey::SysMsgsizeMaxRecommended => ConfigItem::SysMsgsizeMaxRecommended(v), + ConfigKey::SysVersion => ConfigItem::SysVersion(v), + }; + self.set_config_item(item).map(|_| ()) + } else { + self.del_config_item(key).map(|_| ()) } } } /// Returns all available configuration keys concated together. fn get_config_keys_string() -> String { - let keys = Config::iter().fold(String::new(), |mut acc, key| { + let keys = ConfigItem::iter().fold(String::new(), |mut acc, key| { acc += key.as_ref(); acc += " "; acc }); - format!(" {} ", keys) } @@ -169,21 +530,90 @@ mod tests { use crate::test_utils::*; - #[test] - fn test_to_string() { - assert_eq!(Config::MailServer.to_string(), "mail_server"); - assert_eq!(Config::from_str("mail_server"), Ok(Config::MailServer)); - - assert_eq!(Config::SysConfigKeys.to_string(), "sys.config_keys"); - assert_eq!( - Config::from_str("sys.config_keys"), - Ok(Config::SysConfigKeys) - ); + impl ConfigKey { + fn to_key_string(&self) -> String { + let s = self.to_string(); + if s.starts_with("sys_") { + s.replacen("sys_", "sys.", 1) + } else { + s + } + } } #[test] - fn test_default_prop() { - assert_eq!(Config::ImapFolder.get_str("default"), Some("INBOX")); + fn test_to_string() { + assert_eq!( + ConfigItem::MailServer(Default::default()).to_string(), + "mail_server" + ); + assert_eq!( + ConfigItem::from_str("mail_server"), + Ok(ConfigItem::MailServer(Default::default())) + ); + + assert_eq!( + ConfigItem::SysConfigKeys(Default::default()).to_string(), + "sys.config_keys" + ); + assert_eq!( + ConfigItem::from_str("sys.config_keys"), + Ok(ConfigItem::SysConfigKeys(Default::default())) + ); + + // The ConfigItem.to_string() is used as key in the SQL table + // on set operations and ConfigKey.to_string() is the key used + // on get operations. Therefore we want to make sure they are + // the same keys. + for item in ConfigItem::iter() { + let name = item.to_string(); + let key = ConfigKey::from_key_str(&name).unwrap(); + assert_eq!(name, key.to_key_string()); + } + } + + #[test] + fn test_config_item() { + let t = test_context(Some(Box::new(logging_cb))); + + // An item which has a default. + let opt = t.ctx.get_config_item(ConfigKey::ImapFolder); + assert_eq!(opt, Some(ConfigItem::ImapFolder("INBOX".into()))); + + // Set a different value. + t.ctx + .set_config_item(ConfigItem::ImapFolder("DeltaChat".into())) + .unwrap(); + let opt = t.ctx.get_config_item(ConfigKey::ImapFolder); + assert_eq!(opt, Some(ConfigItem::ImapFolder("DeltaChat".into()))); + + // Set another value, testing update. + t.ctx + .set_config_item(ConfigItem::ImapFolder("Chat".into())) + .unwrap(); + let opt = t.ctx.get_config_item(ConfigKey::ImapFolder); + assert_eq!(opt, Some(ConfigItem::ImapFolder("Chat".into()))); + + // Reset to the default. + t.ctx.del_config_item(ConfigKey::ImapFolder).unwrap(); + let opt = t.ctx.get_config_item(ConfigKey::ImapFolder); + assert_eq!(opt, Some(ConfigItem::ImapFolder("INBOX".into()))); + + // An item without default. + let opt = t.ctx.get_config_item(ConfigKey::Addr); + assert!(opt.is_none()); + + // Set the item. + t.ctx + .set_config_item(ConfigItem::Addr("me@example.com".into())) + .unwrap(); + let opt = t.ctx.get_config_item(ConfigKey::Addr); + assert_eq!(opt, Some(ConfigItem::Addr("me@example.com".into()))); + + // Delete the item. + t.ctx.del_config_item(ConfigKey::Addr).unwrap(); + let opt = t.ctx.get_config_item(ConfigKey::Addr); + assert!(opt.is_none()); } #[test]