mirror of
https://github.com/chatmail/core.git
synced 2026-04-17 21:46:35 +03:00
feat: allow the user to replace maps integration (#5678)
with this PR, when an `.xdc` with `request_integration = map` in the manifest is added to the "Saved Messages" chat, it is used _locally_ as an replacement for the shipped maps.xdc (other devices will see the `.xdc` but not use it) this allows easy development and adapting the map to use services that work better in some area. there are lots of known discussions and ideas about adding more barriers of safety. however, after internal discussions, we decided to move forward and also to allow internet, if requested by an integration (as discussed at https://github.com/deltachat/deltachat-core-rust/pull/3516). the gist is to ease development and to make users who want to adapt, actionable _now_, without making things too hard and adding too high barriers or stressing our own resources/power too much. note, that things are still experimental and will be the next time - without the corresponding switch being enabled, nothing will work at all, so we can be quite relaxed here :) for android/ios, things will work directly. for desktop, allow_internet needs to be accepted unconditionally from core. for the future, we might add a question before using an integration and/or add signing. or sth. completely different - but for now, the thing is to get started. nb: "integration" field in the webxdc-info is experimental as well and should not be used in UIs at all currently, it may vanish again and is there mainly for simplicity of the code; therefore, no need to document that. successor of https://github.com/deltachat/deltachat-core-rust/pull/5461 this is how it looks like currently - again, please note that all that is an experiment! <img width=320 src=https://github.com/deltachat/deltachat-core-rust/assets/9800740/f659c891-f46a-4e28-9d0a-b6783d69be8d> <img width=320 src=https://github.com/deltachat/deltachat-core-rust/assets/9800740/54549b3c-a894-4568-9e27-d5f1caea2d22> ... when going out of experimental, there are loots of ideas, eg. changing "Start" to "integrate"
This commit is contained in:
@@ -57,6 +57,7 @@ impl WebxdcMessageInfo {
|
||||
document,
|
||||
summary,
|
||||
source_code_url,
|
||||
request_integration: _,
|
||||
internet_access,
|
||||
self_addr,
|
||||
send_update_interval,
|
||||
|
||||
@@ -2202,7 +2202,9 @@ impl Chat {
|
||||
msg.id = MsgId::new(u32::try_from(raw_id)?);
|
||||
|
||||
maybe_set_logging_xdc(context, msg, self.id).await?;
|
||||
context.update_webxdc_integration_database(msg).await?;
|
||||
context
|
||||
.update_webxdc_integration_database(msg, context)
|
||||
.await?;
|
||||
}
|
||||
context.scheduler.interrupt_ephemeral_task().await;
|
||||
Ok(msg.id)
|
||||
@@ -2977,8 +2979,8 @@ pub(crate) async fn create_send_msg_jobs(context: &Context, msg: &mut Message) -
|
||||
recipients.push(from);
|
||||
}
|
||||
|
||||
// Webxdc integrations are messages, however, shipped with main app and must not be sent out
|
||||
if msg.param.get_int(Param::WebxdcIntegration).is_some() {
|
||||
// Default Webxdc integrations are hidden messages and must not be sent out
|
||||
if msg.param.get_int(Param::WebxdcIntegration).is_some() && msg.hidden {
|
||||
recipients.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,9 @@ pub struct WebxdcManifest {
|
||||
/// Optional URL of webxdc source code.
|
||||
pub source_code_url: Option<String>,
|
||||
|
||||
/// Set to "map" to request integration.
|
||||
pub request_integration: Option<String>,
|
||||
|
||||
/// If the webxdc requests network access.
|
||||
pub request_internet_access: Option<bool>,
|
||||
}
|
||||
@@ -100,6 +103,9 @@ pub struct WebxdcInfo {
|
||||
/// URL of webxdc source code or an empty string.
|
||||
pub source_code_url: String,
|
||||
|
||||
/// Set to "map" to request integration, otherwise an empty string.
|
||||
pub request_integration: String,
|
||||
|
||||
/// If the webxdc is allowed to access the network.
|
||||
/// It should request access, be encrypted
|
||||
/// and sent to self for this.
|
||||
@@ -920,6 +926,9 @@ impl Message {
|
||||
}
|
||||
}
|
||||
|
||||
let request_integration = manifest.request_integration.unwrap_or_default();
|
||||
let is_integrated = self.is_set_as_webxdc_integration(context).await?;
|
||||
|
||||
let internet_access = manifest.request_internet_access.unwrap_or_default()
|
||||
&& self.chat_id.is_self_talk(context).await.unwrap_or_default()
|
||||
&& self.get_showpadlock();
|
||||
@@ -944,7 +953,12 @@ impl Message {
|
||||
.get(Param::WebxdcDocument)
|
||||
.unwrap_or_default()
|
||||
.to_string(),
|
||||
summary: if internet_access {
|
||||
summary: if is_integrated {
|
||||
"🌍 Used as map. Delete to use default. Do not enter sensitive data".to_string()
|
||||
} else if request_integration == "map" {
|
||||
"🌏 To use as map, forward to \"Saved Messages\" again. Do not enter sensitive data"
|
||||
.to_string()
|
||||
} else if internet_access {
|
||||
"Dev Mode: Do not enter sensitive data!".to_string()
|
||||
} else {
|
||||
self.param
|
||||
@@ -957,6 +971,7 @@ impl Message {
|
||||
} else {
|
||||
"".to_string()
|
||||
},
|
||||
request_integration,
|
||||
internet_access,
|
||||
self_addr,
|
||||
send_update_interval: context.ratelimit.read().await.update_interval(),
|
||||
|
||||
@@ -57,14 +57,34 @@ impl Context {
|
||||
}
|
||||
|
||||
// Check if a Webxdc shall be used as an integration and remember that.
|
||||
pub(crate) async fn update_webxdc_integration_database(&self, msg: &Message) -> Result<()> {
|
||||
if msg.viewtype == Viewtype::Webxdc && msg.param.get_int(Param::WebxdcIntegration).is_some()
|
||||
{
|
||||
self.set_config_internal(
|
||||
Config::WebxdcIntegration,
|
||||
Some(&msg.id.to_u32().to_string()),
|
||||
)
|
||||
.await?;
|
||||
pub(crate) async fn update_webxdc_integration_database(
|
||||
&self,
|
||||
msg: &mut Message,
|
||||
context: &Context,
|
||||
) -> Result<()> {
|
||||
if msg.viewtype == Viewtype::Webxdc {
|
||||
let is_integration = if msg.param.get_int(Param::WebxdcIntegration).is_some() {
|
||||
true
|
||||
} else if msg.chat_id.is_self_talk(context).await? {
|
||||
let info = msg.get_webxdc_info(context).await?;
|
||||
if info.request_integration == "map" {
|
||||
msg.param.set_int(Param::WebxdcIntegration, 1);
|
||||
msg.update_param(context).await?;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if is_integration {
|
||||
self.set_config_internal(
|
||||
Config::WebxdcIntegration,
|
||||
Some(&msg.id.to_u32().to_string()),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -101,11 +121,26 @@ impl Message {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the message is an actually used as Webxdc integration.
|
||||
pub(crate) async fn is_set_as_webxdc_integration(&self, context: &Context) -> Result<bool> {
|
||||
if let Some(integration_id) = context
|
||||
.get_config_parsed::<u32>(Config::WebxdcIntegration)
|
||||
.await?
|
||||
{
|
||||
Ok(integration_id == self.id.to_u32())
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::config::Config;
|
||||
use crate::context::Context;
|
||||
use crate::message;
|
||||
use crate::message::{Message, Viewtype};
|
||||
use crate::test_utils::TestContext;
|
||||
use anyhow::Result;
|
||||
use std::time::Duration;
|
||||
@@ -126,4 +161,65 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_overwrite_default_integration() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
let self_chat = &t.get_self_chat().await;
|
||||
assert!(t.init_webxdc_integration(None).await?.is_none());
|
||||
|
||||
async fn assert_integration(t: &Context, name: &str) -> Result<()> {
|
||||
let integration_id = t.init_webxdc_integration(None).await?.unwrap();
|
||||
let integration = Message::load_from_db(t, integration_id).await?;
|
||||
let integration_info = integration.get_webxdc_info(t).await?;
|
||||
assert_eq!(integration_info.name, name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// set default integration
|
||||
let bytes = include_bytes!("../../test-data/webxdc/with-manifest-and-png-icon.xdc");
|
||||
let file = t.get_blobdir().join("maps.xdc");
|
||||
tokio::fs::write(&file, bytes).await.unwrap();
|
||||
t.set_webxdc_integration(file.to_str().unwrap()).await?;
|
||||
assert_integration(&t, "with some icon").await?;
|
||||
|
||||
// send a maps.xdc with insufficient manifest
|
||||
let mut msg = Message::new(Viewtype::Webxdc);
|
||||
msg.set_file_from_bytes(
|
||||
&t,
|
||||
"mapstest.xdc",
|
||||
include_bytes!("../../test-data/webxdc/mapstest-integration-unset.xdc"),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
t.send_msg(self_chat.id, &mut msg).await;
|
||||
assert_integration(&t, "with some icon").await?; // still the default integration
|
||||
|
||||
// send a maps.xdc with manifest including the line `request_integration = "map"`
|
||||
let mut msg = Message::new(Viewtype::Webxdc);
|
||||
msg.set_file_from_bytes(
|
||||
&t,
|
||||
"mapstest.xdc",
|
||||
include_bytes!("../../test-data/webxdc/mapstest-integration-set.xdc"),
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
let sent = t.send_msg(self_chat.id, &mut msg).await;
|
||||
let info = msg.get_webxdc_info(&t).await?;
|
||||
assert!(info.summary.contains("Used as map"));
|
||||
assert_integration(&t, "Maps Test 2").await?;
|
||||
|
||||
// when maps.xdc is received on another device, the integration is not accepted (needs to be forwarded again)
|
||||
let t2 = TestContext::new_alice().await;
|
||||
let msg2 = t2.recv_msg(&sent).await;
|
||||
let info = msg2.get_webxdc_info(&t2).await?;
|
||||
assert!(info.summary.contains("To use as map,"));
|
||||
assert!(t2.init_webxdc_integration(None).await?.is_none());
|
||||
|
||||
// deleting maps.xdc removes the user's integration - the UI will go back to default calling set_webxdc_integration() then
|
||||
message::delete_msgs(&t, &[msg.id]).await?;
|
||||
assert!(t.init_webxdc_integration(None).await?.is_none());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +181,7 @@ mod tests {
|
||||
async fn test_maps_integration() -> Result<()> {
|
||||
let t = TestContext::new_alice().await;
|
||||
|
||||
let bytes = include_bytes!("../../test-data/webxdc/mapstest.xdc");
|
||||
let bytes = include_bytes!("../../test-data/webxdc/mapstest-integration-set.xdc");
|
||||
let file = t.get_blobdir().join("maps.xdc");
|
||||
tokio::fs::write(&file, bytes).await.unwrap();
|
||||
t.set_webxdc_integration(file.to_str().unwrap()).await?;
|
||||
@@ -199,7 +199,7 @@ mod tests {
|
||||
|
||||
let integration = Message::load_from_db(&t, integration_id).await?;
|
||||
let info = integration.get_webxdc_info(&t).await?;
|
||||
assert_eq!(info.name, "Maps Test");
|
||||
assert_eq!(info.name, "Maps Test 2");
|
||||
assert_eq!(info.internet_access, true);
|
||||
|
||||
t.send_webxdc_status_update(
|
||||
|
||||
BIN
test-data/webxdc/mapstest-integration-set.xdc
Normal file
BIN
test-data/webxdc/mapstest-integration-set.xdc
Normal file
Binary file not shown.
Reference in New Issue
Block a user