Compare commits

..

1 Commits

Author SHA1 Message Date
iequidoo
974e32dd76 fix: Don't decrease member add/remove timestamps if they aren't far away in the future
We shouldn't decrease `add_timestamp` and `remove_timestamp` in the `chats_contacts` table normally,
even if remote changes arrive reordered. This particularly makes sense for ad-hoc groups (see
`chat::update_chat_contacts_table()` in `apply_group_changes()`) and in case if we join an encrypted
group which we were a member of before (see `chat::add_to_chat_contacts_table()` call).

Still, limit already stored timestamps in case local clock was in the future and is set back
now. But our clock may be slow, so limit stored timestamps with a remote timestamp if it's
bigger.

NB: `receive_imf::update_chats_contacts_timestamps()` already only increases timestamps, but it's
used only for handling of the "Chat-Group-Member-Timestamps" header, i.e. for encrypted groups.
2026-06-07 21:45:47 -03:00
15 changed files with 39 additions and 69 deletions

View File

@@ -1,37 +1,5 @@
# Changelog # Changelog
## [2.52.0] - 2026-06-09
### Fixes
- Update the channel title after joining if the QR code included a wrong title ([#8260](https://github.com/chatmail/core/pull/8260)).
- Don't send removal message to contact that hasn't been a chat member ([#8298](https://github.com/chatmail/core/pull/8298)).
### Features / Changes
- Add cryptography-related statistics (`number_of_transports`, `key_version`, `key_algorithm`, `pubkey_size`, `number_of_keys`) ([#8293](https://github.com/chatmail/core/pull/8293), [#8297](https://github.com/chatmail/core/pull/8297)).
- Add IMAP folder to `Context::get_info()` ([#8285](https://github.com/chatmail/core/pull/8285)).
### Miscellaneous Tasks
- Update preloaded DNS cache.
- Use default aws-lc-rs cryptography provider for rustls.
- Add exception for unmaintained proc-macro-error2 to deny.toml.
- cargo: bump `pin-project` from 1.1.11 to 1.1.13.
- cargo: bump `tokio` from 1.52.1 to 1.52.3.
- cargo: bump `log` from 0.4.29 to 0.4.30.
- cargo: bump `serde_json` from 1.0.149 to 1.0.150.
- deps: bump EmbarkStudios/cargo-deny-action from 2.0.18 to 2.0.19.
- deps: bump taiki-e/install-action from 2.79.2 to 2.79.10.
### Build system
- nix: fix windows cross-compilation by adding pthreads includes.
### Refactor
- Remove support for building "source" packages for deltachat-rpc-server.
## [2.51.0] - 2026-05-29 ## [2.51.0] - 2026-05-29
### Features / Changes ### Features / Changes
@@ -8329,4 +8297,3 @@ https://github.com/chatmail/core/pulls?q=is%3Apr+is%3Aclosed
[2.49.0]: https://github.com/chatmail/core/compare/v2.48.0..v2.49.0 [2.49.0]: https://github.com/chatmail/core/compare/v2.48.0..v2.49.0
[2.50.0]: https://github.com/chatmail/core/compare/v2.49.0..v2.50.0 [2.50.0]: https://github.com/chatmail/core/compare/v2.49.0..v2.50.0
[2.51.0]: https://github.com/chatmail/core/compare/v2.50.0..v2.51.0 [2.51.0]: https://github.com/chatmail/core/compare/v2.50.0..v2.51.0
[2.52.0]: https://github.com/chatmail/core/compare/v2.51.0..v2.52.0

10
Cargo.lock generated
View File

@@ -1350,7 +1350,7 @@ dependencies = [
[[package]] [[package]]
name = "deltachat" name = "deltachat"
version = "2.52.0" version = "2.51.0-dev"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"astral-tokio-tar", "astral-tokio-tar",
@@ -1459,7 +1459,7 @@ dependencies = [
[[package]] [[package]]
name = "deltachat-jsonrpc" name = "deltachat-jsonrpc"
version = "2.52.0" version = "2.51.0-dev"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-channel 2.5.0", "async-channel 2.5.0",
@@ -1480,7 +1480,7 @@ dependencies = [
[[package]] [[package]]
name = "deltachat-repl" name = "deltachat-repl"
version = "2.52.0" version = "2.51.0-dev"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"deltachat", "deltachat",
@@ -1496,7 +1496,7 @@ dependencies = [
[[package]] [[package]]
name = "deltachat-rpc-server" name = "deltachat-rpc-server"
version = "2.52.0" version = "2.51.0-dev"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"deltachat", "deltachat",
@@ -1525,7 +1525,7 @@ dependencies = [
[[package]] [[package]]
name = "deltachat_ffi" name = "deltachat_ffi"
version = "2.52.0" version = "2.51.0-dev"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"deltachat", "deltachat",

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "deltachat" name = "deltachat"
version = "2.52.0" version = "2.51.0-dev"
edition = "2024" edition = "2024"
license = "MPL-2.0" license = "MPL-2.0"
rust-version = "1.89" rust-version = "1.89"

View File

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

View File

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

View File

@@ -54,5 +54,5 @@
}, },
"type": "module", "type": "module",
"types": "dist/deltachat.d.ts", "types": "dist/deltachat.d.ts",
"version": "2.52.0" "version": "2.51.0-dev"
} }

View File

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

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "deltachat-rpc-client" name = "deltachat-rpc-client"
version = "2.52.0" version = "2.51.0-dev"
license = "MPL-2.0" license = "MPL-2.0"
description = "Python client for Delta Chat core JSON-RPC interface" description = "Python client for Delta Chat core JSON-RPC interface"
classifiers = [ classifiers = [

View File

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

View File

@@ -15,5 +15,5 @@
}, },
"type": "module", "type": "module",
"types": "index.d.ts", "types": "index.d.ts",
"version": "2.52.0" "version": "2.51.0-dev"
} }

View File

@@ -18,11 +18,6 @@ ignore = [
# this should be fixed by upgrading to iroh 1.0 once it is released. # this should be fixed by upgrading to iroh 1.0 once it is released.
"RUSTSEC-2025-0134", "RUSTSEC-2025-0134",
# Unmaintained proc-macro-error2
# Transitive dependency of typescript-type-def 0.5.13.
# <https://rustsec.org/advisories/RUSTSEC-2026-0173>
"RUSTSEC-2026-0173",
# rustls-webpki v0.102.8 # rustls-webpki v0.102.8
# We cannot upgrade to >=0.103.10 because # We cannot upgrade to >=0.103.10 because
# it is a transitive dependency of iroh 0.35.0 # it is a transitive dependency of iroh 0.35.0

View File

@@ -147,7 +147,6 @@
CARGO_BUILD_TARGET = rustTarget; CARGO_BUILD_TARGET = rustTarget;
TARGET_CC = "${pkgsWin64.stdenv.cc}/bin/${pkgsWin64.stdenv.cc.targetPrefix}cc"; TARGET_CC = "${pkgsWin64.stdenv.cc}/bin/${pkgsWin64.stdenv.cc.targetPrefix}cc";
CFLAGS_x86_64_pc_windows_gnu = "-I${pkgsWin64.windows.pthreads}/include";
CARGO_BUILD_RUSTFLAGS = [ CARGO_BUILD_RUSTFLAGS = [
"-C" "-C"
"linker=${TARGET_CC}" "linker=${TARGET_CC}"
@@ -204,7 +203,6 @@
src = pkgs.lib.cleanSource ./.; src = pkgs.lib.cleanSource ./.;
nativeBuildInputs = [ nativeBuildInputs = [
pkgs.perl # Needed to build vendored OpenSSL. pkgs.perl # Needed to build vendored OpenSSL.
pkgs.nasm # aws-lc-sys requires it
]; ];
depsBuildBuild = [ depsBuildBuild = [
winCC winCC
@@ -217,7 +215,6 @@
CARGO_BUILD_TARGET = rustTarget; CARGO_BUILD_TARGET = rustTarget;
TARGET_CC = "${winCC}/bin/${winCC.targetPrefix}cc"; TARGET_CC = "${winCC}/bin/${winCC.targetPrefix}cc";
CFLAGS_i686_pc_windows_gnu = "-I${pkgsWin32.windows.pthreads}/include";
CARGO_BUILD_RUSTFLAGS = [ CARGO_BUILD_RUSTFLAGS = [
"-C" "-C"
"linker=${TARGET_CC}" "linker=${TARGET_CC}"

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "deltachat" name = "deltachat"
version = "2.52.0" version = "2.51.0-dev"
license = "MPL-2.0" license = "MPL-2.0"
description = "Python bindings for the Delta Chat Core library using CFFI against the Rust-implemented libdeltachat" description = "Python bindings for the Delta Chat Core library using CFFI against the Rust-implemented libdeltachat"
readme = "README.rst" readme = "README.rst"

View File

@@ -1 +1 @@
2026-06-09 2026-05-29

View File

@@ -3738,17 +3738,19 @@ pub(crate) async fn update_chat_contacts_table(
id: ChatId, id: ChatId,
contacts: &BTreeSet<ContactId>, contacts: &BTreeSet<ContactId>,
) -> Result<()> { ) -> Result<()> {
// See add_to_chat_contacts_table() for reasoning.
let limit = cmp::max(time().saturating_add(TIMESTAMP_SENT_TOLERANCE), timestamp);
context context
.sql .sql
.transaction(move |transaction| { .transaction(move |transaction| {
// Bump `remove_timestamp` to at least `now` // Bump `remove_timestamp` even for members from `contacts`.
// even for members from `contacts`.
// We add members from `contacts` back below. // We add members from `contacts` back below.
transaction.execute( transaction.execute(
"UPDATE chats_contacts "UPDATE chats_contacts SET
SET remove_timestamp=MAX(add_timestamp+1, ?) add_timestamp=MIN(add_timestamp, ?1),
remove_timestamp=MAX(MIN(remove_timestamp,?1), MIN(add_timestamp,?1)+1, ?)
WHERE chat_id=?", WHERE chat_id=?",
(timestamp, id), (limit, timestamp, id),
)?; )?;
if !contacts.is_empty() { if !contacts.is_empty() {
@@ -3760,9 +3762,8 @@ pub(crate) async fn update_chat_contacts_table(
)?; )?;
for contact_id in contacts { for contact_id in contacts {
// We bumped `add_timestamp` for existing rows above, // We bumped `remove_timestamp` for existing rows above,
// so on conflict it is enough to set `add_timestamp = remove_timestamp` // so on conflict it is enough to set `add_timestamp = remove_timestamp`.
// and this guarantees that `add_timestamp` is no less than `timestamp`.
statement.execute((id, contact_id, timestamp))?; statement.execute((id, contact_id, timestamp))?;
} }
} }
@@ -3779,17 +3780,24 @@ pub(crate) async fn add_to_chat_contacts_table(
chat_id: ChatId, chat_id: ChatId,
contact_ids: &[ContactId], contact_ids: &[ContactId],
) -> Result<()> { ) -> Result<()> {
// Our clock may be slow, so limit stored timestamps with `timestamp` if it's bigger. This way
// we only cap remote timestamps if, in addition, remote changes arrive reordered or we do local
// changes. Also allow some tolerance, moreover, previous removals might lend time from the
// future.
let limit = cmp::max(time().saturating_add(TIMESTAMP_SENT_TOLERANCE), timestamp);
context context
.sql .sql
.transaction(move |transaction| { .transaction(move |transaction| {
let mut add_statement = transaction.prepare( let mut add_statement = transaction.prepare(
"INSERT INTO chats_contacts (chat_id, contact_id, add_timestamp) VALUES(?1, ?2, ?3) "INSERT INTO chats_contacts (chat_id, contact_id, add_timestamp) VALUES(?1, ?2, ?3)
ON CONFLICT (chat_id, contact_id) ON CONFLICT (chat_id, contact_id)
DO UPDATE SET add_timestamp=MAX(remove_timestamp, ?3)", DO UPDATE SET
remove_timestamp=MIN(remove_timestamp, ?4),
add_timestamp=MIN(MAX(add_timestamp,remove_timestamp,?3), ?4)",
)?; )?;
for contact_id in contact_ids { for contact_id in contact_ids {
add_statement.execute((chat_id, contact_id, timestamp))?; add_statement.execute((chat_id, contact_id, timestamp, limit))?;
} }
Ok(()) Ok(())
}) })
@@ -3808,13 +3816,16 @@ pub(crate) async fn remove_from_chat_contacts_table(
contact_id: ContactId, contact_id: ContactId,
) -> Result<bool> { ) -> Result<bool> {
let now = time(); let now = time();
// See add_to_chat_contacts_table() for reasoning.
let limit = now.saturating_add(TIMESTAMP_SENT_TOLERANCE);
let is_past_member = context let is_past_member = context
.sql .sql
.execute( .execute(
"UPDATE chats_contacts "UPDATE chats_contacts SET
SET remove_timestamp=MAX(add_timestamp+1, ?) add_timestamp=MIN(add_timestamp, ?1),
remove_timestamp=MAX(MIN(remove_timestamp,?1), MIN(add_timestamp,?1)+1, ?)
WHERE chat_id=? AND contact_id=?", WHERE chat_id=? AND contact_id=?",
(now, chat_id, contact_id), (limit, now, chat_id, contact_id),
) )
.await? .await?
> 0; > 0;