diff --git a/Cargo.lock b/Cargo.lock index 43ff922f52..55e57da09d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -141,6 +141,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "array-init" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc" + [[package]] name = "arrayref" version = "0.3.9" @@ -355,11 +361,12 @@ dependencies = [ [[package]] name = "async_zip" -version = "0.0.18" +version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c50d65ce1b0e0cb65a785ff615f78860d7754290647d3b983208daa4f85e6" +checksum = "fb7f5f40e1eb30949a266fc900d37fd3267c7baf50c3705ac09c5d8ced5def63" dependencies = [ "async-compression", + "binrw", "crc32fast", "futures-lite", "pin-project", @@ -441,6 +448,30 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "binrw" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ad120d555272286c1017d25165ab8bd74806f13fc85b258484ec7e4ce75458f" +dependencies = [ + "array-init", + "binrw_derive", + "bytemuck", +] + +[[package]] +name = "binrw_derive" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6df92e0e9baae4dc82c7bad7715ca40c0a5c71539057bf2ea04a5c29c980410b" +dependencies = [ + "either", + "owo-colors", + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "bitfields" version = "1.0.0" @@ -4073,6 +4104,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "owo-colors" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c45bb4a6ae1280ec0803b1ef9d3455eb50f01efbbe1447ab020f1d54fba9d8" + [[package]] name = "p256" version = "0.13.2" diff --git a/Cargo.toml b/Cargo.toml index 1fb9dedbe2..4e3d9bf7bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ async-channel = { workspace = true } async-imap = { version = "0.11.3", default-features = false, features = ["runtime-tokio", "compress"] } async-native-tls = { version = "0.6", default-features = false, features = ["runtime-tokio"] } async-smtp = { version = "0.10.2", default-features = false, features = ["runtime-tokio"] } -async_zip = { version = "0.0.18", default-features = false, features = ["deflate", "tokio-fs"] } +async_zip = { version = "0.0.19", default-features = false, features = ["deflate", "tokio-fs"] } base64 = { workspace = true } blake3 = "1.8.2" brotli = { version = "8", default-features=false, features = ["std"] } @@ -103,7 +103,7 @@ tokio-io-timeout = "1.2.1" tokio-rustls = { version = "0.26.2", default-features = false, features = ["tls12", "brotli"] } tokio-stream = { version = "0.1.17", features = ["fs"] } astral-tokio-tar = { version = "0.6.3", default-features = false } -tokio-util = { workspace = true } +tokio-util = { workspace = true, features = ["compat"] } tokio = { workspace = true, features = ["fs", "rt-multi-thread", "macros"] } toml = "0.9" tracing = "0.1.41" diff --git a/src/webxdc.rs b/src/webxdc.rs index f26c23b6f1..fafa2a37c3 100644 --- a/src/webxdc.rs +++ b/src/webxdc.rs @@ -20,14 +20,14 @@ mod maps_integration; use std::cmp::max; use std::collections::HashMap; -use std::io::Cursor; use std::path::Path; -use anyhow::{Context as _, Result, anyhow, bail, ensure, format_err}; +use anyhow::{Context as _, Result, bail, ensure, format_err}; -use async_zip::tokio::read::seek::ZipFileReader as SeekZipFileReader; +use async_zip::base::read1::seek::ZipArchiveReader; use deltachat_contact_tools::sanitize_bidi_characters; use deltachat_derive::FromSql; +use futures::AsyncReadExt as _; use image::{ImageFormat, ImageReader}; use mail_builder::mime::MimePart; use rusqlite::OptionalExtension; @@ -35,6 +35,7 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; use sha2::{Digest, Sha256}; use tokio::{fs::File, io::BufReader}; +use tokio_util::compat::{Compat, TokioAsyncReadCompatExt}; use crate::chat::{self, Chat}; use crate::constants::Chattype; @@ -227,19 +228,6 @@ pub(crate) struct StatusUpdateItemAndSerial { max_serial: StatusUpdateSerial, } -/// Returns an entry index and a reference. -fn find_zip_entry<'a>( - file: &'a async_zip::ZipFile, - name: &str, -) -> Option<(usize, &'a async_zip::StoredZipEntry)> { - for (i, ent) in file.entries().iter().enumerate() { - if ent.filename().as_bytes() == name.as_bytes() { - return Some((i, ent)); - } - } - None -} - /// Status update JSON size soft limit. const STATUS_UPDATE_SIZE_MAX: usize = 100 << 10; @@ -250,18 +238,18 @@ impl Context { return Ok(false); } - let archive = match async_zip::base::read::mem::ZipFileReader::new(file.to_vec()).await { + let archive = match ZipArchiveReader::open(futures_lite::io::Cursor::new(file)).await { Ok(archive) => archive, Err(_) => { - info!(self, "{} cannot be opened as zip-file", &filename); + info!(self, "{filename} cannot be opened as zip-file."); return Ok(false); } }; - if find_zip_entry(archive.file(), "index.html").is_none() { - info!(self, "{} misses index.html", &filename); + if archive.find(b"index.html")?.next().is_none() { + info!(self, "{filename} misses index.html."); return Ok(false); - } + }; Ok(true) } @@ -271,23 +259,11 @@ impl Context { let filename = path.to_str().unwrap_or_default(); let file = BufReader::new(File::open(path).await?); - let valid = match SeekZipFileReader::with_tokio(file).await { - Ok(archive) => { - if find_zip_entry(archive.file(), "index.html").is_none() { - warn!(self, "{} misses index.html", filename); - false - } else { - true - } - } - Err(_) => { - warn!(self, "{} cannot be opened as zip-file", filename); - false - } - }; - - if !valid { - bail!("{filename} is not a valid webxdc file"); + let archive = ZipArchiveReader::open(file.compat()) + .await + .with_context(|| format!("{filename} cannot be opened as zip-file"))?; + if archive.find(b"index.html")?.next().is_none() { + bail!("{filename} misses index.html."); } Ok(()) @@ -848,12 +824,16 @@ fn parse_webxdc_manifest(bytes: &[u8]) -> Result { Ok(manifest) } -async fn get_blob(archive: &mut SeekZipFileReader>, name: &str) -> Result> { - let (i, _) = - find_zip_entry(archive.file(), name).ok_or_else(|| anyhow!("no entry found for {name}"))?; - let mut reader = archive.reader_with_entry(i).await?; +async fn get_blob( + archive: &mut ZipArchiveReader>>, + name: &str, +) -> Result> { + let Some(i) = archive.find(name.as_bytes())?.next() else { + bail!("No entry found for {name}"); + }; + let mut file_reader = archive.file(i).await?; let mut buf = Vec::new(); - reader.read_to_end_checked(&mut buf).await?; + file_reader.read_to_end(&mut buf).await?; Ok(buf) } @@ -863,13 +843,13 @@ impl Message { async fn get_webxdc_archive( &self, context: &Context, - ) -> Result>> { + ) -> Result>>> { let path = self .get_file(context) .ok_or_else(|| format_err!("No webxdc instance file."))?; let path_abs = get_abs_path(context, &path); let file = BufReader::new(File::open(path_abs).await?); - let archive = SeekZipFileReader::with_tokio(file).await?; + let archive = ZipArchiveReader::open(file.compat()).await?; Ok(archive) } @@ -910,7 +890,7 @@ impl Message { let blob = get_blob(&mut archive, name).await?; if name == "icon.png" || name == "icon.jpg" { - let image_reader = ImageReader::new(Cursor::new(&blob)) + let image_reader = ImageReader::new(std::io::Cursor::new(&blob)) .with_guessed_format() .context("Reading from Cursor must never fail")?; match image_reader.format() { @@ -964,9 +944,9 @@ impl Message { } else { self.get_filename().unwrap_or_default() }, - icon: if find_zip_entry(archive.file(), "icon.png").is_some() { + icon: if archive.find(b"icon.png")?.next().is_some() { "icon.png".to_string() - } else if find_zip_entry(archive.file(), "icon.jpg").is_some() { + } else if archive.find(b"icon.jpg")?.next().is_some() { "icon.jpg".to_string() } else { WEBXDC_DEFAULT_ICON.to_string() diff --git a/src/webxdc/webxdc_tests.rs b/src/webxdc/webxdc_tests.rs index 79bdb49d95..61f12a1a30 100644 --- a/src/webxdc/webxdc_tests.rs +++ b/src/webxdc/webxdc_tests.rs @@ -90,7 +90,6 @@ async fn test_send_webxdc_instance() -> Result<()> { let mut instance = Message::new(Viewtype::Webxdc); instance.set_file_from_bytes(&t, "index.html", b"ola!", None)?; assert!(send_msg(&t, chat_id, &mut instance).await.is_err()); - t.assert_warn("cannot be opened as zip-file").await; Ok(()) } @@ -119,8 +118,6 @@ async fn test_send_invalid_webxdc() -> Result<()> { None, )?; assert!(send_msg(&t, chat_id, &mut instance).await.is_err()); - t.assert_warn("cannot be opened as zip-file").await; - t.assert_warn("cannot be opened as zip-file").await; Ok(()) }