Compare commits

...

1 Commits

Author SHA1 Message Date
link2xt
8b06d31190 api: add dc_replace_webxdc() 2023-09-22 10:44:30 +00:00
3 changed files with 137 additions and 9 deletions

View File

@@ -1175,6 +1175,24 @@ int dc_send_webxdc_status_update (dc_context_t* context, uint32_t msg_id, const
*/
char* dc_get_webxdc_status_updates (dc_context_t* context, uint32_t msg_id, uint32_t serial);
/**
* Replaces webxdc app with a new version.
*
* On the JavaScript side this API could be used like this:
* ```
* window.webxdc.replaceWebxdc(blob);
* ```
*
* @memberof dc_context_t
* @param context The context object.
* @param msg_id The ID of the WebXDC message to be replaced.
* @param blob New blob to replace WebXDC with.
* @param n Blob size.
*/
void dc_replace_webxdc(dc_context_t* context, uint32_t msg_id, uint8_t *blob, size_t n);
/**
* Save a draft for a chat in the database.
*

View File

@@ -36,7 +36,7 @@ use deltachat::qr_code_generator::{generate_backup_qr, get_securejoin_qr_svg};
use deltachat::reaction::{get_msg_reactions, send_reaction, Reactions};
use deltachat::stock_str::StockMessage;
use deltachat::stock_str::StockStrings;
use deltachat::webxdc::StatusUpdateSerial;
use deltachat::webxdc::{replace_webxdc, StatusUpdateSerial};
use deltachat::*;
use deltachat::{accounts::Accounts, log::LogExt};
use num_traits::{FromPrimitive, ToPrimitive};
@@ -1097,6 +1097,32 @@ pub unsafe extern "C" fn dc_get_webxdc_status_updates(
.strdup()
}
#[no_mangle]
pub unsafe extern "C" fn dc_replace_webxdc(
context: *mut dc_context_t,
msg_id: u32,
blob: *const u8,
n: libc::size_t,
) {
if context.is_null() {
eprintln!("ignoring careless call to dc_replace_webxdc()");
return;
}
let msg_id = MsgId::new(msg_id);
let blob_slice = std::slice::from_raw_parts(blob, n);
let ctx = &*context;
block_on(async move {
replace_webxdc(ctx, msg_id, blob_slice)
.await
.context("Failed to replace WebXDC")
.log_err(ctx)
.ok();
})
}
#[no_mangle]
pub unsafe extern "C" fn dc_set_draft(
context: *mut dc_context_t,
@@ -1539,14 +1565,10 @@ pub unsafe extern "C" fn dc_delete_chat(context: *mut dc_context_t, chat_id: u32
}
let ctx = &*context;
block_on(async move {
ChatId::new(chat_id)
.delete(ctx)
.await
.context("Failed chat delete")
.log_err(ctx)
.ok();
})
block_on(ChatId::new(chat_id).delete(ctx))
.context("Failed chat delete")
.log_err(ctx)
.ok();
}
#[no_mangle]

View File

@@ -26,6 +26,7 @@ use serde::{Deserialize, Serialize};
use serde_json::Value;
use tokio::io::AsyncReadExt;
use crate::blob::BlobObject;
use crate::chat::Chat;
use crate::constants::Chattype;
use crate::contact::ContactId;
@@ -845,6 +846,35 @@ impl Message {
}
}
/// Replaces WebXDC blob of existing message.
///
/// This API is supposed to be called from within a WebXDC to replace itself
/// e.g. with an updated or persistently reconfigured version.
pub async fn replace_webxdc(context: &Context, msg_id: MsgId, data: &[u8]) -> Result<()> {
let mut msg = Message::load_from_db(context, msg_id).await?;
ensure!(
msg.get_viewtype() == Viewtype::Webxdc,
"Message {msg_id} is not a WebXDC instance"
);
let blob = BlobObject::create(
context,
&msg.get_filename()
.context("Cannot get filename of exising WebXDC instance")?,
data,
)
.await
.context("Failed to create WebXDC replacement blob")?;
let mut param = msg.param.clone();
param.set(Param::File, blob.as_name());
msg.param = param;
msg.update_param(context).await?;
Ok(())
}
#[cfg(test)]
mod tests {
use serde_json::json;
@@ -2623,4 +2653,62 @@ sth_for_the = "future""#
Ok(())
}
/// Tests replacing WebXDC with a newer version.
///
/// Updates should be preserved after upgrading.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_replace_webxdc() -> Result<()> {
let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await;
// Alice sends WebXDC instance.
let alice_chat = alice.create_chat(&bob).await;
let mut alice_instance = create_webxdc_instance(
&alice,
"minimal.xdc",
include_bytes!("../test-data/webxdc/minimal.xdc"),
)
.await?;
alice_instance.set_text("user added text".to_string());
send_msg(&alice, alice_chat.id, &mut alice_instance).await?;
let alice_instance = alice.get_last_msg().await;
assert_eq!(alice_instance.get_text(), "user added text");
// Bob receives that instance.
let alice_sent_instance = alice.pop_sent_msg().await;
let bob_received_instance = bob.recv_msg(&alice_sent_instance).await;
assert_eq!(bob_received_instance.get_text(), "user added text");
// Alice sends WebXDC update.
alice
.send_webxdc_status_update(alice_instance.id, r#"{"payload": 1}"#, "Alice update")
.await?;
alice.flush_status_updates().await?;
let alice_sent_update = alice.pop_sent_msg().await;
bob.recv_msg(&alice_sent_update).await;
assert_eq!(
bob.get_webxdc_status_updates(bob_received_instance.id, StatusUpdateSerial(0))
.await?,
r#"[{"payload":1,"serial":1,"max_serial":1}]"#
);
// Bob replaces WebXDC.
replace_webxdc(
&bob,
bob_received_instance.id,
include_bytes!("../test-data/webxdc/with-minimal-manifest.xdc"),
)
.await
.context("Failed to replace WebXDC")?;
// Updates are not modified.
assert_eq!(
bob.get_webxdc_status_updates(bob_received_instance.id, StatusUpdateSerial(0))
.await?,
r#"[{"payload":1,"serial":1,"max_serial":1}]"#
);
Ok(())
}
}