Commit Graph
10757 Commits
Author SHA1 Message Date
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
link2xt 40c0954e42 chore: update async-imap to 0.12.0
async-imap 0.12.0 uses imap-proto 0.17.0 which includes bugfixes
for parsing responses with unicode characters.

Also upgraded base64 to 0.23 since new async-imap depends on it anyway.
2026-10-08 22:19:32 +00:00
link2xt 3a79324728 refactor: stop excluding COPYUID responses from the logs
We do not move the messages out of the inbox anymore,
so should not get these responses anyway.
2026-10-08 22:19:32 +00:00
link2xt 6ab0cd513a test: do not ignore errors in add_pseudo_transport() 2026-10-08 17:09:55 +00:00
biørn 0bee156449 api! remove unused dc_chatlist_get_summary2() (#8812)
`dc_chatlist_get_summary2()` was introduced for desktop that time,
but then replaced by jsonrpc's `get_chat_list_item_by_id()`.

outside desktop,
the cffi function seems to be never used in the past nor is today.

@adbenitez @Amzd - can you confirm it is not used?

similar for `summaryPreviewImage` - that was used on desktop, and is now
unused member of jsonrpc's ChatListItemFetchResult. question here: i
assume that it is fine to remove the member, and that desktop (maybe
after rebuilding) is fine with that.

@WofWca @nicodh - can you confirm, desktop is fine with the removal of
the member?

for general cleanup,
in an anyway complex area, it makes sense to remove these APIs.
2026-10-07 17:32:42 +02:00
link2xt 9ce20d7763 chore: update rPGP from 0.20.0 to 0.21.0
Main updates are security fix (fixed panic on short session keys)
and stabilized PQC (feature renamed from "draft-pqc" to "pqc").
2026-10-07 14:26:09 +00:00
holger krekel d7376e32b2 refactor: reduce macro-generated lines by >70%, and drop tracing
Every `info!`, `warn!` and `error!` call expanded a complete
`tracing::event!` to mirror its message into `tracing` (#6919),
and that mirror made up most of core's macro output, now removed:
it's down from 219k to 54k lines using

    RUSTC_BOOTSTRAP=1 cargo rustc -p deltachat --lib \
        --profile check -- -Zmacro-stats

A warm build and `touch src/lib.rs` with rustc 1.99.0,

    CARGO_INCREMENTAL=0 RUSTC_WRAPPER= /usr/bin/time -v \
        cargo check -p deltachat

takes about a third less time and 0.37 GiB less peak memory,
`cargo build -p deltachat` about a tenth less time and 0.36 GiB less.
The release binary from `nix build .#deltachat-rpc-server-x86_64-linux`
gets 2.7% smaller.

If we want to use tracing events to integrate better with iroh-debugging,
for example when we move to iroh 1.X,
we could introduce some iroh-relevant tracing events in core.
2026-10-06 23:52:19 +02:00
holger krekel 6378533b6a ci: speedup lint job and the Rust test builds
Since #8350 (2026-09-09) deltachat-jsonrpc-bindings build-depends
on deltachat-jsonrpc, so clippy, nextest and the doctests,
which select the whole workspace, compiled core
and its whole dependency tree a second time for the host.
The lint job also checked every dependency twice, once per panic strategy,
and repeated clippy's checks in a separate all-features `cargo check`.

With a warm cache on 4 CPUs on my machine, the lint job drops
from 150 to 60 seconds and the tests build
from 100 to 75 seconds.
Peak memory falls for lint from
5.1 to 3.5 GiB and for tests
7.5 to 5.4 GiB.
2026-10-06 21:17:34 +02:00
link2xt 1451478911 feat: connect to the most recently successfully used SMTP transport first
When connecting to SMTP, transports are now tried
from the most recently successfully used transport to the least recently used.
Transports that were never used for sending
are tried in the order of increasing ID because init_transports()
tries to select the fastest transport for the first one.

This solves the problem of having to wait for timeout each time
if the first tried transport is down.
2026-10-06 16:54:09 +00:00
link2xt 8468f5b5dd ci: update Rust to 1.99.0 2026-10-03 16:49:16 +00:00
link2xt dbba555a63 chore: update astral-tokio-tar from 0.6.4 to 0.7.0 2026-10-03 16:46:40 +00:00
link2xt 8cedea1958 fix(deltachat-rpc-client): shutdown all threads after RPC server crash
RPC server subprocess may exit for various reasons,
e.g. because of panic or because of debug_assert! during testing,
or because the process is killed manually or by OOM killer.

We still want to shutdown all threads cleanly in this case
and not fail in Rpc.close(). Otherwise Python process cannot exit
as it waits forever for all threads to terminate.

Without the fix, when added test is run in pytest,
pytest prints "ERROR at teardown of test_shutdown" and gets stuck.
When pytest is manually terminated with ^C, the following error is printed:

```
Exception ignored while joining a thread in _thread._shutdown():
Traceback (most recent call last):
  File "/usr/lib/python3.14/threading.py", line 1583, in _shutdown
    _thread_shutdown()
KeyboardInterrupt:
```

_thread_shutdown() is the function waiting for all non-daemon threads to finish.
2026-10-03 16:46:12 +00:00