Skip to content

fix: Don't send removal message to contact that hasn't been a chat member (#8298)#8310

Merged
iequidoo merged 1 commit into
mainfrom
iequidoo/cant_remove_nonmember
Jun 7, 2026
Merged

fix: Don't send removal message to contact that hasn't been a chat member (#8298)#8310
iequidoo merged 1 commit into
mainfrom
iequidoo/cant_remove_nonmember

Conversation

@iequidoo
Copy link
Copy Markdown
Collaborator

@iequidoo iequidoo commented Jun 4, 2026

I.e. don't fail remove_contact_from_chat() for such a contact because there may be a race condition with a remote removal of the contact done without trace, but don't send a removal message and sync message in this case and don't emit a ChatModified event.

If a contact is already a past member, we still send a removal message to the chat, this is safe and protects from lost removal messages, so there's no need to complicate the code in this case.

Fix #8298

Comment thread src/chat.rs Outdated
transaction.execute(
"UPDATE chats_contacts
SET remove_timestamp=MAX(add_timestamp+1, ?)
SET remove_timestamp=MAX(remove_timestamp, add_timestamp+1, ?)
Copy link
Copy Markdown
Collaborator Author

@iequidoo iequidoo Jun 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

timestamp passed to the function may be a remote timestamp, so we need to make sure to not decrease remove_timestamp. The resulting timestamp shall not depend on the order of received messages. The same for add_timestamp in add_to_chat_contacts_table()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change needed for the fix? Looks unrelated.

It can also be that our clock was wrong in the past, then timestamps in the database may be stuck in the future and never limited back to current timestamp with this change.

Copy link
Copy Markdown
Collaborator Author

@iequidoo iequidoo Jun 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can move this to a separate commit/PR and add a test.

If we want to protect from wrong clocks, bumping to add_timestamp+1 also looks incorrect: if it's in the future, we should fix up add_timestamp instead, otherwise it never fixes if this function is called and even increments each time for the passed contacts, see DO UPDATE SET add_timestamp=remove_timestamp below.

This change fixes a more realistic scenario when messages arrive reordered, resulting remove_timestamp shouldn't change because of this. We can protect from wrong clocks as well, but then need to do everything more accurately.

EDIT: Decided to move this to a separate PR at least.
EDIT: Made a fix which decreases member timestamps only when necessary, but not going to create a PR because it seems there are no important cases justifying the code complication: iequidoo/dont-decrease-member-timestamps

Comment thread src/chat/chat_tests.rs
let bob = &tcm.bob().await;
let charlie = &tcm.charlie().await;

let alice_broadcast_id = create_broadcast(alice, "Channel".to_string()).await?;
Copy link
Copy Markdown
Collaborator Author

@iequidoo iequidoo Jun 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same test for groups may be added, but group contact removal shares the common code with broadcasts, so such a test won't test anything in addition

@iequidoo iequidoo marked this pull request as ready for review June 4, 2026 00:49
@iequidoo iequidoo requested a review from adbenitez June 4, 2026 00:50
@iequidoo iequidoo marked this pull request as draft June 4, 2026 01:51
@iequidoo
Copy link
Copy Markdown
Collaborator Author

iequidoo commented Jun 5, 2026

https://github.com/chatmail/core/actions/runs/26922572038/job/79426682834?pr=8310 -- the test_withdraw_securejoin_qr failure is unrelated, alice didn't even receive vc-request-pubkey, probably the test timed out because bob spent time trying to connect to IMAP when configuring:

DEBUG    root:rpc.py:194 account_id=2 got an event {'kind': 'Warning', 'msg': 'Read error on stream 49.12.217.82:993 after reading 0 and writing 246 bytes: timed out.'}
DEBUG    root:rpc.py:194 account_id=2 got an event {'kind': 'Warning', 'msg': 'src/imap/client.rs:151: IMAP failed to connect to ci-chatmail.testrun.org (49.12.217.82:993): timed out.'}
DEBUG    root:rpc.py:194 account_id=2 got an event {'kind': 'Warning', 'msg': 'src/imap.rs:368: IMAP failed to connect to ci-chatmail.testrun.org:993:tls: All connection attempts failed: timed out.'}
DEBUG    root:rpc.py:194 account_id=2 got an event {'kind': 'Info', 'msg': 'src/imap.rs:355: IMAP trying to connect to ci-chatmail.testrun.org:143:starttls.'}
DEBUG    root:rpc.py:194 account_id=2 got an event {'kind': 'Info', 'msg': 'src/net/dns.rs:134: Using memory-cached DNS resolution for ci-chatmail.testrun.org.'}
DEBUG    root:rpc.py:194 account_id=2 got an event {'kind': 'Info', 'msg': 'src/net/dns.rs:180: Resolved ci-chatmail.testrun.org into 49.12.217.82.'}
DEBUG    root:rpc.py:194 account_id=2 got an event {'kind': 'Info', 'msg': 'src/imap/client.rs:122: Attempting IMAP connection to ci-chatmail.testrun.org (49.12.217.82:143).'}
DEBUG    root:rpc.py:194 account_id=2 got an event {'kind': 'Info', 'msg': 'src/imap.rs:393: Logging into IMAP server with LOGIN.'}
DEBUG    root:rpc.py:194 account_id=2 got an event {'kind': 'ImapConnected', 'msg': 'IMAP-LOGIN as zwtahg2yk@ci-chatmail.testrun.org'}
DEBUG    root:rpc.py:194 account_id=2 got an event {'kind': 'ConnectivityChanged'}
DEBUG    root:rpc.py:194 account_id=2 got an event {'kind': 'Info', 'msg': 'src/imap.rs:436: Successfully logged into IMAP server.'}
DEBUG    root:rpc.py:194 account_id=2 got an event {'comment': None, 'kind': 'ConfigureProgress', 'progress': 850}

CFFI Python test_logged_ac_process_ffi_failure failure is unrelated as well.

@iequidoo iequidoo marked this pull request as ready for review June 5, 2026 00:50
Comment thread src/chat.rs
@iequidoo iequidoo requested a review from link2xt June 5, 2026 01:05
@iequidoo iequidoo force-pushed the iequidoo/cant_remove_nonmember branch from b8f2276 to 402e339 Compare June 5, 2026 01:08
Comment thread src/chat.rs Outdated

/// Removes a contact from the chat
/// without leaving a trace.
/// Returns whether the contact has ever been a chat member since this function was called the
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is difficult to understand without reading the code and is not entirely true, past members are also removed after some time.

I think it's better to say that it returns whether any database row was removed, either for an existing member or a "past member".

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to

/// Returns whether the contact was removed, even if it was a past contact. If so, a removal message
/// should be sent if the removal is issued by this device.

…mber (#8298)

I.e. don't fail `remove_contact_from_chat()` for such a contact because there may be a race
condition with a remote removal of the contact done without trace, but don't send a removal message
and sync message in this case and don't emit a `ChatModified` event.

If a contact is already a past member, we still send a removal message to the chat, this is safe and
protects from lost removal messages, so there's no need to complicate the code in this case.
@iequidoo iequidoo force-pushed the iequidoo/cant_remove_nonmember branch from 402e339 to 729ef80 Compare June 6, 2026 16:35
@iequidoo iequidoo requested a review from link2xt June 6, 2026 16:38
@iequidoo iequidoo merged commit c91608e into main Jun 7, 2026
31 checks passed
@iequidoo iequidoo deleted the iequidoo/cant_remove_nonmember branch June 7, 2026 16:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

it is possible to remove non-members from channel

2 participants