You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spamassassin.apache.org by jm...@apache.org on 2007/07/20 14:51:30 UTC
svn commit: r557985 - in
/spamassassin/branches/jm_bug_3852_two_level_configs: ./ lib/Mail/
lib/Mail/SpamAssassin/ lib/Mail/SpamAssassin/Conf/
lib/Mail/SpamAssassin/Plugin/ lib/Mail/SpamAssassin/Util/ t/
Author: jm
Date: Fri Jul 20 05:51:28 2007
New Revision: 557985
URL: http://svn.apache.org/viewvc?view=rev&rev=557985
Log:
add a new double-level hash type, which transparently presents a hash API for an underlying two-tier pair of hashes; continue converting {conf}->{foo} => {conf}->cf_foo for scalars
Added:
spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Util/TwoTierArray.pm
spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Util/TwoTierHash.pm
Modified:
spamassassin/branches/jm_bug_3852_two_level_configs/MANIFEST
spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin.pm
spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Conf.pm
spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Conf/Parser.pm
spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Dns.pm
spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/PerMsgStatus.pm
spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/AutoLearnThreshold.pm
spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/Check.pm
spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/HTMLEval.pm
spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/HeaderEval.pm
spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/MIMEEval.pm
spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Util.pm
spamassassin/branches/jm_bug_3852_two_level_configs/t/priorities.t
Modified: spamassassin/branches/jm_bug_3852_two_level_configs/MANIFEST
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_bug_3852_two_level_configs/MANIFEST?view=diff&rev=557985&r1=557984&r2=557985
==============================================================================
--- spamassassin/branches/jm_bug_3852_two_level_configs/MANIFEST (original)
+++ spamassassin/branches/jm_bug_3852_two_level_configs/MANIFEST Fri Jul 20 05:51:28 2007
@@ -504,3 +504,5 @@
t/root_spamd_x_u.t
t/spamc_x_E_R.t
t/spamc_x_e.t
+lib/Mail/SpamAssassin/Util/TwoTierArray.pm
+lib/Mail/SpamAssassin/Util/TwoTierHash.pm
Modified: spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin.pm?view=diff&rev=557985&r1=557984&r2=557985
==============================================================================
--- spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin.pm (original)
+++ spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin.pm Fri Jul 20 05:51:28 2007
@@ -349,7 +349,7 @@
$self->{username} = (Mail::SpamAssassin::Util::portable_getpwuid ($>))[0];
}
- $self->create_locker();
+ $self->create_locker(1);
$self->{resolver} = Mail::SpamAssassin::DnsResolver->new($self);
@@ -357,16 +357,19 @@
}
sub create_locker {
- my ($self) = @_;
+ my ($self, $withoutconf) = @_;
my $class;
- my $m = $self->{conf}->{lock_method};
+ my $m;
+ if (!$withoutconf) {
+ $m = $self->{conf}->cf_lock_method;
+ }
# let people choose what they want -- even if they may not work on their
# OS. (they could be using cygwin!)
- if ($m eq 'win32') { $class = 'Win32'; }
- elsif ($m eq 'flock') { $class = 'Flock'; }
- elsif ($m eq 'nfssafe') { $class = 'UnixNFSSafe'; }
+ if ($m && $m eq 'win32') { $class = 'Win32'; }
+ elsif ($m && $m eq 'flock') { $class = 'Flock'; }
+ elsif ($m && $m eq 'nfssafe') { $class = 'UnixNFSSafe'; }
else {
# OS-specific defaults
if (Mail::SpamAssassin::Util::am_running_on_windows()) {
Modified: spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Conf.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Conf.pm?view=diff&rev=557985&r1=557984&r2=557985
==============================================================================
--- spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Conf.pm (original)
+++ spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Conf.pm Fri Jul 20 05:51:28 2007
@@ -82,6 +82,8 @@
use Mail::SpamAssassin::Conf::Parser;
use Mail::SpamAssassin::Logger;
use Mail::SpamAssassin::Util::TieOneStringHash;
+use Mail::SpamAssassin::Util::TwoTierHash;
+use Mail::SpamAssassin::Util::TwoTierArray;
use File::Spec;
use strict;
@@ -242,7 +244,7 @@
}
}
- if ($relative && !exists $self->{scoreset}->[0]->{$rule}) {
+ if ($relative && !defined($self->get_score_for_rule($rule))) {
info("config: score: relative score without previous setting in " .
"configuration");
return $INVALID_VALUE;
@@ -256,11 +258,16 @@
# Set the actual scoreset values appropriately
for my $index (0..3) {
- my $score = $relative ?
- $self->{scoreset}->[$index]->{$rule} + $scores[$index] :
- $scores[$index];
-
- $self->{scoreset}->[$index]->{$rule} = $score + 0.0;
+ my $score;
+ if ($relative) {
+ my $base = (defined $self->{tiers}->[1]->{scoreset}->[$index]->{$rule})
+ ? $self->{tiers}->[1]->{scoreset}->[$index]->{$rule}
+ : $self->{tiers}->[0]->{scoreset}->[$index]->{$rule};
+ $score = $base + $scores[$index] + 0.0;
+ } else {
+ $score = $scores[$index] + 0.0;
+ }
+ $self->{activetier}->{scoreset}->[$index]->{$rule} = $score;
}
}
}
@@ -661,14 +668,16 @@
# We only deal with From, Subject, and To ...
elsif ($hdr =~ /^(?:From|Subject|To)$/) {
unless (defined $string && $string =~ /\S/) {
- delete $self->{rewrite_header}->{$hdr};
+ # undef => "deleted". we cannot really delete since that
+ # will kill lower tiers
+ $self->{activetier}->{rewrite_header}->{$hdr} = undef;
return;
}
if ($hdr ne 'Subject') {
$string =~ tr/()/[]/;
}
- $self->{rewrite_header}->{$hdr} = $string;
+ $self->{activetier}->{rewrite_header}->{$hdr} = $string;
return;
}
else {
@@ -676,7 +685,8 @@
info("config: rewrite_header: ignoring $hdr, not From, Subject, or To");
return $INVALID_VALUE;
}
- }
+ },
+ type => $CONF_TYPE_HASH_KEY_VALUE
});
=item add_header { spam | ham | all } header_name string
@@ -735,10 +745,10 @@
$hline = join("\\", @line);
chop($hline); # remove dummy newline again
if (($type eq "ham") || ($type eq "all")) {
- $self->{headers_ham}->{$name} = $hline;
+ $self->{activetier}->{headers_ham}->{$name} = $hline;
}
if (($type eq "spam") || ($type eq "all")) {
- $self->{headers_spam}->{$name} = $hline;
+ $self->{activetier}->{headers_spam}->{$name} = $hline;
}
}
});
@@ -770,10 +780,10 @@
return if ( $name eq "Checker-Version" );
if (($type eq "ham") || ($type eq "all")) {
- delete $self->{headers_ham}->{$name};
+ $self->{activetier}->{headers_ham}->{$name} = undef;
}
if (($type eq "spam") || ($type eq "all")) {
- delete $self->{headers_spam}->{$name};
+ $self->{activetier}->{headers_spam}->{$name} = undef;
}
}
});
@@ -795,11 +805,13 @@
setting => 'clear_headers',
code => sub {
my ($self, $key, $value, $line) = @_;
- for my $name (keys %{ $self->{headers_ham} }) {
- delete $self->{headers_ham}->{$name} if $name ne "Checker-Version";
- }
- for my $name (keys %{ $self->{headers_spam} }) {
- delete $self->{headers_spam}->{$name} if $name ne "Checker-Version";
+
+ foreach my $type (qw(headers_ham headers_spam)) {
+ for my $name ($self->get_all_tier_keys($type)) {
+ next if ($name eq "Checker-Version");
+ # override/replace all others with undef
+ $self->{activetier}->{$type}->{$name} = undef;
+ }
}
}
});
@@ -842,11 +854,12 @@
return $INVALID_VALUE;
}
- $self->{report_safe} = $value+0;
- if (! $self->{report_safe}) {
- $self->{headers_spam}->{"Report"} = "_REPORT_";
+ $self->{activetier}->{report_safe} = $value+0;
+ if (! $self->{activetier}->{report_safe}) {
+ $self->{activetier}->{headers_spam}->{"Report"} = "_REPORT_";
}
- }
+ },
+ type => $CONF_TYPE_BOOL
});
=back
@@ -942,8 +955,9 @@
return $INVALID_VALUE;
}
- $self->{normalize_charset} = 1;
- }
+ $self->{activetier}->{normalize_charset} = 1;
+ },
+ type => $CONF_TYPE_BOOL
});
@@ -1023,9 +1037,9 @@
return $MISSING_REQUIRED_VALUE;
}
foreach my $net (split (/\s+/, $value)) {
- $self->{trusted_networks}->add_cidr ($net);
+ $self->{activetier}->{trusted_networks}->add_cidr ($net);
}
- $self->{trusted_networks_configured} = 1;
+ $self->{activetier}->{trusted_networks_configured} = 1;
}
});
@@ -1039,8 +1053,8 @@
setting => 'clear_trusted_networks',
code => sub {
my ($self, $key, $value, $line) = @_;
- $self->{trusted_networks} = $self->new_netset();
- $self->{trusted_networks_configured} = 0;
+ $self->{activetier}->{trusted_networks} = $self->new_netset();
+ $self->{activetier}->{trusted_networks_configured} = 0;
}
});
@@ -1080,9 +1094,9 @@
return $MISSING_REQUIRED_VALUE;
}
foreach my $net (split (/\s+/, $value)) {
- $self->{internal_networks}->add_cidr ($net);
+ $self->{activetier}->{internal_networks}->add_cidr ($net);
}
- $self->{internal_networks_configured} = 1;
+ $self->{activetier}->{internal_networks_configured} = 1;
}
});
@@ -1096,8 +1110,8 @@
setting => 'clear_internal_networks',
code => sub {
my ($self, $key, $value, $line) = @_;
- $self->{internal_networks} = $self->new_netset();
- $self->{internal_networks_configured} = 0;
+ $self->{activetier}->{internal_networks} = $self->new_netset();
+ $self->{activetier}->{internal_networks_configured} = 0;
}
});
@@ -1138,9 +1152,9 @@
return $MISSING_REQUIRED_VALUE;
}
foreach my $net (split (/\s+/, $value)) {
- $self->{msa_networks}->add_cidr ($net);
+ $self->{activetier}->{msa_networks}->add_cidr ($net);
}
- $self->{msa_networks_configured} = 1;
+ $self->{activetier}->{msa_networks_configured} = 1;
}
});
@@ -1154,8 +1168,8 @@
setting => 'clear_msa_networks',
code => sub {
my ($self, $key, $value, $line) = @_;
- $self->{msa_networks} = Mail::SpamAssassin::NetSet->new(); # not new_netset
- $self->{msa_networks_configured} = 0;
+ $self->{activetier}->{msa_networks} = Mail::SpamAssassin::NetSet->new(); # not new_netset
+ $self->{activetier}->{msa_networks_configured} = 0;
}
});
@@ -1212,18 +1226,19 @@
code => sub {
my ($self, $key, $value, $line) = @_;
if ($value =~ /^test(?::\s+.+)?$/) {
- $self->{dns_available} = $value;
+ $self->{activetier}->{dns_available} = $value;
}
elsif ($value =~ /^(?:yes|1)$/) {
- $self->{dns_available} = 'yes';
+ $self->{activetier}->{dns_available} = 'yes';
}
elsif ($value =~ /^(?:no|0)$/) {
- $self->{dns_available} = 'no';
+ $self->{activetier}->{dns_available} = 'no';
}
else {
return $INVALID_VALUE;
}
- }
+ },
+ type => $CONF_TYPE_STRING
});
=item dns_test_interval n (default: 600 seconds)
@@ -1239,8 +1254,9 @@
code => sub {
my ($self, $key, $value, $line) = @_;
if ($value !~ /^\d+$/) { return $INVALID_VALUE; }
- $self->{dns_test_interval} = $value;
- }
+ $self->{activetier}->{dns_test_interval} = $value;
+ },
+ type => $CONF_TYPE_NUMERIC
});
=back
@@ -1316,7 +1332,7 @@
if ($value eq '') {
return $MISSING_REQUIRED_VALUE;
}
- push (@{$self->{bayes_ignore_headers}}, split(/\s+/, $value));
+ push (@{$self->{activetier}->{bayes_ignore_headers}}, split(/\s+/, $value));
}
});
@@ -1533,10 +1549,11 @@
return $INVALID_VALUE;
}
- $self->{lock_method} = $value;
+ $self->{activetier}->{lock_method} = $value;
# recreate the locker
$self->{main}->create_locker();
- }
+ },
+ type => $CONF_TYPE_STRING
});
=item fold_headers ( 0 | 1 ) (default: 1)
@@ -1574,7 +1591,7 @@
if ($value eq '') {
return $MISSING_REQUIRED_VALUE;
}
- push(@{$self->{report_safe_copy_headers}}, split(/\s+/, $value));
+ push(@{$self->{activetier}->{report_safe_copy_headers}}, split(/\s+/, $value));
}
});
@@ -1799,7 +1816,8 @@
$self->{allow_user_rules} = $value+0;
dbg("config: " . ($self->{allow_user_rules} ? "allowing":"not allowing") . " user rules!");
- }
+ },
+ type => $CONF_TYPE_BOOL
});
=item redirector_pattern /pattern/modifiers
@@ -1834,7 +1852,7 @@
$pattern = "(?".$3.")".$pattern if $3;
$pattern = qr/$pattern/;
- push @{$self->{main}->{conf}->{redirector_patterns}}, $pattern;
+ push @{$self->{activetier}->{redirector_patterns}}, $pattern;
# dbg("config: adding redirector regex: " . $value);
}
});
@@ -2070,7 +2088,7 @@
}
elsif ($value =~ /^(\S+)\s+exists:(.*)$/) {
$self->{parser}->add_test ($1, "$2 =~ /./", $TYPE_HEAD_TESTS);
- $self->{descriptions}->{$1} = "Found a $2 header";
+ $self->{activetier}->{descriptions}->{$1} = "Found a $2 header";
}
else {
my @values = split(/\s+/, $value, 2);
@@ -3081,97 +3099,184 @@
my $self = {
main => shift,
registered_commands => [],
+ plugins_loaded => { },
+ eval_plugins => { },
+ errors => 0,
}; bless ($self, $class);
$self->{parser} = Mail::SpamAssassin::Conf::Parser->new($self);
+
+ # create a new config tier for the basic, system-wide config
+ $self->{tiers}->[0] = $self->new_tier();
+ $self->{activetier} = $self->{tiers}->[0];
+ $self->create_lookup_doublehashes();
+
+ # and populate some defaults:
$self->{parser}->register_commands($self->set_default_commands());
- $self->{errors} = 0;
- $self->{plugins_loaded} = { };
+ # Make sure we add in X-Spam-Checker-Version
+ $self->{tiers}->[0]->{headers_spam}->{"Checker-Version"} =
+ "SpamAssassin _VERSION_ (_SUBVERSION_) on _HOSTNAME_";
+ $self->{tiers}->[0]->{headers_ham}->{"Checker-Version"} =
+ $self->{tiers}->[0]->{headers_spam}->{"Checker-Version"};
- $self->{tests} = { };
- $self->{test_types} = { };
- $self->{scoreset} = [ {}, {}, {}, {} ];
- $self->{scoreset_current} = 0;
- $self->set_score_set (0);
- $self->{tflags} = { };
- $self->{source_file} = { };
+ # these should potentially be settable by end-users
+ # perhaps via plugin?
+ $self->{num_check_received} = 9;
+ $self->{bayes_expiry_pct} = 0.75;
+ $self->{bayes_expiry_period} = 43200;
+ $self->{bayes_expiry_max_exponent} = 9;
+
+ $self->{encapsulated_content_description} = 'original message before SpamAssassin';
+
+ # testing stuff
+ $self->{regression_tests} = { };
+
+ $self;
+}
+
+sub new_tier {
+ my ($self) = @_;
+
+ my $tier = { };
+
+ # the guideline is, if a config setting is something that can be set
+ # in the user config file, and should be discarded for scanning as
+ # another user -- it should be stored in a tier rather than on $self.
+
+ $tier->{tests} = { };
+ $tier->{test_types} = { };
+ $tier->{scoreset} = [ {}, {}, {}, {} ];
+ $tier->{tflags} = { };
+ $tier->{source_file} = { };
+ $tier->{meta_dependencies} = { };
# keep descriptions in a slow but space-efficient single-string
# data structure
- tie %{$self->{descriptions}}, 'Mail::SpamAssassin::Util::TieOneStringHash'
+ tie %{$tier->{descriptions}}, 'Mail::SpamAssassin::Util::TieOneStringHash'
or warn "tie failed";
# after parsing, tests are refiled into these hashes for each test type.
# this allows e.g. a full-text test to be rewritten as a body test in
# the user's user_prefs file.
- $self->{body_tests} = { };
- $self->{uri_tests} = { };
- $self->{uri_evals} = { }; # not used/implemented yet
- $self->{head_tests} = { };
- $self->{head_evals} = { };
- $self->{body_evals} = { };
- $self->{full_tests} = { };
- $self->{full_evals} = { };
- $self->{rawbody_tests} = { };
- $self->{rawbody_evals} = { };
- $self->{meta_tests} = { };
- $self->{eval_plugins} = { };
- $self->{duplicate_rules} = { };
+ $tier->{body_tests} = { };
+ $tier->{uri_tests} = { };
+ $tier->{uri_evals} = { }; # not used/implemented yet
+ $tier->{head_tests} = { };
+ $tier->{head_evals} = { };
+ $tier->{body_evals} = { };
+ $tier->{full_tests} = { };
+ $tier->{full_evals} = { };
+ $tier->{rawbody_tests} = { };
+ $tier->{rawbody_evals} = { };
+ $tier->{meta_tests} = { };
+ $tier->{duplicate_rules} = { };
+
+ $tier->{rewrite_header} = { };
+ $tier->{user_rules_to_compile} = { };
+ $tier->{user_defined_rules} = { };
+ $tier->{headers_spam} = { };
+ $tier->{headers_ham} = { };
+ $tier->{priorities} = { };
+
+ $tier->{bayes_ignore_headers} = [ ];
+ $tier->{bayes_ignore_from} = { };
+ $tier->{bayes_ignore_to} = { };
+
+ $tier->{whitelist_auth} = { };
+ $tier->{whitelist_from} = { };
+ $tier->{whitelist_allows_relays} = { };
+ $tier->{blacklist_from} = { };
+
+ $tier->{blacklist_to} = { };
+ $tier->{whitelist_to} = { };
+ $tier->{more_spam_to} = { };
+ $tier->{all_spam_to} = { };
+
+ $tier->{trusted_networks} = $self->new_netset();
+ $tier->{internal_networks} = $self->new_netset();
+ $tier->{msa_networks} = Mail::SpamAssassin::NetSet->new(); # not new_netset
+ $tier->{trusted_networks_configured} = 0;
+ $tier->{internal_networks_configured} = 0;
- # testing stuff
- $self->{regression_tests} = { };
+ $tier->{scoreset_current} = 0;
+ $self->set_score_set (0);
+
+ return $tier;
+}
- $self->{rewrite_header} = { };
- $self->{user_rules_to_compile} = { };
- $self->{user_defined_rules} = { };
- $self->{headers_spam} = { };
- $self->{headers_ham} = { };
-
- $self->{bayes_ignore_headers} = [ ];
- $self->{bayes_ignore_from} = { };
- $self->{bayes_ignore_to} = { };
-
- $self->{whitelist_auth} = { };
- $self->{whitelist_from} = { };
- $self->{whitelist_allows_relays} = { };
- $self->{blacklist_from} = { };
-
- $self->{blacklist_to} = { };
- $self->{whitelist_to} = { };
- $self->{more_spam_to} = { };
- $self->{all_spam_to} = { };
-
- $self->{trusted_networks} = $self->new_netset();
- $self->{internal_networks} = $self->new_netset();
- $self->{msa_networks} = Mail::SpamAssassin::NetSet->new(); # not new_netset
- $self->{trusted_networks_configured} = 0;
- $self->{internal_networks_configured} = 0;
+# create a new config tier for user configuration
+sub push_tier {
+ my ($self) = @_;
+ $self->{tiers}->[1] = $self->new_tier();
+ $self->{activetier} = $self->{tiers}->[1];
+ $self->create_lookup_doublehashes();
+}
- # Make sure we add in X-Spam-Checker-Version
- $self->{headers_spam}->{"Checker-Version"} =
- "SpamAssassin _VERSION_ (_SUBVERSION_) on _HOSTNAME_";
- $self->{headers_ham}->{"Checker-Version"} =
- $self->{headers_spam}->{"Checker-Version"};
+# and delete it once the user is no longer active
+sub pop_tier {
+ my ($self) = @_;
+ delete $self->{tiers}->[1];
+ $self->{activetier} = $self->{tiers}->[0];
+}
- # these should potentially be settable by end-users
- # perhaps via plugin?
- $self->{num_check_received} = 9;
- $self->{bayes_expiry_pct} = 0.75;
- $self->{bayes_expiry_period} = 43200;
- $self->{bayes_expiry_max_exponent} = 9;
+# create "double-hash" lookup structures that point into the 2 tiers of
+# config data, providing a transparent UI so that calling code doesn't have
+# to know the gritty details in many cases
+sub create_lookup_doublehashes {
+ my ($self) = @_;
+ foreach my $hash (qw(
- $self->{encapsulated_content_description} = 'original message before SpamAssassin';
+ tflags meta_dependencies source_file priorities rewrite_header
+ headers_spam headers_ham duplicate_rules rules_to_replace
+ test_types priority
- $self;
+ ))
+ {
+ $self->new_two_tier_hash($hash);
+ }
+ foreach my $hash (qw(
+
+ report_safe_copy_headers redirector_patterns
+
+ ))
+ {
+ $self->new_two_tier_array($hash);
+ }
+}
+
+sub new_two_tier_hash {
+ my ($self, $name, $t0, $t1) = @_;
+ if (!defined $t0) {
+ $t0 = $self->{tiers}->[0]->{$name} = { };
+ }
+ if (!defined $t1) {
+ $t1 = $self->{tiers}->[1]->{$name} = { };
+ }
+ delete $self->{$name};
+ tie %{$self->{$name}}, 'Mail::SpamAssassin::Util::TwoTierHash', $t0, $t1
+ or warn "tie failed";
+}
+
+sub new_two_tier_array {
+ my ($self, $name, $t0, $t1) = @_;
+ if (!defined $t0) {
+ $t0 = $self->{tiers}->[0]->{$name} = [ ];
+ }
+ if (!defined $t1) {
+ $t1 = $self->{tiers}->[1]->{$name} = [ ];
+ }
+ delete $self->{$name};
+ tie @{$self->{$name}}, 'Mail::SpamAssassin::Util::TwoTierArray', $t0, $t1
+ or warn "tie failed";
}
sub mtime {
my $self = shift;
if (@_) {
- $self->{mtime} = shift;
+ $self->{activetier}->{mtime} = shift;
}
- return $self->{mtime};
+ return $self->{activetier}->{mtime};
}
###########################################################################
@@ -3190,77 +3295,47 @@
sub set_score_set {
my ($self, $set) = @_;
- $self->{scores} = $self->{scoreset}->[$set];
- $self->{scoreset_current} = $set;
+ $self->{activetier}->{scoreset_current} = $set;
+
+ # use TwoTierHash objects to represent certain hashes; lookups
+ # in these "hashes" will fall through correctly to the other tiers.
+ $self->new_two_tier_hash('scores',
+ $self->{tiers}->[0]->{scoreset}->[$set],
+ $self->{tiers}->[1]->{scoreset}->[$set]);
+
dbg("config: score set $set chosen.");
}
sub get_score_set {
my($self) = @_;
- return $self->{scoreset_current};
-}
-
-sub get_rule_types {
- my ($self) = @_;
- return @rule_types;
+ return $self->{activetier}->{scoreset_current};
}
-sub get_rule_keys {
- my ($self, $test_type, $priority) = @_;
+sub delete_rule {
+ my ($self, $type, $rulename, $priority) = @_;
# special case rbl_evals since they do not have a priority
- if ($test_type eq 'rbl_evals') {
- return keys(%{$self->{$test_type}});
- }
-
- if (defined($priority)) {
- return keys(%{$self->{$test_type}->{$priority}});
- }
- else {
- my @rules;
- foreach my $pri (keys(%{$self->{priorities}})) {
- push(@rules, keys(%{$self->{$test_type}->{$pri}}));
+ if ($type eq 'rbl_evals') {
+ if ($self->{tiers}->[1]->{$type}->{$rulename}) {
+ return delete $self->{tiers}->[1]->{$type}->{$rulename};
+ } else {
+ return delete $self->{tiers}->[0]->{$type}->{$rulename};
}
- return @rules;
- }
-}
-
-sub get_rule_value {
- my ($self, $test_type, $rulename, $priority) = @_;
-
- # special case rbl_evals since they do not have a priority
- if ($test_type eq 'rbl_evals') {
- return keys(%{$self->{$test_type}->{$rulename}});
}
if (defined($priority)) {
- return $self->{$test_type}->{$priority}->{$rulename};
- }
- else {
- foreach my $pri (keys(%{$self->{priorities}})) {
- if (exists($self->{$test_type}->{$pri}->{$rulename})) {
- return $self->{$test_type}->{$pri}->{$rulename};
- }
+ if ($self->{tiers}->[1]->{$type}->{$priority}->{$rulename}) {
+ return delete $self->{tiers}->[1]->{$type}->{$priority}->{$rulename};
+ } else {
+ return delete $self->{tiers}->[0]->{$type}->{$priority}->{$rulename};
}
- return undef; # if we get here we didn't find the rule
- }
-}
-
-sub delete_rule {
- my ($self, $test_type, $rulename, $priority) = @_;
-
- # special case rbl_evals since they do not have a priority
- if ($test_type eq 'rbl_evals') {
- return delete($self->{$test_type}->{$rulename});
- }
-
- if (defined($priority)) {
- return delete($self->{$test_type}->{$priority}->{$rulename});
}
else {
- foreach my $pri (keys(%{$self->{priorities}})) {
- if (exists($self->{$test_type}->{$pri}->{$rulename})) {
- return delete($self->{$test_type}->{$pri}->{$rulename});
+ foreach my $pri ($self->get_all_tier_keys("priorities")) {
+ if ($self->{tiers}->[1]->{$type}->{$pri}->{$rulename}) {
+ return delete $self->{tiers}->[1]->{$type}->{$pri}->{$rulename};
+ } else {
+ return delete $self->{tiers}->[0]->{$type}->{$pri}->{$rulename};
}
}
return undef; # if we get here we didn't find the rule
@@ -3330,33 +3405,9 @@
} # add_meta_depends()
sub is_rule_active {
- my ($self, $test_type, $rulename, $priority) = @_;
-
- # special case rbl_evals since they do not have a priority
- if ($test_type eq 'rbl_evals') {
- return 0 unless ($self->{$test_type}->{$rulename});
- return ($self->{scores}->{$rulename});
- }
-
- # first determine if the rule is defined
- if (defined($priority)) {
- # we have a specific priority
- return 0 unless ($self->{$test_type}->{$priority}->{$rulename});
- }
- else {
- # no specific priority so we must loop over all currently defined
- # priorities to see if the rule is defined
- my $found_p = 0;
- foreach my $pri (keys %{$self->{priorities}}) {
- if ($self->{$test_type}->{$pri}->{$rulename}) {
- $found_p = 1;
- last;
- }
- }
- return 0 unless ($found_p);
- }
-
- return ($self->{scores}->{$rulename});
+ my ($self, $type, $rulename, $priority) = @_;
+ return 0 unless ($self->get_rule_value($type, $rulename, $priority));
+ return $self->get_score_for_rule($rulename);
}
###########################################################################
@@ -3398,10 +3449,135 @@
}
###########################################################################
+# Tiering-aware accessor methods
+
+sub get_all_tier_keys {
+ my ($self, $hashname) = @_;
+ if (defined $self->{tiers}->[1]->{$hashname}) {
+ return Mail::SpamAssassin::Util::uniq_list(
+ keys %{ $self->{tiers}->[0]->{$hashname} },
+ keys %{ $self->{tiers}->[1]->{$hashname} }
+ );
+ } else {
+ return keys %{ $self->{tiers}->[0]->{$hashname} };
+ }
+}
+
+sub get_all_tier_lists {
+ my ($self, $listname) = @_;
+ my $l0 = $self->{tiers}->[0]->{$listname};
+ my $l1 = $self->{tiers}->[1]->{$listname};
+ if ($l1) {
+ if ($l0) {
+ return Mail::SpamAssassin::Util::uniq_list(@{$l0}, @{$l1});
+ } else {
+ return @{ $l1 };
+ }
+ } else {
+ if ($l0) {
+ return @{ $l0 };
+ } else {
+ return ();
+ }
+ }
+}
+
+sub tier_lookup {
+ my ($self, $hashname, $rule) = @_;
+ # note: exists(), not defined(), since this way we can "delete" the
+ # data from a lower tier by overriding it with undef
+ if (exists $self->{tiers}->[1]->{$hashname}->{$rule}) {
+ return $self->{tiers}->[1]->{$hashname}->{$rule};
+ } else {
+ return $self->{tiers}->[0]->{$hashname}->{$rule};
+ }
+}
sub get_description_for_rule {
my ($self, $rule) = @_;
- return $self->{descriptions}->{$rule};
+ return $self->tier_lookup("descriptions", $rule);
+}
+
+sub get_tflags_for_rule {
+ my ($self, $rule) = @_;
+ return $self->{tflags}->{$rule} || ''; # always provide a string default
+}
+
+sub get_score_for_rule {
+ my ($self, $rule) = @_;
+ return $self->{scores}->{$rule};
+}
+
+sub get_score_in_scoreset {
+ my ($self, $rule, $set) = @_;
+ if ($self->{tiers}->[1]->{scoreset}->[$set]->{$rule}) {
+ return $self->{tiers}->[1]->{scoreset}->[$set]->{$rule};
+ } else {
+ return $self->{tiers}->[0]->{scoreset}->[$set]->{$rule};
+ }
+}
+
+sub get_rule_types {
+ my ($self) = @_;
+ return @rule_types;
+}
+
+sub get_rule_keys {
+ my ($self, $type, $priority) = @_;
+
+ my @rules;
+ if ($type eq 'rbl_evals') {
+ # special case rbl_evals since they do not have a priority
+ push @rules, keys(%{$self->{tiers}->[0]->{$type}});
+ push @rules, keys(%{$self->{tiers}->[1]->{$type}});
+ }
+ elsif (defined($priority)) {
+ push @rules, keys(%{$self->{tiers}->[0]->{$type}->{$priority}});
+ push @rules, keys(%{$self->{tiers}->[1]->{$type}->{$priority}});
+ }
+ else {
+ foreach my $pri ($self->get_all_tier_keys("priorities")) {
+ push @rules, keys(%{$self->{tiers}->[0]->{$type}->{$pri}});
+ push @rules, keys(%{$self->{tiers}->[1]->{$type}->{$pri}});
+ }
+ }
+ return @rules;
+}
+
+sub get_rule_value {
+ my ($self, $type, $rulename, $priority) = @_;
+
+ # special case rbl_evals since they do not have a priority
+ if ($type eq 'rbl_evals') {
+ if ($self->{tiers}->[1]->{$type}->{$rulename}) {
+ return keys %{$self->{tiers}->[1]->{$type}->{$rulename}};
+ } else {
+ return keys %{$self->{tiers}->[0]->{$type}->{$rulename}};
+ }
+ }
+
+ if (defined($priority)) {
+ if ($self->{tiers}->[1]->{$type}->{$priority}->{$rulename}) {
+ return $self->{tiers}->[1]->{$type}->{$priority}->{$rulename};
+ } else {
+ return $self->{tiers}->[0]->{$type}->{$priority}->{$rulename};
+ }
+ }
+ else {
+ foreach my $pri ($self->get_all_tier_keys("priorities")) {
+ if ($self->{tiers}->[1]->{$type}->{$pri}->{$rulename}) {
+ return $self->{tiers}->[1]->{$type}->{$pri}->{$rulename};
+ } else {
+ return $self->{tiers}->[0]->{$type}->{$pri}->{$rulename};
+ }
+ }
+ return undef; # if we get here we didn't find the rule
+ }
+}
+
+sub get_all_priorities {
+ my ($self) = @_;
+ return $self->get_all_tier_keys('priorities');
}
###########################################################################
@@ -3415,7 +3591,7 @@
return 1;
} elsif ($type == $TYPE_META_TESTS) {
- my $tflags = $self->{tflags}->{$rulename}; $tflags ||= '';
+ my $tflags = $self->get_tflags_for_rule($rulename);
if ($tflags =~ m/\bnet\b/i) {
return 0;
} else {
@@ -3438,7 +3614,7 @@
return 1;
} elsif ($type == $TYPE_META_TESTS) {
- my $tflags = $self->{tflags}->{$rulename}; $tflags ||= '';
+ my $tflags = $self->get_tflags_for_rule($rulename);
if ($tflags =~ m/\bnet\b/i) {
return 0;
} else {
@@ -3554,9 +3730,9 @@
if (!$self->{main}->{keep_config_parsing_metadata} &&
!$self->{allow_user_rules})
{
- delete $self->{if_stack};
- delete $self->{source_file};
- delete $self->{meta_dependencies};
+ delete $self->{activetier}->{if_stack};
+ delete $self->{activetier}->{source_file};
+ delete $self->{activetier}->{meta_dependencies};
}
}
@@ -3571,7 +3747,8 @@
sub finish {
my ($self) = @_;
- untie %{$self->{descriptions}};
+ if ($self->{tiers}->[0]->{descriptions}) { untie %{$self->{tiers}->[0]->{descriptions}}; }
+ if ($self->{tiers}->[1]->{descriptions}) { untie %{$self->{tiers}->[1]->{descriptions}}; }
%{$self} = ();
}
Modified: spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Conf/Parser.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Conf/Parser.pm?view=diff&rev=557985&r1=557984&r2=557985
==============================================================================
--- spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Conf/Parser.pm (original)
+++ spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Conf/Parser.pm Fri Jul 20 05:51:28 2007
@@ -64,11 +64,16 @@
- $CONF_TYPE_HASH_KEY_VALUE: hash key/value pair,
like "describe" or tflags
-If this is set, a 'code' block is assigned based on the type.
+If this is set, a 'code' block is assigned based on the type, if
+none already exists, and an accessor of the right type will be used
+for the generated $conf->cf_setting() method.
Note that C<$CONF_TYPE_HASH_KEY_VALUE>-type settings require that the
value be non-empty, otherwise they'll produce a warning message.
+If a type is omitted, a default accessor will be generated using the
+$CONF_TYPE_STRING type.
+
=item code
A subroutine to deal with the setting. Only used if B<type> is not set. ONE OF
@@ -166,7 +171,7 @@
# Implementations for default accessors.
# access a scalar configuration setting. e.g.:
# my $val = $conf->cf_foo_bar();
-my $cf_accessor_scalar_value = '
+our $cf_accessor_scalar_value = '
sub cf_${SETTING} {
if (defined $_[0]->{tiers}->[1]->{${SETTING}}) {
@@ -180,10 +185,13 @@
# access a hash key=>value configuration setting. e.g.:
# my $val = $conf->cf_foo_bar("baz");
-my $cf_accessor_hash_key_value = '
+# note use of "exists" instead of "defined"; this is deliberate so that
+# "deletion" of a sys-level setting can take place at user-level,
+# by setting val to undef
+our $cf_accessor_hash_key_value = '
sub cf_${SETTING} {
- if (defined $_[0]->{tiers}->[1]->{${SETTING}}->{$_[1]}) {
+ if (exists $_[0]->{tiers}->[1]->{${SETTING}}->{$_[1]}) {
return $_[0]->{tiers}->[1]->{${SETTING}}->{$_[1]};
} else {
return $_[0]->{tiers}->[0]->{${SETTING}}->{$_[1]};
@@ -199,7 +207,7 @@
# }
# TODO: it would be more efficient to fix calling code to iterate
# through the tiers instead of using this API.
-my $cf_accessor_addrlist_value = '
+our $cf_accessor_addrlist_value = '
sub cf_${SETTING} {
if (defined $_[0]->{tiers}->[1]->{${SETTING}}) {
@@ -236,7 +244,7 @@
# note! exists, not defined -- we want to be able to set
# "undef" default values.
if (exists($cmd->{default})) {
- $conf->{$cmd->{setting}} = $cmd->{default};
+ $conf->{activetier}->{$cmd->{setting}} = $cmd->{default};
}
$self->setup_accessor($cmd);
@@ -590,14 +598,18 @@
if ($conf->{lint_rules}) {
# Check for description and score issues in lint fashion
- while ( my $k = each %{$conf->{descriptions}} ) {
- if (!exists $conf->{tests}->{$k}) {
+ foreach my $k ($conf->get_all_tier_keys('descriptions')) {
+ if (!exists $conf->{tiers}->[0]->{tests}->{$k}
+ && !exists $conf->{tiers}->[1]->{tests}->{$k})
+ {
$self->lint_warn("config: warning: description exists for non-existent rule $k\n", $k);
}
}
- while ( my($sk) = each %{$conf->{scores}} ) {
- if (!exists $conf->{tests}->{$sk}) {
+ foreach my $sk ($conf->get_all_tier_keys('scores')) {
+ if (!exists $conf->{tiers}->[0]->{tests}->{$sk}
+ && !exists $conf->{tiers}->[1]->{tests}->{$sk})
+ {
$self->lint_warn("config: warning: score set for non-existent rule $sk\n", $sk);
}
}
@@ -613,14 +625,17 @@
my ($self) = @_;
my $conf = $self->{conf};
- while ( my $k = each %{$conf->{tests}} ) {
- if ( ! exists $conf->{scores}->{$k} ) {
+ my $ss = $conf->{activetier}->{scoreset};
+ while ( my $k = each %{$conf->{activetier}->{tests}} ) {
+ if ( ! exists $ss->[0]->{$k} ) {
# T_ rules (in a testing probationary period) get low, low scores
my $set_score = ($k =~/^T_/) ? 0.01 : 1.0;
- $set_score = -$set_score if ( ($conf->{tflags}->{$k}||'') =~ /\bnice\b/ );
+ if ($conf->get_tflags_for_rule($k) =~ /\bnice\b/) {
+ $set_score = -$set_score;
+ }
for my $index (0..3) {
- $conf->{scoreset}->[$index]->{$k} = $set_score;
+ $ss->[$index]->{$k} = $set_score;
}
}
}
@@ -632,9 +647,10 @@
my ($self, $cmd) = @_;
# find a default accessor method, if possible
- if (!defined $cmd->{accessor} && $cmd->{type}) {
- my $type = $cmd->{type};
- if ($type == $Mail::SpamAssassin::Conf::CONF_TYPE_STRING) {
+ if (!defined $cmd->{accessor}) {
+ my $type = $cmd->{accessortype} || $cmd->{type};
+
+ if (!$type || $type == $Mail::SpamAssassin::Conf::CONF_TYPE_STRING) {
$cmd->{accessor} = $cf_accessor_scalar_value;
}
elsif ($type == $Mail::SpamAssassin::Conf::CONF_TYPE_BOOL) {
@@ -716,7 +732,7 @@
return $Mail::SpamAssassin::Conf::INVALID_VALUE;
}
- $conf->{$key} = $value+0.0;
+ $conf->{activetier}->{$key} = $value+0.0;
}
sub set_bool_value {
@@ -739,7 +755,7 @@
return $Mail::SpamAssassin::Conf::INVALID_VALUE;
}
- $conf->{$key} = $value+0;
+ $conf->{activetier}->{$key} = $value+0;
}
sub set_string_value {
@@ -749,7 +765,7 @@
return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
}
- $conf->{$key} = $value;
+ $conf->{activetier}->{$key} = $value;
}
sub set_hash_key_value {
@@ -760,7 +776,7 @@
return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
}
- $conf->{$key}->{$k} = $v;
+ $conf->{activetier}->{$key}->{$k} = $v;
}
sub set_addrlist_value {
@@ -784,12 +800,12 @@
sub set_template_append {
my ($conf, $key, $value, $line) = @_;
if ( $value =~ /^"(.*?)"$/ ) { $value = $1; }
- $conf->{$key} .= $value."\n";
+ $conf->{activetier}->{$key} .= $value."\n";
}
sub set_template_clear {
my ($conf, $key, $value, $line) = @_;
- $conf->{$key} = '';
+ $conf->{activetier}->{$key} = '';
}
###########################################################################
@@ -812,10 +828,10 @@
dbg("conf: finish parsing");
- while (my ($name, $text) = each %{$conf->{tests}}) {
- my $type = $conf->{test_types}->{$name};
+ while (my ($name, $text) = each %{$conf->{activetier}->{tests}}) {
+ my $type = $conf->{activetier}->{test_types}->{$name};
my $priority = $conf->{priority}->{$name} || 0;
- $conf->{priorities}->{$priority}++;
+ $conf->{activetier}->{priorities}->{$priority}++;
# eval type handling
if (($type & 1) == 1) {
@@ -827,24 +843,24 @@
# we've already warned about this
}
elsif ($type == $Mail::SpamAssassin::Conf::TYPE_BODY_EVALS) {
- $conf->{body_evals}->{$priority}->{$name} = $packed;
+ $conf->{activetier}->{body_evals}->{$priority}->{$name} = $packed;
}
elsif ($type == $Mail::SpamAssassin::Conf::TYPE_HEAD_EVALS) {
- $conf->{head_evals}->{$priority}->{$name} = $packed;
+ $conf->{activetier}->{head_evals}->{$priority}->{$name} = $packed;
}
elsif ($type == $Mail::SpamAssassin::Conf::TYPE_RBL_EVALS) {
# We don't do priorities for $Mail::SpamAssassin::Conf::TYPE_RBL_EVALS
# we also use the arrayref instead of the packed string
- $conf->{rbl_evals}->{$name} = [ $function, @$argsref ];
+ $conf->{activetier}->{rbl_evals}->{$name} = [ $function, @$argsref ];
}
elsif ($type == $Mail::SpamAssassin::Conf::TYPE_RAWBODY_EVALS) {
- $conf->{rawbody_evals}->{$priority}->{$name} = $packed;
+ $conf->{activetier}->{rawbody_evals}->{$priority}->{$name} = $packed;
}
elsif ($type == $Mail::SpamAssassin::Conf::TYPE_FULL_EVALS) {
- $conf->{full_evals}->{$priority}->{$name} = $packed;
+ $conf->{activetier}->{full_evals}->{$priority}->{$name} = $packed;
}
#elsif ($type == $Mail::SpamAssassin::Conf::TYPE_URI_EVALS) {
- # $conf->{uri_evals}->{$priority}->{$name} = $packed;
+ # $conf->{activetier}->{uri_evals}->{$priority}->{$name} = $packed;
#}
else {
$self->lint_warn("unknown type $type for $name: $text", $name);
@@ -857,22 +873,22 @@
# non-eval tests
else {
if ($type == $Mail::SpamAssassin::Conf::TYPE_BODY_TESTS) {
- $conf->{body_tests}->{$priority}->{$name} = $text;
+ $conf->{activetier}->{body_tests}->{$priority}->{$name} = $text;
}
elsif ($type == $Mail::SpamAssassin::Conf::TYPE_HEAD_TESTS) {
- $conf->{head_tests}->{$priority}->{$name} = $text;
+ $conf->{activetier}->{head_tests}->{$priority}->{$name} = $text;
}
elsif ($type == $Mail::SpamAssassin::Conf::TYPE_META_TESTS) {
- $conf->{meta_tests}->{$priority}->{$name} = $text;
+ $conf->{activetier}->{meta_tests}->{$priority}->{$name} = $text;
}
elsif ($type == $Mail::SpamAssassin::Conf::TYPE_URI_TESTS) {
- $conf->{uri_tests}->{$priority}->{$name} = $text;
+ $conf->{activetier}->{uri_tests}->{$priority}->{$name} = $text;
}
elsif ($type == $Mail::SpamAssassin::Conf::TYPE_RAWBODY_TESTS) {
- $conf->{rawbody_tests}->{$priority}->{$name} = $text;
+ $conf->{activetier}->{rawbody_tests}->{$priority}->{$name} = $text;
}
elsif ($type == $Mail::SpamAssassin::Conf::TYPE_FULL_TESTS) {
- $conf->{full_tests}->{$priority}->{$name} = $text;
+ $conf->{activetier}->{full_tests}->{$priority}->{$name} = $text;
}
else {
$self->lint_warn("unknown type $type for $name: $text", $name);
@@ -891,25 +907,25 @@
if (!$conf->{allow_user_rules}) {
# free up stuff we no longer need
- delete $conf->{tests};
- delete $conf->{priority};
- delete $conf->{test_types};
+ delete $conf->{activetier}->{tests};
+ delete $conf->{activetier}->{priority};
+ delete $conf->{activetier}->{test_types};
}
}
sub trace_meta_dependencies {
my ($self) = @_;
my $conf = $self->{conf};
- $conf->{meta_dependencies} = { };
+ %{$conf->{activetier}->{meta_dependencies}} = ( );
- foreach my $name (keys %{$conf->{tests}}) {
- next unless ($conf->{test_types}->{$name}
+ foreach my $name (keys %{$conf->{activetier}->{tests}}) {
+ next unless ($conf->{activetier}->{test_types}->{$name}
== $Mail::SpamAssassin::Conf::TYPE_META_TESTS);
my $deps = [ ];
my $alreadydone = { };
$self->_meta_deps_recurse($conf, $name, $name, $deps, $alreadydone);
- $conf->{meta_dependencies}->{$name} = join (' ', @{$deps});
+ $conf->{activetier}->{meta_dependencies}->{$name} = join (' ', @{$deps});
}
}
@@ -921,7 +937,7 @@
$alreadydone->{$name} = 1;
# Obviously, don't trace empty or nonexistent rules
- my $rule = $conf->{tests}->{$name};
+ my $rule = $conf->{activetier}->{tests}->{$name};
return unless $rule;
# Lex the rule into tokens using a rather simple RE method ...
@@ -933,7 +949,7 @@
# has to be an alpha+numeric token
next if ($token =~ /^(?:\W+|[+-]?\d+(?:\.\d+)?)$/);
# and has to be a rule name
- next unless exists $conf->{tests}->{$token};
+ next unless exists $conf->{activetier}->{tests}->{$token};
# add and recurse
push @{$deps}, $token;
@@ -945,8 +961,8 @@
my ($self) = @_;
my $conf = $self->{conf};
- die unless $conf->{meta_dependencies}; # order requirement
- my $pri = $conf->{priority};
+ die unless $conf->{activetier}->{meta_dependencies}; # order requirement
+ my $pri = $conf->{activetier}->{priority};
# sort into priority order, lowest first -- this way we ensure that if we
# rearrange the pri of a rule early on, we cannot accidentally increase its
@@ -957,7 +973,7 @@
{
# we only need to worry about meta rules -- they are the
# only type of rules which depend on other rules
- my $deps = $conf->{meta_dependencies}->{$rule};
+ my $deps = $conf->{activetier}->{meta_dependencies}->{$rule};
next unless (defined $deps);
my $basepri = $pri->{$rule};
@@ -977,9 +993,9 @@
my %names_for_text = ();
my %dups = ();
- while (my ($name, $text) = each %{$conf->{tests}}) {
- my $type = $conf->{test_types}->{$name};
- my $tf = ($conf->{tflags}->{$name}||''); $tf =~ s/\s+/ /gs;
+ while (my ($name, $text) = each %{$conf->{activetier}->{tests}}) {
+ my $type = $conf->{activetier}->{test_types}->{$name};
+ my $tf = $conf->get_tflags_for_rule($name); $tf =~ s/\s+/ /gs;
# ensure similar, but differently-typed, rules are not marked as dups;
# take tflags into account too due to "tflags multiple"
$text = "$type\t$text\t$tf";
@@ -997,7 +1013,7 @@
my $first_pri;
my @names = sort {$a cmp $b} split(' ', $names_for_text{$text});
foreach my $name (@names) {
- my $priority = $conf->{priority}->{$name} || 0;
+ my $priority = $conf->{activetier}->{priority}->{$name} || 0;
if (!defined $first || $priority < $first_pri) {
$first_pri = $priority;
@@ -1010,11 +1026,11 @@
foreach my $name (@names) {
next if $name eq $first;
push @dups, $name;
- delete $conf->{tests}->{$name};
+ delete $conf->{activetier}->{tests}->{$name};
}
dbg("rules: $first merged duplicates: ".join(' ', @dups));
- $conf->{duplicate_rules}->{$first} = \@dups;
+ $conf->{activetier}->{duplicate_rules}->{$first} = \@dups;
}
}
@@ -1058,17 +1074,28 @@
my ($self) = @_;
my $conf = $self->{conf};
+ # if we're in tier 1 (user configs) copy the booleans from the lower
+ # tier
+ if ($conf->{activetier} == $conf->{tiers}->[1]) {
+ $conf->{tiers}->[1]->{trusted_networks_configured} ||=
+ $conf->{tiers}->[0]->{trusted_networks_configured};
+ $conf->{tiers}->[1]->{internal_networks_configured} ||=
+ $conf->{tiers}->[0]->{internal_networks_configured};
+ }
+
# validate trusted_networks and internal_networks, bug 4760.
# check that all internal_networks are listed in trusted_networks
# too. do the same for msa_networks, but check msa_networks against
# internal_networks if trusted_networks aren't defined
- my ($nt, $matching_against);
- if ($conf->{trusted_networks_configured}) {
- $nt = $conf->{trusted_networks};
+ my ($nt0, $nt1, $matching_against);
+ if ($conf->{activetier}->{trusted_networks_configured}) {
+ $nt0 = $conf->{tiers}->[0]->{trusted_networks};
+ $nt1 = $conf->{tiers}->[1]->{trusted_networks};
$matching_against = 'trusted_networks';
- } elsif ($conf->{internal_networks_configured}) {
- $nt = $conf->{internal_networks};
+ } elsif ($conf->{activetier}->{internal_networks_configured}) {
+ $nt0 = $conf->{tiers}->[0]->{internal_networks};
+ $nt1 = $conf->{tiers}->[1]->{internal_networks};
$matching_against = 'internal_networks';
} else {
return;
@@ -1084,7 +1111,11 @@
foreach my $net (@{$net_list->{nets}}) {
# don't check to see if an excluded network is included - that's senseless
- if (!$net->{exclude} && !$nt->contains_net($net)) {
+ if (!$net->{exclude}
+ && !($nt0 && $nt0->contains_net($net))
+ && !($nt1 && $nt1->contains_net($net))
+ )
+ {
my $msg = "$matching_against doesn't contain $net_type entry '".
($net->{as_string})."'";
@@ -1153,24 +1184,24 @@
return unless $self->is_meta_valid($name, $text);
}
- $conf->{tests}->{$name} = $text;
- $conf->{test_types}->{$name} = $type;
+ $conf->{activetier}->{tests}->{$name} = $text;
+ $conf->{activetier}->{test_types}->{$name} = $type;
if ($type == $Mail::SpamAssassin::Conf::TYPE_META_TESTS) {
- $conf->{priority}->{$name} ||= 500;
+ $conf->{activetier}->{priority}->{$name} ||= 500;
}
else {
- $conf->{priority}->{$name} ||= 0;
+ $conf->{activetier}->{priority}->{$name} ||= 0;
}
- $conf->{priority}->{$name} ||= 0;
- $conf->{source_file}->{$name} = $self->{currentfile};
+ $conf->{activetier}->{priority}->{$name} ||= 0;
+ $conf->{activetier}->{source_file}->{$name} = $self->{currentfile};
if ($self->{main}->{keep_config_parsing_metadata}) {
- $conf->{if_stack}->{$name} = $self->get_if_stack_as_string();
+ $conf->{activetier}->{if_stack}->{$name} = $self->get_if_stack_as_string();
}
if ($self->{scoresonly}) {
- $conf->{user_rules_to_compile}->{$type} = 1;
- $conf->{user_defined_rules}->{$name} = 1;
+ $conf->{activetier}->{user_rules_to_compile}->{$type} = 1;
+ $conf->{activetier}->{user_defined_rules}->{$name} = 1;
}
}
@@ -1294,7 +1325,7 @@
$re =~ s/([^\*\?_a-zA-Z0-9])/\\$1/g; # escape any possible metachars
$re =~ tr/?/./; # "?" -> "."
$re =~ s/\*+/\.\*/g; # "*" -> "any string"
- $conf->{$singlelist}->{$addr} = "^${re}\$";
+ $conf->{activetier}->{$singlelist}->{$addr} = "^${re}\$";
}
}
@@ -1303,17 +1334,25 @@
my $conf = $self->{conf};
$addr = lc $addr;
- if ($conf->{$listname}->{$addr}) {
- push @{$conf->{$listname}->{$addr}{domain}}, $domain;
+ if ($conf->{activetier}->{$listname}->{$addr}) {
+ push @{$conf->{activetier}->{$listname}->{$addr}{domain}}, $domain;
+ }
+ elsif ($conf->{tiers}->[0]->{$listname}->{$addr}) {
+ # copy entry from a lower tier so we can extend it
+ $conf->{activetier}->{$listname}->{$addr}{re} =
+ $conf->{tiers}->[0]->{$listname}->{$addr}{re};
+ @{$conf->{activetier}->{$listname}->{$addr}{domain}} =
+ @{$conf->{tiers}->[0]->{$listname}->{$addr}{domain}};
}
else {
+ # create a new entry
my $re = $addr;
$re =~ s/[\000\\\(]/_/gs; # paranoia
$re =~ s/([^\*\?_a-zA-Z0-9])/\\$1/g; # escape any possible metachars
$re =~ tr/?/./; # "?" -> "."
$re =~ s/\*+/\.\*/g; # "*" -> "any string"
- $conf->{$listname}->{$addr}{re} = "^${re}\$";
- $conf->{$listname}->{$addr}{domain} = [ $domain ];
+ $conf->{activetier}->{$listname}->{$addr}{re} = "^${re}\$";
+ $conf->{activetier}->{$listname}->{$addr}{domain} = [ $domain ];
}
}
@@ -1322,7 +1361,7 @@
my $conf = $self->{conf};
foreach my $addr (@addrs) {
- delete($conf->{$singlelist}->{$addr});
+ delete $conf->{activetier}->{$singlelist}->{$addr};
}
}
@@ -1331,7 +1370,7 @@
my $conf = $self->{conf};
foreach my $addr (@addrs) {
- delete($conf->{$listname}->{$addr});
+ delete $conf->{activetier}->{$listname}->{$addr};
}
}
Modified: spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Dns.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Dns.pm?view=diff&rev=557985&r1=557984&r2=557985
==============================================================================
--- spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Dns.pm (original)
+++ spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Dns.pm Fri Jul 20 05:51:28 2007
@@ -330,7 +330,7 @@
return if !defined $self->{async}->get_last_start_lookup_time();
- my $deadline = $self->{conf}->{rbl_timeout} + $self->{async}->get_last_start_lookup_time();
+ my $deadline = $self->{conf}->cf_rbl_timeout + $self->{async}->get_last_start_lookup_time();
my $now = time;
# should not give up before at least attempting to collect some responses
@@ -355,11 +355,11 @@
@left = $self->{async}->get_pending_lookups();
# complete_lookups could cause a change in get_last_start_lookup_time
- $deadline = $self->{conf}->{rbl_timeout} +
+ $deadline = $self->{conf}->cf_rbl_timeout +
$self->{async}->get_last_start_lookup_time();
# dynamic timeout
- my $dynamic = (int($self->{conf}->{rbl_timeout}
+ my $dynamic = (int($self->{conf}->cf_rbl_timeout
* (1 - 0.7*(($total - @left) / $total) ** 2) + 1)
+ $self->{async}->get_last_start_lookup_time());
$deadline = $dynamic if ($dynamic < $deadline);
@@ -372,7 +372,7 @@
return if !defined $self->{async}->get_last_start_lookup_time();
- my $deadline = $self->{conf}->{rbl_timeout} + $self->{async}->get_last_start_lookup_time();
+ my $deadline = $self->{conf}->cf_rbl_timeout + $self->{async}->get_last_start_lookup_time();
my $now = time;
# should not give up before at least attempting to collect some responses
@@ -396,11 +396,11 @@
# complete_lookups() may have called completed_callback, which may call
# start_lookup() again (like in URIDNSBL), so get_last_start_lookup_time
# may have changed and deadline needs to be recomputed
- $deadline = $self->{conf}->{rbl_timeout} +
+ $deadline = $self->{conf}->cf_rbl_timeout +
$self->{async}->get_last_start_lookup_time();
# dynamic timeout
- my $dynamic = (int($self->{conf}->{rbl_timeout}
+ my $dynamic = (int($self->{conf}->cf_rbl_timeout
* (1 - 0.7*(($total - @left) / $total) ** 2) + 1)
+ $self->{async}->get_last_start_lookup_time());
$deadline = $dynamic if ($dynamic < $deadline);
@@ -628,8 +628,8 @@
sub is_dns_available {
my ($self) = @_;
- my $dnsopt = $self->{conf}->{dns_available};
- my $dnsint = $self->{conf}->{dns_test_interval} || 600;
+ my $dnsopt = $self->{conf}->cf_dns_available;
+ my $dnsint = $self->{conf}->cf_dns_test_interval || 600;
my @domains;
$LAST_DNS_CHECK ||= 0;
Modified: spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/PerMsgStatus.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/PerMsgStatus.pm?view=diff&rev=557985&r1=557984&r2=557985
==============================================================================
--- spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/PerMsgStatus.pm (original)
+++ spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/PerMsgStatus.pm Fri Jul 20 05:51:28 2007
@@ -178,7 +178,7 @@
$self->{score} = (sprintf "%0.3f", $self->{score}) + 0;
dbg("check: is spam? score=".$self->{score}.
- " required=".$self->{conf}->{required_score});
+ " required=".$self->{conf}->cf_required_score);
dbg("check: tests=".$self->get_names_of_tests_hit());
dbg("check: subtests=".$self->get_names_of_subtests_hit());
$self->{is_spam} = $self->is_spam();
@@ -204,8 +204,8 @@
sub learn {
my ($self) = @_;
- if (!$self->{conf}->{bayes_auto_learn} ||
- !$self->{conf}->{use_bayes} ||
+ if (!$self->{conf}->cf_bayes_auto_learn ||
+ !$self->{conf}->cf_use_bayes ||
$self->{disable_auto_learning})
{
$self->{auto_learn_status} = "disabled";
@@ -323,9 +323,8 @@
# This function needs to use use sum($score[scoreset % 2]) not just {score}.
# otherwise we shift what we autolearn on and it gets really wierd. - tvd
- my $orig_scoreset = $self->{conf}->get_score_set();
+ my $orig_scoreset = $self->{conf}->get_score_set;
my $new_scoreset = $orig_scoreset;
- my $scores = $self->{conf}->{scores};
if (($orig_scoreset & 2) == 0) { # we don't need to recompute
dbg("learn: auto-learn: currently using scoreset $orig_scoreset");
@@ -333,10 +332,8 @@
else {
$new_scoreset = $orig_scoreset & ~2;
dbg("learn: auto-learn: currently using scoreset $orig_scoreset, recomputing score based on scoreset $new_scoreset");
- $scores = $self->{conf}->{scoreset}->[$new_scoreset];
}
- my $tflags = $self->{conf}->{tflags};
my $points = 0;
# Just in case this function is called multiple times, clear out the
@@ -346,33 +343,36 @@
$self->{head_only_points} = 0;
foreach my $test (@{$self->{test_names_hit}}) {
+ my $score = $self->{conf}->get_score_in_scoreset($test, $new_scoreset);
+ my $tf = $self->{conf}->get_tflags_for_rule($test);
+
# According to the documentation, noautolearn, userconf, and learn
# rules are ignored for autolearning.
- if (exists $tflags->{$test}) {
- next if $tflags->{$test} =~ /\bnoautolearn\b/;
- next if $tflags->{$test} =~ /\buserconf\b/;
+ if ($tf) {
+ next if $tf =~ /\b(?:noautolearn|userconf)\b/;
# Keep track of the learn points for an additional autolearn check.
# Use the original scoreset since it'll be 0 in sets 0 and 1.
- if ($tflags->{$test} =~ /\blearn\b/) {
+ if ($tf =~ /\blearn\b/) {
# we're guaranteed that the score will be defined
- $self->{learned_points} += $self->{conf}->{scoreset}->[$orig_scoreset]->{$test};
+ $self->{learned_points} +=
+ $self->{conf}->get_score_in_scoreset($test, $orig_scoreset);
next;
}
}
# ignore tests with 0 score in this scoreset
- next if ($scores->{$test} == 0);
+ next if ($score == 0);
# Go ahead and add points to the proper locations
if (!$self->{conf}->maybe_header_only ($test)) {
- $self->{body_only_points} += $scores->{$test};
+ $self->{body_only_points} += $score;
}
if (!$self->{conf}->maybe_body_only ($test)) {
- $self->{head_only_points} += $scores->{$test};
+ $self->{head_only_points} += $score;
}
- $points += $scores->{$test};
+ $points += $score;
}
# Figure out the final value we'll use for autolearning
@@ -395,7 +395,7 @@
sub is_spam {
my ($self) = @_;
# changed to test this so sub-tests can ask "is_spam" during a run
- return ($self->{score} >= $self->{conf}->{required_score});
+ return ($self->{score} >= $self->{conf}->cf_required_score);
}
###########################################################################
@@ -463,14 +463,11 @@
sub get_required_score {
my ($self) = @_;
- return $self->{conf}->{required_score};
+ return $self->{conf}->cf_required_score;
}
# left as backward compatibility
-sub get_required_hits {
- my ($self) = @_;
- return $self->{conf}->{required_score};
-}
+sub get_required_hits { return $_[0]->get_required_score(); }
###########################################################################
@@ -504,7 +501,7 @@
if (!exists $self->{'report'}) {
my $report;
- $report = $self->{conf}->{report_template};
+ $report = $self->{conf}->cf_report_template;
$report ||= '(no report template found)';
$report = $self->_replace_tags($report);
@@ -619,7 +616,7 @@
my $msg = $self->{msg}->get_mbox_separator() || '';
- if ($self->{is_spam} && $self->{conf}->{report_safe}) {
+ if ($self->{is_spam} && $self->{conf}->cf_report_safe) {
$msg .= $self->rewrite_report_safe();
}
else {
@@ -656,8 +653,8 @@
# the report charset
my $report_charset = "; charset=iso-8859-1";
- if ($self->{conf}->{report_charset}) {
- $report_charset = "; charset=" . $self->{conf}->{report_charset};
+ if ($self->{conf}->cf_report_charset) {
+ $report_charset = "; charset=" . $self->{conf}->cf_report_charset;
}
# the SpamAssassin report
@@ -764,7 +761,7 @@
my $ct = $self->{msg}->get_header("Content-Type");
if (defined $ct && $ct ne '' && $ct !~ m{text/plain}i) {
$disposition = "attachment";
- $report .= $self->_replace_tags($self->{conf}->{unsafe_report_template});
+ $report .= $self->_replace_tags($self->{conf}->cf_unsafe_report_template);
# if we wanted to defang the attachment, this would be the place
}
else {
@@ -772,7 +769,7 @@
}
my $type = "message/rfc822";
- $type = "text/plain" if $self->{conf}->{report_safe} > 1;
+ $type = "text/plain" if $self->{conf}->cf_report_safe > 1;
my $description = $self->{conf}->{'encapsulated_content_description'};
@@ -842,7 +839,7 @@
my $created_subject = 0;
my $subject = $self->{msg}->get_pristine_header('Subject');
if (!defined($subject) && $self->{is_spam}
- && exists $self->{conf}->{rewrite_header}->{'Subject'})
+ && defined $self->{conf}->{rewrite_header}->{'Subject'})
{
push(@pristine_headers, "Subject: \n");
$created_subject = 1;
@@ -940,7 +937,7 @@
$hdr_data = $self->_replace_tags($hdr_data);
$hdr_data =~ s/(?:\r?\n)+$//; # make sure there are no trailing newlines ...
- if ($self->{conf}->{fold_headers}) {
+ if ($self->{conf}->cf_fold_headers) {
if ($hdr_data =~ /\n/) {
$hdr_data =~ s/\s*\n\s*/\n\t/g;
return $hdr_data;
@@ -1144,7 +1141,7 @@
sub _get_tag_value_for_required_score {
my $self = shift;
- return sprintf("%2.1f", $self->{conf}->{required_score});
+ return sprintf("%2.1f", $self->{conf}->cf_required_score);
}
sub _get_tag {
@@ -1170,8 +1167,8 @@
SUBVERSION => sub { $Mail::SpamAssassin::SUB_VERSION },
HOSTNAME => sub {
- $self->{conf}->{report_hostname} ||
- Mail::SpamAssassin::Util::fq_hostname();
+ $self->{conf}->cf_report_hostname ||
+ Mail::SpamAssassin::Util::fq_hostname();
},
REMOTEHOSTNAME => sub {
@@ -1198,7 +1195,7 @@
return $lasthop ? $lasthop->{helo} : '';
},
- CONTACTADDRESS => sub { $self->{conf}->{report_contact}; },
+ CONTACTADDRESS => sub { $self->{conf}->cf_report_contact; },
BAYES => sub {
defined($self->{bayes_score}) ?
@@ -1259,11 +1256,10 @@
my $arg = (shift || ",");
my $line = '';
foreach my $test (sort @{$self->{test_names_hit}}) {
- if (!$line) {
- $line .= $test . "=" . $self->{conf}->{scores}->{$test};
- } else {
- $line .= $arg . $test . "=" . $self->{conf}->{scores}->{$test};
+ if ($line) {
+ $line .= $arg;
}
+ $line .= $test . "=" . $self->{conf}->get_score_for_rule($test);
}
return $line ? $line : 'none';
},
@@ -2298,17 +2294,18 @@
# Rely on the 'envelope-sender-header' header if the user has configured one.
# Assume that because they have configured it, their MTA will always add it.
# This will prevent us falling through and picking up inappropriate headers.
- if (defined $self->{conf}->{envelope_sender_header}) {
+ if (defined $self->{conf}->cf_envelope_sender_header) {
+ my $hdr = $self->{conf}->cf_envelope_sender_header;
# make sure we get the most recent copy - there can be only one EnvelopeSender.
- $envf = $self->get($self->{conf}->{envelope_sender_header}.":addr");
+ $envf = $self->get($hdr.":addr");
# ok if it contains an "@" sign, or is "" (ie. "<>" without the < and >)
goto ok if defined $envf && ($envf =~ /\@/ || $envf =~ /^$/);
# Warn them if it's configured, but not there or not usable.
if (defined $envf) {
chomp $envf;
- dbg("message: envelope_sender_header '$self->{conf}->{envelope_sender_header}: $envf' is not an FQDN, ignoring");
+ dbg("message: envelope_sender_header '$hdr: $envf' is not an FQDN, ignoring");
} else {
- dbg("message: envelope_sender_header '".$self->{conf}->{envelope_sender_header}."' not found in message");
+ dbg("message: envelope_sender_header '$hdr' not found in message");
}
# Couldn't get envelope-sender using the configured header.
return;
Modified: spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/AutoLearnThreshold.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/AutoLearnThreshold.pm?view=diff&rev=557985&r1=557984&r2=557985
==============================================================================
--- spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/AutoLearnThreshold.pm (original)
+++ spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/AutoLearnThreshold.pm Fri Jul 20 05:51:28 2007
@@ -124,8 +124,8 @@
# Figure out min/max for autolearning.
# Default to specified auto_learn_threshold settings
- my $min = $conf->{bayes_auto_learn_threshold_nonspam};
- my $max = $conf->{bayes_auto_learn_threshold_spam};
+ my $min = $conf->cf_bayes_auto_learn_threshold_nonspam;
+ my $max = $conf->cf_bayes_auto_learn_threshold_spam;
# Find out what score we should consider this message to have ...
my $score = $scan->get_autolearn_points();
Modified: spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/Check.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/Check.pm?view=diff&rev=557985&r1=557984&r2=557985
==============================================================================
--- spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/Check.pm (original)
+++ spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/Check.pm Fri Jul 20 05:51:28 2007
@@ -159,6 +159,7 @@
sub run_rbl_eval_tests {
my ($self, $pms) = @_;
my ($rulename, $pat, @args);
+ my $conf = $pms->{conf};
# XXX - possible speed up, moving this check out of the subroutine into Check->new()
if ($self->{main}->{local_tests_only}) {
@@ -166,8 +167,9 @@
return 0;
}
- while (my ($rulename, $test) = each %{$pms->{conf}->{rbl_evals}}) {
- my $score = $pms->{conf}->{scores}->{$rulename};
+ foreach my $rulename ($conf->get_rule_keys('rbl_evals')) {
+ my $test = $conf->get_rule_value('rbl_evals', $rulename);
+ my $score = $conf->get_score_for_rule($rulename);
next unless $score;
$pms->{test_log_msgs} = (); # clear test state
@@ -233,7 +235,8 @@
if (defined $opts{pre_loop_body}) {
$opts{pre_loop_body}->($self, $pms, $conf, %nopts);
}
- while (my($rulename, $test) = each %{$opts{testhash}->{$priority}}) {
+ foreach my $rulename ($conf->get_rule_keys($opts{confhashname}, $priority)) {
+ my $test = $conf->get_rule_value($opts{confhashname}, $rulename, $priority);
$opts{loop_body}->($self, $pms, $conf, $rulename, $test, %nopts);
}
if (defined $opts{post_loop_body}) {
@@ -302,7 +305,7 @@
$self->run_generic_tests ($pms, $priority,
consttype => $Mail::SpamAssassin::Conf::TYPE_META_TESTS,
type => 'meta',
- testhash => $pms->{conf}->{meta_tests},
+ confhashname => 'meta_tests',
args => [ ],
loop_body => sub
{
@@ -331,7 +334,7 @@
# warnings; this is better than adding a 0 to a hash for every
# rule referred to in a meta...
$meta{$rulename} .= "(\$h->{'$token'} || 0) ";
-
+
if (!exists $conf->{scores}->{$token}) {
dbg("rules: meta test $rulename has undefined dependency '$token'");
}
@@ -388,14 +391,16 @@
if (!defined $conf->{meta_dependencies}->{ $metas[$i] }) {
warn "no meta_dependencies defined for $metas[$i]";
}
- my $alldeps = join ' ', grep {
- ($tflags->{$_}||'') =~ /\bnet\b/
- } split (' ', $conf->{meta_dependencies}->{ $metas[$i] } );
-
- if ($alldeps ne '') {
- $self->add_evalstr ('
- $self->ensure_rules_are_complete(q{'.$metas[$i].'}, qw{'.$alldeps.'});
- ');
+ else {
+ my $alldeps = join ' ', grep {
+ ($tflags->{$_}||'') =~ /\bnet\b/
+ } split (' ', $conf->{meta_dependencies}->{ $metas[$i] } );
+
+ if ($alldeps ne '') {
+ $self->add_evalstr ('
+ $self->ensure_rules_are_complete(q{'.$metas[$i].'}, qw{'.$alldeps.'});
+ ');
+ }
}
# Add this meta rule to the eval line
@@ -437,7 +442,7 @@
$self->run_generic_tests ($pms, $priority,
consttype => $Mail::SpamAssassin::Conf::TYPE_HEAD_TESTS,
type => 'head',
- testhash => $pms->{conf}->{head_tests},
+ confhashname => 'head_tests',
args => [ ],
loop_body => sub
{
@@ -547,7 +552,7 @@
$self->run_generic_tests ($pms, $priority,
consttype => $Mail::SpamAssassin::Conf::TYPE_BODY_TESTS,
type => 'body',
- testhash => $pms->{conf}->{body_tests},
+ confhashname => 'body_tests',
args => [ @$textary ],
loop_body => sub
{
@@ -619,7 +624,7 @@
$self->run_generic_tests ($pms, $priority,
consttype => $Mail::SpamAssassin::Conf::TYPE_URI_TESTS,
type => 'uri',
- testhash => $pms->{conf}->{uri_tests},
+ confhashname => 'uri_tests',
args => [ @uris ],
loop_body => sub
{
@@ -686,7 +691,7 @@
$self->run_generic_tests ($pms, $priority,
consttype => $Mail::SpamAssassin::Conf::TYPE_RAWBODY_TESTS,
type => 'rawbody',
- testhash => $pms->{conf}->{rawbody_tests},
+ confhashname => 'rawbody_tests',
args => [ @$textary ],
loop_body => sub
{
@@ -756,7 +761,7 @@
$self->run_generic_tests ($pms, $priority,
consttype => $Mail::SpamAssassin::Conf::TYPE_FULL_TESTS,
type => 'full',
- testhash => $pms->{conf}->{full_tests},
+ confhashname => 'full_tests',
args => [ $fullmsgref ],
pre_loop_body => sub
{
@@ -789,35 +794,32 @@
my ($self, $pms, $priority) = @_;
return unless (defined($pms->{conf}->{head_evals}->{$priority}));
$self->run_eval_tests ($pms, $Mail::SpamAssassin::Conf::TYPE_HEAD_EVALS,
- $pms->{conf}->{head_evals}->{$priority}, '', $priority);
+ 'head_evals', '', $priority);
}
sub do_body_eval_tests {
my ($self, $pms, $priority, $bodystring) = @_;
return unless (defined($pms->{conf}->{body_evals}->{$priority}));
$self->run_eval_tests ($pms, $Mail::SpamAssassin::Conf::TYPE_BODY_EVALS,
- $pms->{conf}->{body_evals}->{$priority}, 'BODY: ',
- $priority, $bodystring);
+ 'body_evals', 'BODY: ', $priority, $bodystring);
}
sub do_rawbody_eval_tests {
my ($self, $pms, $priority, $bodystring) = @_;
return unless (defined($pms->{conf}->{rawbody_evals}->{$priority}));
$self->run_eval_tests ($pms, $Mail::SpamAssassin::Conf::TYPE_RAWBODY_EVALS,
- $pms->{conf}->{rawbody_evals}->{$priority}, 'RAW: ',
- $priority, $bodystring);
+ 'rawbody_evals', 'RAW: ', $priority, $bodystring);
}
sub do_full_eval_tests {
my ($self, $pms, $priority, $fullmsgref) = @_;
return unless (defined($pms->{conf}->{full_evals}->{$priority}));
$self->run_eval_tests($pms, $Mail::SpamAssassin::Conf::TYPE_FULL_EVALS,
- $pms->{conf}->{full_evals}->{$priority}, '',
- $priority, $fullmsgref);
+ 'full_evals', '', $priority, $fullmsgref);
}
sub run_eval_tests {
- my ($self, $pms, $testtype, $evalhash, $prepend2desc, $priority, @extraevalargs) = @_;
+ my ($self, $pms, $testtype, $confhashname, $prepend2desc, $priority, @extraevalargs) = @_;
return if $self->{main}->call_plugins("have_shortcircuited",
{ permsgstatus => $pms });
@@ -867,7 +869,7 @@
};
}
- while (my ($rulename, $test) = each %{$evalhash}) {
+ foreach my $rulename ($conf->get_rule_keys($confhashname, $priority)) {
if ($tflagsref->{$rulename}) {
# If the rule is a net rule, and we are in a non-net scoreset, skip it.
if ($tflagsref->{$rulename} =~ /\bnet\b/) {
@@ -879,6 +881,7 @@
}
}
+ my $test = $conf->get_rule_value($confhashname, $rulename, $priority);
my ($function, $argstr) = ($test,'');
if ($test =~ s/^([^,]+)(,.*)$//gs) {
($function, $argstr) = ($1,$2);
Modified: spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/HTMLEval.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/HTMLEval.pm?view=diff&rev=557985&r1=557984&r2=557985
==============================================================================
--- spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/HTMLEval.pm (original)
+++ spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/HTMLEval.pm Fri Jul 20 05:51:28 2007
@@ -89,7 +89,7 @@
return 0 unless exists $pms->{html}{charsets};
- my @locales = Mail::SpamAssassin::Util::get_my_locales($pms->{conf}->{ok_locales});
+ my @locales = Mail::SpamAssassin::Util::get_my_locales($pms->{conf}->cf_ok_locales);
return 0 if grep { $_ eq "all" } @locales;
my $okay = 0;
Modified: spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/HeaderEval.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/HeaderEval.pm?view=diff&rev=557985&r1=557984&r2=557985
==============================================================================
--- spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/HeaderEval.pm (original)
+++ spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/HeaderEval.pm Fri Jul 20 05:51:28 2007
@@ -116,7 +116,7 @@
my ($self, $pms) = @_;
my $hdr;
- my @locales = Mail::SpamAssassin::Util::get_my_locales($self->{main}->{conf}->{ok_locales});
+ my @locales = Mail::SpamAssassin::Util::get_my_locales($self->{main}->{conf}->cf_ok_locales);
return 0 if grep { $_ eq "all" } @locales;
Modified: spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/MIMEEval.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/MIMEEval.pm?view=diff&rev=557985&r1=557984&r2=557985
==============================================================================
--- spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/MIMEEval.pm (original)
+++ spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Plugin/MIMEEval.pm Fri Jul 20 05:51:28 2007
@@ -68,7 +68,7 @@
my $type = $pms->get('Content-Type');
- my @locales = Mail::SpamAssassin::Util::get_my_locales($self->{main}->{conf}->{ok_locales});
+ my @locales = Mail::SpamAssassin::Util::get_my_locales($self->{main}->{conf}->cf_ok_locales);
return 0 if grep { $_ eq "all" } @locales;
@@ -196,7 +196,7 @@
}
if (! $pms->{mime_faraway_charset}) {
- my @l = Mail::SpamAssassin::Util::get_my_locales($self->{main}->{conf}->{ok_locales});
+ my @l = Mail::SpamAssassin::Util::get_my_locales($self->{main}->{conf}->cf_ok_locales);
if (!(grep { $_ eq "all" } @l) &&
!Mail::SpamAssassin::Locales::is_charset_ok_for_locales($charset, @l))
Modified: spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Util.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Util.pm?view=diff&rev=557985&r1=557984&r2=557985
==============================================================================
--- spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Util.pm (original)
+++ spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Util.pm Fri Jul 20 05:51:28 2007
@@ -1550,6 +1550,21 @@
###########################################################################
+sub uniq_list {
+ my %u=();
+ return grep {
+ defined
+ } map {
+ if (exists $u{$_}) {
+ undef;
+ } else {
+ $u{$_} = undef; $_;
+ }
+ } @_;
+}
+
+###########################################################################
+
1;
=back
Added: spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Util/TwoTierArray.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Util/TwoTierArray.pm?view=auto&rev=557985
==============================================================================
--- spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Util/TwoTierArray.pm (added)
+++ spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Util/TwoTierArray.pm Fri Jul 20 05:51:28 2007
@@ -0,0 +1,106 @@
+# A tied object presenting an array API to a two-tiered pair of arrays
+
+# <@LICENSE>
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to you 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::Util::TwoTierArray;
+
+use strict;
+use warnings;
+use Carp qw(croak);
+
+use Tie::Array;
+
+our @ISA = qw(Tie::Array);
+
+# structure: 2 arrays, "tier 0" and "tier 1". all writes go to tier 1,
+# and all reads from tier 1, and if not found there, tier 0. In
+# effect tier 1 overrides tier 0. Note that writes will NEVER affect
+# tier 0; create a new object to modify the contents of that tier.
+
+###########################################################################
+
+sub TIEARRAY {
+ my $class = shift;
+ my $a0 = shift;
+ my $a1 = shift;
+ my $self = {
+ a0 => $a0 || [],
+ a1 => $a1 || [],
+ };
+ return bless $self, $class;
+}
+
+sub STORE {
+ my ($self, $i, $v) = @_;
+ my $a0size = scalar @{$self->{a0}};
+ if ($i > $a0size) {
+ $self->{a1}->[$i - $a0size] = $v;
+ } else {
+ # a write to the a0 area! we cannot do this!
+ croak "cannot write to immutable tier 0 part of array: $i / $a0size";
+ }
+}
+
+sub FETCH {
+ my ($self, $i) = @_;
+ my $a0size = scalar @{$self->{a0}};
+ if ($i > $a0size) {
+ return $self->{a1}->[$i - $a0size];
+ } else {
+ return $self->{a0}->[$i];
+ }
+}
+
+sub FETCHSIZE {
+ my ($self) = @_;
+ return scalar(@{$self->{a0}}) + scalar(@{$self->{a1}});
+}
+
+sub STORESIZE {
+ my ($self, $count) = @_;
+ my $a0size = scalar @{$self->{a0}};
+ if ($count > $a0size) {
+ @{$self->{a1}} = $count - $a0size;
+ } else {
+ # a write to the a0 area! we cannot do this!
+ croak "cannot resize immutable tier 0 part of array: $count / $a0size";
+ }
+}
+
+sub EXISTS {
+ my ($self, $i) = @_;
+ my $a0size = scalar @{$self->{a0}};
+ if ($i > $a0size) {
+ return exists $self->{a1}->[$i - $a0size];
+ } else {
+ return exists $self->{a0}->[$i];
+ }
+}
+
+sub DELETE {
+ my ($self, $i) = @_;
+ my $a0size = scalar @{$self->{a0}};
+ if ($i > $a0size) {
+ delete $self->{a1}->[$i - $a0size];
+ } else {
+ # a write to the a0 area! we cannot do this!
+ croak "cannot write to immutable tier 0 part of array: $i / $a0size";
+ }
+}
+
+1;
Added: spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Util/TwoTierHash.pm
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Util/TwoTierHash.pm?view=auto&rev=557985
==============================================================================
--- spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Util/TwoTierHash.pm (added)
+++ spamassassin/branches/jm_bug_3852_two_level_configs/lib/Mail/SpamAssassin/Util/TwoTierHash.pm Fri Jul 20 05:51:28 2007
@@ -0,0 +1,103 @@
+# A tied object presenting a hash API to a two-tiered pair of hashes
+
+# <@LICENSE>
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to you 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::Util::TwoTierHash;
+
+use strict;
+use warnings;
+use Carp qw(croak);
+
+our @ISA = qw();
+
+# structure: 2 hashes, "tier 0" and "tier 1". all writes go to tier 1,
+# and all reads from tier 1, and if not found there, tier 0. In
+# effect tier 1 overrides tier 0. Note that writes will NEVER affect
+# tier 0; create a new object to modify the contents of that tier.
+
+###########################################################################
+
+sub TIEHASH {
+ my $class = shift;
+ my $h0 = shift;
+ my $h1 = shift;
+ my $self = { h0 => $h0, h1 => $h1 };
+ return bless $self, $class;
+}
+
+sub STORE {
+ my ($self, $k, $v) = @_;
+ $self->{h1}->{$k} = $v;
+ 1;
+}
+
+sub FETCH {
+ my ($self, $k) = @_;
+ if (exists $self->{h1}->{$k}) {
+ return $self->{h1}->{$k};
+ } else {
+ return $self->{h0}->{$k};
+ }
+}
+
+sub EXISTS {
+ my ($self, $k) = @_;
+ if (exists $self->{h1}->{$k}) {
+ return 1;
+ } elsif (exists $self->{h0}->{$k}) {
+ return 1;
+ } else {
+ return;
+ }
+}
+
+sub DELETE {
+ my ($self, $k) = @_;
+ return delete $self->{h1}->{$k};
+}
+
+sub FIRSTKEY {
+ my ($self) = @_;
+ $self->{_keys} = make_keys_list($self->{h0}, $self->{h1});
+ return each %{$self->{_keys}};
+}
+
+sub make_keys_list {
+ my ($h0, $h1) = @_;
+ my %keys = ();
+ foreach my $k (keys %{$h0}) { $keys{$k} = 1; }
+ foreach my $k (keys %{$h1}) { $keys{$k} = 1; }
+ return \%keys;
+}
+
+sub NEXTKEY {
+ my ($self, $lastk) = @_;
+ return each %{$self->{_keys}};
+}
+
+sub CLEAR {
+ my ($self) = @_;
+ $self->{h1} = { };
+}
+
+sub SCALAR {
+ my ($self) = @_;
+ return scalar $self->{h1};
+}
+
+1;
Modified: spamassassin/branches/jm_bug_3852_two_level_configs/t/priorities.t
URL: http://svn.apache.org/viewvc/spamassassin/branches/jm_bug_3852_two_level_configs/t/priorities.t?view=diff&rev=557985&r1=557984&r2=557985
==============================================================================
--- spamassassin/branches/jm_bug_3852_two_level_configs/t/priorities.t (original)
+++ spamassassin/branches/jm_bug_3852_two_level_configs/t/priorities.t Fri Jul 20 05:51:28 2007
@@ -99,7 +99,7 @@
sub assert_rule_pri {
my ($r, $pri) = @_;
- if (defined $conf->{rbl_evals}->{$r}) {
+ if (defined $conf->{activetier}->{rbl_evals}->{$r}) {
# ignore rbl_evals; they do not use the priority system at all
return 1;
}
@@ -109,11 +109,11 @@
full_evals rawbody_evals head_evals body_evals
))
{
- if (defined $conf->{$ruletype}->{$pri}->{$r}) {
+ if (defined $conf->{activetier}->{$ruletype}->{$pri}->{$r}) {
return 1;
}
- foreach my $foundpri (keys %{$conf->{priorities}}) {
- next unless (defined $conf->{$ruletype}->{$foundpri}->{$r});
+ foreach my $foundpri (keys %{$conf->{activetier}->{priorities}}) {
+ next unless (defined $conf->{activetier}->{$ruletype}->{$foundpri}->{$r});
warn "FAIL: rule '$r' not found at priority $pri; found at $foundpri\n";
return 0;
}