From 6ebbb47012c43c0c9656f5ef256107d9a6861c39 Mon Sep 17 00:00:00 2001 From: LanQin_ Date: Wed, 17 Jun 2026 14:15:03 +0800 Subject: [PATCH] =?UTF-8?q?fix(mail):=20=E7=BB=9F=E4=B8=80=E9=82=AE?= =?UTF-8?q?=E7=AE=B1=E6=9F=A5=E8=AF=A2=E7=9A=84=E5=A4=A7=E5=B0=8F=E5=86=99?= =?UTF-8?q?=E4=B8=8E=E5=88=AB=E5=90=8D=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 Postfix/Dovecot 的 SQL 查询改为忽略大小写匹配,减少地址大小写导致的投递失败。 - 支持带 `+` 的详细地址解析,并基于域表关联定位邮箱目录。 - 启用 `lda_mailbox_autocreate`,确保投递到详细邮箱时可自动创建邮箱目录。 --- deploy/dovecot/dovecot-sql.conf.ext | 2 +- deploy/dovecot/dovecot.conf | 1 + deploy/postfix/sqlite-aliases.cf | 2 +- deploy/postfix/sqlite-mailboxes.cf | 2 +- deploy/postfix/sqlite-sender-bcc.cf | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/deploy/dovecot/dovecot-sql.conf.ext b/deploy/dovecot/dovecot-sql.conf.ext index 6d748a2..e1285d0 100644 --- a/deploy/dovecot/dovecot-sql.conf.ext +++ b/deploy/dovecot/dovecot-sql.conf.ext @@ -2,4 +2,4 @@ driver = sqlite connect = /data/lanqin.db default_pass_scheme = BLF-CRYPT password_query = SELECT address AS user, password_hash AS password FROM mailboxes WHERE address = '%u' AND status = 'active' -user_query = SELECT '/var/mail/vhosts/' || substr(address, instr(address, '@') + 1) || '/' || local_part AS home, 'maildir:/var/mail/vhosts/' || substr(address, instr(address, '@') + 1) || '/' || local_part || '/Maildir' AS mail, 5000 AS uid, 5000 AS gid FROM mailboxes WHERE address = '%u' AND status = 'active' UNION SELECT '/var/mail/vhosts/' || substr('%u', instr('%u', '@') + 1) || '/__unregistered__' AS home, 'maildir:/var/mail/vhosts/' || substr('%u', instr('%u', '@') + 1) || '/__unregistered__/Maildir' AS mail, 5000 AS uid, 5000 AS gid WHERE EXISTS (SELECT 1 FROM system_settings WHERE key='catchAllEnabled' AND value='true') AND EXISTS (SELECT 1 FROM domains WHERE name=substr('%u', instr('%u', '@') + 1) AND status='active') AND NOT EXISTS (SELECT 1 FROM mailboxes WHERE address='%u' AND status='active') +user_query = SELECT '/var/mail/vhosts/' || d.name || '/' || m.local_part AS home, 'maildir:/var/mail/vhosts/' || d.name || '/' || m.local_part || '/Maildir' AS mail, 5000 AS uid, 5000 AS gid FROM mailboxes m JOIN domains d ON d.id=m.domain_id WHERE d.name=lower(substr('%u', instr('%u', '@') + 1)) AND m.local_part=lower(CASE WHEN instr(substr('%u', 1, instr('%u', '@') - 1), '+') > 0 THEN substr(substr('%u', 1, instr('%u', '@') - 1), 1, instr(substr('%u', 1, instr('%u', '@') - 1), '+') - 1) ELSE substr('%u', 1, instr('%u', '@') - 1) END) AND m.status='active' AND d.status='active' UNION SELECT '/var/mail/vhosts/' || lower(substr('%u', instr('%u', '@') + 1)) || '/__unregistered__' AS home, 'maildir:/var/mail/vhosts/' || lower(substr('%u', instr('%u', '@') + 1)) || '/__unregistered__/Maildir' AS mail, 5000 AS uid, 5000 AS gid WHERE EXISTS (SELECT 1 FROM system_settings WHERE key='catchAllEnabled' AND value='true') AND EXISTS (SELECT 1 FROM domains WHERE name=lower(substr('%u', instr('%u', '@') + 1)) AND status='active') AND NOT EXISTS (SELECT 1 FROM mailboxes m JOIN domains d ON d.id=m.domain_id WHERE d.name=lower(substr('%u', instr('%u', '@') + 1)) AND m.local_part=lower(CASE WHEN instr(substr('%u', 1, instr('%u', '@') - 1), '+') > 0 THEN substr(substr('%u', 1, instr('%u', '@') - 1), 1, instr(substr('%u', 1, instr('%u', '@') - 1), '+') - 1) ELSE substr('%u', 1, instr('%u', '@') - 1) END) AND m.status='active') diff --git a/deploy/dovecot/dovecot.conf b/deploy/dovecot/dovecot.conf index f6690cb..4f0eab5 100644 --- a/deploy/dovecot/dovecot.conf +++ b/deploy/dovecot/dovecot.conf @@ -69,5 +69,6 @@ service auth { protocol lmtp { postmaster_address = postmaster@localhost recipient_delimiter = + + lda_mailbox_autocreate = yes lmtp_save_to_detail_mailbox = yes } diff --git a/deploy/postfix/sqlite-aliases.cf b/deploy/postfix/sqlite-aliases.cf index ace7263..37f5193 100644 --- a/deploy/postfix/sqlite-aliases.cf +++ b/deploy/postfix/sqlite-aliases.cf @@ -1,2 +1,2 @@ dbpath = /data/lanqin.db -query = SELECT destination FROM aliases WHERE source='%s' AND enabled=1 UNION SELECT address FROM mailboxes WHERE address='%s' AND status='active' +query = SELECT destination FROM aliases WHERE lower(source)=lower('%s') AND enabled=1 UNION SELECT address FROM mailboxes WHERE lower(address)=lower('%s') AND status='active' diff --git a/deploy/postfix/sqlite-mailboxes.cf b/deploy/postfix/sqlite-mailboxes.cf index 0f10975..9885ae1 100644 --- a/deploy/postfix/sqlite-mailboxes.cf +++ b/deploy/postfix/sqlite-mailboxes.cf @@ -1,2 +1,2 @@ dbpath = /data/lanqin.db -query = SELECT 'vhosts/' || substr(address, instr(address, '@') + 1) || '/' || local_part || '/Maildir/' FROM mailboxes WHERE address='%s' AND status='active' UNION SELECT 'vhosts/' || substr('%s', instr('%s', '@') + 1) || '/__unregistered__/Maildir/' WHERE EXISTS (SELECT 1 FROM system_settings WHERE key='catchAllEnabled' AND value='true') AND EXISTS (SELECT 1 FROM domains WHERE name=substr('%s', instr('%s', '@') + 1) AND status='active') AND NOT EXISTS (SELECT 1 FROM mailboxes WHERE address='%s' AND status='active') +query = SELECT 'vhosts/' || d.name || '/' || m.local_part || '/Maildir/' FROM mailboxes m JOIN domains d ON d.id=m.domain_id WHERE d.name=lower(substr('%s', instr('%s', '@') + 1)) AND m.local_part=lower(CASE WHEN instr(substr('%s', 1, instr('%s', '@') - 1), '+') > 0 THEN substr(substr('%s', 1, instr('%s', '@') - 1), 1, instr(substr('%s', 1, instr('%s', '@') - 1), '+') - 1) ELSE substr('%s', 1, instr('%s', '@') - 1) END) AND m.status='active' AND d.status='active' UNION SELECT 'vhosts/' || lower(substr('%s', instr('%s', '@') + 1)) || '/__unregistered__/Maildir/' WHERE EXISTS (SELECT 1 FROM system_settings WHERE key='catchAllEnabled' AND value='true') AND EXISTS (SELECT 1 FROM domains WHERE name=lower(substr('%s', instr('%s', '@') + 1)) AND status='active') AND NOT EXISTS (SELECT 1 FROM mailboxes m JOIN domains d ON d.id=m.domain_id WHERE d.name=lower(substr('%s', instr('%s', '@') + 1)) AND m.local_part=lower(CASE WHEN instr(substr('%s', 1, instr('%s', '@') - 1), '+') > 0 THEN substr(substr('%s', 1, instr('%s', '@') - 1), 1, instr(substr('%s', 1, instr('%s', '@') - 1), '+') - 1) ELSE substr('%s', 1, instr('%s', '@') - 1) END) AND m.status='active') diff --git a/deploy/postfix/sqlite-sender-bcc.cf b/deploy/postfix/sqlite-sender-bcc.cf index d8f967d..05cd7a9 100644 --- a/deploy/postfix/sqlite-sender-bcc.cf +++ b/deploy/postfix/sqlite-sender-bcc.cf @@ -1,2 +1,2 @@ dbpath = /data/lanqin.db -query = SELECT local_part || '+Sent@' || substr(address, instr(address, '@') + 1) FROM mailboxes WHERE address='%s' AND status='active' +query = SELECT local_part || '+Sent@' || substr(address, instr(address, '@') + 1) FROM mailboxes WHERE lower(address)=lower('%s') AND status='active'