10768 Commits
Author SHA1 Message Date
link2xt 4ad53321f8 chore: upgrade tokio from 1.53.1 to 1.53.3
Makes cargo-deny stop complaining about https://rustsec.org/advisories/RUSTSEC-2026-0386
This is not even a security issue and we don't use mentioned functions anyway.
2026-10-24 17:14:33 +00:00
link2xt f0be6c387e fix: ignore html/* MIME parts
HTML parts have text/html MIME type.
html/* is not a registered MIME type: https://www.iana.org/assignments/media-types
2026-10-24 17:08:12 +00:00
link2xt 4ce056c85c refactor(scheduler): do not wait for IMAP and SMTP loop to be started
If we spawned a task, we alread know that the loop is started,
there is no need to send empty value from the task back to the scheduler.
2026-10-24 17:01:05 +00:00
link2xt df19d9d1df fix: fix the order of arguments to merge_openpgp_certificates() call
This fixes flaky tests/test_securejoin.py::test_qr_scan_updates_new_relay_address
which hanged during the second securejoin on fast network (e.g. when testing against local container)
as Bob received securejoin pubkey message from Alice but did not merge the new relay list.
2026-10-24 16:52:41 +00:00
link2xt ca4820aec3 test: add test_import_public_key_tiebreak() 2026-10-24 16:52:41 +00:00
link2xt a4363aa308 test: add test_merge_openpgp_certificates_tiebreak()
This tests that the order of arguments
to merge_openpgp_certificates() is important
if signature timestamp is the same.
2026-10-24 16:52:41 +00:00
link2xt 6767500ea2 refactor: factor out Contact.relay_addresses() 2026-10-24 16:52:41 +00:00
link2xt dedc160a2a refactor: pass current timestamp as an argument to signature_subpackets()
pk_encrypt() already uses current timestamp to select the key for encryption,
no need to use a potentially different timestamp to sign the message.
2026-10-24 16:52:41 +00:00
link2xt 7a1df15f92 docs: document arguments to secret_key_to_public_key()
It is not obvious that relay list is comma-separated.
2026-10-24 16:52:41 +00:00
link2xt 1a632bdade chore: update clipboard-win from 5.4.0 to 5.4.3
Versions <= 5.4.1 are yanked, apparently because of this change in unsafe code:
https://github.com/DoumanAsh/clipboard-win/commit/38c3c3fef175c20ed8f8e33dacb9b1e3bb8eca87
Not a big problem because the crate is only used in repl via rustyline dependency,
but cargo-deny complains about the yanked crate.

Diff can be viewed at https://cratediff.com/crate/clipboard-win/5.4.0/5.4.3/
2026-10-24 15:28:38 +00:00
link2xt 98289e8dbb chore: bump verion to 2.66.0-dev 2026-10-22 20:33:10 +00:00
link2xt 706ee7a48c chore(release): prepare for 2.65.0 v2.65.0 2026-10-22 20:19:22 +00:00
link2xt 65da01f863 refactor: create all keyupdates in a single transaction 2026-10-22 20:06:10 +00:00
link2xt 7f3cafd792 refactor: make non_chat_headers() synchronous 2026-10-22 20:06:10 +00:00
link2xt 01fcfc17be refactor: make insert_into_smtp() synchronous 2026-10-22 20:06:10 +00:00
link2xt 52de603753 refactor: make message::insert_tombstone() synchronous
It is only doing SQL, so does not need to be async.
This way insert_into_smtp() can use a single transaction
to insert a tombstone and enqueue the message.
2026-10-22 20:06:10 +00:00
B. Petersen 4d4ec874d2 chore: remove unused broadcast icon 2026-10-22 21:57:52 +02:00
link2xt 41eed949ac chore: add deny.toml exception for RUSTSEC-2026-0382
It is a rustls-webpki v0.102.8 dependency of iroh 0.35.0 which cannot be upgraded.
2026-10-22 16:40:22 +00:00
link2xt 53fe350ebb chore: update webpki-roots 0.26.8 to 0.26.11 2026-10-22 16:40:22 +00:00
link2xt 6bf5c07794 chore: update rustls-webpki 0.103.15 to 0.103.16 2026-10-22 16:40:22 +00:00
biørn b4cc114195 feat: show info message for unpinning (#8852)
as recently discussed with @adbenitez and @Amzd, it is better to add an
info message also for unpinning a message:

otherwise the message "Alice pinned a message" may look like a bug if
there is nothing actually pinned as Bob unpinned for whatever reason
afterwards.

technically, this is a simple change (famous last words) as a messages
were always sent on pinning as well as on unpinning, but for unpinning,
they were additionally hidden.

now the "unpin" info message is visible, however, still tuned down: it
is not notified and it does not bubble up in the chatlist (both similar
to many info messages about group changes)

ftr, this is a minor change, and it should not block ongoing releases
wrt pinned messages
2026-10-22 14:55:22 +02:00
link2xt e16e452091 fix: don't ignore join_next() errors in background_fetch_any()
join_next() failing means something went really wrong, e.g. panic inside a task.
We also don't cancel the tasks other than by dropping JoinSet,
so join_next() should never return an error.
2026-10-22 10:10:09 +00:00
link2xt 26ec01deaa docs: remove outdated sentence saying QR scan aborts started securejoin processes
This is not true since bbb267331c
and there is a test test_parallel_securejoin()
2026-10-21 04:56:31 +00:00
link2xt 8e9c16baca chore: update webpki-roots to 1.0.10
It removes compromised emSign CA,
see https://emudhra.we-have-a-problem.com/ for details.
We have duplicate dependency because of iroh 0.35
still dependencing on the old version,
but most important is that IMAP/SMTP connections
are using the new version.
2026-10-20 15:44:09 +00:00
link2xt 8d2919d23e chore: bump version to 2.65.0-dev 2026-10-20 12:02:02 +00:00
link2xt dd4641071e chore(release): prepare for 2.64.0 v2.64.0 2026-10-20 11:37:33 +00:00
link2xt 7a129352c8 feat: do not copy Chat-Version header into unprotected headers
spec.md already documents Header Confidentiality Policy
that hides Chat-Version header in encrypted messages,
but so far we have copied the header into unprotected headers for compatibility.

Since 2.45.0 prefetch does not look at the unprotected Chat-Version
do decide if the message should be downloaded
(change b94792706a)
and since 2.50 show_emails setting which controlled
whether only "chat messages" are processed
is removed (change 63596a4940).
2026-10-20 10:58:50 +00:00
link2xt 38749e0e9c fix(imap): do not busy loop if there are no connection candidates and interrupt channel is closed
Without connection candidates IMAP busy loops when you try to stop I/O
because interrupt channel gets closed and recv() always returns an error immediately,
but no connection is attempted afterwards so IMAP loop keeps calling .prepare()
in a loop without ever yielding control to executor, so it cannot even be cancelled.

New JSON-RPC test test_pseudo_transport_start_stop_io reproduces the problem
from CFFI test tests/test_3_offline.py::TestOfflineChat::test_send_lots_of_offline_msgs
which added one transport without connection candidates and started I/O.
2026-10-20 10:17:39 +00:00
gluek c827cb17c8 fix: do not apply channel reaction summaries as reactions
A reaction summary of a broadcast channel is sent as an empty reaction
to the latest channel message. When another device of the channel owner
received it, it removed the owner's own reaction on that message and
queued the message for the next summary. So the owner's devices sent
the summary to each other, and to all subscribers, every 10 minutes
forever.
2026-10-20 08:42:52 +00:00
link2xt 94641df57b refactor: clean up remaining clippy::arithmetic_side_effects in mimeparser
self.parts.remove(i) can panic if "i" is out of range,
but now it is obvious that it will not because "i" is returned from position(),
and clippy is happy.
2026-10-20 06:45:03 +00:00
link2xt 5de39ba6a4 fix: avoid integer overflow when multiplying received image width by height
This is a minor fix, worst case an image with very large dimensions will not be classified as a File.
Mostly removing `clippy::arithmetic_side_effects`.
2026-10-20 06:45:03 +00:00
B. Petersen 286c21a1e5 add jsonrpc for get_summary_text 2026-10-19 23:45:55 +02:00
biørn 6dde761ea5 feat: do not spam channel summaries with reactions (#8829)
while showing reactions for channel summaries was regarded as "let's
try" when channel reactions were introduced, meanwhile, after some
experience, it only clutters and spams the chatlist.

for the subscriber, this is anyways only "you reacted", which is often
wrong as there are other reactions. for the owner it is about some names
they do not know. the idea of having some "feedback" for the owner is
still fulfilled as reactions are (optionally) notified - so owner can
still have the "ping ping ping" and relax :)

finallly, this is also what telegram is doing with their channels
2026-10-19 20:30:30 +02:00
link2xt 9637ecff6b ci: test with Python 3.15
Also moved python version to environment variable
so status checks will be named
"JSON-RPC Python tests (ubuntu-latest, latest)" instead of
"JSON-RPC Python tests (ubuntu-latest, 3.14)" and we don't have to
update the list of required status checks each time the version changes.
We already do the same for Rust.
2026-10-18 13:04:38 +00:00
WofWca dce6438d95 refactor: move call_state_ext to CallInfo 2026-10-16 14:49:42 +00:00
WofWca 151f12b724 fix: races in get_summary_text_ext, improve perf
Remove the two unnecessary DB calls that fetch the message
(which could possibly be different from the current `Message`).

Now the only async thing in `get_summary_text_ext` that remains
is `get_webxdc_info`.
See https://github.com/chatmail/core/pull/8826.
2026-10-16 14:49:42 +00:00
pancake d62438d03c fix: abort scheduler tasks after shutdown timeout (#8832)
The timeout currently owns each IMAP and SMTP JoinHandle. When 30
seconds elapse, dropping the timeout drops the handle and detaches the
task instead of cancelling it. On Haiku, a network task stuck behind a
missed or delayed socket wakeup can therefore outlive scheduler
shutdown, retaining connections and interfering with restart or
active-account deletion.

Borrow each handle while waiting, then explicitly abort and await it if
shutdown times out. This guarantees that scheduler shutdown does not
complete while a timed-out network task is still running.
2026-10-16 10:14:29 +00:00
link2xt 60e1d59182 chore: bump rustls from 0.23.45 to 0.23.46
This fixes
https://rustsec.org/advisories/RUSTSEC-2026-0346
https://rustsec.org/advisories/RUSTSEC-2026-0347
https://rustsec.org/advisories/RUSTSEC-2026-0348
https://rustsec.org/advisories/RUSTSEC-2026-0349
2026-10-16 08:33:45 +00:00
Hocuri 64e58755c0 fix: Encode '@' with '%40' again in QR codes (#8825)
Fix #8824

This commit fixes the following problem:

This is a Delta Chat invite link: https://i.delta.chat/#CFAE4957FFEB8529CC1A5CF568D0EFE4139D5749&v=3&x=8lJEkOodZ01W-Xz5sOvAI1JN&i=XonKX2YYHM14I5c-PBj3YAED&s=Pc4aW8eePr_lO1OFnwUJ5fVB&a=pbkwwo8z7%40nine.testrun.org&r=uydmygf8t@chat.vim.wtf,13xe893jx%40tarpit.fun&n=Alice&g=Great+group

It's linkified correctly on Github, but WhatsApp makes only part of the link clickable, so that the inviter name gets lost. A group invite link additionally loses the group name and gets converted into a 1:1 invite.

When I replace the `@` with `%40`, it gets linkified correctly.

Apparently, WhatsApp tries to be smart and assumes that if there is an `@` in the link, it's almost over, and treats any non-ascii character as the end of the link.

...So, if we want our links to render correctly on WhatsApp, I guess we do need to replace `@` with `%40`. That's a very simple one-line fix (plus tests). It will just make the links look a little bit uglier.
2026-10-14 17:18:30 +02:00
link2xt d766641ce2 feat: switch to another SMTP transport when current one returns an error
Classic email servers sometimes start rejecting all messages and ask the user to solve web CAPTCHA, but still allow to connect.
In this case we want to switch to another SMTP server by moving the transport to the end of the list returned by smtp::sorted_transports.

We disconnect after any message sending failure, so next message or resent same message will be sent over another other transport.

Permanent SMTP error does not necessarily mean that switching to another server will help.
For example if the user tries to send a large message, any server will reject it, and it is not a failure of the server.
We do not try to distinguish between error messages, so it is expected that we may
sometimes switch to another server when it is unnecessary.
2026-10-13 00:43:51 +00:00
link2xt 5510fb5a18 refactor(smtp): introduce smtp::Connection structure
It is now impossible for SMTP client to be connected
without having a From address and transport_id available.

Follow-up to 70730490b2
2026-10-12 23:35:58 +00:00
link2xt 036b928a7f feat: take key flags and expiration into account when selecting the key
Test keys were generated with GnuPG 2.4.9 from Arch Linux.

The script that was used to generate the key for test_select_pk_for_encryption:
```
export GNUPGHOME="$PWD/gnupghome"
rm -fr "$GNUPGHOME"
mkdir -p "$GNUPGHOME"
chmod 700 "$GNUPGHOME"

# --faked-system-time example is from the gpg man page
GPG="gpg --batch --quiet --faked-system-time 20070924T154812"

USERID='Alice <alice@example.org>'

# Generate the primary key.
# "default" means certification+signing primary key
# "never" to make the key not expire, otherwise gpg generates expiring key by default.
$GPG --passphrase '' --quick-generate-key "$USERID" ed25519 default never

# --quick-add-key requires that keys are referred to by the fingerprint.
# Output of --with-colons is described in /usr/share/doc/gnupg/DETAILS
FINGERPRINT="$($GPG --list-secret-keys --with-colons | awk -F: '$1 == "fpr" { print $10 }')"

# Authentication-only RSA key, should not be used for encryption.
$GPG --passphrase '' --quick-add-key "$FINGERPRINT" rsa auth never

# Expired (considering the fake date) subkey.
$GPG --passphrase '' --quick-add-key "$FINGERPRINT" cv25519 encr 1d

# Signing subkey.
$GPG --passphrase '' --quick-add-key "$FINGERPRINT" ed25519 sign never

# Usable encryption subkey.
$GPG --passphrase '' --quick-add-key "$FINGERPRINT" cv25519 encr never

# Expiring subkey that is not expired in the beginning of 2008.
$GPG --passphrase '' --quick-add-key "$FINGERPRINT" cv25519 encr 1y

# Another encryption subkey that should not be used because the first one is preferred.
$GPG --passphrase '' --quick-add-key "$FINGERPRINT" cv25519 encr never

$GPG --armor --export "$FINGERPRINT" > alice.tpk.asc
```

Key for test_select_only_expired_subkey was generated with:
```
#!/bin/sh
set -e
export GNUPGHOME="$PWD/gnupghome"
rm -fr  "$GNUPGHOME"
mkdir -p "$GNUPGHOME"
chmod 700 "$GNUPGHOME"

GPG="gpg --batch --quiet --faked-system-time 20070924T154812"
USERID='Alice <alice@example.org>'
$GPG --passphrase '' --quick-generate-key "$USERID" ed25519 default never
FINGERPRINT="$($GPG --list-secret-keys --with-colons | awk -F: '$1 == "fpr" { print $10 }')"
$GPG --passphrase '' --quick-add-key "$FINGERPRINT" cv25519 encr 1y
$GPG --armor --export "$FINGERPRINT" > alice.tpk.asc
```

Key for test_expiring_subkey_selection was generated with:
```
#!/bin/sh
set -e
export GNUPGHOME="$PWD/gnupghome"
rm -fr  "$GNUPGHOME"
mkdir -p "$GNUPGHOME"
chmod 700 "$GNUPGHOME"

GPG="gpg --batch --quiet"
USERID='Alice <alice@example.org>'

$GPG --passphrase '' --faked-system-time 20250101T000000 --quick-generate-key "$USERID" ed25519 default never
FINGERPRINT="$($GPG --list-secret-keys --with-colons | awk -F: '$1 == "fpr" { print $10 }')"

$GPG --passphrase '' --faked-system-time 20250101T000000 --quick-add-key "$FINGERPRINT" cv25519 encr never

$GPG --passphrase '' --faked-system-time 20260601T000000 --quick-add-key "$FINGERPRINT" cv25519 encr 1m
$GPG --passphrase '' --faked-system-time 20260601T000000 --quick-add-key "$FINGERPRINT" cv25519 encr 2w
$GPG --passphrase '' --faked-system-time 20260608T000000 --quick-add-key "$FINGERPRINT" cv25519 encr 2w

$GPG --armor --export "$FINGERPRINT" > alice.tpk.asc
```
2026-10-10 21:01:24 +00:00
link2xt f3b043f111 refactor: remove Context argument from secret_key_to_public_key() 2026-10-10 21:01:24 +00:00
link2xt 694ef8ba76 chore: bump version to 2.64.0-dev 2026-10-09 17:59:58 +00:00
link2xt de6f469710 chore(release): prepare for 2.63.0 v2.63.0 2026-10-09 17:39:17 +00:00
link2xt 81140d514f fix: do not reencode animated WebPs into JPEG
Reencoding animated WebPs into JPEG loses all frames except the first one.
Avatars we still reencode because we don't want animated avatars anyway.

animated.webp is produced with this script using Pillow 12.3.0:
```
#!/usr/bin/env python3
from PIL import Image, ImageOps

with Image.open("avatar900x900.png") as im:
    frames = [im.rotate(x) for x in range(10, 360, 10)]
    im.save(
        "animated.webp",
        "webp",
        append_images=frames,
        lossless=True,
        duration=200,
        save_all=True,
    )
```

Resulting image is 677K, just slightly larger than the size needed to trigger reencoding.
2026-10-09 15:48:18 +00:00
holger krekel a8b6246052 fix: (linux-only) make sure large attachments return memory to kernel
The test fails on main with ~150MB memory remaining allocated
after sending/receiving two 20MB messages,
and stays well below when explicitely setting libc's M_MMAP_THRESHOLD.
2026-10-09 17:12:17 +02:00
biørnandHocuri 4ea5cddc09 api: new dc_msg_get_summary_text() (#8813)
this PR adds a new `msg_get_summary_text(add_forwarded, add_type_emoji,
approx_chars)` api, replacing the old `get_summarytext(approx_chars)`.

checking the concrete existing and new usecases, the new options are
required, eg. for:

- pinned messages: they do not show the user name (therefore, there
should not be a "forward" indicator), and may or may not not e.g.
display image left of string (this makes type emoji optional)

- notifications, quotes: they show user name (therefore, "forward"
indicator needed) and often display e.g. image a bit off (so type emoji
is potentially useful, cmp. whatsapp, but potentially also optional)

- copy to clipboard: clipboard should get forwarded and emoji, but text
should not be truncated and linends should be preserved - esp. the
latter was a silent bug in at least android. to support that better,
`approx_chars` can be set to `0` to skip truncation and unwrapping.

| Use case | `add_forwarded` | `add_type_emoji` | `approx_chars>0` |
| --- | :---: | :---: | :---: |
| Notification | ✅ | ✅ (potentially ❌) | ✅ |
| Quote | ✅  | ✅ (potentially ❌) | ✅ |
| Pinned message | ❌  | ❌ (rarely  ✅) | ✅ |
| Copy to clipboard | ✅ | ✅ | ❌  |
| Chatlist (core internal) | ✅ | ✅ | ✅ |
| Reaction summary (core internal) | ❌ | ✅ | ✅ |

(the get_summary() returning a "lot" can maybe be removed at some point,
in favor of adding the usernames directly to get_summary_text() and
changing add_forwarded to add_usernames. but that is out of scope)

jsonrpc can be added easily once we settle on name etc. 

references: https://github.com/chatmail/core/pull/8812 ,
https://github.com/chatmail/core/pull/8546

cc @Amzd , @adbenitez

---------

Co-authored-by: Hocuri <hocuri@gmx.de>
2026-10-09 13:49:59 +00:00
link2xt f8edd9136a chore: update mail-builder to 1.0 2026-10-09 12:49:55 +00:00
link2xt 697304e98a test: test sending and receiving long filename with emojis
mail-builder changed how such filename is encoded in Content-Disposition between versions 0.5.0 and 1.0.0.
We can parse both encodings already and the test was not failing, but it is worth adding this corner case to the tests.

mail-builder 0.5.0 encoded the filename like this:

Content-Disposition: attachment;
	filename="=?utf-8?Q?very_long_filename_with_emoji_=F0=9F=98=80_very_long_file_name_with_emoji_=F0=9F=AB=A0_very_long_file_name_with_emoji.tar.gz?="

mail-builder 1.0.0 encodes the filename like this:

Content-Disposition: attachment;
 filename*0*=UTF-8''very%20long%20filename%20with%20emoji%20%F0%9F%98%80%20;
 filename*1*=very%20long%20file%20name%20with%20emoji%20%F0%9F%AB%A0%20very;
 filename*2*=%20long%20file%20name%20with%20emoji.tar.gz
2026-10-09 12:49:55 +00:00