There is many ways to do what you desire, but I'm prefer the next approach.
spamassassin
is launched as local service available at file socket instead of IP port. That is for performance.
exim
is compiled with content-scan option and configured to interact with spamassasin
that rate messages and return the score. If score is higher than threshold (49 points in my case) some special headers will be added to the message:
spamd_address = /tmp/spamd.sock
. . . . .
acl_data:
accept hosts = : +relay_from_hosts
accept condition = ${if >{$message_size}{1M}}
warn spam = spamd
condition = ${if >{$spam_score_int}{49}}
add_header = X-Spam-Score: $spam_score_int
add_header = X-Spam-Ooops: Detected
log_message = SpamAssassin detected mesage
accept
I haven't check for spam messages larger than 1 MiB as far as spam is usually way smaller. Then I've deliver the message to the user's mailbox. I do that via transport through dovecot's deliver
:
localdelivery:
driver = pipe
user = mailnull
command = /usr/local/libexec/dovecot/deliver -d $local_part@$domain -f $sender_address
temp_errors = 64 : 69 : 70: 71 : 72 : 73 : 74 : 75 : 78
envelope_to_add
return_path_add
delivery_date_add
log_output
dovecot
have a pidgeonhole
plugin that implement sieve
functionality. All messages are proceeded through the global filter that look like that:
####
require "fileinto";
if exists "X-Spam-Ooops"
{
fileinto "Junk";
stop;
}
else
{
# Trigger to launch next script in sequence
keep;
}
####
The logic here is straightforward: if delivered message have the X-Spam-Ooops
header (assigned by exim
on the sa
score), it will be delivered into the INBOX.Junk
subfolder for each particular user. else
branch with keep
verb inside is needed for further proceeding the message through the custom user's filter(s) after global filter has been proceeded.