Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow the user to replace maps integration #5678

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions deltachat-jsonrpc/src/api/types/webxdc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ impl WebxdcMessageInfo {
document,
summary,
source_code_url,
request_integration: _,
internet_access,
} = message.get_webxdc_info(context).await?;

Expand Down
8 changes: 5 additions & 3 deletions src/chat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2161,7 +2161,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)
Expand Down Expand Up @@ -2934,8 +2936,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();
}

Expand Down
17 changes: 16 additions & 1 deletion src/webxdc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,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>,
}
Expand All @@ -92,6 +95,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.
Expand Down Expand Up @@ -818,6 +824,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();
Expand All @@ -840,7 +849,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
Expand All @@ -853,6 +867,7 @@ impl Message {
} else {
"".to_string()
},
request_integration,
internet_access,
})
}
Expand Down
112 changes: 104 additions & 8 deletions src/webxdc/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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? {
iequidoo marked this conversation as resolved.
Show resolved Hide resolved
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(())
}
Expand Down Expand Up @@ -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;
Expand All @@ -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(())
}
}
4 changes: 2 additions & 2 deletions src/webxdc/maps_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,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?;
Expand All @@ -198,7 +198,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(
Expand Down
Binary file added test-data/webxdc/mapstest-integration-set.xdc
Binary file not shown.
Loading