castris
Verified User
Versions: DirectAdmin 1.713, Dovecot 2.4.5, Debian/Ubuntu (reproduced on two independent servers).
Summary
Saving the SpamAssassin/Rspamd "deliver to spam folder" setting (
Dovecot 2.4 writes
Steps to reproduce
Actual result
Expected result
The file keeps its format: for a v2 file,
Suggested fix
Either of these would avoid the problem:
Impact
Every account with Dovecot 2.4 v2
Workaround
Rewrite each affected file with the header on line 1:
Detection:
Summary
Saving the SpamAssassin/Rspamd "deliver to spam folder" setting (
CMD_API_SPAMASSASSIN action=save where=userspamfolder) queues action=defaultspam. That task appends INBOX.spam to every mailbox's Maildir/subscriptions and then sorts the whole file alphabetically, dropping blank lines.Dovecot 2.4 writes
subscriptions in its v2 format: the first line must be the header V<TAB>2, followed by an empty line, with the folder hierarchy separated by TAB (Archive<TAB>2023). After defaultspam sorts the file, the header is no longer on line 1, so Dovecot parses the file as v1 and treats the TAB as part of the folder name. LSUB then returns non-existent folders such as Archive<TAB>2023 (and even V<TAB>2), while the real subfolders are no longer subscribed. IMAP clients that show only subscribed folders (Thunderbird by default) hide every subfolder of the account. No messages are lost; only the subscription list is corrupted.Steps to reproduce
- Create a mailbox, e.g.
[email protected]. DirectAdmin createssubscriptionsin v1 format (single lineINBOX.spam). - Let Dovecot 2.4 write the file in v2 format. Simplest way: delete the file and subscribe some folders, which is what happens on any mailbox whose file was created by Dovecot 2.4 (e.g. after a migration):
Bash:rm /home/USER/imap/example.com/test/Maildir/subscriptions for f in Archive Archive.2023 Archive.2024 Varios; do doveadm mailbox create -u [email protected] "$f" 2>/dev/null doveadm mailbox subscribe -u [email protected] "$f" done cat -A /home/USER/imap/example.com/test/Maildir/subscriptions # V^I2$ # $ # Archive$ # Archive^I2023$ # Archive^I2024$ # Varios$ - Save the spam-folder setting for the domain (panel or API):
Bash:curl -s "$DA_URL/CMD_API_SPAMASSASSIN" -d action=save -d domain=example.com -d where=userspamfolder ... - Wait for
dataskqto runaction=defaultspam&domain=example.com&username=USER.
Actual result
Code:
Archive$
Archive^I2023$
Archive^I2024$
INBOX.spam$
V^I2$
Varios$
- The new entry is written in v1 form (
INBOX.spam, dot separator) inside a v2 file. - The whole file is sorted in C order, header included, so
V<TAB>2ends up in the middle. - The empty line after the header is removed.
Bash:
doveadm mailbox list -s -u [email protected] | cat -A
# Archive$
# Archive^I2023$
# Archive^I2024$
# INBOX.spam$
# V^I2$
# Varios$
Expected result
The file keeps its format: for a v2 file,
V<TAB>2 stays on line 1 followed by the empty line, existing entries keep their order and separator, and the spam folder is added as INBOX<TAB>spam. For a v1 file (no header), the current behaviour is harmless.Suggested fix
Either of these would avoid the problem:
- Let Dovecot manage the file:
doveadm mailbox subscribe -u <user> INBOX.spaminstead of editingsubscriptionsdirectly. Dovecot keeps whatever format the file already has. - If the file is edited directly: detect a first line equal to
V<TAB>2, keep the header and the following empty line untouched, append the entry with TAB as separator, and do not sort the file.
Impact
Every account with Dovecot 2.4 v2
subscriptions files is affected each time the spam-folder setting is saved for one of its domains. On the server where we found it, 52 of 68 mailboxes of one account were broken by a single save; the users saw all their subfolders "disappear" in Thunderbird.Workaround
Rewrite each affected file with the header on line 1:
Bash:
f=/home/USER/imap/DOMAIN/BOX/Maildir/subscriptions
tmp=$(mktemp)
{ printf 'V\t2\n\n'
grep -vP '^V\t2$' "$f" | grep -v '^$' | sed 's/^INBOX\.spam$/INBOX\tspam/' | awk '!seen[$0]++'
} > "$tmp" && cat "$tmp" > "$f" && rm -f "$tmp"
Detection:
grep -nP '^V\t2$' subscriptions returns a line number other than 1, or doveadm mailbox list -s -u <user> | cat -A | grep '\^I' returns any line.