diff --git a/src/chat.rs b/src/chat.rs index edecee71e..7fad34fea 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -25,8 +25,8 @@ use crate::context::Context; use crate::dc_receive_imf::ReceivedMsg; use crate::dc_tools::{ dc_create_id, dc_create_outgoing_rfc724_mid, dc_create_smeared_timestamp, - dc_create_smeared_timestamps, dc_get_abs_path, dc_gm2local_offset, improve_single_line_input, - time, IsNoneOrEmpty, + dc_create_smeared_timestamps, dc_get_abs_path, dc_get_filebytes, dc_gm2local_offset, + improve_single_line_input, time, IsNoneOrEmpty, }; use crate::ephemeral::{delete_expired_messages, schedule_ephemeral_task, Timer as EphemeralTimer}; use crate::events::EventType; @@ -37,7 +37,7 @@ use crate::mimeparser::SystemMessage; use crate::param::{Param, Params}; use crate::peerstate::{Peerstate, PeerstateVerifiedStatus}; use crate::stock_str; -use crate::webxdc::WEBXDC_SUFFIX; +use crate::webxdc::{WEBXDC_SENDING_LIMIT, WEBXDC_SUFFIX}; /// An chat item, such as a message or a marker. #[derive(Debug, Copy, Clone)] @@ -1842,12 +1842,22 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> { } } - if msg.viewtype == Viewtype::Webxdc && blob.suffix() != Some(WEBXDC_SUFFIX) { - bail!( - "webxdc message {} does not have suffix {}", - blob, - WEBXDC_SUFFIX - ); + if msg.viewtype == Viewtype::Webxdc { + if blob.suffix() != Some(WEBXDC_SUFFIX) { + bail!( + "webxdc message {} does not have suffix {}", + blob, + WEBXDC_SUFFIX + ); + } else if dc_get_filebytes(context, blob.to_abs_path()).await + > WEBXDC_SENDING_LIMIT as u64 + { + bail!( + "webxdc message {} exceeds acceptable size of {} bytes", + blob.as_name(), + WEBXDC_SENDING_LIMIT + ); + } } info!( diff --git a/src/webxdc.rs b/src/webxdc.rs index 11b0437bc..7bf81176f 100644 --- a/src/webxdc.rs +++ b/src/webxdc.rs @@ -20,6 +20,23 @@ use zip::ZipArchive; pub const WEBXDC_SUFFIX: &str = "xdc"; const WEBXDC_DEFAULT_ICON: &str = "__webxdc__/default-icon.png"; +/// Defines the maximal size in bytes of an .xdc file that can be sent. +/// +/// We introduce a limit to force developer to create small .xdc +/// to save user's traffic and disk space for a better ux. +/// +/// The 100K limit should also let .xdc pass worse-quality auto-download filters +/// which are usually 160K incl. base64 overhead. +/// +/// The limit is also an experiment to see how small we can go; +/// it is planned to raise that limit as needed in subsequent versions. +pub(crate) const WEBXDC_SENDING_LIMIT: usize = 102400; + +/// Be more tolerant for .xdc sizes on receiving - +/// might be, the senders version uses already a larger limit +/// and not showing the .xdc on some devices would be even worse ux. +const WEBXDC_RECEIVING_LIMIT: usize = 1048576; + /// Raw information read from manifest.toml #[derive(Debug, Deserialize)] #[non_exhaustive] @@ -79,7 +96,16 @@ impl Context { let reader = std::io::Cursor::new(buf); if let Ok(mut archive) = zip::ZipArchive::new(reader) { if let Ok(_index_html) = archive.by_name("index.html") { - return Ok(true); + if buf.len() <= WEBXDC_RECEIVING_LIMIT { + return Ok(true); + } else { + error!( + self, + "{} exceeds acceptable size of {} bytes.", + &filename, + WEBXDC_SENDING_LIMIT + ); + } } } } @@ -369,6 +395,16 @@ mod tests { use async_std::fs::File; use async_std::io::WriteExt; + #[allow(clippy::assertions_on_constants)] + #[async_std::test] + async fn test_webxdc_file_limits() -> Result<()> { + assert!(WEBXDC_SENDING_LIMIT >= 32768); + assert!(WEBXDC_SENDING_LIMIT < 16777216); + assert!(WEBXDC_RECEIVING_LIMIT >= WEBXDC_SENDING_LIMIT * 2); + assert!(WEBXDC_RECEIVING_LIMIT < 16777216); + Ok(()) + } + #[async_std::test] async fn test_is_webxdc_file() -> Result<()> { let t = TestContext::new().await;