rename w30 to webxdc

This commit is contained in:
B. Petersen
2022-01-01 15:30:16 +01:00
committed by bjoern
parent 7ff25f282e
commit 8e8c10c438
20 changed files with 222 additions and 220 deletions

View File

@@ -983,39 +983,39 @@ uint32_t dc_send_videochat_invitation (dc_context_t* context, uint32_t chat_id);
/** /**
* An w30 instance send a status update to its other members. * An webxdc instance send a status update to its other members.
* *
* In js-land, that would be mapped to sth. as: * In js-land, that would be mapped to sth. as:
* ``` * ```
* success = window.webxdc.sendUpdate('move A3 B4', '{"action":"move","src":"A3","dest":"B4"}'); * success = window.webxdc.sendUpdate('move A3 B4', '{"action":"move","src":"A3","dest":"B4"}');
* ``` * ```
* `context` and `msg_id` is not needed in js as that is unique within an w30 instance. * `context` and `msg_id` is not needed in js as that is unique within an webxdc instance.
* See dc_get_w30_status_updates() for the receiving counterpart. * See dc_get_webxdc_status_updates() for the receiving counterpart.
* *
* If the w30 instance is a draft, the update is not send immediately. * If the webxdc instance is a draft, the update is not send immediately.
* Instead, the updates are collected and sent out in batch when the instance is actually sent. * Instead, the updates are collected and sent out in batch when the instance is actually sent.
* This allows preparing w30 instances, * This allows preparing webxdc instances,
* eg. defining a poll with predefined answers. * eg. defining a poll with predefined answers.
* *
* Other members will be informed by #DC_EVENT_W30_STATUS_UPDATE that there is a new update. * Other members will be informed by #DC_EVENT_WEBXDC_STATUS_UPDATE that there is a new update.
* You will also get the #DC_EVENT_W30_STATUS_UPDATE yourself * You will also get the #DC_EVENT_WEBXDC_STATUS_UPDATE yourself
* and the update you're sent will also be included in dc_get_w30_status_updates(). * and the update you're sent will also be included in dc_get_webxdc_status_updates().
* *
* @memberof dc_context_t * @memberof dc_context_t
* @param context The context object * @param context The context object
* @param msg_id id of the message with the w30 instance * @param msg_id id of the message with the webxdc instance
* @param descr user-visible description of the json-data, * @param descr user-visible description of the json-data,
* in case of a chess game, eg. the move. * in case of a chess game, eg. the move.
* @param json program-readable data, the actual payload * @param json program-readable data, the actual payload
* @return 1=success, 0=error * @return 1=success, 0=error
*/ */
int dc_send_w30_status_update (dc_context_t* context, uint32_t msg_id, const char* descr, const char* json); int dc_send_webxdc_status_update (dc_context_t* context, uint32_t msg_id, const char* descr, const char* json);
/** /**
* Get status updates. * Get status updates.
* The status updates may be sent by yourself or by other members using dc_send_w30_status_update(). * The status updates may be sent by yourself or by other members using dc_send_webxdc_status_update().
* In both cases, you will be informed by #DC_EVENT_W30_STATUS_UPDATE * In both cases, you will be informed by #DC_EVENT_WEBXDC_STATUS_UPDATE
* whenever there is a new update. * whenever there is a new update.
* *
* In js-land, that would be mapped to sth. as: * In js-land, that would be mapped to sth. as:
@@ -1030,15 +1030,15 @@ int dc_send_w30_status_update (dc_context_t* context, uint32_t msg_id, const cha
* *
* @memberof dc_context_t * @memberof dc_context_t
* @param context The context object * @param context The context object
* @param msg_id id of the message with the w30 instance * @param msg_id id of the message with the webxdc instance
* @param status_update_id Can be used to filter out only a concrete status update. * @param status_update_id Can be used to filter out only a concrete status update.
* When set to 0, all known status updates are returned. * When set to 0, all known status updates are returned.
* @return JSON-array containing the requested updates, * @return JSON-array containing the requested updates,
* each element was created by dc_send_w30_status_update() * each element was created by dc_send_webxdc_status_update()
* on this or other devices. * on this or other devices.
* If there are no updates, an empty JSON-array is returned. * If there are no updates, an empty JSON-array is returned.
*/ */
char* dc_get_w30_status_updates (dc_context_t* context, uint32_t msg_id, uint32_t status_update_id); char* dc_get_webxdc_status_updates (dc_context_t* context, uint32_t msg_id, uint32_t status_update_id);
/** /**
* Save a draft for a chat in the database. * Save a draft for a chat in the database.
@@ -3676,17 +3676,17 @@ char* dc_msg_get_filemime (const dc_msg_t* msg);
/** /**
* Return file from inside an archive. * Return file from inside an webxdc message.
* Currently, this works for W30 messages only.
* *
* @param msg The W30 instance. * @memberof dc_msg_t
* @param msg The webxdc instance.
* @param filename The name inside the archive, * @param filename The name inside the archive,
* must be given as a relative path (no leading `/`). * must be given as a relative path (no leading `/`).
* @param ret_bytes Pointer to a size_t. The size of the blob will be written here. * @param ret_bytes Pointer to a size_t. The size of the blob will be written here.
* @return The blob must be released using dc_str_unref() after usage. * @return The blob must be released using dc_str_unref() after usage.
* NULL if there is no such file in the archive or on errors. * NULL if there is no such file in the archive or on errors.
*/ */
char* dc_msg_get_blob_from_archive (const dc_msg_t* msg, const char* filename, size_t* ret_bytes); char* dc_msg_get_webxdc_blob (const dc_msg_t* msg, const char* filename, size_t* ret_bytes);
/** /**
@@ -4848,12 +4848,12 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
/** /**
* w30-Message. * webxdc-Message.
* Message with HTML5, CSS and related content. * Message with HTML5, CSS and related content.
* *
* To send data to a w30 instance, use dc_send_w30_status_update() * To send data to a webxdc instance, use dc_send_webxdc_status_update()
*/ */
#define DC_MSG_W30 80 #define DC_MSG_WEBXDC 80
/** /**
@@ -5550,18 +5550,18 @@ void dc_event_unref(dc_event_t* event);
/** /**
* w30 status update received. * webxdc status update received.
* To get the received status update, use dc_get_w30_status_updates(). * To get the received status update, use dc_get_webxdc_status_updates().
* To send status updates, use dc_send_w30_status_update(). * To send status updates, use dc_send_webxdc_status_update().
* *
* Note, that you do not get events that arrive when the app is not running; * Note, that you do not get events that arrive when the app is not running;
* instead, you can use dc_get_w30_status_updates() to get all status updates * instead, you can use dc_get_webxdc_status_updates() to get all status updates
* and catch up that way. * and catch up that way.
* *
* @param data1 (int) msg_id * @param data1 (int) msg_id
* @param data2 (int) status_update_id * @param data2 (int) status_update_id
*/ */
#define DC_EVENT_W30_STATUS_UPDATE 2120 #define DC_EVENT_WEBXDC_STATUS_UPDATE 2120
/** /**

View File

@@ -37,7 +37,7 @@ use deltachat::ephemeral::Timer as EphemeralTimer;
use deltachat::key::DcKey; use deltachat::key::DcKey;
use deltachat::message::MsgId; use deltachat::message::MsgId;
use deltachat::stock_str::StockMessage; use deltachat::stock_str::StockMessage;
use deltachat::w30::StatusUpdateId; use deltachat::webxdc::StatusUpdateId;
use deltachat::*; use deltachat::*;
use deltachat::{accounts::Accounts, log::LogExt}; use deltachat::{accounts::Accounts, log::LogExt};
@@ -501,7 +501,7 @@ pub unsafe extern "C" fn dc_event_get_data1_int(event: *mut dc_event_t) -> libc:
EventType::ImexFileWritten(_) => 0, EventType::ImexFileWritten(_) => 0,
EventType::SecurejoinInviterProgress { contact_id, .. } EventType::SecurejoinInviterProgress { contact_id, .. }
| EventType::SecurejoinJoinerProgress { contact_id, .. } => *contact_id as libc::c_int, | EventType::SecurejoinJoinerProgress { contact_id, .. } => *contact_id as libc::c_int,
EventType::W30StatusUpdate { msg_id, .. } => msg_id.to_u32() as libc::c_int, EventType::WebxdcStatusUpdate { msg_id, .. } => msg_id.to_u32() as libc::c_int,
} }
} }
@@ -543,7 +543,7 @@ pub unsafe extern "C" fn dc_event_get_data2_int(event: *mut dc_event_t) -> libc:
EventType::SecurejoinInviterProgress { progress, .. } EventType::SecurejoinInviterProgress { progress, .. }
| EventType::SecurejoinJoinerProgress { progress, .. } => *progress as libc::c_int, | EventType::SecurejoinJoinerProgress { progress, .. } => *progress as libc::c_int,
EventType::ChatEphemeralTimerModified { timer, .. } => timer.to_u32() as libc::c_int, EventType::ChatEphemeralTimerModified { timer, .. } => timer.to_u32() as libc::c_int,
EventType::W30StatusUpdate { EventType::WebxdcStatusUpdate {
status_update_id, .. status_update_id, ..
} => status_update_id.to_u32() as libc::c_int, } => status_update_id.to_u32() as libc::c_int,
} }
@@ -587,7 +587,7 @@ pub unsafe extern "C" fn dc_event_get_data2_str(event: *mut dc_event_t) -> *mut
| EventType::SecurejoinJoinerProgress { .. } | EventType::SecurejoinJoinerProgress { .. }
| EventType::ConnectivityChanged | EventType::ConnectivityChanged
| EventType::SelfavatarChanged | EventType::SelfavatarChanged
| EventType::W30StatusUpdate { .. } | EventType::WebxdcStatusUpdate { .. }
| EventType::ChatEphemeralTimerModified { .. } => ptr::null_mut(), | EventType::ChatEphemeralTimerModified { .. } => ptr::null_mut(),
EventType::ConfigureProgress { comment, .. } => { EventType::ConfigureProgress { comment, .. } => {
if let Some(comment) = comment { if let Some(comment) = comment {
@@ -878,40 +878,40 @@ pub unsafe extern "C" fn dc_send_videochat_invitation(
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn dc_send_w30_status_update( pub unsafe extern "C" fn dc_send_webxdc_status_update(
context: *mut dc_context_t, context: *mut dc_context_t,
msg_id: u32, msg_id: u32,
descr: *const libc::c_char, descr: *const libc::c_char,
json: *const libc::c_char, json: *const libc::c_char,
) -> libc::c_int { ) -> libc::c_int {
if context.is_null() { if context.is_null() {
eprintln!("ignoring careless call to dc_send_w30_status_update()"); eprintln!("ignoring careless call to dc_send_webxdc_status_update()");
return 0; return 0;
} }
let ctx = &*context; let ctx = &*context;
block_on(ctx.send_w30_status_update( block_on(ctx.send_webxdc_status_update(
MsgId::new(msg_id), MsgId::new(msg_id),
&to_string_lossy(descr), &to_string_lossy(descr),
&to_string_lossy(json), &to_string_lossy(json),
)) ))
.log_err(ctx, "Failed to send w30 update") .log_err(ctx, "Failed to send webxdc update")
.is_ok() as libc::c_int .is_ok() as libc::c_int
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn dc_get_w30_status_updates( pub unsafe extern "C" fn dc_get_webxdc_status_updates(
context: *mut dc_context_t, context: *mut dc_context_t,
msg_id: u32, msg_id: u32,
status_update_id: u32, status_update_id: u32,
) -> *mut libc::c_char { ) -> *mut libc::c_char {
if context.is_null() { if context.is_null() {
eprintln!("ignoring careless call to dc_get_w30_status_updates()"); eprintln!("ignoring careless call to dc_get_webxdc_status_updates()");
return "".strdup(); return "".strdup();
} }
let ctx = &*context; let ctx = &*context;
block_on(ctx.get_w30_status_updates( block_on(ctx.get_webxdc_status_updates(
MsgId::new(msg_id), MsgId::new(msg_id),
if status_update_id == 0 { if status_update_id == 0 {
None None
@@ -3071,13 +3071,13 @@ pub unsafe extern "C" fn dc_msg_get_filename(msg: *mut dc_msg_t) -> *mut libc::c
} }
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn dc_msg_get_blob_from_archive( pub unsafe extern "C" fn dc_msg_get_webxdc_blob(
msg: *mut dc_msg_t, msg: *mut dc_msg_t,
filename: *const libc::c_char, filename: *const libc::c_char,
ret_bytes: *mut libc::size_t, ret_bytes: *mut libc::size_t,
) -> *mut libc::c_char { ) -> *mut libc::c_char {
if msg.is_null() || filename.is_null() || ret_bytes.is_null() { if msg.is_null() || filename.is_null() || ret_bytes.is_null() {
eprintln!("ignoring careless call to dc_msg_get_blob_from_archive()"); eprintln!("ignoring careless call to dc_msg_get_webxdc_blob()");
return ptr::null_mut(); return ptr::null_mut();
} }
let ffi_msg = &*msg; let ffi_msg = &*msg;
@@ -3085,7 +3085,7 @@ pub unsafe extern "C" fn dc_msg_get_blob_from_archive(
let blob = block_on(async move { let blob = block_on(async move {
ffi_msg ffi_msg
.message .message
.get_blob_from_archive(ctx, &to_string_lossy(filename)) .get_webxdc_blob(ctx, &to_string_lossy(filename))
.await .await
}); });
match blob { match blob {

View File

@@ -1,33 +1,33 @@
# W30 User Guide # Webxdc User Guide
## W30 File Format ## Webxdc File Format
- a **W30** app is a **ZIP-file** with the extension `.w30` - a **Webxdc app** is a **ZIP-file** with the extension `.xdc`
- the ZIP-file must contain at least the file `index.html` - the ZIP-file must contain at least the file `index.html`
- if the W30 app is started, `index.html` is opened in a restricted webview - if the Webxdc app is started, `index.html` is opened in a restricted webview
that allow accessing resources only from the ZIP-file that allow accessing resources only from the ZIP-file
## W30 API ## Webxdc API
W30 apps can do anything that is allowed in webviews - Webxdc apps can do anything that is allowed in webviews -
html, css, images, canvas, javascript and so on. html, css, images, canvas, javascript and so on.
There are some additional APIs available once `deltachat.js` is included There are some additional APIs available once `webxdc.js` is included
(the file will be provided by the concrete implementations, (the file will be provided by the concrete implementations,
no need to add `deltachat.js` to your ZIP-file): no need to add `webxdc.js` to your ZIP-file):
``` ```
<script src="deltachat.js></script> <script src="webxdc.js></script>
``` ```
### sendUpdate() ### sendUpdate()
``` ```
window.deltachat.sendUpdate(descr, payload); window.webxdc.sendUpdate(descr, payload);
``` ```
W30 apps are usually shared in a chat and run independently on each peer. Webxdc apps are usually shared in a chat and run independently on each peer.
To get a shared state, the peers use `sendUpdate()` to send updates to each other. To get a shared state, the peers use `sendUpdate()` to send updates to each other.
- `descr`: short, human-readable description what this update is about. - `descr`: short, human-readable description what this update is about.
@@ -40,7 +40,7 @@ will receive the update by the callback given to `setUpdateListener()`.
### setUpdateListener() ### setUpdateListener()
``` ```
window.deltachat.setUpdateListener((update) => {}); window.webxdc.setUpdateListener((update) => {});
``` ```
With `setUpdateListener()` you define a callback that receives the updates With `setUpdateListener()` you define a callback that receives the updates
@@ -55,21 +55,21 @@ The callback is called for updates sent by you or other peers.
### getAllUpdates() ### getAllUpdates()
``` ```
payloads = window.deltachat.getAllUpdates() payloads = window.webxdc.getAllUpdates()
``` ```
In case your W30 was just started, In case your Webxdc was just started,
you may want to reconstruct the state from the last run - you may want to reconstruct the state from the last run -
and also incorporate updates that may have arrived while the app was not running. and also incorporate updates that may have arrived while the app was not running.
- `payloads`: the function returns all previous updates in an array, - `payloads`: the function returns all previous updates in an array,
eg. `[{payload: "foo"},{payload: "bar"}]` eg. `[{payload: "foo"},{payload: "bar"}]`
if `deltachat.sendUpdate("foo"); deltachat.sendUpdate("bar");` was called on the last run. if `webxdc.sendUpdate("foo"); webxdc.sendUpdate("bar");` was called on the last run.
### selfAddr() ### selfAddr()
``` ```
addr = window.deltachat.selfAddr() addr = window.webxdc.selfAddr()
``` ```
Returns the peer's own address. Returns the peer's own address.
@@ -78,7 +78,7 @@ just send the address along with the payload,
and, if needed, compare the payload addresses against selfAddr() later on. and, if needed, compare the payload addresses against selfAddr() later on.
## W30 Example ## Webxdc Example
The following example shows an input field and every input is show on all peers. The following example shows an input field and every input is show on all peers.
@@ -87,7 +87,7 @@ The following example shows an input field and every input is show on all peers
<html> <html>
<head> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<script src="deltachat.js"></script> <script src="webxdc.js"></script>
</head> </head>
<body> <body>
<input id="input" type="text"/> <input id="input" type="text"/>
@@ -97,21 +97,21 @@ The following example shows an input field and every input is show on all peers
function sendMsg() { function sendMsg() {
msg = document.getElementById("input").value; msg = document.getElementById("input").value;
window.deltachat.sendUpdate('Someone typed "'+msg+'".', msg); window.webxdc.sendUpdate('Someone typed "'+msg+'".', msg);
} }
function receiveUpdate(update) { function receiveUpdate(update) {
document.getElementById('output').innerHTML += update.payload + "<br>"; document.getElementById('output').innerHTML += update.payload + "<br>";
} }
window.deltachat.setUpdateListener(receiveUpdate); window.webxdc.setUpdateListener(receiveUpdate);
</script> </script>
</body> </body>
</html> </html>
``` ```
For a more advanved example, see https://github.com/r10s/w30-poll/ . For a more advanved example, see https://github.com/r10s/webxdc-poll/ .
## Closing Remarks ## Closing Remarks

View File

@@ -387,7 +387,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
sendfile <file> [<text>]\n\ sendfile <file> [<text>]\n\
sendhtml <file for html-part> [<text for plain-part>]\n\ sendhtml <file for html-part> [<text for plain-part>]\n\
sendsyncmsg\n\ sendsyncmsg\n\
sendw30 <msg-id> <json status update>\n\ sendupdate <msg-id> <json status update>\n\
videochat\n\ videochat\n\
draft [<text>]\n\ draft [<text>]\n\
devicemsg <text>\n\ devicemsg <text>\n\
@@ -908,14 +908,14 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
Some(msg_id) => println!("sync message sent as {}.", msg_id), Some(msg_id) => println!("sync message sent as {}.", msg_id),
None => println!("sync message not needed."), None => println!("sync message not needed."),
}, },
"sendw30" => { "sendupdate" => {
ensure!( ensure!(
!arg1.is_empty() && !arg2.is_empty(), !arg1.is_empty() && !arg2.is_empty(),
"Arguments <msg-id> <json status update> expected" "Arguments <msg-id> <json status update> expected"
); );
let msg_id = MsgId::new(arg1.parse()?); let msg_id = MsgId::new(arg1.parse()?);
context context
.send_w30_status_update(msg_id, "this is a w30 status update", arg2) .send_webxdc_status_update(msg_id, "this is a webxdc status update", arg2)
.await?; .await?;
} }
"videochat" => { "videochat" => {

View File

@@ -191,7 +191,7 @@ const CHAT_COMMANDS: [&str; 36] = [
"sendfile", "sendfile",
"sendhtml", "sendhtml",
"sendsyncmsg", "sendsyncmsg",
"sendw30", "sendupdate",
"videochat", "videochat",
"draft", "draft",
"listmedia", "listmedia",

View File

@@ -37,7 +37,7 @@ use crate::mimeparser::SystemMessage;
use crate::param::{Param, Params}; use crate::param::{Param, Params};
use crate::peerstate::{Peerstate, PeerstateVerifiedStatus}; use crate::peerstate::{Peerstate, PeerstateVerifiedStatus};
use crate::stock_str; use crate::stock_str;
use crate::w30::W30_SUFFIX; use crate::webxdc::WEBXDC_SUFFIX;
/// An chat item, such as a message or a marker. /// An chat item, such as a message or a marker.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@@ -646,8 +646,8 @@ impl ChatId {
.await? .await?
.context("no file stored in params")?; .context("no file stored in params")?;
msg.param.set(Param::File, blob.as_name()); msg.param.set(Param::File, blob.as_name());
if blob.suffix() == Some(W30_SUFFIX) { if blob.suffix() == Some(WEBXDC_SUFFIX) {
msg.viewtype = Viewtype::W30; msg.viewtype = Viewtype::Webxdc;
} }
} }
} }
@@ -1800,7 +1800,7 @@ pub(crate) fn msgtype_has_file(msgtype: Viewtype) -> bool {
Viewtype::Video => true, Viewtype::Video => true,
Viewtype::File => true, Viewtype::File => true,
Viewtype::VideochatInvitation => false, Viewtype::VideochatInvitation => false,
Viewtype::W30 => true, Viewtype::Webxdc => true,
} }
} }
@@ -1842,8 +1842,12 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> {
} }
} }
if msg.viewtype == Viewtype::W30 && blob.suffix() != Some(W30_SUFFIX) { if msg.viewtype == Viewtype::Webxdc && blob.suffix() != Some(WEBXDC_SUFFIX) {
bail!("w30 message {} does not have suffix {}", blob, W30_SUFFIX); bail!(
"webxdc message {} does not have suffix {}",
blob,
WEBXDC_SUFFIX
);
} }
info!( info!(

View File

@@ -296,8 +296,8 @@ pub enum Viewtype {
/// Message is an invitation to a videochat. /// Message is an invitation to a videochat.
VideochatInvitation = 70, VideochatInvitation = 70,
/// Message is an w30 object. /// Message is an webxdc instance.
W30 = 80, Webxdc = 80,
} }
impl Default for Viewtype { impl Default for Viewtype {
@@ -342,7 +342,7 @@ mod tests {
Viewtype::VideochatInvitation, Viewtype::VideochatInvitation,
Viewtype::from_i32(70).unwrap() Viewtype::from_i32(70).unwrap()
); );
assert_eq!(Viewtype::W30, Viewtype::from_i32(80).unwrap()); assert_eq!(Viewtype::Webxdc, Viewtype::from_i32(80).unwrap());
} }
#[test] #[test]

View File

@@ -258,7 +258,7 @@ pub(crate) async fn dc_receive_imf_inner(
} }
} }
if let Some(ref status_update) = mime_parser.w30_status_update { if let Some(ref status_update) = mime_parser.webxdc_status_update {
if let Err(err) = context if let Err(err) = context
.receive_status_update(insert_msg_id, status_update) .receive_status_update(insert_msg_id, status_update)
.await .await
@@ -869,7 +869,7 @@ async fn add_parts(
info!(context, "Existing non-decipherable message. (TRASH)"); info!(context, "Existing non-decipherable message. (TRASH)");
} }
if mime_parser.w30_status_update.is_some() && mime_parser.parts.len() == 1 { if mime_parser.webxdc_status_update.is_some() && mime_parser.parts.len() == 1 {
if let Some(part) = mime_parser.parts.first() { if let Some(part) = mime_parser.parts.first() {
if part.typ == Viewtype::Text && part.msg.is_empty() { if part.typ == Viewtype::Text && part.msg.is_empty() {
chat_id = Some(DC_CHAT_ID_TRASH); chat_id = Some(DC_CHAT_ID_TRASH);

View File

@@ -9,7 +9,7 @@ use strum::EnumProperty;
use crate::chat::ChatId; use crate::chat::ChatId;
use crate::ephemeral::Timer as EphemeralTimer; use crate::ephemeral::Timer as EphemeralTimer;
use crate::message::MsgId; use crate::message::MsgId;
use crate::w30::StatusUpdateId; use crate::webxdc::StatusUpdateId;
#[derive(Debug)] #[derive(Debug)]
pub struct Events { pub struct Events {
@@ -329,7 +329,7 @@ pub enum EventType {
SelfavatarChanged, SelfavatarChanged,
#[strum(props(id = "2120"))] #[strum(props(id = "2120"))]
W30StatusUpdate { WebxdcStatusUpdate {
msg_id: MsgId, msg_id: MsgId,
status_update_id: StatusUpdateId, status_update_id: StatusUpdateId,
}, },

View File

@@ -87,7 +87,7 @@ pub mod stock_str;
mod sync; mod sync;
mod token; mod token;
mod update_helper; mod update_helper;
pub mod w30; pub mod webxdc;
#[macro_use] #[macro_use]
mod dehtml; mod dehtml;
mod color; mod color;

View File

@@ -1168,12 +1168,12 @@ pub fn guess_msgtype_from_suffix(path: &Path) -> Option<(Viewtype, &str)> {
"ttf" => (Viewtype::File, "font/ttf"), "ttf" => (Viewtype::File, "font/ttf"),
"vcard" => (Viewtype::File, "text/vcard"), "vcard" => (Viewtype::File, "text/vcard"),
"vcf" => (Viewtype::File, "text/vcard"), "vcf" => (Viewtype::File, "text/vcard"),
"w30" => (Viewtype::W30, "application/w30+zip"),
"wav" => (Viewtype::File, "audio/wav"), "wav" => (Viewtype::File, "audio/wav"),
"weba" => (Viewtype::File, "audio/webm"), "weba" => (Viewtype::File, "audio/webm"),
"webm" => (Viewtype::Video, "video/webm"), "webm" => (Viewtype::Video, "video/webm"),
"webp" => (Viewtype::Image, "image/webp"), // iOS via SDWebImage, Android since 4.0 "webp" => (Viewtype::Image, "image/webp"), // iOS via SDWebImage, Android since 4.0
"wmv" => (Viewtype::Video, "video/x-ms-wmv"), "wmv" => (Viewtype::Video, "video/x-ms-wmv"),
"xdc" => (Viewtype::Webxdc, "application/webxdc+zip"),
"xhtml" => (Viewtype::File, "application/xhtml+xml"), "xhtml" => (Viewtype::File, "application/xhtml+xml"),
"xlsx" => ( "xlsx" => (
Viewtype::File, Viewtype::File,
@@ -1704,8 +1704,8 @@ mod tests {
Some((Viewtype::File, "text/html")) Some((Viewtype::File, "text/html"))
); );
assert_eq!( assert_eq!(
guess_msgtype_from_suffix(Path::new("foo/file.w30")), guess_msgtype_from_suffix(Path::new("foo/file.xdc")),
Some((Viewtype::W30, "application/w30+zip")) Some((Viewtype::Webxdc, "application/webxdc+zip"))
); );
} }

View File

@@ -641,7 +641,7 @@ impl<'a> MimeFactory<'a> {
"Content-Type".to_string(), "Content-Type".to_string(),
"multipart/report; report-type=multi-device-sync".to_string(), "multipart/report; report-type=multi-device-sync".to_string(),
)) ))
} else if self.msg.param.get_cmd() == SystemMessage::W30StatusUpdate { } else if self.msg.param.get_cmd() == SystemMessage::WebxdcStatusUpdate {
PartBuilder::new().header(( PartBuilder::new().header((
"Content-Type".to_string(), "Content-Type".to_string(),
"multipart/report; report-type=status-update".to_string(), "multipart/report; report-type=status-update".to_string(),
@@ -922,7 +922,7 @@ impl<'a> MimeFactory<'a> {
} }
SystemMessage::LocationOnly SystemMessage::LocationOnly
| SystemMessage::MultiDeviceSync | SystemMessage::MultiDeviceSync
| SystemMessage::W30StatusUpdate => { | SystemMessage::WebxdcStatusUpdate => {
// This should prevent automatic replies, // This should prevent automatic replies,
// such as non-delivery reports. // such as non-delivery reports.
// //
@@ -1159,11 +1159,11 @@ impl<'a> MimeFactory<'a> {
let ids = self.msg.param.get(Param::Arg2).unwrap_or_default(); let ids = self.msg.param.get(Param::Arg2).unwrap_or_default();
parts.push(context.build_sync_part(json.to_string()).await); parts.push(context.build_sync_part(json.to_string()).await);
self.sync_ids_to_delete = Some(ids.to_string()); self.sync_ids_to_delete = Some(ids.to_string());
} else if command == SystemMessage::W30StatusUpdate { } else if command == SystemMessage::WebxdcStatusUpdate {
let json = self.msg.param.get(Param::Arg).unwrap_or_default(); let json = self.msg.param.get(Param::Arg).unwrap_or_default();
parts.push(context.build_status_update_part(json).await); parts.push(context.build_status_update_part(json).await);
} else if self.msg.viewtype == Viewtype::W30 { } else if self.msg.viewtype == Viewtype::Webxdc {
let json = context.get_w30_status_updates(self.msg.id, None).await?; let json = context.get_webxdc_status_updates(self.msg.id, None).await?;
if json != "[]" { if json != "[]" {
parts.push(context.build_status_update_part(&json).await); parts.push(context.build_status_update_part(&json).await);
} }

View File

@@ -66,7 +66,7 @@ pub struct MimeMessage {
pub location_kml: Option<location::Kml>, pub location_kml: Option<location::Kml>,
pub message_kml: Option<location::Kml>, pub message_kml: Option<location::Kml>,
pub(crate) sync_items: Option<SyncItems>, pub(crate) sync_items: Option<SyncItems>,
pub(crate) w30_status_update: Option<String>, pub(crate) webxdc_status_update: Option<String>,
pub(crate) user_avatar: Option<AvatarAction>, pub(crate) user_avatar: Option<AvatarAction>,
pub(crate) group_avatar: Option<AvatarAction>, pub(crate) group_avatar: Option<AvatarAction>,
pub(crate) mdn_reports: Vec<Report>, pub(crate) mdn_reports: Vec<Report>,
@@ -138,8 +138,8 @@ pub enum SystemMessage {
MultiDeviceSync = 20, MultiDeviceSync = 20,
// Sync message that contains a json payload // Sync message that contains a json payload
// sent to the other w30 instances // sent to the other webxdc instances
W30StatusUpdate = 30, WebxdcStatusUpdate = 30,
} }
impl Default for SystemMessage { impl Default for SystemMessage {
@@ -301,7 +301,7 @@ impl MimeMessage {
location_kml: None, location_kml: None,
message_kml: None, message_kml: None,
sync_items: None, sync_items: None,
w30_status_update: None, webxdc_status_update: None,
user_avatar: None, user_avatar: None,
group_avatar: None, group_avatar: None,
failure_report: None, failure_report: None,
@@ -544,7 +544,7 @@ impl MimeMessage {
}; };
if let Some(ref subject) = self.get_subject() { if let Some(ref subject) = self.get_subject() {
if !self.has_chat_version() && self.w30_status_update.is_none() { if !self.has_chat_version() && self.webxdc_status_update.is_none() {
part.msg = subject.to_string(); part.msg = subject.to_string();
} }
} }
@@ -1019,11 +1019,11 @@ impl MimeMessage {
return; return;
} }
let msg_type = if context let msg_type = if context
.is_w30_file(filename, decoded_data) .is_webxdc_file(filename, decoded_data)
.await .await
.unwrap_or(false) .unwrap_or(false)
{ {
Viewtype::W30 Viewtype::Webxdc
} else if filename.ends_with(".kml") { } else if filename.ends_with(".kml") {
// XXX what if somebody sends eg an "location-highlights.kml" // XXX what if somebody sends eg an "location-highlights.kml"
// attachment unrelated to location streaming? // attachment unrelated to location streaming?
@@ -1057,7 +1057,7 @@ impl MimeMessage {
let serialized = String::from_utf8_lossy(decoded_data) let serialized = String::from_utf8_lossy(decoded_data)
.parse() .parse()
.unwrap_or_default(); .unwrap_or_default();
self.w30_status_update = Some(serialized); self.webxdc_status_update = Some(serialized);
return; return;
} else { } else {
msg_type msg_type

View File

@@ -137,9 +137,9 @@ impl Message {
append_text = false; append_text = false;
stock_str::videochat_invitation(context).await stock_str::videochat_invitation(context).await
} }
Viewtype::W30 => { Viewtype::Webxdc => {
append_text = true; append_text = true;
"W30".to_string() "Webxdc".to_string()
} }
Viewtype::Text | Viewtype::Unknown => { Viewtype::Text | Viewtype::Unknown => {
if self.param.get_cmd() != SystemMessage::LocationOnly { if self.param.get_cmd() != SystemMessage::LocationOnly {

View File

@@ -1,4 +1,4 @@
//! # Handle W30 messages. //! # Handle webxdc messages.
use crate::constants::Viewtype; use crate::constants::Viewtype;
use crate::context::Context; use crate::context::Context;
@@ -15,7 +15,7 @@ use serde_json::Value;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::io::Read; use std::io::Read;
pub const W30_SUFFIX: &str = "w30"; pub const WEBXDC_SUFFIX: &str = "xdc";
/// Status Update ID. /// Status Update ID.
#[derive( #[derive(
@@ -51,8 +51,8 @@ pub(crate) struct StatusUpdateItem {
} }
impl Context { impl Context {
pub(crate) async fn is_w30_file(&self, filename: &str, buf: &[u8]) -> Result<bool> { pub(crate) async fn is_webxdc_file(&self, filename: &str, buf: &[u8]) -> Result<bool> {
if filename.ends_with(W30_SUFFIX) { if filename.ends_with(WEBXDC_SUFFIX) {
let reader = std::io::Cursor::new(buf); let reader = std::io::Cursor::new(buf);
if let Ok(mut archive) = zip::ZipArchive::new(reader) { if let Ok(mut archive) = zip::ZipArchive::new(reader) {
if let Ok(_index_html) = archive.by_name("index.html") { if let Ok(_index_html) = archive.by_name("index.html") {
@@ -85,22 +85,22 @@ impl Context {
Ok(StatusUpdateId(u32::try_from(rowid)?)) Ok(StatusUpdateId(u32::try_from(rowid)?))
} }
/// Sends a status update for an w30 instance. /// Sends a status update for an webxdc instance.
/// ///
/// If the instance is a draft, /// If the instance is a draft,
/// the status update is sent once the instance is actually sent. /// the status update is sent once the instance is actually sent.
/// ///
/// If an update is sent immediately, the message-id of the update-message is returned, /// If an update is sent immediately, the message-id of the update-message is returned,
/// this update-message is visible in chats, however, the id may be useful. /// this update-message is visible in chats, however, the id may be useful.
pub async fn send_w30_status_update( pub async fn send_webxdc_status_update(
&self, &self,
instance_msg_id: MsgId, instance_msg_id: MsgId,
descr: &str, descr: &str,
payload: &str, payload: &str,
) -> Result<Option<MsgId>> { ) -> Result<Option<MsgId>> {
let instance = Message::load_from_db(self, instance_msg_id).await?; let instance = Message::load_from_db(self, instance_msg_id).await?;
if instance.viewtype != Viewtype::W30 { if instance.viewtype != Viewtype::Webxdc {
bail!("send_w30_status_update: is no w30 message"); bail!("send_webxdc_status_update: is no webxdc message");
} }
let status_update_id = self let status_update_id = self
@@ -109,8 +109,7 @@ impl Context {
match instance.state { match instance.state {
MessageState::Undefined | MessageState::OutPreparing | MessageState::OutDraft => { MessageState::Undefined | MessageState::OutPreparing | MessageState::OutDraft => {
// send update once the instance is actually send; // send update once the instance is actually send
// on sending, the updates are retrieved using get_w30_status_updates_with_format() then.
Ok(None) Ok(None)
} }
_ => { _ => {
@@ -123,16 +122,18 @@ impl Context {
hidden: true, hidden: true,
..Default::default() ..Default::default()
}; };
status_update.param.set_cmd(SystemMessage::W30StatusUpdate); status_update
.param
.set_cmd(SystemMessage::WebxdcStatusUpdate);
status_update.param.set( status_update.param.set(
Param::Arg, Param::Arg,
self.get_w30_status_updates(instance_msg_id, Some(status_update_id)) self.get_webxdc_status_updates(instance_msg_id, Some(status_update_id))
.await?, .await?,
); );
status_update.set_quote(self, &instance).await?; status_update.set_quote(self, &instance).await?;
let status_update_msg_id = let status_update_msg_id =
chat::send_msg(self, instance.chat_id, &mut status_update).await?; chat::send_msg(self, instance.chat_id, &mut status_update).await?;
self.emit_event(EventType::W30StatusUpdate { self.emit_event(EventType::WebxdcStatusUpdate {
msg_id: instance_msg_id, msg_id: instance_msg_id,
status_update_id, status_update_id,
}); });
@@ -157,17 +158,17 @@ impl Context {
/// `msg_id` may be an instance (in case there are initial status updates) /// `msg_id` may be an instance (in case there are initial status updates)
/// or a reply to an instance (for all other updates). /// or a reply to an instance (for all other updates).
/// ///
/// `json` is an array containing one or more update items as created by send_w30_status_update(), /// `json` is an array containing one or more update items as created by send_webxdc_status_update(),
/// the array is parsed using serde, the single payloads are used as is. /// the array is parsed using serde, the single payloads are used as is.
pub(crate) async fn receive_status_update(&self, msg_id: MsgId, json: &str) -> Result<()> { pub(crate) async fn receive_status_update(&self, msg_id: MsgId, json: &str) -> Result<()> {
let msg = Message::load_from_db(self, msg_id).await?; let msg = Message::load_from_db(self, msg_id).await?;
let instance = if msg.viewtype == Viewtype::W30 { let instance = if msg.viewtype == Viewtype::Webxdc {
msg msg
} else if let Some(parent) = msg.parent(self).await? { } else if let Some(parent) = msg.parent(self).await? {
if parent.viewtype == Viewtype::W30 { if parent.viewtype == Viewtype::Webxdc {
parent parent
} else { } else {
bail!("receive_status_update: message is not the child of a W30 message.") bail!("receive_status_update: message is not the child of a webxdc message.")
} }
} else { } else {
bail!("receive_status_update: status message has no parent.") bail!("receive_status_update: status message has no parent.")
@@ -181,7 +182,7 @@ impl Context {
&*serde_json::to_string(&update_item.payload)?, &*serde_json::to_string(&update_item.payload)?,
) )
.await?; .await?;
self.emit_event(EventType::W30StatusUpdate { self.emit_event(EventType::WebxdcStatusUpdate {
msg_id: instance.id, msg_id: instance.id,
status_update_id, status_update_id,
}); });
@@ -195,7 +196,7 @@ impl Context {
/// Example: `[{"payload":"any update data"},{"payload":"another update data"}]` /// Example: `[{"payload":"any update data"},{"payload":"another update data"}]`
/// The updates may be filtered by a given status_update_id; /// The updates may be filtered by a given status_update_id;
/// if no updates are available, an empty JSON-array is returned. /// if no updates are available, an empty JSON-array is returned.
pub async fn get_w30_status_updates( pub async fn get_webxdc_status_updates(
&self, &self,
instance_msg_id: MsgId, instance_msg_id: MsgId,
status_update_id: Option<StatusUpdateId>, status_update_id: Option<StatusUpdateId>,
@@ -229,13 +230,13 @@ impl Context {
impl Message { impl Message {
/// Return file form inside an archive. /// Return file form inside an archive.
/// Currently, this works only if the message is an w30 instance. /// Currently, this works only if the message is an webxdc instance.
pub async fn get_blob_from_archive(&self, context: &Context, name: &str) -> Result<Vec<u8>> { pub async fn get_webxdc_blob(&self, context: &Context, name: &str) -> Result<Vec<u8>> {
ensure!(self.viewtype == Viewtype::W30, "No w30 instance."); ensure!(self.viewtype == Viewtype::Webxdc, "No webxdc instance.");
let archive = self let archive = self
.get_file(context) .get_file(context)
.ok_or_else(|| format_err!("No w30 instance file."))?; .ok_or_else(|| format_err!("No webxdc instance file."))?;
let archive = dc_open_file_std(context, archive)?; let archive = dc_open_file_std(context, archive)?;
let mut archive = zip::ZipArchive::new(archive)?; let mut archive = zip::ZipArchive::new(archive)?;
@@ -265,81 +266,81 @@ mod tests {
use async_std::io::WriteExt; use async_std::io::WriteExt;
#[async_std::test] #[async_std::test]
async fn test_is_w30_file() -> Result<()> { async fn test_is_webxdc_file() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
assert!( assert!(
!t.is_w30_file( !t.is_webxdc_file(
"bad-ext-no-zip.txt", "bad-ext-no-zip.txt",
include_bytes!("../test-data/message/issue_523.txt") include_bytes!("../test-data/message/issue_523.txt")
) )
.await? .await?
); );
assert!( assert!(
!t.is_w30_file( !t.is_webxdc_file(
"bad-ext-good-zip.txt", "bad-ext-good-zip.txt",
include_bytes!("../test-data/w30/minimal.w30") include_bytes!("../test-data/webxdc/minimal.xdc")
) )
.await? .await?
); );
assert!( assert!(
!t.is_w30_file( !t.is_webxdc_file(
"good-ext-no-zip.w30", "good-ext-no-zip.xdc",
include_bytes!("../test-data/message/issue_523.txt") include_bytes!("../test-data/message/issue_523.txt")
) )
.await? .await?
); );
assert!( assert!(
!t.is_w30_file( !t.is_webxdc_file(
"good-ext-no-index-html.w30", "good-ext-no-index-html.xdc",
include_bytes!("../test-data/w30/no-index-html.w30") include_bytes!("../test-data/webxdc/no-index-html.xdc")
) )
.await? .await?
); );
assert!( assert!(
t.is_w30_file( t.is_webxdc_file(
"good-ext-good-zip.w30", "good-ext-good-zip.xdc",
include_bytes!("../test-data/w30/minimal.w30") include_bytes!("../test-data/webxdc/minimal.xdc")
) )
.await? .await?
); );
Ok(()) Ok(())
} }
async fn create_w30_instance(t: &TestContext) -> Result<Message> { async fn create_webxdc_instance(t: &TestContext) -> Result<Message> {
let file = t.get_blobdir().join("minimal.w30"); let file = t.get_blobdir().join("minimal.xdc");
File::create(&file) File::create(&file)
.await? .await?
.write_all(include_bytes!("../test-data/w30/minimal.w30")) .write_all(include_bytes!("../test-data/webxdc/minimal.xdc"))
.await?; .await?;
let mut instance = Message::new(Viewtype::File); let mut instance = Message::new(Viewtype::File);
instance.set_file(file.to_str().unwrap(), None); instance.set_file(file.to_str().unwrap(), None);
Ok(instance) Ok(instance)
} }
async fn send_w30_instance(t: &TestContext, chat_id: ChatId) -> Result<Message> { async fn send_webxdc_instance(t: &TestContext, chat_id: ChatId) -> Result<Message> {
let mut instance = create_w30_instance(t).await?; let mut instance = create_webxdc_instance(t).await?;
let instance_msg_id = send_msg(t, chat_id, &mut instance).await?; let instance_msg_id = send_msg(t, chat_id, &mut instance).await?;
Message::load_from_db(t, instance_msg_id).await Message::load_from_db(t, instance_msg_id).await
} }
#[async_std::test] #[async_std::test]
async fn test_send_w30_instance() -> Result<()> { async fn test_send_webxdc_instance() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
// send as .w30 file // send as .xdc file
let instance = send_w30_instance(&t, chat_id).await?; let instance = send_webxdc_instance(&t, chat_id).await?;
assert_eq!(instance.viewtype, Viewtype::W30); assert_eq!(instance.viewtype, Viewtype::Webxdc);
assert_eq!(instance.get_filename(), Some("minimal.w30".to_string())); assert_eq!(instance.get_filename(), Some("minimal.xdc".to_string()));
assert_eq!(instance.chat_id, chat_id); assert_eq!(instance.chat_id, chat_id);
// sending using bad extension is not working, even when setting Viewtype to W30 // sending using bad extension is not working, even when setting Viewtype to webxdc
let file = t.get_blobdir().join("index.html"); let file = t.get_blobdir().join("index.html");
File::create(&file) File::create(&file)
.await? .await?
.write_all("<html>ola!</html>".as_ref()) .write_all("<html>ola!</html>".as_ref())
.await?; .await?;
let mut instance = Message::new(Viewtype::W30); let mut instance = Message::new(Viewtype::Webxdc);
instance.set_file(file.to_str().unwrap(), None); instance.set_file(file.to_str().unwrap(), None);
assert!(send_msg(&t, chat_id, &mut instance).await.is_err()); assert!(send_msg(&t, chat_id, &mut instance).await.is_err());
@@ -347,22 +348,22 @@ mod tests {
} }
#[async_std::test] #[async_std::test]
async fn test_receive_w30_instance() -> Result<()> { async fn test_receive_webxdc_instance() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
dc_receive_imf( dc_receive_imf(
&t, &t,
include_bytes!("../test-data/message/w30_good_extension.eml"), include_bytes!("../test-data/message/webxdc_good_extension.eml"),
"INBOX", "INBOX",
false, false,
) )
.await?; .await?;
let instance = t.get_last_msg().await; let instance = t.get_last_msg().await;
assert_eq!(instance.viewtype, Viewtype::W30); assert_eq!(instance.viewtype, Viewtype::Webxdc);
assert_eq!(instance.get_filename(), Some("minimal.w30".to_string())); assert_eq!(instance.get_filename(), Some("minimal.xdc".to_string()));
dc_receive_imf( dc_receive_imf(
&t, &t,
include_bytes!("../test-data/message/w30_bad_extension.eml"), include_bytes!("../test-data/message/webxdc_bad_extension.eml"),
"INBOX", "INBOX",
false, false,
) )
@@ -375,23 +376,24 @@ mod tests {
} }
#[async_std::test] #[async_std::test]
async fn test_delete_w30_instance() -> Result<()> { async fn test_delete_webxdc_instance() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
let mut instance = create_w30_instance(&t).await?; let mut instance = create_webxdc_instance(&t).await?;
chat_id.set_draft(&t, Some(&mut instance)).await?; chat_id.set_draft(&t, Some(&mut instance)).await?;
let instance = chat_id.get_draft(&t).await?.unwrap(); let instance = chat_id.get_draft(&t).await?.unwrap();
t.send_w30_status_update(instance.id, "descr", "42").await?; t.send_webxdc_status_update(instance.id, "descr", "42")
.await?;
assert_eq!( assert_eq!(
t.get_w30_status_updates(instance.id, None).await?, t.get_webxdc_status_updates(instance.id, None).await?,
r#"[{"payload":42}]"#.to_string() r#"[{"payload":42}]"#.to_string()
); );
// set_draft(None) deletes the message without the need to simulate network // set_draft(None) deletes the message without the need to simulate network
chat_id.set_draft(&t, None).await?; chat_id.set_draft(&t, None).await?;
assert_eq!( assert_eq!(
t.get_w30_status_updates(instance.id, None).await?, t.get_webxdc_status_updates(instance.id, None).await?,
"[]".to_string() "[]".to_string()
); );
assert_eq!( assert_eq!(
@@ -408,15 +410,15 @@ mod tests {
async fn test_create_status_update_record() -> Result<()> { async fn test_create_status_update_record() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
let instance = send_w30_instance(&t, chat_id).await?; let instance = send_webxdc_instance(&t, chat_id).await?;
assert_eq!(t.get_w30_status_updates(instance.id, None).await?, "[]"); assert_eq!(t.get_webxdc_status_updates(instance.id, None).await?, "[]");
let id = t let id = t
.create_status_update_record(instance.id, "\n\n{\"foo\":\"bar\"}\n") .create_status_update_record(instance.id, "\n\n{\"foo\":\"bar\"}\n")
.await?; .await?;
assert_eq!( assert_eq!(
t.get_w30_status_updates(instance.id, Some(id)).await?, t.get_webxdc_status_updates(instance.id, Some(id)).await?,
r#"[{"payload":{"foo":"bar"}}]"# r#"[{"payload":{"foo":"bar"}}]"#
); );
@@ -429,11 +431,11 @@ mod tests {
.await .await
.is_err()); .is_err());
assert_eq!( assert_eq!(
t.get_w30_status_updates(instance.id, Some(id)).await?, t.get_webxdc_status_updates(instance.id, Some(id)).await?,
r#"[{"payload":{"foo":"bar"}}]"# r#"[{"payload":{"foo":"bar"}}]"#
); );
assert_eq!( assert_eq!(
t.get_w30_status_updates(instance.id, None).await?, t.get_webxdc_status_updates(instance.id, None).await?,
r#"[{"payload":{"foo":"bar"}}]"# r#"[{"payload":{"foo":"bar"}}]"#
); );
@@ -441,12 +443,12 @@ mod tests {
.create_status_update_record(instance.id, r#"{"foo2":"bar2"}"#) .create_status_update_record(instance.id, r#"{"foo2":"bar2"}"#)
.await?; .await?;
assert_eq!( assert_eq!(
t.get_w30_status_updates(instance.id, Some(id)).await?, t.get_webxdc_status_updates(instance.id, Some(id)).await?,
r#"[{"payload":{"foo2":"bar2"}}]"# r#"[{"payload":{"foo2":"bar2"}}]"#
); );
t.create_status_update_record(instance.id, "true").await?; t.create_status_update_record(instance.id, "true").await?;
assert_eq!( assert_eq!(
t.get_w30_status_updates(instance.id, None).await?, t.get_webxdc_status_updates(instance.id, None).await?,
r#"[{"payload":{"foo":"bar"}}, r#"[{"payload":{"foo":"bar"}},
{"payload":{"foo2":"bar2"}}, {"payload":{"foo2":"bar2"}},
{"payload":true}]"# {"payload":true}]"#
@@ -459,7 +461,7 @@ mod tests {
async fn test_receive_status_update() -> Result<()> { async fn test_receive_status_update() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
let instance = send_w30_instance(&t, chat_id).await?; let instance = send_webxdc_instance(&t, chat_id).await?;
assert!(t assert!(t
.receive_status_update(instance.id, r#"foo: bar"#) .receive_status_update(instance.id, r#"foo: bar"#)
@@ -477,14 +479,14 @@ mod tests {
t.receive_status_update(instance.id, r#"[{"payload":{"foo":"bar"}}]"#) t.receive_status_update(instance.id, r#"[{"payload":{"foo":"bar"}}]"#)
.await?; .await?;
assert_eq!( assert_eq!(
t.get_w30_status_updates(instance.id, None).await?, t.get_webxdc_status_updates(instance.id, None).await?,
r#"[{"payload":{"foo":"bar"}}]"# r#"[{"payload":{"foo":"bar"}}]"#
); );
t.receive_status_update(instance.id, r#" [ {"payload" :42} , {"payload": 23} ] "#) t.receive_status_update(instance.id, r#" [ {"payload" :42} , {"payload": 23} ] "#)
.await?; .await?;
assert_eq!( assert_eq!(
t.get_w30_status_updates(instance.id, None).await?, t.get_webxdc_status_updates(instance.id, None).await?,
r#"[{"payload":{"foo":"bar"}}, r#"[{"payload":{"foo":"bar"}},
{"payload":42}, {"payload":42},
{"payload":23}]"# {"payload":23}]"#
@@ -496,15 +498,15 @@ mod tests {
async fn expect_status_update_event(t: &TestContext, instance_id: MsgId) -> Result<()> { async fn expect_status_update_event(t: &TestContext, instance_id: MsgId) -> Result<()> {
let event = t let event = t
.evtracker .evtracker
.get_matching(|evt| matches!(evt, EventType::W30StatusUpdate { .. })) .get_matching(|evt| matches!(evt, EventType::WebxdcStatusUpdate { .. }))
.await; .await;
match event { match event {
EventType::W30StatusUpdate { EventType::WebxdcStatusUpdate {
msg_id, msg_id,
status_update_id, status_update_id,
} => { } => {
assert_eq!( assert_eq!(
t.get_w30_status_updates(msg_id, Some(status_update_id)) t.get_webxdc_status_updates(msg_id, Some(status_update_id))
.await?, .await?,
r#"[{"payload":{"foo":"bar"}}]"# r#"[{"payload":{"foo":"bar"}}]"#
); );
@@ -516,19 +518,19 @@ mod tests {
} }
#[async_std::test] #[async_std::test]
async fn test_send_w30_status_update() -> Result<()> { async fn test_send_webxdc_status_update() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
// Alice sends an w30 instance and a status update // Alice sends an webxdc instance and a status update
let alice_chat = alice.create_chat(&bob).await; let alice_chat = alice.create_chat(&bob).await;
let alice_instance = send_w30_instance(&alice, alice_chat.id).await?; let alice_instance = send_webxdc_instance(&alice, alice_chat.id).await?;
let sent1 = &alice.pop_sent_msg().await; let sent1 = &alice.pop_sent_msg().await;
assert_eq!(alice_instance.viewtype, Viewtype::W30); assert_eq!(alice_instance.viewtype, Viewtype::Webxdc);
assert!(!sent1.payload().contains("report-type=status-update")); assert!(!sent1.payload().contains("report-type=status-update"));
let status_update_msg_id = alice let status_update_msg_id = alice
.send_w30_status_update(alice_instance.id, "descr text", r#"{"foo":"bar"}"#) .send_webxdc_status_update(alice_instance.id, "descr text", r#"{"foo":"bar"}"#)
.await? .await?
.unwrap(); .unwrap();
expect_status_update_event(&alice, alice_instance.id).await?; expect_status_update_event(&alice, alice_instance.id).await?;
@@ -548,18 +550,18 @@ mod tests {
assert!(sent2.payload().contains("descr text")); assert!(sent2.payload().contains("descr text"));
assert_eq!( assert_eq!(
alice alice
.get_w30_status_updates(alice_instance.id, None) .get_webxdc_status_updates(alice_instance.id, None)
.await?, .await?,
r#"[{"payload":{"foo":"bar"}}]"# r#"[{"payload":{"foo":"bar"}}]"#
); );
alice alice
.send_w30_status_update(alice_instance.id, "bla text", r#"{"snipp":"snapp"}"#) .send_webxdc_status_update(alice_instance.id, "bla text", r#"{"snipp":"snapp"}"#)
.await? .await?
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
alice alice
.get_w30_status_updates(alice_instance.id, None) .get_webxdc_status_updates(alice_instance.id, None)
.await?, .await?,
r#"[{"payload":{"foo":"bar"}}, r#"[{"payload":{"foo":"bar"}},
{"payload":{"snipp":"snapp"}}]"# {"payload":{"snipp":"snapp"}}]"#
@@ -570,7 +572,7 @@ mod tests {
let bob_instance = bob.get_last_msg().await; let bob_instance = bob.get_last_msg().await;
let bob_chat_id = bob_instance.chat_id; let bob_chat_id = bob_instance.chat_id;
assert_eq!(bob_instance.rfc724_mid, alice_instance.rfc724_mid); assert_eq!(bob_instance.rfc724_mid, alice_instance.rfc724_mid);
assert_eq!(bob_instance.viewtype, Viewtype::W30); assert_eq!(bob_instance.viewtype, Viewtype::Webxdc);
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 1); assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 1);
bob.recv_msg(sent2).await; bob.recv_msg(sent2).await;
@@ -578,7 +580,7 @@ mod tests {
assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 1); assert_eq!(bob_chat_id.get_msg_cnt(&bob).await?, 1);
assert_eq!( assert_eq!(
bob.get_w30_status_updates(bob_instance.id, None).await?, bob.get_webxdc_status_updates(bob_instance.id, None).await?,
r#"[{"payload":{"foo":"bar"}}]"# r#"[{"payload":{"foo":"bar"}}]"#
); );
@@ -588,57 +590,57 @@ mod tests {
alice2.recv_msg(sent2).await; alice2.recv_msg(sent2).await;
let alice2_instance = alice2.get_last_msg().await; let alice2_instance = alice2.get_last_msg().await;
let alice2_chat_id = alice2_instance.chat_id; let alice2_chat_id = alice2_instance.chat_id;
assert_eq!(alice2_instance.viewtype, Viewtype::W30); assert_eq!(alice2_instance.viewtype, Viewtype::Webxdc);
assert_eq!(alice2_chat_id.get_msg_cnt(&alice2).await?, 1); assert_eq!(alice2_chat_id.get_msg_cnt(&alice2).await?, 1);
Ok(()) Ok(())
} }
#[async_std::test] #[async_std::test]
async fn test_draft_and_send_w30_status_update() -> Result<()> { async fn test_draft_and_send_webxdc_status_update() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await; let bob = TestContext::new_bob().await;
let alice_chat_id = alice.create_chat(&bob).await.id; let alice_chat_id = alice.create_chat(&bob).await.id;
// prepare w30 instance, // prepare webxdc instance,
// status updates are not sent for drafts, therefore send_w30_status_update() returns Ok(None) // status updates are not sent for drafts, therefore send_webxdc_status_update() returns Ok(None)
let mut alice_instance = create_w30_instance(&alice).await?; let mut alice_instance = create_webxdc_instance(&alice).await?;
alice_chat_id alice_chat_id
.set_draft(&alice, Some(&mut alice_instance)) .set_draft(&alice, Some(&mut alice_instance))
.await?; .await?;
let mut alice_instance = alice_chat_id.get_draft(&alice).await?.unwrap(); let mut alice_instance = alice_chat_id.get_draft(&alice).await?.unwrap();
let status_update_msg_id = alice let status_update_msg_id = alice
.send_w30_status_update(alice_instance.id, "descr", r#"{"foo":"bar"}"#) .send_webxdc_status_update(alice_instance.id, "descr", r#"{"foo":"bar"}"#)
.await?; .await?;
assert_eq!(status_update_msg_id, None); assert_eq!(status_update_msg_id, None);
let status_update_msg_id = alice let status_update_msg_id = alice
.send_w30_status_update(alice_instance.id, "descr", r#"42"#) .send_webxdc_status_update(alice_instance.id, "descr", r#"42"#)
.await?; .await?;
assert_eq!(status_update_msg_id, None); assert_eq!(status_update_msg_id, None);
// send w30 instance, // send webxdc instance,
// the initial status updates are sent together in the same message // the initial status updates are sent together in the same message
let alice_instance_id = send_msg(&alice, alice_chat_id, &mut alice_instance).await?; let alice_instance_id = send_msg(&alice, alice_chat_id, &mut alice_instance).await?;
let sent1 = alice.pop_sent_msg().await; let sent1 = alice.pop_sent_msg().await;
let alice_instance = Message::load_from_db(&alice, alice_instance_id).await?; let alice_instance = Message::load_from_db(&alice, alice_instance_id).await?;
assert_eq!(alice_instance.viewtype, Viewtype::W30); assert_eq!(alice_instance.viewtype, Viewtype::Webxdc);
assert_eq!( assert_eq!(
alice_instance.get_filename(), alice_instance.get_filename(),
Some("minimal.w30".to_string()) Some("minimal.xdc".to_string())
); );
assert_eq!(alice_instance.chat_id, alice_chat_id); assert_eq!(alice_instance.chat_id, alice_chat_id);
// bob receives the instance together with the initial updates in a single message // bob receives the instance together with the initial updates in a single message
bob.recv_msg(&sent1).await; bob.recv_msg(&sent1).await;
let bob_instance = bob.get_last_msg().await; let bob_instance = bob.get_last_msg().await;
assert_eq!(bob_instance.viewtype, Viewtype::W30); assert_eq!(bob_instance.viewtype, Viewtype::Webxdc);
assert_eq!(bob_instance.get_filename(), Some("minimal.w30".to_string())); assert_eq!(bob_instance.get_filename(), Some("minimal.xdc".to_string()));
assert!(sent1.payload().contains("Content-Type: application/json")); assert!(sent1.payload().contains("Content-Type: application/json"));
assert!(sent1.payload().contains("status-update.json")); assert!(sent1.payload().contains("status-update.json"));
assert!(sent1.payload().contains(r#""payload":{"foo":"bar"}"#)); assert!(sent1.payload().contains(r#""payload":{"foo":"bar"}"#));
assert_eq!( assert_eq!(
bob.get_w30_status_updates(bob_instance.id, None).await?, bob.get_webxdc_status_updates(bob_instance.id, None).await?,
r#"[{"payload":{"foo":"bar"}}, r#"[{"payload":{"foo":"bar"}},
{"payload":42}]"# {"payload":42}]"#
); );
@@ -647,85 +649,82 @@ mod tests {
} }
#[async_std::test] #[async_std::test]
async fn test_send_w30_status_update_to_non_w30() -> Result<()> { async fn test_send_webxdc_status_update_to_non_webxdc() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
let msg_id = send_text_msg(&t, chat_id, "ho!".to_string()).await?; let msg_id = send_text_msg(&t, chat_id, "ho!".to_string()).await?;
assert!(t assert!(t
.send_w30_status_update(msg_id, "descr", r#"{"foo":"bar"}"#) .send_webxdc_status_update(msg_id, "descr", r#"{"foo":"bar"}"#)
.await .await
.is_err()); .is_err());
Ok(()) Ok(())
} }
#[async_std::test] #[async_std::test]
async fn test_get_blob_from_archive() -> Result<()> { async fn test_get_webxdc_blob() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
let instance = send_w30_instance(&t, chat_id).await?; let instance = send_webxdc_instance(&t, chat_id).await?;
let buf = instance.get_blob_from_archive(&t, "index.html").await?; let buf = instance.get_webxdc_blob(&t, "index.html").await?;
assert_eq!(buf.len(), 188); assert_eq!(buf.len(), 188);
assert!(String::from_utf8_lossy(&buf).contains("document.write")); assert!(String::from_utf8_lossy(&buf).contains("document.write"));
assert!(instance assert!(instance
.get_blob_from_archive(&t, "not-existent.html") .get_webxdc_blob(&t, "not-existent.html")
.await .await
.is_err()); .is_err());
Ok(()) Ok(())
} }
#[async_std::test] #[async_std::test]
async fn test_get_blob_from_archive_with_absolute_paths() -> Result<()> { async fn test_get_webxdc_blob_with_absolute_paths() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
let instance = send_w30_instance(&t, chat_id).await?; let instance = send_webxdc_instance(&t, chat_id).await?;
let buf = instance.get_blob_from_archive(&t, "/index.html").await?; let buf = instance.get_webxdc_blob(&t, "/index.html").await?;
assert!(String::from_utf8_lossy(&buf).contains("document.write")); assert!(String::from_utf8_lossy(&buf).contains("document.write"));
assert!(instance assert!(instance.get_webxdc_blob(&t, "/not-there").await.is_err());
.get_blob_from_archive(&t, "/not-there")
.await
.is_err());
Ok(()) Ok(())
} }
#[async_std::test] #[async_std::test]
async fn test_get_blob_from_archive_subdirs() -> Result<()> { async fn test_get_webxdc_blob_with_subdirs() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
let file = t.get_blobdir().join("some-files.w30"); let file = t.get_blobdir().join("some-files.xdc");
File::create(&file) File::create(&file)
.await? .await?
.write_all(include_bytes!("../test-data/w30/some-files.w30")) .write_all(include_bytes!("../test-data/webxdc/some-files.xdc"))
.await?; .await?;
let mut instance = Message::new(Viewtype::W30); let mut instance = Message::new(Viewtype::Webxdc);
instance.set_file(file.to_str().unwrap(), None); instance.set_file(file.to_str().unwrap(), None);
chat_id.set_draft(&t, Some(&mut instance)).await?; chat_id.set_draft(&t, Some(&mut instance)).await?;
let buf = instance.get_blob_from_archive(&t, "index.html").await?; let buf = instance.get_webxdc_blob(&t, "index.html").await?;
assert_eq!(buf.len(), 65); assert_eq!(buf.len(), 65);
assert!(String::from_utf8_lossy(&buf).contains("many files")); assert!(String::from_utf8_lossy(&buf).contains("many files"));
let buf = instance.get_blob_from_archive(&t, "subdir/bla.txt").await?; let buf = instance.get_webxdc_blob(&t, "subdir/bla.txt").await?;
assert_eq!(buf.len(), 4); assert_eq!(buf.len(), 4);
assert!(String::from_utf8_lossy(&buf).starts_with("bla")); assert!(String::from_utf8_lossy(&buf).starts_with("bla"));
let buf = instance let buf = instance
.get_blob_from_archive(&t, "subdir/subsubdir/text.md") .get_webxdc_blob(&t, "subdir/subsubdir/text.md")
.await?; .await?;
assert_eq!(buf.len(), 24); assert_eq!(buf.len(), 24);
assert!(String::from_utf8_lossy(&buf).starts_with("this is a markdown file")); assert!(String::from_utf8_lossy(&buf).starts_with("this is a markdown file"));
let buf = instance let buf = instance
.get_blob_from_archive(&t, "subdir/subsubdir/text2.md") .get_webxdc_blob(&t, "subdir/subsubdir/text2.md")
.await?; .await?;
assert_eq!(buf.len(), 22); assert_eq!(buf.len(), 22);
assert!(String::from_utf8_lossy(&buf).starts_with("another markdown")); assert!(String::from_utf8_lossy(&buf).starts_with("another markdown"));
let buf = instance let buf = instance
.get_blob_from_archive(&t, "anotherdir/anothersubsubdir/foo.txt") .get_webxdc_blob(&t, "anotherdir/anothersubsubdir/foo.txt")
.await?; .await?;
assert_eq!(buf.len(), 4); assert_eq!(buf.len(), 4);
assert!(String::from_utf8_lossy(&buf).starts_with("foo")); assert!(String::from_utf8_lossy(&buf).starts_with("foo"));

View File

@@ -1,4 +1,4 @@
Subject: W30 object attached Subject: webxdc object attached
Message-ID: 67890@example.org Message-ID: 67890@example.org
Date: Fri, 03 Dec 2021 10:00:27 +0000 Date: Fri, 03 Dec 2021 10:00:27 +0000
To: alice@example.org To: alice@example.org
@@ -10,11 +10,10 @@ Content-Type: multipart/mixed; boundary="==BREAK=="
--==BREAK== --==BREAK==
Content-Type: text/plain; charset=utf-8 Content-Type: text/plain; charset=utf-8
w30 with correct extension; webxdc with bad extension and bad content.
this is not that important here.
--==BREAK== --==BREAK==
Content-Type: application/html+w30 Content-Type: application/webxdc+zip
Content-Disposition: attachment; filename=index.html Content-Disposition: attachment; filename=index.html
<html>hey!<html> <html>hey!<html>

View File

@@ -1,4 +1,4 @@
Subject: W30 object attached Subject: webxdc object attached
Message-ID: 12345@example.org Message-ID: 12345@example.org
Date: Fri, 03 Dec 2021 10:00:27 +0000 Date: Fri, 03 Dec 2021 10:00:27 +0000
To: alice@example.org To: alice@example.org
@@ -10,13 +10,13 @@ Content-Type: multipart/mixed; boundary="==BREAK=="
--==BREAK== --==BREAK==
Content-Type: text/plain; charset=utf-8 Content-Type: text/plain; charset=utf-8
w30 with good extension; webxdc with good extension;
the mimetype is ignored then, the mimetype is ignored then,
content is checked. content is checked.
--==BREAK== --==BREAK==
Content-Type: text/html Content-Type: text/html
Content-Disposition: attachment; filename=minimal.w30 Content-Disposition: attachment; filename=minimal.xdc
Content-Transfer-Encoding: base64 Content-Transfer-Encoding: base64
UEsDBBQAAgAIAFJqnVOItjSofAAAALwAAAAKABwAaW5kZXguaHRtbFVUCQADG1LMYV1SzGF1eAsAAQ UEsDBBQAAgAIAFJqnVOItjSofAAAALwAAAAKABwAaW5kZXguaHRtbFVUCQADG1LMYV1SzGF1eAsAAQ