Короче.
Плюнул на не работающую отправку писем из system-filter.
Потом плюнул на не работающую отправку писем с помощью программы mail из bash.
И написал скрипт на perl, который все делает сам.
Добавил в system-filter обработку писем со спамом, и обработку писем пользователей.
Теперь схема такая:
Если письмо определилось, как спам, то в таблицу заносится запись о нем, а отсылающему генерируется письмо "напишите нам на
antispam@xxx.ru".
Если отсылатель - человек, то он отвечает на письмо, system-filter ловит этот ответ, заносит человека в whitelist, и шлет ему письмо "все ОК".
После многочисленных мудоханий с кривыми стандартами, получилась рабочая схема.
Адреса и subject письма корректно парсятся и декодируются, скрипт отправляет корректные письма.
При приеме письма, его текст сохраняется в папку /var/spool/mail/.xxx_spam_save
Туда же сохраняются логи.
Смотрите, чтобы у вас эта папка существовала и exim мог в неё писать.
Ну или поменяйте на свою.
Так же, нужно везде поменять xxx на ваш домен (а то будете долбиться на xxx.ru

)
Ещё момент, когда одно письмо приходит на много адресов, то и ответ идет со множества адресов

Но это уже нужно резать средствами exim.
Рекомендую сделать ящик antispam, и проверять его - некоторые могут попросить добавить в whitelist совсем не те ящики, с которых пишут.
И нужно будет добавить их ручками.
Понадобилось добавить в конфиг exim'а опцию:
А в acl для rcpt в самом начале добавить:
Код: Выделить всё
warn set acl_m1 = ${lc:${sender_address_local_part}@${sender_address_domain}}
warn set acl_m2 = ${lc:${local_part}@${domain}}
Плюс транспорт для сохранения сообщений в файл:
Код: Выделить всё
# system save
system_save:
driver = appendfile
file = /var/spool/mail/${address_file}
delivery_date_add
envelope_to_add
return_path_add
user = mail
group = mail
mode = 0600
directory_mode = 0750
create_directory = true
И, после всего, что у меня было с exim, получился вот такой system-filter:
Код: Выделить всё
# recieve antispam mails
if
( $acl_m2 contains "antispam@xxx.ru"
or $h_to: contains "antispam@xxx.ru" )
and not $h_x-xxx-spam: is "answer"
and $acl_m1 is "${lookup mysql{SELECT `sender_address` FROM `xxx_spam_save` \
WHERE `sender_address` = LOWER('$acl_m1') AND `whitelisted` = 0 AND `active` = 1 LIMIT 1}}"
and $acl_m1 is "${lookup mysql{UPDATE `xxx_spam_save` SET `whitelisted` = 1, \
`whitelist_date` = now() WHERE `sender_address` = LOWER('$acl_m1') \
AND `active` = 1}{$acl_m1}}"
then
save ".xxx_spam_save/${message_exim_id}.save.antispam"
pipe "/etc/exim/antispam.pl \"antispam\" \"$acl_m1\" \"$h_from:\" \"$rh_from:\" \"$acl_m2\" \"$h_to:\" \"$rh_to:\" \"$h_subject:\" \"$rh_subject:\""
unseen finish
endif
# block spam mails
if
not $acl_m3 is "accept"
and not $acl_m1 contains "MAILER-DAEMON@"
and not $acl_m1 is ""
and not $acl_m1 is "<>"
and not $acl_m1 is "@"
and ( $header_subject: contains "HOIMAMAHOI"
or $h_x-spam-score: contains "+++++"
or $h_x-dspam-result: contains "Spam" )
and not $acl_m2 contains "antispam@xxx.ru"
and not $h_x-xxx-spam: is "answer"
and not $acl_m1 is "${lookup mysql{SELECT `sender_address` FROM `xxx_spam_save` WHERE \
`sender_address` = LOWER('$acl_m1') and `whitelisted` = 1 AND `active` = 1 LIMIT 1}}"
and $acl_m1 is "${lookup mysql{REPLACE INTO xxx_spam_save SET `sender_address` = LOWER('$acl_m1'), \
`last_message_id` = '$message_exim_id', `active` = 1, `last_change_date` = now()}{$acl_m1}}"
then
save ".xxx_spam_save/${message_exim_id}.save.spam"
pipe "/etc/exim/antispam.pl \"spam\" \"$acl_m1\" \"$h_from:\" \"$rh_from:\" \"$acl_m2\" \"$h_to:\" \"$rh_to:\" \"$h_subject:\" \"$rh_subject:\""
seen finish
endif
Понадобилась вот такая табличка в БД:
Код: Выделить всё
CREATE TABLE `xxx_spam_save` (
`id` int(10) unsigned NOT NULL auto_increment,
`sender_address` varchar(255) NOT NULL default '',
`last_message_id` varchar(255) NOT NULL default '',
`whitelisted` tinyint(1) NOT NULL default '0',
`active` tinyint(1) NOT NULL default '1',
`last_change_date` datetime NOT NULL default '0000-00-00 00:00:00',
`whitelist_date` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`sender_address`),
KEY `id` (`id`)
) ENGINE=InnoDB;
Ну и сам скрипт /etc/exim/antispam.pl:
Код: Выделить всё
#!/usr/bin/perl
$| = 1;
$debug = 1;
$log_file = '/var/spool/mail/.xxx_spam_save/' . $$ . '.sendmail';
$mailserver = 'mail.xxx.ru';
use Net::SMTP;
use Text::Iconv;
use MIME::Base64;
use MIME::QuotedPrint;
sub com_log_open
{
return unless ($debug);
return (open $log, ">$_[0]" or die "$_[0]: $!");
}
sub com_log
{
return unless ($debug);
return unless (defined $log);
print $log "$_[0]\n";
}
sub com_log_close
{
return unless ($debug);
return unless (defined $log);
close $log;
}
com_log_open $log_file;
sub stripe
{
$line = shift;
com_log "to stripe: '$line'";
chomp $line;
$line =~ s/(^\s+|\s+$|[\n\r]+)//g;
com_log "striped: '$line'";
return $line;
}
$goal = shift;
com_log "goal: '$goal'";
$from = shift;
com_log "from: '$from'";
$from_full = shift;
com_log "from_full: '$from_full'";
$from_orig = shift;
com_log "from_orig: '$from_orig'";
$to = shift;
com_log "to: '$to'";
$to_full = shift;
com_log "to_full: '$to_full'";
$to_orig = shift;
com_log "to_orig: '$to_orig'";
$subj = shift;
com_log "subj: '$subj'";
$subj_orig = shift;
com_log "subj_orig: '$subj_orig'";
$from_old = $from_full;
com_log "from_old: '$from_old'";
$to_old = $to_full;
com_log "to_old: '$to_old'";
$subj_old = $subj;
com_log "subj_old: '$subj_old'";
$from = stripe $from;
$from = lc $from;
$from =~ s/\"//g;
com_log "cool from: '$from'";
$to = stripe $to;
$to = lc $to;
$to =~ s/\"//g;
com_log "cool to: '$to'";
$from_orig = stripe $from_orig;
com_log "cool from_orig: '$from_orig'";
$to_orig = stripe $to_orig;
com_log "cool to_orig: '$to_orig'";
$subj_orig = stripe $subj_orig;
com_log "cool subj_orig: '$subj_orig'";
$subj =~ s/^[\s\t]*[SPAM] //g;
if($subj =~ /[\r\n]{0,}[\s\t]{0,}\*\*\*SPAM\*\*\*/)
{
($i, $k) = $subj =~ /^[\s\t]{0,}(.+?)[\s\t]{0,}[\r\n]{0,}[\s\t]{0,}\*\*\*SPAM\*\*\*[\s\t]{0,}(.+?)[\s\t]{0,}$/;
com_log "subj_one: '$i'";
com_log "subj_two: '$k'";
if($i eq $k)
{
com_log "subjects equal, cutting";
$subj = $i;
}
}
if(($from_encode) = $from_orig =~ /^=\?([^\?]+?)\?[^\?]*\?[^\?]+?\?\=/)
{
com_log "from_encode: '$from_encode'";
$from_convert = Text::Iconv->new($from_encode, "utf8");
$from_full = $from_convert->convert($from_full);
com_log "coded from_full: '$from_full'";
$from_result = $from_convert->retval;
com_log "from_result: '$from_result'";
}
else
{
com_log 'no from_encode';
}
if(($to_encode) = $to_orig =~ /^=\?([^\?]+?)\?[^\?]*\?[^\?]+?\?\=/)
{
com_log "to_encode: '$to_encode'";
$to_convert = Text::Iconv->new($to_encode, "utf8");
$to_full = $to_convert->convert($to_full);
com_log "coded to_full: '$to_full'";
$to_result = $to_convert->retval;
com_log "to_result: '$to_result'";
}
else
{
com_log 'no to_encode';
}
if(($subj_encode) = $subj_orig =~ /^=\?([^\?]+?)\?[^\?]*\?[^\?]+?\?\=/)
{
com_log "subj_encode: '$subj_encode'";
$subj_convert = Text::Iconv->new($subj_encode, "utf8");
$subj = $subj_convert->convert($subj);
com_log "coded subj: '$subj'";
$subj_result = $subj_convert->retval;
com_log "subj_result: '$subj_result'";
}
else
{
com_log 'no subj_encode';
}
$cut_subj = $subj;
if(length($cut_subj) > 80)
{
$cut_subj = substr $cut_subj, 0, 77;
$cut_subj .= '...';
}
$subject = encode_base64("Re: $subj", '');
chomp $subject;
com_log "encoded subject: '$subject'";
$subject = '=?UTF-8?B?' . $subject . '?=';
com_log "full subject: '$subject'";
$support_addr = 'support@xxx.ru';
$addr = 'xspam@xxx.ru';
com_log "addr: '$addr'";
$h1 = 'X-xxx-spam: answer';
com_log "h1: '$h1'";
$h2 = 'MIME-Version: 1.0';
com_log "h2: '$h2'";
$h3= 'Content-Type: text/plain; charset=UTF-8';
com_log "h3: '$h3'";
$h4 = 'Content-Transfer-Encoding: base64';
com_log "h4: '$h4'";
$h5 = "From: $to_orig";
com_log "h5: '$h5'";
$h6 = "To: $from_orig";
com_log "h6: '$h6'";
$h7 = "Subject: $subject";
com_log "h7: '$h7'";
$h8 = "Reply-To: $addr";
com_log "h8: '$h8'";
if($goal eq 'spam')
{ $text = qq~
Тут текст о том, что письмо, отправленное на адрес "$to_full" (с темой "$cut_subj") было автоматически определено как спам.
Но если вы не спаммер, а человек, напишите нам по адресу $addr (или просто ответьте на данное письмо), и вы занесетесь в вайтлист.
Желательно ещё добавить тоже самое на английском.
~; }
elsif($goal eq 'antispam')
{ $text = qq~
Тут пишите, что все хорошо, больше такого не повторится.
Ну и повторите отправку первоначального письма.
Желательно ещё добавить тоже самое на английском.
~; }
else
{ $text = qq~
Ошибка!
Error!
~; }
com_log "===TEXT===";
com_log $text;
com_log "===TEXT===";
$text = encode_base64($text);
com_log "===ENCODED TEXT===";
com_log $text;
com_log "===ENCODED TEXT===";
$smtp = Net::SMTP->new($mailserver);
$smtp->mail($to_orig);
$smtp->to($from_orig);
$smtp->data();
$smtp->datasend($h1 . "\n");
$smtp->datasend($h2 . "\n");
$smtp->datasend($h3 . "\n");
$smtp->datasend($h4 . "\n");
$smtp->datasend($h5 . "\n");
$smtp->datasend($h6 . "\n");
$smtp->datasend($h7 . "\n");
$smtp->datasend($h8 . "\n");
$smtp->datasend("\n");
$smtp->datasend($text . "\n");
$smtp->dataend();
$smtp->quit;