View | Details | Raw Unified | Return to bug 4778
Collapse All | Expand All

(-)lib/Mail/SpamAssassin.pm (+2 lines)
Lines 300-305 Link Here
300
300
301
  $self->{resolver} = Mail::SpamAssassin::DnsResolver->new($self);
301
  $self->{resolver} = Mail::SpamAssassin::DnsResolver->new($self);
302
302
303
  $self->{conf}->load_plugin('Mail::SpamAssassin::Plugin::BodyTests');
304
303
  $self;
305
  $self;
304
}
306
}
305
307
(-)lib/Mail/SpamAssassin/PerMsgStatus.pm (-10 / +2 lines)
Lines 156-162 Link Here
156
    $self->run_rbl_eval_tests ($self->{conf}->{rbl_evals});
156
    $self->run_rbl_eval_tests ($self->{conf}->{rbl_evals});
157
    my $needs_dnsbl_harvest_p = 1; # harvest needs to be run
157
    my $needs_dnsbl_harvest_p = 1; # harvest needs to be run
158
158
159
    my $decoded = $self->get_decoded_stripped_body_text_array();
160
    my $bodytext = $self->get_decoded_body_text_array();
159
    my $bodytext = $self->get_decoded_body_text_array();
161
    my $fulltext = $self->{msg}->get_pristine();
160
    my $fulltext = $self->{msg}->get_pristine();
162
161
Lines 193-201 Link Here
193
      $self->do_head_tests($priority);
192
      $self->do_head_tests($priority);
194
      $self->do_head_eval_tests($priority);
193
      $self->do_head_eval_tests($priority);
195
194
196
      $self->do_body_tests($priority, $decoded);
197
      $self->do_body_uri_tests($priority, @uris);
195
      $self->do_body_uri_tests($priority, @uris);
198
      $self->do_body_eval_tests($priority, $decoded);
199
  
196
  
200
      $self->do_rawbody_tests($priority, $bodytext);
197
      $self->do_rawbody_tests($priority, $bodytext);
201
      $self->do_rawbody_eval_tests($priority, $bodytext);
198
      $self->do_rawbody_eval_tests($priority, $bodytext);
Lines 203-208 Link Here
203
      $self->do_full_tests($priority, \$fulltext);
200
      $self->do_full_tests($priority, \$fulltext);
204
      $self->do_full_eval_tests($priority, \$fulltext);
201
      $self->do_full_eval_tests($priority, \$fulltext);
205
202
203
      $self->{main}->call_plugins ("check_priority", { permsgstatus => $self, priority => $priority });
204
206
      # we may need to call this more often than once through the loop, but
205
      # we may need to call this more often than once through the loop, but
207
      # it needs to be done at least once, either at the beginning or the end.
206
      # it needs to be done at least once, either at the beginning or the end.
208
      $self->{main}->call_plugins ("check_tick", { permsgstatus => $self });
207
      $self->{main}->call_plugins ("check_tick", { permsgstatus => $self });
Lines 222-228 Link Here
222
221
223
    # finished running rules
222
    # finished running rules
224
    delete $self->{current_rule_name};
223
    delete $self->{current_rule_name};
225
    undef $decoded;
226
    undef $bodytext;
224
    undef $bodytext;
227
    undef $fulltext;
225
    undef $fulltext;
228
226
Lines 2447-2458 Link Here
2447
  $self->run_eval_tests ($self->{conf}->{head_evals}->{$priority}, '');
2445
  $self->run_eval_tests ($self->{conf}->{head_evals}->{$priority}, '');
2448
}
2446
}
2449
2447
2450
sub do_body_eval_tests {
2451
  my ($self, $priority, $bodystring) = @_;
2452
  return unless (defined($self->{conf}->{body_evals}->{$priority}));
2453
  $self->run_eval_tests ($self->{conf}->{body_evals}->{$priority}, 'BODY: ', $bodystring);
2454
}
2455
2456
sub do_rawbody_eval_tests {
2448
sub do_rawbody_eval_tests {
2457
  my ($self, $priority, $bodystring) = @_;
2449
  my ($self, $priority, $bodystring) = @_;
2458
  return unless (defined($self->{conf}->{rawbody_evals}->{$priority}));
2450
  return unless (defined($self->{conf}->{rawbody_evals}->{$priority}));
(-)lib/Mail/SpamAssassin/Conf/Parser.pm (-24 / +88 lines)
Lines 490-497 Link Here
490
}
490
}
491
491
492
# Let's do some linting here ...
492
# Let's do some linting here ...
493
# This is called from _parse(), BTW, so we can check for $conf->{tests}
494
# easily before finish_parsing() is called and deletes it.
495
#
493
#
496
sub lint_check {
494
sub lint_check {
497
  my ($self) = @_;
495
  my ($self) = @_;
Lines 502-514 Link Here
502
  {
500
  {
503
    # Check for description and score issues in lint fashion
501
    # Check for description and score issues in lint fashion
504
    while ( ($k,$v) = each %{$conf->{descriptions}} ) {
502
    while ( ($k,$v) = each %{$conf->{descriptions}} ) {
505
      if (!exists $conf->{tests}->{$k}) {
503
      if (!exists $conf->{source_file}->{$k}) {
506
        $self->lint_warn("config: warning: description exists for non-existent rule $k\n", $k);
504
        $self->lint_warn("config: warning: description exists for non-existent rule $k\n", $k);
507
      }
505
      }
508
    }
506
    }
509
507
510
    while ( my($sk) = each %{$conf->{scores}} ) {
508
    while ( my($sk) = each %{$conf->{scores}} ) {
511
      if (!exists $conf->{tests}->{$sk}) {
509
      if (!exists $conf->{source_file}->{$sk}) {
512
        $self->lint_warn("config: warning: score set for non-existent rule $sk\n", $sk);
510
        $self->lint_warn("config: warning: score set for non-existent rule $sk\n", $sk);
513
      }
511
      }
514
    }
512
    }
Lines 525-531 Link Here
525
  my $conf = $self->{conf};
523
  my $conf = $self->{conf};
526
  my ($k, $v);
524
  my ($k, $v);
527
525
528
  while ( ($k,$v) = each %{$conf->{tests}} ) {
526
  while ( ($k,$v) = each %{$conf->{source_file}} ) {
529
    if ( ! exists $conf->{scores}->{$k} ) {
527
    if ( ! exists $conf->{scores}->{$k} ) {
530
      # T_ rules (in a testing probationary period) get low, low scores
528
      # T_ rules (in a testing probationary period) get low, low scores
531
      my $set_score = ($k =~/^T_/) ? 0.01 : 1.0;
529
      my $set_score = ($k =~/^T_/) ? 0.01 : 1.0;
Lines 647-656 Link Here
647
645
648
###########################################################################
646
###########################################################################
649
647
648
sub prioritize_tests {
649
  my ($self, $rulelist);
650
  my $conf = $self->{conf};
651
  my $rv = {};
652
  while (my ($name, $def) = each %$rulelist) {
653
    my $priority = $conf->{priority}->{$name} || 0;
654
    $conf->{priorities}->{$priority}++;
655
656
    $rv->{$priority}->{$name} = $def;
657
  }
658
  return $rv;
659
}
660
650
sub finish_parsing {
661
sub finish_parsing {
651
  my ($self) = @_;
662
  my ($self) = @_;
652
  my $conf = $self->{conf};
663
  my $conf = $self->{conf};
653
664
665
#BUG4778: below to be removed
654
  while (my ($name, $text) = each %{$conf->{tests}}) {
666
  while (my ($name, $text) = each %{$conf->{tests}}) {
655
    my $type = $conf->{test_types}->{$name};
667
    my $type = $conf->{test_types}->{$name};
656
    my $priority = $conf->{priority}->{$name} || 0;
668
    my $priority = $conf->{priority}->{$name} || 0;
Lines 677-685 Link Here
677
	if ($args) {
689
	if ($args) {
678
          $self->lint_warn("syntax error (unparsable argument: $args) for eval function: $name: $text", $name);
690
          $self->lint_warn("syntax error (unparsable argument: $args) for eval function: $name: $text", $name);
679
	}
691
	}
680
        elsif ($type == $Mail::SpamAssassin::Conf::TYPE_BODY_EVALS) {
681
          $conf->{body_evals}->{$priority}->{$name} = \@args;
682
        }
683
        elsif ($type == $Mail::SpamAssassin::Conf::TYPE_HEAD_EVALS) {
692
        elsif ($type == $Mail::SpamAssassin::Conf::TYPE_HEAD_EVALS) {
684
          $conf->{head_evals}->{$priority}->{$name} = \@args;
693
          $conf->{head_evals}->{$priority}->{$name} = \@args;
685
        }
694
        }
Lines 706-715 Link Here
706
    }
715
    }
707
    # non-eval tests
716
    # non-eval tests
708
    else {
717
    else {
709
      if ($type == $Mail::SpamAssassin::Conf::TYPE_BODY_TESTS) {
718
      if ($type == $Mail::SpamAssassin::Conf::TYPE_HEAD_TESTS) {
710
        $conf->{body_tests}->{$priority}->{$name} = $text;
711
      }
712
      elsif ($type == $Mail::SpamAssassin::Conf::TYPE_HEAD_TESTS) {
713
        $conf->{head_tests}->{$priority}->{$name} = $text;
719
        $conf->{head_tests}->{$priority}->{$name} = $text;
714
      }
720
      }
715
      elsif ($type == $Mail::SpamAssassin::Conf::TYPE_META_TESTS) {
721
      elsif ($type == $Mail::SpamAssassin::Conf::TYPE_META_TESTS) {
Lines 738-743 Link Here
738
      }
744
      }
739
    }
745
    }
740
  }
746
  }
747
#BUG4778: above to be removed
741
748
742
  $self->lint_trusted_networks();
749
  $self->lint_trusted_networks();
743
750
Lines 788-802 Link Here
788
795
789
###########################################################################
796
###########################################################################
790
797
791
sub add_test {
798
sub define_test {
792
  my ($self, $name, $text, $type) = @_;
799
  my ($self, $name, $autolearn) = @_;
793
  my $conf = $self->{conf};
800
  my $conf = $self->{conf};
801
  $autolearn ||= $Mail::SpamAssassin::Conf::AUTOLEARN_NONE;
794
802
795
  # Don't allow invalid names ...
803
  # Don't allow invalid names ...
796
  if ($name !~ /^\D\w*$/) {
804
  if ($name !~ /^\D\w*$/) {
797
    $self->lint_warn("config: error: rule '$name' has invalid characters ".
805
    $self->lint_warn("config: error: rule '$name' has invalid characters ".
798
	   "(not Alphanumeric + Underscore + starting with a non-digit)\n", $name);
806
	   "(not Alphanumeric + Underscore + starting with a non-digit)\n", $name);
799
    return;
807
    return 0;
800
  }
808
  }
801
809
802
  # Also set a hard limit for ALL rules (rule names longer than 242
810
  # Also set a hard limit for ALL rules (rule names longer than 242
Lines 805-811 Link Here
805
  if (length $name > 200) {
813
  if (length $name > 200) {
806
    $self->lint_warn("config: error: rule '$name' is way too long ".
814
    $self->lint_warn("config: error: rule '$name' is way too long ".
807
	   "(recommended maximum length is 22 characters)\n", $name);
815
	   "(recommended maximum length is 22 characters)\n", $name);
808
    return;
816
    return 0;
809
  }
817
  }
810
818
811
  # Warn about, but use, long rule names during --lint
819
  # Warn about, but use, long rule names during --lint
Lines 816-824 Link Here
816
    }
824
    }
817
  }
825
  }
818
826
827
  $conf->{tflags}->{$name} ||= '';
828
  $conf->{priority}->{$name} ||= 0;
829
  $conf->{source_file}->{$name} = $self->{currentfile};
830
  $conf->{if_stack}->{$name} = $self->get_if_stack_as_string();
831
  $conf->{autolearn}->{$name} = $autolearn;
832
833
  if ($self->{scoresonly}) {
834
    $conf->{user_defined_rules}->{$name} = 1;
835
  }
836
837
  return 1;
838
}
839
840
sub parse_eval_test {
841
  my ($self, $name, $text) = @_;
842
  my @args;
843
  if (my ($function, $args) = ($text =~ m/(.*?)\s*\((.*?)\)\s*$/)) {
844
    if ($args) {
845
      # bug 4419: Parse quoted strings, unquoted alphanumerics/floats and
846
      # both unquoted IPv4 and IPv6 addresses.  s// is used so that we can
847
      # determine whether or not we successfully parsed ALL arguments.
848
      while ($args =~ s/^\s*(?:['"](.*?)['"]|([\d\.:A-Za-z]+?))\s*(?:,\s*|$)//) {
849
	if (defined $1) {
850
	  push @args, $1;
851
	}
852
	else {
853
	  push @args, $2;
854
	}
855
      }
856
    }
857
    unshift(@args, $function);
858
    if ($args) {
859
      $self->lint_warn("syntax error (unparsable argument: $args) for eval function: $name: $text", $name);
860
      return;
861
    }
862
  }
863
  else {
864
    $self->lint_warn("syntax error for eval function $name: $text", $name);
865
    return;
866
  }
867
  return \@args;
868
}
869
870
#BUG4778: function to be removed
871
sub add_test {
872
  my ($self, $name, $text, $type) = @_;
873
  my $conf = $self->{conf};
874
819
  # all of these rule types are regexps
875
  # all of these rule types are regexps
820
  if ($type == $Mail::SpamAssassin::Conf::TYPE_BODY_TESTS ||
876
  if ($type == $Mail::SpamAssassin::Conf::TYPE_FULL_TESTS ||
821
      $type == $Mail::SpamAssassin::Conf::TYPE_FULL_TESTS ||
822
      $type == $Mail::SpamAssassin::Conf::TYPE_RAWBODY_TESTS ||
877
      $type == $Mail::SpamAssassin::Conf::TYPE_RAWBODY_TESTS ||
823
      $type == $Mail::SpamAssassin::Conf::TYPE_URI_TESTS)
878
      $type == $Mail::SpamAssassin::Conf::TYPE_URI_TESTS)
824
  {
879
  {
Lines 835-850 Link Here
835
    return unless $self->is_meta_valid($name, $text);
890
    return unless $self->is_meta_valid($name, $text);
836
  }
891
  }
837
892
893
  my $autolearn = $Mail::SpamAssassin::Conf::AUTOLEARN_NONE;
894
  if (($type == $Mail::SpamAssassin::Conf::TYPE_URI_TESTS) ||
895
      ($type == $Mail::SpamAssassin::Conf::TYPE_URI_EVALS)) {
896
    $autolearn = $Mail::SpamAssassin::Conf::AUTOLEARN_BODY;
897
  }
898
  elsif (($type == $Mail::SpamAssassin::Conf::TYPE_HEAD_TESTS) ||
899
	 ($type == $Mail::SpamAssassin::Conf::TYPE_HEAD_EVALS)) {
900
    $autolearn = $Mail::SpamAssassin::Conf::AUTOLEARN_HEAD;
901
  }
902
  elsif ($type == $Mail::SpamAssassin::Conf::TYPE_META_TESTS) {
903
    $autolearn = $Mail::SpamAssassin::Conf::AUTOLEARN_META;
904
  }
905
906
  return unless $self->define_test($name, $autolearn);
907
838
  $conf->{tests}->{$name} = $text;
908
  $conf->{tests}->{$name} = $text;
839
  $conf->{test_types}->{$name} = $type;
909
  $conf->{test_types}->{$name} = $type;
840
  $conf->{tflags}->{$name} ||= '';
841
  $conf->{priority}->{$name} ||= 0;
842
  $conf->{source_file}->{$name} = $self->{currentfile};
843
  $conf->{if_stack}->{$name} = $self->get_if_stack_as_string();
844
845
  if ($self->{scoresonly}) {
910
  if ($self->{scoresonly}) {
846
    $conf->{user_rules_to_compile}->{$type} = 1;
911
    $conf->{user_rules_to_compile}->{$type} = 1;
847
    $conf->{user_defined_rules}->{$name} = 1;
848
  }
912
  }
849
}
913
}
850
914
(-)lib/Mail/SpamAssassin/Plugin/BodyTests.pm (+110 lines)
Line 0 Link Here
1
package Mail::SpamAssassin::Plugin::BodyTests;
2
3
use Mail::SpamAssassin::Plugin;
4
use Mail::SpamAssassin::Logger;
5
use Mail::SpamAssassin::Conf;
6
7
use strict;
8
use warnings;
9
10
use vars qw(@ISA);
11
@ISA = qw(Mail::SpamAssassin::Plugin);
12
13
sub new {
14
  my $class = shift;
15
  my $main = shift;
16
17
  $class = ref($class) || $class;
18
  my $self = $class->SUPER::new($main);
19
  bless ($self, $class);
20
21
  $self->set_config($main->{conf});
22
23
  return $self;
24
}
25
26
sub set_config {
27
  my ($self, $conf) = @_;
28
  my @cmds;
29
30
  $self->{body_evals} = { };
31
  $self->{body_tests} = { };
32
33
=item body SYMBOLIC_TEST_NAME /pattern/modifiers
34
35
Define a body pattern test.  C<pattern> is a Perl regular expression.  Note:
36
as per the header tests, C<#> must be escaped (C<\#>) or else it is considered
37
the beginning of a comment.
38
39
The 'body' in this case is the textual parts of the message body;
40
any non-text MIME parts are stripped, and the message decoded from
41
Quoted-Printable or Base-64-encoded format if necessary.  The message
42
Subject header is considered part of the body and becomes the first
43
paragraph when running the rules.  All HTML tags and line breaks will
44
be removed before matching.
45
46
=item body SYMBOLIC_TEST_NAME eval:name_of_eval_method([args])
47
48
Define a body eval test.  See above.
49
50
=cut
51
52
  push (@cmds, {
53
    setting => 'body',
54
    is_frequent => 1,
55
    is_priv => 1,
56
    code => sub {
57
      my ($self, $key, $value, $line) = @_;
58
      if ($value =~ /^(\S+)\s+eval:(.*)$/) {
59
	my $name = $1;
60
	my $def = $conf->{parser}->parse_eval_test($name, $2);
61
	if (defined($def) && 
62
	    $conf->{parser}->define_test($name, $Mail::SpamAssassin::Conf::AUTOLEARN_BODY)) {
63
	  $self->{body_evals}->{$name} = $def;
64
	}
65
	else {
66
	  return $Mail::SpamAssassin::Conf::INVALID_VALUE;
67
        }
68
      }
69
      else {
70
	my @values = split(/\s+/, $value, 2);
71
	if (@values != 2) {
72
	  return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
73
	}
74
	my ($name, $text) = @values;
75
	unless ($conf->{parser}->is_delimited_regexp_valid($name, $text) &&
76
		$conf->{parser}->define_test($name, $Mail::SpamAssassin::Conf::AUTOLEARN_BODY)) {
77
	  return $Mail::SpamAssassin::Conf::INVALID_VALUE;
78
	}
79
        $self->{body_tests}->{$name} = $text;
80
      }
81
    }
82
  });
83
84
  $conf->{parser}->register_commands(\@cmds);
85
}
86
87
sub finish_parsing_end {
88
  my ($self, $arg) = @_;
89
  my $conf = $arg->{conf};
90
  $self->{body_tests} = $conf->{parser}->prioritize_tests($self->{body_tests});
91
  $self->{body_evals} = $conf->{parser}->prioritize_tests($self->{body_evals});
92
}
93
94
sub check_priority {
95
  my ($self, $arg) = @_;
96
  my $permsgstatus = $arg->{permsgstatus};
97
  my $priority = $arg->{priority};
98
  my $conf = $permsgstatus->{conf};
99
  
100
  return unless $self->{body_tests}->{$priority} ||
101
    $self->{body_evals}->{$priority};
102
103
  my $decoded = $permsgstatus->get_decoded_stripped_body_text_array();
104
105
  $permsgstatus->do_body_tests($priority, $decoded)
106
      if $self->{body_tests}->{$priority};
107
108
  $permsgstatus->run_eval_tests($self->{body_evals}->{$priority}, 'BODY: ', $decoded)
109
      if $self->{body_evals}->{$priority};
110
}
0
  + native
111
  + native
(-)lib/Mail/SpamAssassin/Plugin/URIDNSBL.pm (-1 / +1 lines)
Lines 173-179 Link Here
173
  $scanstate->{active_rules_rhsbl} = { };
173
  $scanstate->{active_rules_rhsbl} = { };
174
  $scanstate->{active_rules_revipbl} = { };
174
  $scanstate->{active_rules_revipbl} = { };
175
  foreach my $rulename (keys %{$scanner->{conf}->{uridnsbls}}) {
175
  foreach my $rulename (keys %{$scanner->{conf}->{uridnsbls}}) {
176
    next unless ($scanner->{conf}->is_rule_active('body_evals',$rulename));
176
    next unless ($scanner->{conf}->is_rule_active($rulename));
177
177
178
    my $rulecf = $scanstate->{scanner}->{conf}->{uridnsbls}->{$rulename};
178
    my $rulecf = $scanstate->{scanner}->{conf}->{uridnsbls}->{$rulename};
179
    if ($rulecf->{is_rhsbl}) {
179
    if ($rulecf->{is_rhsbl}) {
(-)lib/Mail/SpamAssassin/Conf.pm (-80 / +27 lines)
Lines 88-106 Link Here
88
  @MIGRATED_SETTINGS
88
  @MIGRATED_SETTINGS
89
89
90
$TYPE_HEAD_TESTS $TYPE_HEAD_EVALS
90
$TYPE_HEAD_TESTS $TYPE_HEAD_EVALS
91
$TYPE_BODY_TESTS $TYPE_BODY_EVALS $TYPE_FULL_TESTS $TYPE_FULL_EVALS
91
$TYPE_FULL_TESTS $TYPE_FULL_EVALS
92
$TYPE_RAWBODY_TESTS $TYPE_RAWBODY_EVALS $TYPE_URI_TESTS $TYPE_URI_EVALS
92
$TYPE_RAWBODY_TESTS $TYPE_RAWBODY_EVALS $TYPE_URI_TESTS $TYPE_URI_EVALS
93
$TYPE_META_TESTS $TYPE_RBL_EVALS
93
$TYPE_META_TESTS $TYPE_RBL_EVALS
94
95
$AUTOLEARN_NONE $AUTOLEARN_HEAD $AUTOLEARN_BODY $AUTOLEARN_META
94
};
96
};
95
97
96
@ISA = qw();
98
@ISA = qw();
97
99
100
#BUG4778: below to be removed
98
# odd => eval test.  Not constants so they can be shared with Parser
101
# odd => eval test.  Not constants so they can be shared with Parser
99
# TODO: move to Constants.pm?
102
# TODO: move to Constants.pm?
100
$TYPE_HEAD_TESTS    = 0x0008;
103
$TYPE_HEAD_TESTS    = 0x0008;
101
$TYPE_HEAD_EVALS    = 0x0009;
104
$TYPE_HEAD_EVALS    = 0x0009;
102
$TYPE_BODY_TESTS    = 0x000a;
103
$TYPE_BODY_EVALS    = 0x000b;
104
$TYPE_FULL_TESTS    = 0x000c;
105
$TYPE_FULL_TESTS    = 0x000c;
105
$TYPE_FULL_EVALS    = 0x000d;
106
$TYPE_FULL_EVALS    = 0x000d;
106
$TYPE_RAWBODY_TESTS = 0x000e;
107
$TYPE_RAWBODY_TESTS = 0x000e;
Lines 110-120 Link Here
110
$TYPE_META_TESTS    = 0x0012;
111
$TYPE_META_TESTS    = 0x0012;
111
$TYPE_RBL_EVALS     = 0x0013;
112
$TYPE_RBL_EVALS     = 0x0013;
112
113
113
my @rule_types = ("body_tests", "uri_tests", "uri_evals",
114
my @rule_types = ("body_tests_obsolete", "uri_tests", "uri_evals",
114
                  "head_tests", "head_evals", "body_evals", "full_tests",
115
                  "head_tests", "head_evals", "body_evals_obsolete", "full_tests",
115
                  "full_evals", "rawbody_tests", "rawbody_evals",
116
                  "full_evals", "rawbody_tests", "rawbody_evals",
116
		  "rbl_evals", "meta_tests");
117
		  "rbl_evals", "meta_tests");
118
#BUG4778: above to be removed
117
119
120
$AUTOLEARN_NONE = 0;
121
$AUTOLEARN_HEAD = 1;
122
$AUTOLEARN_BODY = 2;
123
$AUTOLEARN_META = 3;
124
118
$VERSION = 'bogus';     # avoid CPAN.pm picking up version strings later
125
$VERSION = 'bogus';     # avoid CPAN.pm picking up version strings later
119
126
120
# these are variables instead of constants so that other classes can
127
# these are variables instead of constants so that other classes can
Lines 1943-1986 Link Here
1943
    }
1950
    }
1944
  });
1951
  });
1945
1952
1946
=item body SYMBOLIC_TEST_NAME /pattern/modifiers
1947
1948
Define a body pattern test.  C<pattern> is a Perl regular expression.  Note:
1949
as per the header tests, C<#> must be escaped (C<\#>) or else it is considered
1950
the beginning of a comment.
1951
1952
The 'body' in this case is the textual parts of the message body;
1953
any non-text MIME parts are stripped, and the message decoded from
1954
Quoted-Printable or Base-64-encoded format if necessary.  The message
1955
Subject header is considered part of the body and becomes the first
1956
paragraph when running the rules.  All HTML tags and line breaks will
1957
be removed before matching.
1958
1959
=item body SYMBOLIC_TEST_NAME eval:name_of_eval_method([args])
1960
1961
Define a body eval test.  See above.
1962
1963
=cut
1964
1965
  push (@cmds, {
1966
    setting => 'body',
1967
    is_frequent => 1,
1968
    is_priv => 1,
1969
    code => sub {
1970
      my ($self, $key, $value, $line) = @_;
1971
      if ($value =~ /^(\S+)\s+eval:(.*)$/) {
1972
        $self->{parser}->add_test ($1, $2, $TYPE_BODY_EVALS);
1973
      }
1974
      else {
1975
	my @values = split(/\s+/, $value, 2);
1976
	if (@values != 2) {
1977
	  return $MISSING_REQUIRED_VALUE;
1978
	}
1979
        $self->{parser}->add_test (@values, $TYPE_BODY_TESTS);
1980
      }
1981
    }
1982
  });
1983
1984
=item uri SYMBOLIC_TEST_NAME /pattern/modifiers
1953
=item uri SYMBOLIC_TEST_NAME /pattern/modifiers
1985
1954
1986
Define a uri pattern test.  C<pattern> is a Perl regular expression.  Note: as
1955
Define a uri pattern test.  C<pattern> is a Perl regular expression.  Note: as
Lines 2822-2833 Link Here
2822
  # after parsing, tests are refiled into these hashes for each test type.
2791
  # after parsing, tests are refiled into these hashes for each test type.
2823
  # this allows e.g. a full-text test to be rewritten as a body test in
2792
  # this allows e.g. a full-text test to be rewritten as a body test in
2824
  # the user's user_prefs file.
2793
  # the user's user_prefs file.
2825
  $self->{body_tests} = { };
2826
  $self->{uri_tests}  = { };
2794
  $self->{uri_tests}  = { };
2827
  $self->{uri_evals}  = { }; # not used/implemented yet
2795
  $self->{uri_evals}  = { }; # not used/implemented yet
2828
  $self->{head_tests} = { };
2796
  $self->{head_tests} = { };
2829
  $self->{head_evals} = { };
2797
  $self->{head_evals} = { };
2830
  $self->{body_evals} = { };
2831
  $self->{full_tests} = { };
2798
  $self->{full_tests} = { };
2832
  $self->{full_evals} = { };
2799
  $self->{full_evals} = { };
2833
  $self->{rawbody_tests} = { };
2800
  $self->{rawbody_tests} = { };
Lines 2912-2922 Link Here
2912
  return $self->{scoreset_current};
2879
  return $self->{scoreset_current};
2913
}
2880
}
2914
2881
2882
#BUG4778: function to be removed
2915
sub get_rule_types {
2883
sub get_rule_types {
2916
  my ($self) = @_;
2884
  my ($self) = @_;
2917
  return @rule_types;
2885
  return @rule_types;
2918
}
2886
}
2919
2887
2888
#BUG4778: function to be removed
2920
sub get_rule_keys {
2889
sub get_rule_keys {
2921
  my ($self, $test_type, $priority) = @_;
2890
  my ($self, $test_type, $priority) = @_;
2922
2891
Lines 2937-2942 Link Here
2937
  }
2906
  }
2938
}
2907
}
2939
2908
2909
#BUG4778: function to be removed
2940
sub get_rule_value {
2910
sub get_rule_value {
2941
  my ($self, $test_type, $rulename, $priority) = @_;
2911
  my ($self, $test_type, $rulename, $priority) = @_;
2942
2912
Lines 2984-2989 Link Here
2984
# Remove all rules that don't match the given regexp (or are sub-rules of
2954
# Remove all rules that don't match the given regexp (or are sub-rules of
2985
# meta-tests that match the regexp).
2955
# meta-tests that match the regexp).
2986
2956
2957
#BUG4778: below to be reimplemented
2987
sub trim_rules {
2958
sub trim_rules {
2988
  my ($self, $regexp) = @_;
2959
  my ($self, $regexp) = @_;
2989
2960
Lines 3019-3024 Link Here
3019
  }
2990
  }
3020
} # trim_rules()
2991
} # trim_rules()
3021
2992
2993
#BUG4778: function to be moved into meta rule type plugin and/or reimplemented
3022
sub add_meta_depends {
2994
sub add_meta_depends {
3023
  my ($self, $meta) = @_;
2995
  my ($self, $meta) = @_;
3024
2996
Lines 3042-3073 Link Here
3042
} # add_meta_depends()
3014
} # add_meta_depends()
3043
3015
3044
sub is_rule_active {
3016
sub is_rule_active {
3045
  my ($self, $test_type, $rulename, $priority) = @_;
3017
  my ($self, $rulename) = @_;
3046
3018
3047
  # special case rbl_evals since they do not have a priority
3019
  return 0 unless $self->{source_file}->{$rulename};
3048
  if ($test_type eq 'rbl_evals') {
3049
    return 0 unless ($self->{$test_type}->{$rulename});
3050
    return ($self->{scores}->{$rulename});
3051
  }
3052
3020
3053
  # first determine if the rule is defined
3054
  if (defined($priority)) {
3055
    # we have a specific priority
3056
    return 0 unless ($self->{$test_type}->{$priority}->{$rulename});
3057
  }
3058
  else {
3059
    # no specific priority so we must loop over all currently defined
3060
    # priorities to see if the rule is defined
3061
    my $found_p = 0;
3062
    foreach my $pri (keys %{$self->{priorities}}) {
3063
      if ($self->{$test_type}->{$pri}->{$rulename}) {
3064
        $found_p = 1;
3065
        last;
3066
      }
3067
    }
3068
    return 0 unless ($found_p);
3069
  }
3070
3071
  return ($self->{scores}->{$rulename});
3021
  return ($self->{scores}->{$rulename});
3072
}
3022
}
3073
3023
Lines 3112-3124 Link Here
3112
3062
3113
sub maybe_header_only {
3063
sub maybe_header_only {
3114
  my($self,$rulename) = @_;
3064
  my($self,$rulename) = @_;
3115
  my $type = $self->{test_types}->{$rulename};
3065
  my $autolearn = $self->{autolearn}->{$rulename};
3116
  return 0 if (!defined ($type));
3066
  return 0 if (!defined ($autolearn));
3117
3067
3118
  if (($type == $TYPE_HEAD_TESTS) || ($type == $TYPE_HEAD_EVALS)) {
3068
  if ($autolearn == $AUTOLEARN_HEAD) {
3119
    return 1;
3069
    return 1;
3120
3070
3121
  } elsif ($type == $TYPE_META_TESTS) {
3071
  } elsif ($autolearn == $AUTOLEARN_META) {
3122
    my $tflags = $self->{tflags}->{$rulename}; $tflags ||= '';
3072
    my $tflags = $self->{tflags}->{$rulename}; $tflags ||= '';
3123
    if ($tflags =~ m/\bnet\b/i) {
3073
    if ($tflags =~ m/\bnet\b/i) {
3124
      return 0;
3074
      return 0;
Lines 3132-3147 Link Here
3132
3082
3133
sub maybe_body_only {
3083
sub maybe_body_only {
3134
  my($self,$rulename) = @_;
3084
  my($self,$rulename) = @_;
3135
  my $type = $self->{test_types}->{$rulename};
3085
  my $autolearn = $self->{autolearn}->{$rulename};
3136
  return 0 if (!defined ($type));
3086
  return 0 if (!defined ($autolearn));
3137
3087
3138
  if (($type == $TYPE_BODY_TESTS) || ($type == $TYPE_BODY_EVALS)
3088
  if ($autolearn == $AUTOLEARN_BODY) {
3139
        || ($type == $TYPE_URI_TESTS) || ($type == $TYPE_URI_EVALS))
3140
  {
3141
    # some rawbody go off of headers...
3142
    return 1;
3089
    return 1;
3143
3090
3144
  } elsif ($type == $TYPE_META_TESTS) {
3091
  } elsif ($autolearn == $AUTOLEARN_META) {
3145
    my $tflags = $self->{tflags}->{$rulename}; $tflags ||= '';
3092
    my $tflags = $self->{tflags}->{$rulename}; $tflags ||= '';
3146
    if ($tflags =~ m/\bnet\b/i) {
3093
    if ($tflags =~ m/\bnet\b/i) {
3147
      return 0;
3094
      return 0;

Return to bug 4778