Can dovecot sieve query a database file?
Hi,
can dovecot sieve query any external database ( berkeley database file, rest api, whatever) to determine the target folder by sender address?
Background:
I have to migrate a mailbox from an old ubuntu system running with mailfilter as a local delivery agent, and dovecot just as an IMAP server.
Since recent ubuntu versions do not have mailfilter anymore (abandoned for a decade) I need to substitute the mailfilter file with dovecot sieve.
The old mailfilter script contained something like
if (gdbmopen(".mailfilter.gdbm", "R") == 0) { if ( (box=gdbmfetch(tolower($FROM),"D")) ne "" ) to $box
if ( /^From: .*<(.*)>/ ) { if ( (box=gdbmfetch(tolower($MATCH1),"D")) ne "" ) to $box }
gdbmclose }
which allowed to sort mails into mailboxes depending on the sender for hundreds of sender addresses in a fast end efficient way.
It wouldn't make sense and be too slow to translate this into a sieve script with a thousand
if header :matches ...
elsif header :matches
...
Is there a way to do this efficently in dovecot's sieve?
regards
Hadmut
at that point, why not run scripts already?
On October 5, 2025 12:54:48 AM GMT+03:00, Hadmut Danisch via dovecot <dovecot@dovecot.org> wrote:
Hi,
can dovecot sieve query any external database ( berkeley database file, rest api, whatever) to determine the target folder by sender address?
Background:
I have to migrate a mailbox from an old ubuntu system running with mailfilter as a local delivery agent, and dovecot just as an IMAP server.
Since recent ubuntu versions do not have mailfilter anymore (abandoned for a decade) I need to substitute the mailfilter file with dovecot sieve.
The old mailfilter script contained something like
if (gdbmopen(".mailfilter.gdbm", "R") == 0) { if ( (box=gdbmfetch(tolower($FROM),"D")) ne "" ) to $box
if ( /^From: .*<(.*)>/ ) { if ( (box=gdbmfetch(tolower($MATCH1),"D")) ne "" ) to $box }
gdbmclose }
which allowed to sort mails into mailboxes depending on the sender for hundreds of sender addresses in a fast end efficient way.
It wouldn't make sense and be too slow to translate this into a sieve script with a thousand
if header :matches ...
elsif header :matches
...
Is there a way to do this efficently in dovecot's sieve?
regards
Hadmut
dovecot mailing list -- dovecot@dovecot.org To unsubscribe send an email to dovecot-leave@dovecot.org
On Sun, Oct 05, 2025 at 07:34:14AM +0300, Sulev-Madis Silber via dovecot wrote:
at that point, why not run scripts already?
Because I wanted to do it just straigthforward, keep things together in one place, not open a new djungle of error sources and complications, and not run a new process for every single mail that comes in.
And because I'd consider this as the MDA's task and wouldn't love to attach external scripts just to help it do it's job.
And because if I had to do everything with a self written script, I wouldn't bother to use dovecot's sieve at all and just use my script.
:-)
On 05/10/2025 11:34, Hadmut Danisch via dovecot wrote:
On Sun, Oct 05, 2025 at 07:34:14AM +0300, Sulev-Madis Silber via dovecot wrote:
at that point, why not run scripts already?
Because I wanted to do it just straigthforward, keep things together in one place, not open a new djungle of error sources and complications, and not run a new process for every single mail that comes in.
And because I'd consider this as the MDA's task and wouldn't love to attach external scripts just to help it do it's job.
And because if I had to do everything with a self written script, I wouldn't bother to use dovecot's sieve at all and just use my script.
:-) Does your old server speak IMAP? If so, would imapsync be suitable?
Hadmut Danisch via dovecot <dovecot@dovecot.org> writes:
Hi,
can dovecot sieve query any external database ( berkeley database file, rest api, whatever) to determine the target folder by sender address?
dovecot alone cannot do this.
But I believe that you can do in your sieve file:
--8<---------------cut here---------------start------------->8--- require [ "vnd.dovecot.execute", "vnd.dovecot.pipe" ,"vnd.dovecot.filter", "fileinto" ] // I do not know if all vnd.* are really needed execute :pipe :output "calculated_mailbox" "fancy_script.sh" [ "parameter1" , "parameter2" , ... ] ; fileinto "${calculated_mailbox}"; --8<---------------cut here---------------end--------------->8---
and your "fancy_script" can do anything you want. Be aware, that dovecot enforces timeout for script.
KJ
-- http://wolnelektury.pl/wesprzyj/teraz/ How long does it take a DEC field service engineer to change a lightbulb?
It depends on how many bad ones he brought with him.
On October 10, 2025 9:18:35 AM GMT+03:00, "Kamil Jońca via dovecot" <dovecot@dovecot.org> wrote:
Hadmut Danisch via dovecot <dovecot@dovecot.org> writes:
Hi,
can dovecot sieve query any external database ( berkeley database file, rest api, whatever) to determine the target folder by sender address?
dovecot alone cannot do this.
But I believe that you can do in your sieve file:
--8<---------------cut here---------------start------------->8--- require [ "vnd.dovecot.execute", "vnd.dovecot.pipe" ,"vnd.dovecot.filter", "fileinto" ] // I do not know if all vnd.* are really needed execute :pipe :output "calculated_mailbox" "fancy_script.sh" [ "parameter1" , "parameter2" , ... ] ; fileinto "${calculated_mailbox}"; --8<---------------cut here---------------end--------------->8---
and your "fancy_script" can do anything you want. Be aware, that dovecot enforces timeout for script.
KJ
i heard he doesn't want it. but sieve can't possibly include everything and also a kitchen sink directly inside it
i use it in mail monitoring so i know that mail enters via smtp and it also goes thru sieve
require [ "variables", "regex", "fileinto", "mailbox", "imap4flags", "vnd.dovecot.execute" ];
and in the end, just before
fileinto :create "yyy/${aaa}";
i have this
if string :is "${bbb}" "xxx" { execute :pipe "zzz.sh"; discard; stop; }
while script does this
#!/bin/sh -Cefu
set -Cefu
file=/ddd/fff.eee
temp_file="$file.tmp"
umask 022
rm -f "$temp_file"
( cat; printf 'ts=%d\r\n' "date +%s
" ) > "$temp_file"
mv -f "$temp_file" "$file"
for completeness, other end is
#!/bin/sh -Cefu
set -Cefu
cd /000 || exit 1
to=111
dst_file=222
status_file=tmp/status
debug=
_fail() { echo "$0: fail: $1" | wall }
if [ ! -t 0 ]
then
sleep "jot -r 1 0 10
"
fi
rm -f "$status_file"
str="env LANG=C tr -cd '[:alnum:]' < /dev/urandom | head -c 128
"
ts_start="date +%s
"
echo "$str" | mail "$to"
timeout -k 40 30 tail -0 -F "$dst_file" | tr -u -d '\r'
| egrep --line-buffered "^$str$" | while read line
do
ts_end_remote="grep -m 1 ^ts= \"$dst_file\" | cut -d = -f 2 \ | tr -d '\r'
"
if echo "$ts_end_remote" | egrep -q '^[0-9]+$'
then
echo ok > "$status_file"
if [ "$debug" ]
then
echo "ok in ~$((ts_end_remote - ts_start))s"
fi
else
echo fail > "$status_file"
_fail "invalid ts_end_remote"
fi
pkill -P "$$" timeout
done
if [ ! -f "$status_file" ] then _fail timeout fi
rm -f "$status_file"
note, can contain fbsd specifics
i don't think external programs are bad, but you need to verify they are secure and they don't consume too many resources
querying a database is expensive enough
just don't self-ddos it with every-mail-script that performs 10s long unoptimized sql queries
or, worse
yolo-pipe bobby-tables content from the internet into your non-input-checked utility
both issues can be mitigated by proper techniques!
Sulev-Madis Silber via dovecot <dovecot@dovecot.org> writes:
[...]
i heard he doesn't want it.
Yes. After sending my reply I read it. But for now I don't know any other solution.
but sieve can't possibly include everything and also a kitchen sink directly inside it
I believe that some kind of db access may be desired, but not neccesrily prioritzed by dovecot devlopers.
KJ
-- http://wolnelektury.pl/wesprzyj/teraz/ You know that feeling when you're leaning back on a stool and it starts to tip over? Well, that's how I feel all the time. -- Steven Wright
participants (4)
-
Hadmut Danisch
-
Kamil Jońca
-
Nick Howitt
-
Sulev-Madis Silber