Compare commits

...

4 Commits

Author SHA1 Message Date
link2xt
5d9b887624 chore(release): prepare for 2.14.0 2025-09-12 06:06:36 +00:00
link2xt
12c0e298f5 test: test sending SDP offer and answer with newlines 2025-09-12 02:37:57 +00:00
link2xt
f9aec7af0d fix: B-encode SDP offer and answer sent in headers
SDP offer and answer contain newlines.
Without the fix these newlines are not encoded at all
and break the header into multiple headers
or even prevent parsing of the following headers.
2025-09-12 02:37:57 +00:00
link2xt
b181d78dd5 fix(param): split params only on \n
str.lines() splits on both \n and \r\n
We use \n as a field separator,
so \r\n should not separate the fields.
2025-09-12 02:37:57 +00:00
15 changed files with 67 additions and 39 deletions

View File

@@ -1,5 +1,26 @@
# Changelog
## [2.14.0] - 2025-09-12
### API-Changes
- Put the chattype into the SecurejoinInviterProgress event ([#7181](https://github.com/chatmail/core/pull/7181)).
### Fixes
- param: Split params only on \n.
- B-encode SDP offer and answer sent in headers.
### Refactor
- Use recv_msg_trash() instead of recv_msg_opt().
- Prepare_msg_raw(): don't return MsgId.
### Tests
- Message is OutFailed if all keys are missing ([#6849](https://github.com/chatmail/core/pull/6849)).
- Test sending SDP offer and answer with newlines.
## [2.13.0] - 2025-09-09
### API-Changes
@@ -6725,3 +6746,5 @@ https://github.com/chatmail/core/pulls?q=is%3Apr+is%3Aclosed
[2.10.0]: https://github.com/chatmail/core/compare/v2.9.0..v2.10.0
[2.11.0]: https://github.com/chatmail/core/compare/v2.10.0..v2.11.0
[2.12.0]: https://github.com/chatmail/core/compare/v2.11.0..v2.12.0
[2.13.0]: https://github.com/chatmail/core/compare/v2.12.0..v2.13.0
[2.14.0]: https://github.com/chatmail/core/compare/v2.13.0..v2.14.0

10
Cargo.lock generated
View File

@@ -1296,7 +1296,7 @@ dependencies = [
[[package]]
name = "deltachat"
version = "2.13.0"
version = "2.14.0"
dependencies = [
"anyhow",
"async-broadcast",
@@ -1406,7 +1406,7 @@ dependencies = [
[[package]]
name = "deltachat-jsonrpc"
version = "2.13.0"
version = "2.14.0"
dependencies = [
"anyhow",
"async-channel 2.5.0",
@@ -1428,7 +1428,7 @@ dependencies = [
[[package]]
name = "deltachat-repl"
version = "2.13.0"
version = "2.14.0"
dependencies = [
"anyhow",
"deltachat",
@@ -1444,7 +1444,7 @@ dependencies = [
[[package]]
name = "deltachat-rpc-server"
version = "2.13.0"
version = "2.14.0"
dependencies = [
"anyhow",
"deltachat",
@@ -1473,7 +1473,7 @@ dependencies = [
[[package]]
name = "deltachat_ffi"
version = "2.13.0"
version = "2.14.0"
dependencies = [
"anyhow",
"deltachat",

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat"
version = "2.13.0"
version = "2.14.0"
edition = "2024"
license = "MPL-2.0"
rust-version = "1.85"

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat_ffi"
version = "2.13.0"
version = "2.14.0"
description = "Deltachat FFI"
edition = "2018"
readme = "README.md"

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat-jsonrpc"
version = "2.13.0"
version = "2.14.0"
description = "DeltaChat JSON-RPC API"
edition = "2021"
license = "MPL-2.0"

View File

@@ -54,5 +54,5 @@
},
"type": "module",
"types": "dist/deltachat.d.ts",
"version": "2.13.0"
"version": "2.14.0"
}

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat-repl"
version = "2.13.0"
version = "2.14.0"
license = "MPL-2.0"
edition = "2021"
repository = "https://github.com/chatmail/core"

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "deltachat-rpc-client"
version = "2.13.0"
version = "2.14.0"
description = "Python client for Delta Chat core JSON-RPC interface"
classifiers = [
"Development Status :: 5 - Production/Stable",

View File

@@ -1,6 +1,6 @@
[package]
name = "deltachat-rpc-server"
version = "2.13.0"
version = "2.14.0"
description = "DeltaChat JSON-RPC server"
edition = "2021"
readme = "README.md"

View File

@@ -15,5 +15,5 @@
},
"type": "module",
"types": "index.d.ts",
"version": "2.13.0"
"version": "2.14.0"
}

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "deltachat"
version = "2.13.0"
version = "2.14.0"
description = "Python bindings for the Delta Chat Core library using CFFI against the Rust-implemented libdeltachat"
readme = "README.rst"
requires-python = ">=3.8"

View File

@@ -1 +1 @@
2025-09-09
2025-09-12

View File

@@ -18,6 +18,10 @@ async fn assert_text(t: &TestContext, call_id: MsgId, text: &str) -> Result<()>
Ok(())
}
// Offer and answer examples from <https://www.rfc-editor.org/rfc/rfc3264>
const PLACE_INFO: &str = "v=0\r\no=alice 2890844526 2890844526 IN IP4 host.anywhere.com\r\ns=\r\nc=IN IP4 host.anywhere.com\r\nt=0 0\r\nm=audio 62986 RTP/AVP 0 4 18\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:4 G723/8000\r\na=rtpmap:18 G729/8000\r\na=inactive\r\n";
const ACCEPT_INFO: &str = "v=0\r\no=bob 2890844730 2890844731 IN IP4 host.example.com\r\ns=\r\nc=IN IP4 host.example.com\r\nt=0 0\r\nm=audio 54344 RTP/AVP 0 4\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:4 G723/8000\r\na=inactive\r\n";
async fn setup_call() -> Result<CallSetup> {
let mut tcm = TestContextManager::new();
let alice = tcm.alice().await;
@@ -32,7 +36,7 @@ async fn setup_call() -> Result<CallSetup> {
// Alice's other device sees the same message as an outgoing call.
let alice_chat = alice.create_chat(&bob).await;
let test_msg_id = alice
.place_outgoing_call(alice_chat.id, "place-info-123".to_string())
.place_outgoing_call(alice_chat.id, PLACE_INFO.to_string())
.await?;
let sent1 = alice.pop_sent_msg().await;
assert_eq!(sent1.sender_msg_id, test_msg_id);
@@ -44,7 +48,7 @@ async fn setup_call() -> Result<CallSetup> {
let info = t.load_call_by_id(m.id).await?;
assert!(!info.is_incoming());
assert!(!info.is_accepted());
assert_eq!(info.place_call_info, "place-info-123");
assert_eq!(info.place_call_info, PLACE_INFO);
assert_text(t, m.id, "Outgoing call").await?;
}
@@ -61,7 +65,7 @@ async fn setup_call() -> Result<CallSetup> {
let info = t.load_call_by_id(m.id).await?;
assert!(info.is_incoming());
assert!(!info.is_accepted());
assert_eq!(info.place_call_info, "place-info-123");
assert_eq!(info.place_call_info, PLACE_INFO);
assert_text(t, m.id, "Incoming call").await?;
}
@@ -90,7 +94,7 @@ async fn accept_call() -> Result<CallSetup> {
} = setup_call().await?;
// Bob accepts the incoming call
bob.accept_incoming_call(bob_call.id, "accept-info-456".to_string())
bob.accept_incoming_call(bob_call.id, ACCEPT_INFO.to_string())
.await?;
assert_text(&bob, bob_call.id, "Incoming call").await?;
bob.evtracker
@@ -99,7 +103,7 @@ async fn accept_call() -> Result<CallSetup> {
let sent2 = bob.pop_sent_msg().await;
let info = bob.load_call_by_id(bob_call.id).await?;
assert!(info.is_accepted());
assert_eq!(info.place_call_info, "place-info-123");
assert_eq!(info.place_call_info, PLACE_INFO);
bob2.recv_msg_trash(&sent2).await;
assert_text(&bob, bob_call.id, "Incoming call").await?;
@@ -120,12 +124,12 @@ async fn accept_call() -> Result<CallSetup> {
ev,
EventType::OutgoingCallAccepted {
msg_id: alice2_call.id,
accept_call_info: "accept-info-456".to_string()
accept_call_info: ACCEPT_INFO.to_string()
}
);
let info = alice.load_call_by_id(alice_call.id).await?;
assert!(info.is_accepted());
assert_eq!(info.place_call_info, "place-info-123");
assert_eq!(info.place_call_info, PLACE_INFO);
alice2.recv_msg_trash(&sent2).await;
assert_text(&alice2, alice2_call.id, "Outgoing call").await?;

View File

@@ -1580,27 +1580,15 @@ impl MimeFactory {
);
}
if msg.param.exists(Param::WebrtcRoom) {
if let Some(offer) = msg.param.get(Param::WebrtcRoom) {
headers.push((
"Chat-Webrtc-Room",
mail_builder::headers::raw::Raw::new(
msg.param
.get(Param::WebrtcRoom)
.unwrap_or_default()
.to_string(),
)
.into(),
mail_builder::headers::raw::Raw::new(b_encode(offer)).into(),
));
} else if msg.param.exists(Param::WebrtcAccepted) {
} else if let Some(answer) = msg.param.get(Param::WebrtcAccepted) {
headers.push((
"Chat-Webrtc-Accepted",
mail_builder::headers::raw::Raw::new(
msg.param
.get(Param::WebrtcAccepted)
.unwrap_or_default()
.to_string(),
)
.into(),
mail_builder::headers::raw::Raw::new(b_encode(answer)).into(),
));
}
@@ -1894,5 +1882,17 @@ fn render_rfc724_mid(rfc724_mid: &str) -> String {
}
}
/// Encodes UTF-8 string as a single B-encoded-word.
///
/// We manually encode some headers because as of
/// version 0.4.4 mail-builder crate does not encode
/// newlines correctly if they appear in a text header.
fn b_encode(value: &str) -> String {
format!(
"=?utf-8?B?{}?=",
base64::engine::general_purpose::STANDARD.encode(value)
)
}
#[cfg(test)]
mod mimefactory_tests;

View File

@@ -265,7 +265,7 @@ impl str::FromStr for Params {
/// or from an upgrade (when a key is dropped but was used in the past)
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let mut inner = BTreeMap::new();
let mut lines = s.lines().peekable();
let mut lines = s.split('\n').peekable();
while let Some(line) = lines.next() {
if let [key, value] = line.splitn(2, '=').collect::<Vec<_>>()[..] {
@@ -457,6 +457,7 @@ mod tests {
let mut params = Params::new();
params.set(Param::Height, "foo\nbar=baz\nquux");
params.set(Param::Width, "\n\n\na=\n=");
params.set(Param::WebrtcRoom, "foo\r\nbar\r\n\r\nbaz\r\n");
assert_eq!(params.to_string().parse::<Params>().unwrap(), params);
}