diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index ae0b6476e..c8debe196 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -359,6 +359,11 @@ char* dc_get_blobdir (const dc_context_t* context); * to not mess up with non-delivery-reports or read-receipts. * 0=no limit (default). * Changes affect future messages only. + * - `ui.*` = All keys prefixed by `ui.` can be used by the user-interfaces for system-specific purposes. + * The prefix should be followed by the system and maybe subsystem, + * eg. `ui.desktop.foo`, `ui.desktop.linux.bar`, `ui.android.foo`, `ui.dc40.bar`, `ui.bot.simplebot.baz`. + * These keys go to backups and allow easy per-account settings when using @ref dc_accounts_t, + * however, are not handled by the core otherwise. * * If you want to retrieve a value, use dc_get_config(). * diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index cb27bc503..faf4ce11a 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -134,20 +134,30 @@ pub unsafe extern "C" fn dc_set_config( } let ctx = &*context; let key = to_string_lossy(key); - match config::Config::from_str(&key) { - Ok(key) => block_on(async move { - let value = to_opt_string_lossy(value); - ctx.set_config(key, value.as_deref()) + let value = to_opt_string_lossy(value); + + block_on(async move { + if key.starts_with("ui.") { + ctx.set_ui_config(&key, value.as_deref()) .await .with_context(|| format!("Can't set {} to {:?}", key, value)) .log_err(ctx, "dc_set_config() failed") .is_ok() as libc::c_int - }), - Err(_) => { - warn!(ctx, "dc_set_config(): invalid key"); - 0 + } else { + match config::Config::from_str(&key) { + Ok(key) => ctx + .set_config(key, value.as_deref()) + .await + .with_context(|| format!("Can't set {} to {:?}", key, value)) + .log_err(ctx, "dc_set_config() failed") + .is_ok() as libc::c_int, + Err(_) => { + warn!(ctx, "dc_set_config(): invalid key"); + 0 + } + } } - } + }) } #[no_mangle] @@ -160,20 +170,33 @@ pub unsafe extern "C" fn dc_get_config( return "".strdup(); } let ctx = &*context; - match config::Config::from_str(&to_string_lossy(key)) { - Ok(key) => block_on(async move { - ctx.get_config(key) + + let key = to_string_lossy(key); + + block_on(async move { + if key.starts_with("ui.") { + ctx.get_ui_config(&key) .await - .log_err(ctx, "Can't get config") + .log_err(ctx, "Can't get ui-config") .unwrap_or_default() .unwrap_or_default() .strdup() - }), - Err(_) => { - warn!(ctx, "dc_get_config(): invalid key"); - "".strdup() + } else { + match config::Config::from_str(&key) { + Ok(key) => ctx + .get_config(key) + .await + .log_err(ctx, "Can't get config") + .unwrap_or_default() + .unwrap_or_default() + .strdup(), + Err(_) => { + warn!(ctx, "dc_get_config(): invalid key"); + "".strdup() + } + } } - } + }) } #[no_mangle] diff --git a/src/config.rs b/src/config.rs index 23875b9b6..d4c71afcb 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,6 @@ //! # Key-value configuration management. -use anyhow::Result; +use anyhow::{ensure, Result}; use strum::{EnumProperty, IntoEnumIterator}; use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty, EnumString}; @@ -337,6 +337,21 @@ impl Context { .await?; Ok(()) } + + /// Sets an ui-specific key-value pair. + /// Keys must be prefixed by `ui.` + /// and should be followed by the name of the system and maybe subsystem, + /// eg. `ui.desktop.linux.foo`, `ui.desktop.macos.bar`, `ui.ios.foobar`. + pub async fn set_ui_config(&self, key: &str, value: Option<&str>) -> Result<()> { + ensure!(key.starts_with("ui."), "set_ui_config(): prefix missing."); + self.sql.set_raw_config(key, value).await + } + + /// Gets an ui-specific value set by set_ui_config(). + pub async fn get_ui_config(&self, key: &str) -> Result> { + ensure!(key.starts_with("ui."), "get_ui_config(): prefix missing."); + self.sql.get_raw_config(key).await + } } /// Returns all available configuration keys concated together. @@ -389,4 +404,25 @@ mod tests { let media_quality = constants::MediaQuality::from_i32(media_quality).unwrap_or_default(); assert_eq!(media_quality, constants::MediaQuality::Worse); } + + #[async_std::test] + async fn test_ui_config() -> Result<()> { + let t = TestContext::new().await; + + assert_eq!(t.get_ui_config("ui.desktop.linux.systray").await?, None); + + t.set_ui_config("ui.android.screen_security", Some("safe")) + .await?; + assert_eq!( + t.get_ui_config("ui.android.screen_security").await?, + Some("safe".to_string()) + ); + + t.set_ui_config("ui.android.screen_security", None).await?; + assert_eq!(t.get_ui_config("ui.android.screen_security").await?, None); + + assert!(t.set_ui_config("configured", Some("bar")).await.is_err()); + + Ok(()) + } }