blob: 51bfa3167551f5083d52a3523abd9ad769d5d4a8 [file] [log] [blame]
# <@LICENSE>
# Copyright 2004 Apache Software Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# </@LICENSE>
package Mail::SpamAssassin::Plugin::P595Body;
use Mail::SpamAssassin::Plugin;
use Mail::SpamAssassin::Logger;
use Mail::SpamAssassin::Plugin::BodyRuleBaseExtractor;
use Mail::SpamAssassin::Plugin::OneLineBodyRuleType;
use Mail::SpamAssassin::Util qw(qr_to_string);
use strict;
use warnings;
# use bytes;
use re 'taint';
our @ISA = qw(Mail::SpamAssassin::Plugin);
sub new {
my $class = shift;
my $mailsaobject = shift;
$class = ref($class) || $class;
my $self = $class->SUPER::new($mailsaobject);
bless ($self, $class);
$self->{one_line_body} = Mail::SpamAssassin::Plugin::OneLineBodyRuleType->new();
if ($] < 5.009005) {
die "this plugin requires perl 5.9.5 or later";
}
return $self;
}
###########################################################################
sub finish_parsing_end {
my ($self, $params) = @_;
my $conf = $params->{conf};
my $main = $self->{main};
$conf->{skip_body_rules} ||= { };
$self->setup_test_set ($conf, $conf->{body_tests}, 'body');
}
sub setup_test_set {
my ($self, $conf, $test_set, $ruletype) = @_;
foreach my $pri (keys %{$test_set}) {
my $nicepri = $pri; $nicepri =~ s/-/neg/g;
$self->setup_test_set_pri($conf, $test_set->{$pri},
$ruletype.'_'.$nicepri, $pri);
}
}
sub setup_test_set_pri {
my ($self, $conf, $rules, $ruletype, $pri) = @_;
my $alternates = [];
while (my ($rule, $pat) = each %{$conf->{body_tests}->{$pri}}) {
# ignore rules marked for ReplaceTags work!
next if ($conf->{replace_rules}->{$rule});
# ignore regex capture template rules
next if ($conf->{capture_rules}->{$rule});
next if ($conf->{capture_template_rules}->{$rule});
#$pat = Mail::SpamAssassin::Util::regexp_remove_delimiters($pat);
$pat = qr_to_string($conf->{test_qrs}->{$rule});
next unless !$pat;
# use the REGMARK feature:
# see http://taint.org/2006/11/16/154546a.html#comment-1011
#
push @{$alternates}, "$pat(*:$rule)";
# TODO: need a cleaner way to do this. I expect when rule types
# are implementable in plugins, I can do it that way
$conf->{skip_body_rules}->{$rule} = 1;
}
my $sub = '
sub {
our $REGMARK;
our @matched = ();
$_[0] =~ m#('.join('|', @{$alternates}).')(?{
push @matched, $REGMARK;
})(*FAIL)#;
return @matched;
}
';
# warn "JMD $sub";
$conf->{$ruletype}->{trie_re_sub} = eval $sub;
if ($@) { warn "REGMARK sub compilation failed: $@"; }
}
###########################################################################
# delegate these to the OneLineBodyRuleType object
sub check_start {
my ($self, $params) = @_;
$self->{one_line_body}->check_start($params);
}
sub check_rules_at_priority {
my ($self, $params) = @_;
$self->{one_line_body}->check_rules_at_priority($params);
}
sub check_cleanup {
my ($self, $params) = @_;
$self->{one_line_body}->check_cleanup($params);
}
###########################################################################
sub run_body_fast_scan {
my ($self, $params) = @_;
return unless ($params->{ruletype} eq 'body');
my $pri = $params->{priority};
my $nicepri = $params->{priority}; $nicepri =~ s/-/neg/g;
my $ruletype = ($params->{ruletype}.'_'.$nicepri);
my $scanner = $params->{permsgstatus};
my $conf = $scanner->{conf};
my $trie_re_sub = $conf->{$ruletype}->{trie_re_sub};
if (!$trie_re_sub)
{
dbg("zoom: run_body_fast_scan for $ruletype skipped, no rules");
return;
}
my $do_dbg = (would_log('dbg', 'zoom') > 1);
my $scoresptr = $conf->{scores};
dbg("zoom: run_body_fast_scan for $ruletype start");
{
no strict "refs";
foreach my $line (@{$params->{lines}})
{
my @caught = $trie_re_sub->($line);
next unless (scalar @caught > 0);
my %alreadydone;
foreach my $rulename (@caught) {
{
next if not defined $rulename;
# only try each rule once per line
next if exists $alreadydone{$rulename};
$alreadydone{$rulename} = undef;
# ignore 0-scored rules, of course
next unless $scoresptr->{$rulename};
$scanner->got_hit($rulename, "BODY: ", ruletype => "p595_body");
}
}
}
use strict "refs";
}
dbg("zoom: run_body_fast_scan for $ruletype done");
}
sub finish {
my ($self) = @_;
my $do_dbg = (would_log('dbg', 'zoom') > 1);
return unless $do_dbg;
my $miss = $self->{rule2xs_misses};
foreach my $r (sort { $miss->{$a} <=> $miss->{$b} } keys %{$miss}) {
dbg("zoom: %s misses for rule2xs rule %s", $miss->{$r},$r);
}
}
###########################################################################
1;