Skip to content

Commit

Permalink
fix: base64-encode webxdc updates
Browse files Browse the repository at this point in the history
Webxdc update messages may contain
long lines that get hard-wrapped
and may corrupt JSON if the message
is not encrypted.

base64-encode the update part
to avoid hard wrapping.

This is not necessary for encrypted
messages, but does not introduce
size overhead as OpenPGP messages
are compressed.
  • Loading branch information
link2xt committed Jul 26, 2023
1 parent a33c91a commit 2f24edd
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 3 deletions.
21 changes: 21 additions & 0 deletions python/tests/test_1_online.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,27 @@ def test_webxdc_message(acfactory, data, lp):
assert len(list(ac2.direct_imap.conn.fetch(AND(seen=True)))) == 1


def test_webxdc_huge_update(acfactory, data, lp):
ac1, ac2 = acfactory.get_online_accounts(2)
chat = ac1.create_chat(ac2)

msg1 = Message.new_empty(ac1, "webxdc")
msg1.set_text("message1")
msg1.set_file(data.get_path("webxdc/minimal.xdc"))
msg1 = chat.send_msg(msg1)
assert msg1.is_webxdc()
assert msg1.filename

msg2 = ac2._evtracker.wait_next_incoming_message()
assert msg2.is_webxdc()

payload = "A" * 1000
assert msg1.send_status_update({"payload": payload}, "some test data")
ac2._evtracker.get_matching("DC_EVENT_WEBXDC_STATUS_UPDATE")
update = msg2.get_status_updates()[0]
assert update["payload"] == payload


def test_webxdc_download_on_demand(acfactory, data, lp):
ac1, ac2 = acfactory.get_online_accounts(2)
acfactory.introduce_each_other([ac1, ac2])
Expand Down
2 changes: 1 addition & 1 deletion src/mimefactory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1368,7 +1368,7 @@ impl<'a> MimeFactory<'a> {
///
/// This line length limit is an
/// [RFC5322 requirement](https://tools.ietf.org/html/rfc5322#section-2.1.1).
fn wrapped_base64_encode(buf: &[u8]) -> String {
pub(crate) fn wrapped_base64_encode(buf: &[u8]) -> String {
let base64 = base64::engine::general_purpose::STANDARD.encode(buf);
let mut chars = base64.chars();
std::iter::repeat_with(|| chars.by_ref().take(78).collect::<String>())
Expand Down
7 changes: 5 additions & 2 deletions src/webxdc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use crate::contact::ContactId;
use crate::context::Context;
use crate::download::DownloadState;
use crate::message::{Message, MessageState, MsgId, Viewtype};
use crate::mimefactory::wrapped_base64_encode;
use crate::mimeparser::SystemMessage;
use crate::param::Param;
use crate::param::Params;
Expand Down Expand Up @@ -535,13 +536,16 @@ impl Context {
}

pub(crate) fn build_status_update_part(&self, json: &str) -> PartBuilder {
let encoded_body = wrapped_base64_encode(json.as_bytes());

PartBuilder::new()
.content_type(&"application/json".parse::<mime::Mime>().unwrap())
.header((
"Content-Disposition",
"attachment; filename=\"status-update.json\"",
))
.body(json)
.header(("Content-Transfer-Encoding", "base64"))
.body(encoded_body)
}

/// Receives status updates from receive_imf to the database
Expand Down Expand Up @@ -1785,7 +1789,6 @@ mod tests {
assert_eq!(bob_instance.get_filename(), Some("minimal.xdc".to_string()));
assert!(sent1.payload().contains("Content-Type: application/json"));
assert!(sent1.payload().contains("status-update.json"));
assert!(sent1.payload().contains(r#""payload":{"foo":"bar"}"#));
assert_eq!(
bob.get_webxdc_status_updates(bob_instance.id, StatusUpdateSerial(0))
.await?,
Expand Down

0 comments on commit 2f24edd

Please sign in to comment.