Index: lib/Mail/SpamAssassin.pm =================================================================== --- lib/Mail/SpamAssassin.pm (.../vendor/SpamAssassin/3.1.x) (revision 39233) +++ lib/Mail/SpamAssassin.pm (.../trunk/email/spamassassin) (revision 39233) @@ -134,7 +134,7 @@ # first 3 are BSDish, latter 2 Linuxish @site_rules_path = ( - '__local_rules_dir__', + '/home/emailswitch/spamdef/current/lrules', '__prefix__/etc/mail/spamassassin', '__prefix__/etc/spamassassin', '/usr/local/etc/spamassassin', @@ -312,8 +312,8 @@ # didn't set PREFIX, we should have an estimated guess ready, values # substituted at 'make' time $self->{PREFIX} ||= '@@PREFIX@@'; - $self->{DEF_RULES_DIR} ||= '@@DEF_RULES_DIR@@'; - $self->{LOCAL_RULES_DIR} ||= '@@LOCAL_RULES_DIR@@'; + $self->{DEF_RULES_DIR} ||= '/home/emailswitch/spamdef/current'; + $self->{LOCAL_RULES_DIR} ||= '/home/emailswitch/spamdef/current/lrules'; $self->{LOCAL_STATE_DIR} ||= '@@LOCAL_STATE_DIR@@'; $self->{conf} ||= new Mail::SpamAssassin::Conf ($self); Index: lib/Mail/SpamAssassin/BayesStore/DBM.pm =================================================================== --- lib/Mail/SpamAssassin/BayesStore/DBM.pm (.../vendor/SpamAssassin/3.1.x) (revision 39233) +++ lib/Mail/SpamAssassin/BayesStore/DBM.pm (.../trunk/email/spamassassin) (revision 39233) @@ -130,6 +130,7 @@ sub tie_db_readonly { my ($self) = @_; + my $been_here = 0; if (!$self->HAS_DBM_MODULE) { dbg("bayes: " . $self->DBM_MODULE . " module not installed, cannot use bayes"); @@ -146,6 +147,8 @@ return 0; } +try_again: + $self->read_db_configs(); my $path = $main->sed_path($main->{conf}->{bayes_path}); @@ -195,7 +198,13 @@ if ($self->_check_db_version() != 0) { warn("bayes: bayes db version ".$self->{db_version}." is not able to be used, aborting!"); $self->untie_db(); - return 0; + if ($been_here) + { return 0; } # prevent some sort of inifinte loop + #lets try tying read-write to force an upgrade + return 0 unless $self->tie_db_writable(); + $been_here = 1; + $self->untie_db(); + goto try_again; } $self->{already_tied} = 1; Index: lib/Mail/SpamAssassin/Constants.pm =================================================================== --- lib/Mail/SpamAssassin/Constants.pm (.../vendor/SpamAssassin/3.1.x) (revision 39233) +++ lib/Mail/SpamAssassin/Constants.pm (.../trunk/email/spamassassin) (revision 39233) @@ -38,7 +38,9 @@ META_TEST_MIN_PRIORITY HARVEST_DNSBL_PRIORITY MBX_SEPARATOR MAX_BODY_LINE_LENGTH MAX_HEADER_KEY_LENGTH MAX_HEADER_VALUE_LENGTH MAX_HEADER_LENGTH ARITH_EXPRESSION_LEXER AI_TIME_UNKNOWN - MAX_URI_LENGTH + CUDA_TEST_HIGH_PRIORITY CUDA_REQUIRED_SCORE MAX_URI_LENGTH + CUDA_URIDNSBL_PRIORITY HARVEST_URIDNSBL_PRIORITY + CUDA_DKIM_PRIORITY ); %EXPORT_TAGS = ( @@ -263,6 +265,13 @@ use constant META_TEST_MIN_PRIORITY => 500; use constant HARVEST_DNSBL_PRIORITY => 500; +# DMT +use constant CUDA_TEST_HIGH_PRIORITY => -1000; +use constant CUDA_REQUIRED_SCORE => 10; +use constant CUDA_URIDNSBL_PRIORITY => -573; +use constant HARVEST_URIDNSBL_PRIORITY => 500; +use constant CUDA_DKIM_PRIORITY => -5000; +# END DMT # regular expression that matches message separators in The University of # Washington's MBX mailbox format @@ -285,7 +294,7 @@ use constant MAX_HEADER_LENGTH => 65536; # maximum byte length of any given URI -use constant MAX_URI_LENGTH => 1024; +use constant MAX_URI_LENGTH => 8192; # used for meta rules and "if" conditionals in Conf::Parser use constant ARITH_EXPRESSION_LEXER => qr/(?: Index: lib/Mail/SpamAssassin/Message.pm =================================================================== --- lib/Mail/SpamAssassin/Message.pm (.../vendor/SpamAssassin/3.1.x) (revision 39233) +++ lib/Mail/SpamAssassin/Message.pm (.../trunk/email/spamassassin) (revision 39233) @@ -109,7 +109,8 @@ # Ok, go ahead and do the message "parsing" my($opts) = @_; - my $message = $opts->{'message'} || \*STDIN; + my $message = $opts->{'data'} || $opts->{'message'} || \*STDIN; + #my $message = $opts->{'message'} || \*STDIN; my $parsenow = $opts->{'parsenow'} || 0; # Specifies whether or not to parse message/rfc822 parts into its own tree. @@ -249,6 +250,27 @@ } } } + # rescue header from lastline + if ($header) { + # Yes, the /s is needed to match \n too. + my ($key, $value) = split (/:\s*(?=.)/s, $header, 2); + + # If it's not a valid header (aka: not in the form "foo: bar"), skip it. + if (defined $value) { + # limit the length of the pairs we store + if (length($key) > MAX_HEADER_KEY_LENGTH) { + $key = substr($key, 0, MAX_HEADER_KEY_LENGTH); + $self->{'truncated_header'} = 1; + } + if (length($value) > MAX_HEADER_VALUE_LENGTH) { + $value = substr($value, 0, MAX_HEADER_VALUE_LENGTH); + $self->{'truncated_header'} = 1; + } + $self->header($key, $value); + + } + } + undef $header; # Store the pristine body for later -- store as a copy since @message Index: lib/Mail/SpamAssassin/PerMsgStatus.pm =================================================================== --- lib/Mail/SpamAssassin/PerMsgStatus.pm (.../vendor/SpamAssassin/3.1.x) (revision 39233) +++ lib/Mail/SpamAssassin/PerMsgStatus.pm (.../trunk/email/spamassassin) (revision 39233) @@ -147,7 +147,6 @@ $self->{conf}->set_score_set ($set|2); } - { # bug 4353: # Do this before the RBL tests are kicked off. The metadata parsing # will figure out the (un)trusted relays and such, which are used in the @@ -157,13 +156,24 @@ # Here, we launch all the DNS RBL queries and let them run while we # inspect the message $self->run_rbl_eval_tests ($self->{conf}->{rbl_evals}); - my $needs_dnsbl_harvest_p = 1; # harvest needs to be run + # harvest the DNS results + $self->harvest_dnsbl_queries(); + + # finish the DNS results + $self->rbl_finish(); + $self->{main}->call_plugins ("check_post_dnsbl", { permsgstatus => $self }); + $self->{resolver}->finish_socket() if $self->{resolver}; + if ($self->{score} < CUDA_REQUIRED_SCORE) + { my $decoded = $self->get_decoded_stripped_body_text_array(); my $bodytext = $self->get_decoded_body_text_array(); my $fulltext = $self->{msg}->get_pristine(); my @uris = $self->get_uri_list(); + # DMT + my $needs_uridnsbl_harvest_p = 0; + # END DMT foreach my $priority (sort { $a <=> $b } keys %{$self->{conf}->{priorities}}) { # no need to run if there are no priorities at this level. This can @@ -171,20 +181,26 @@ next unless ($self->{conf}->{priorities}->{$priority} > 0); dbg("check: running tests for priority: $priority"); - - # only harvest the dnsbl queries once priority HARVEST_DNSBL_PRIORITY + #DMT - if URIDNSBL plugin is loaded then start checks + if ($priority == CUDA_URIDNSBL_PRIORITY) { + $self->{main}->call_plugins ("start_uribl_check", { permsgstatus => $self }); + $needs_uridnsbl_harvest_p = 1; + } + # only harvest the URIDNSBL queries once priority HARVEST_URIDNSBL_PRIORITY # has been reached and then only run once - if ($priority >= HARVEST_DNSBL_PRIORITY && $needs_dnsbl_harvest_p) { + if ($priority >= HARVEST_URIDNSBL_PRIORITY && $needs_uridnsbl_harvest_p) { # harvest the DNS results $self->harvest_dnsbl_queries(); - $needs_dnsbl_harvest_p = 0; + $needs_uridnsbl_harvest_p = 0; # finish the DNS results $self->rbl_finish(); - $self->{main}->call_plugins ("check_post_dnsbl", { permsgstatus => $self }); + $self->{main}->call_plugins ("check_post_uridnsbl", { permsgstatus => $self }); $self->{resolver}->finish_socket() if $self->{resolver}; } + #END DMT + # since meta tests must have a priority of META_TEST_MIN_PRIORITY or # higher then there is no reason to even call the do_meta_tests method # if we are less than that. @@ -209,17 +225,23 @@ # we may need to call this more often than once through the loop, but # it needs to be done at least once, either at the beginning or the end. $self->{main}->call_plugins ("check_tick", { permsgstatus => $self }); + # DMT - if we've just finished the high priority tests then check the + # score to see if we can short-circuit + if ($priority <= CUDA_TEST_HIGH_PRIORITY) { + last if ($self->{score} >= CUDA_REQUIRED_SCORE); + } + # END DMT } - # sanity check, it is possible that no rules >= HARVEST_DNSBL_PRIORITY ran so the harvest + # sanity check, it is possible that no rules >= HARVEST_URIDNSBL_PRIORITY ran so the harvest # may not have run yet. Check, and if so, go ahead and harvest here. - if ($needs_dnsbl_harvest_p) { - # harvest the DNS results + if ($needs_uridnsbl_harvest_p) { + # harvest the URIDNSBL plugin results $self->harvest_dnsbl_queries(); - # finish the DNS results + # finish the DNS results for the URIDNSBL plugin $self->rbl_finish(); - $self->{main}->call_plugins ("check_post_dnsbl", { permsgstatus => $self }); + $self->{main}->call_plugins ("check_post_uridnsbl", { permsgstatus => $self }); $self->{resolver}->finish_socket() if $self->{resolver}; } @@ -1255,6 +1277,8 @@ return "\n" . ($self->{tag_data}->{REPORT} || ""); }, + SUMMARY => sub { return $self->{tag_data}->{SUMMARY} || "" }, + ); my $data; @@ -2330,7 +2354,7 @@ # and run it. eval $evalstr; if ($@) { - warn("rules: failed to compile body tests, skipping:\n" . "\t($@)\n"); + warn("rules: failed to compile rawbody tests, skipping:\n" . "\t($@)\n"); $self->{rule_errors}++; } else { @@ -2699,7 +2723,9 @@ return 0; } - while (my ($rulename, $test) = each %{$evalhash}) { + my $test; + foreach my $rulename (sort { $a cmp $b } keys %{$evalhash}) { + $test = $evalhash->{$rulename}; my $score = $self->{conf}->{scores}->{$rulename}; next unless $score; @@ -2766,10 +2792,10 @@ $area ||= ''; if ($score >= 10 || $score <= -10) { - $score = sprintf("%4.0f", $score); + $score = sprintf("%5.0f", $score); } else { - $score = sprintf("%4.1f", $score); + $score = sprintf("%5.2f", $score); } # save both summaries Index: lib/Mail/SpamAssassin/NoMailAudit.pm =================================================================== --- lib/Mail/SpamAssassin/NoMailAudit.pm (.../vendor/SpamAssassin/3.1.x) (revision 0) +++ lib/Mail/SpamAssassin/NoMailAudit.pm (.../trunk/email/spamassassin) (revision 39233) @@ -0,0 +1,39 @@ +# Mail message object, used by SpamAssassin. This was written to eliminate, as +# much as possible, SpamAssassin's dependency on Mail::Audit and the +# Mail::Internet, Net::SMTP, etc. module set it requires. +# +# This is more efficient (less modules, dependencies and unused code loaded), +# and fixes some bugs found in Mail::Audit, as well as working around some +# side-effects of features of Mail::Internet that we don't use. It's also more +# lenient about the incoming message, in the spirit of the IETF dictum 'be +# liberal in what you accept'. +# +# A regexp from Mail::Header is used. Mail::Header is Copyright (c) 1995-2001 +# Graham Barr . All rights reserved. This program is free +# software; you can redistribute it and/or modify it under the same terms as +# Perl itself. +# +package Mail::SpamAssassin::NoMailAudit; + +use strict; + +use Mail::SpamAssassin::Message; + +@Mail::SpamAssassin::NoMailAudit::ISA = ( + 'Mail::SpamAssassin::Message' +); + +# --------------------------------------------------------------------------- + +sub new { + my $class = shift; + my %opts = @_; + + my $self = $class->SUPER::new({@_}); + #my $self = $class->SUPER::new(@_); + + #$self->{header_order} = [ ]; + + return $self; +} + Index: lib/Mail/SpamAssassin/Conf/Parser.pm =================================================================== --- lib/Mail/SpamAssassin/Conf/Parser.pm (.../vendor/SpamAssassin/3.1.x) (revision 39233) +++ lib/Mail/SpamAssassin/Conf/Parser.pm (.../trunk/email/spamassassin) (revision 39233) @@ -246,7 +246,7 @@ next unless($line); # skip empty lines # handle i18n - if ($line =~ s/^lang\s+(\S+)\s+//) { next if ($lang !~ /^$1/i); } + if ($line =~ s/^lang\s+(\S+)\s+//) { next if ($self->{conf}->{ok_langs} !~ /$1/i); } my($key, $value) = split(/\s+/, $line, 2); $key = lc $key; @@ -661,9 +661,36 @@ my ($self) = @_; my $conf = $self->{conf}; + my $keep_count = 0; + my $chuck_count = 0; + + # gather stats on chuckable rules + my @score_chucks = (0,0,0,0); + while (my ($name, $text) = each %{$conf->{tests}}) { my $type = $conf->{test_types}->{$name}; my $priority = $conf->{priority}->{$name} || 0; + + # don't includes rules that have a score of 0.0 + my $score = 0.0; + for my $index (0..3) { + $score += abs($conf->{scoreset}->[$index]->{$name}); + if (abs($conf->{scoreset}->[$index]->{$name}) < .01) { + $score_chucks[$index]++; + } + } + if ($score < 0.001) { + dbg( "chucking rule $name score=$score"); + $chuck_count++; + next; + } + else + { + dbg( "keeping rule $name score=$score"); + $keep_count++; + } + + $conf->{priorities}->{$priority}++; # eval type handling @@ -758,6 +785,9 @@ # named this way just in case we ever want a "finish_parsing_start" $conf->{main}->call_plugins("finish_parsing_end", { conf => $conf }); + dbg("Keeping $keep_count rules, chucking $chuck_count rules"); + dbg("chuckability table $score_chucks[0] $score_chucks[1] $score_chucks[2] $score_chucks[3]"); + delete $conf->{tests}; # free it up delete $conf->{priority}; # free it up } Index: lib/Mail/SpamAssassin/EvalTests.pm =================================================================== --- lib/Mail/SpamAssassin/EvalTests.pm (.../vendor/SpamAssassin/3.1.x) (revision 39233) +++ lib/Mail/SpamAssassin/EvalTests.pm (.../trunk/email/spamassassin) (revision 39233) @@ -30,12 +30,16 @@ use Mail::SpamAssassin::MailingList; use Mail::SpamAssassin::PerMsgStatus; use Mail::SpamAssassin::Constants qw(:ip); +use Mail::SpamAssassin::Timeout; use Digest::SHA1 qw(sha1_hex); use Fcntl; use File::Path; use Time::Local; use File::Basename; +# DMT +use IO::Socket::INET; +# END DMT use constant HAS_DB_FILE => eval { require DB_File; }; @@ -344,6 +348,8 @@ return if ($rcvd =~ /from mail pickup service by hotmail\.com with Microsoft SMTPSVC;/); + return if ($rcvd =~ /from \S+ \(\[\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\]\) by \S+\.hotmail(?:\.msn)?\.com with Microsoft SMTPSVC/); + # Microsoft passes Hotmail mail directly to MSN Group servers. return if $self->check_for_msn_groups_headers(); @@ -1194,14 +1200,26 @@ ########################################################################### +sub check_lots_of_cc_lines { + my ($self) = @_; + local ($_); + $_ = $self->get('Cc'); + my @count = /\n/gs; + if ($#count > 20) { return 1; } + return 0; +} + +########################################################################### + sub check_rbl_backend { my ($self, $rule, $set, $rbl_server, $type, $subtest) = @_; local ($_); # First check that DNS is available, if not do not perform this check return 0 if $self->{conf}->{skip_rbl_checks}; - return 0 unless $self->is_dns_available(); - $self->load_resolver(); + return 0 unless $self->load_resolver(); + # If the bbl hit then only query other RBLs if they would score higher if hit + return 0 if (defined($self->{dnsresult}->{BARRACUDA_HEADER_FP20}) && $self->{conf}->{scores}->{$rule} <= $self->{conf}->{scores}->{BARRACUDA_HEADER_FP20}); if (($rbl_server !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) && (index($rbl_server, '.') >= 0) && @@ -1310,6 +1328,57 @@ } dbg("dns: only inspecting the following IPs: ".join(", ", @ips)); +# DMT + if ($set eq "cuda") { + return 0 unless (defined($self->{conf}->{use_bbl}) && $self->{conf}->{use_bbl} ne 'ignore'); + my $socket; + my $timeout = 2; + my $timer = Mail::SpamAssassin::Timeout->new({ secs => $timeout }); + my $err = $timer->run_and_catch(sub { + $socket = IO::Socket::INET->new(PeerAddr => "$rbl_server", + PeerPort => 600, + Proto => "tcp", + Type => SOCK_STREAM + ); + return 0 unless $socket; + my $answer; + my @answers = (); + my %logs = (); + $self->{rbl_launch} = time; + foreach my $ip (@ips) { + next unless ($ip =~ /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/); + print $socket "get $ip\n"; + $answer = <$socket>; + next if ($answer =~ /^200 DUNNO/ || $answer =~ /^200 tag_rwl/); + if ($answer =~ /^200 554/) { + $answer =~ s/.*;(.*)/$1/; + $answer =~ s/^\s*//; + $answer =~ s/\s*$//; + } elsif ($answer =~ /^200 TAGALL/) { + $answer = "http://www.barracuda.com/reputation?ip=$ip"; + } + $answer = "<$answer>"; + $logs{$answer} = 1; + push(@answers, $answer); + } + if (@answers > 0) { + $self->{dnsresult}->{$rule} = \%logs; + $self->{dnsuri}->{"dns:rbl.barracuda.com/"} = \@answers; + } + close($socket) if ($socket); + }); + + if ($timer->timed_out()) { + dbg("dns: bbl lookup timeout after $timeout seconds"); + } elsif ($err) { + chomp($err); + dbg("dns: bbl lookup failed: $err"); + } + close($socket) if ($socket); + return 0; + } +# END DMT + eval { foreach my $ip (@ips) { next unless ($ip =~ /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/); @@ -2628,7 +2697,7 @@ my ($self) = @_; return 0 unless ($self->get("MIME-Version") =~ /Apple Message framework/); - return 0 unless ($self->get("X-Mailer") =~ /^Apple Mail \(\d+\.\d+\)/); + return 0 unless ($self->get("X-Mailer") =~ /^Apple Mail \(\d+\.\d+(?:\.\d+)?\)/); return 0 unless ($self->get("Message-Id") =~ /^<[A-F0-9]+(?:-[A-F0-9]+){4}\@\S+.\S+>$/); return 1; @@ -3172,4 +3241,68 @@ return $self->{'uri_truncated'}; } +sub check_zip_size { + my ($self, $body, $size) = @_; + if (defined $self->{zip_size}) { + return ($self->{zip_size} < $size); + } + return 0; +} + +sub check_pdf_size { + my ($self, $body, $size) = @_; + if (defined $self->{pdf_size}) { + return ($self->{pdf_size} < $size); + } + return 0; +} + +sub check_empty_pdf_text_content { + my ($self, $body) = @_; + if (defined $self->{pdf_text_content}) { + return 1 if ($self->{pdf_text_content} =~ /^\x0c$/); + } + return 0; +} + +sub check_pdf_error { + my ($self, $body, $error) = @_; + if (defined $self->{pdf_error_text}) { + if ($self->{pdf_error_text} =~ /$error/) { + return 1; + } + } + return 0; +} + +# check information about a PDF; valid fields to query are: +# +# Title, Producer, CreationDate, ModDate, Tagged, Pages, Encrypted, Page size, File size, Optimized, PDF version +# +sub check_pdf_info { + my ($self, $body, $field, $value) = @_; + $field = lc $field; + if (defined $self->{pdf_info}->{"$field"}) { + if ($self->{pdf_info}->{"$field"} =~ /$value/) { + return 1; + } + } + return 0; +} + +# check PDF page size in specified range +# +sub check_pdf_page_size_range { + my ($self, $body, $horiz_max, $horiz_min, $vert_max, $vert_min) = @_; + if (defined $self->{pdf_info}->{"page size"}) { + $self->{pdf_info}->{"page size"} =~ /(\d+)\sx\s(\d+)/; + my $horiz = $1; + my $vert = $2; + if ($horiz >= $horiz_min && $horiz <= $horiz_max && $vert >= $vert_min && $vert <= $vert_max) { + return 1; + } + } + return 0; +} + 1; Index: lib/Mail/SpamAssassin/Plugin/URIDNSBL.pm =================================================================== --- lib/Mail/SpamAssassin/Plugin/URIDNSBL.pm (.../vendor/SpamAssassin/3.1.x) (revision 39233) +++ lib/Mail/SpamAssassin/Plugin/URIDNSBL.pm (.../trunk/email/spamassassin) (revision 39233) @@ -152,15 +152,11 @@ # once the metadata is parsed, we can access the URI list. So start off # the lookups here! -sub parsed_metadata { +# DMT - changed name from parsed_metadata so that I can control when calling +sub start_uribl_check { my ($self, $opts) = @_; my $scanner = $opts->{permsgstatus}; - if (!$scanner->is_dns_available()) { - $self->{dns_not_available} = 1; - return; - } - $self->{scanner} = $scanner; my $scanstate = $scanner->{uribl_scanstate} = { self => $self, @@ -392,7 +388,8 @@ return 1; } -sub check_post_dnsbl { +# DMT - changed from check_post_dnsbl so that I can control when calling +sub check_post_uridnsbl { my ($self, $opts) = @_; return if ($self->{dns_not_available}); Index: lib/Mail/SpamAssassin/Conf.pm =================================================================== --- lib/Mail/SpamAssassin/Conf.pm (.../vendor/SpamAssassin/3.1.x) (revision 39233) +++ lib/Mail/SpamAssassin/Conf.pm (.../trunk/email/spamassassin) (revision 39233) @@ -839,6 +839,12 @@ type => $CONF_TYPE_STRING }); + push (@cmds, { + setting => 'ok_langs', + default => '', + type => $CONF_TYPE_STRING + }); + =back =head2 NETWORK TEST OPTIONS @@ -1069,7 +1075,7 @@ push (@cmds, { setting => 'dns_available', - default => 'test', + default => 'no', code => sub { my ($self, $key, $value, $line) = @_; if ($value !~ /^(yes|no|test|test:\s+.+)$/) { return $INVALID_VALUE; } @@ -1125,7 +1131,7 @@ push (@cmds, { setting => 'bayes_auto_learn', - default => 1, + default => 0, type => $CONF_TYPE_BOOL }); @@ -2536,6 +2542,72 @@ =back +=item use_bwl and use_bbl +=cut + push (@cmds, { + setting => 'use_bwl', + default => 'off', + code => sub { + my ($self, $key, $value, $line) = @_; + if ($value !~ /^(off|on)$/) { return $INVALID_VALUE; } + $self->{use_bwl} = $1; + } + }); + + push (@cmds, { + setting => 'use_bbl', + default => 'ignore', + code => sub { + my ($self, $key, $value, $line) = @_; + if ($value !~ /^(block|quarantine|tag|ignore)$/) { return $INVALID_VALUE; } + $self->{use_bbl} = $1; + } + }); + +=back + +=item dkim_enable (yes | no) + +Is DKIM checking turned on. + +=cut + + push (@cmds, { + setting => 'dkim_enable', + code => sub { + my ($self, $key, $value, $line) = @_; + unless (defined $value && $value !~ /^$/) { + return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE; + } + unless (defined $value && $value =~ /yes|no/) { + return $Mail::SpamAssassin::Conf::INVALID_VALUE; + } + $self->{dkim_enable} = $value; + } + }); + +=item dkim_inspect_all (yes | no) + +Do we query policy when message is not signed. + +=cut + + push (@cmds, { + setting => 'dkim_inspect_all', + code => sub { + my ($self, $key, $value, $line) = @_; + unless (defined $value && $value !~ /^$/) { + return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE; + } + unless (defined $value && $value =~ /yes|no/) { + return $Mail::SpamAssassin::Conf::INVALID_VALUE; + } + $self->{dkim_inspect_all} = $value; + } + }); + +=back + =head1 PREPROCESSING OPTIONS =over 4