You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spamassassin.apache.org by mm...@apache.org on 2010/11/30 17:35:16 UTC
svn commit: r1040623 - in /spamassassin/trunk/lib/Mail/SpamAssassin: Dns.pm
PerMsgStatus.pm Plugin/ASN.pm Plugin/DCC.pm Plugin/DKIM.pm Plugin/Pyzor.pm
Author: mmartinec
Date: Tue Nov 30 16:35:15 2010
New Revision: 1040623
URL: http://svn.apache.org/viewvc?rev=1040623&view=rev
Log:
Bug 6517: New infrastructure triggering actions when their dependency tags become available
Modified:
spamassassin/trunk/lib/Mail/SpamAssassin/Dns.pm
spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgStatus.pm
spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/ASN.pm
spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DCC.pm
spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DKIM.pm
spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Pyzor.pm
Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Dns.pm
URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Dns.pm?rev=1040623&r1=1040622&r2=1040623&view=diff
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Dns.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Dns.pm Tue Nov 30 16:35:15 2010
@@ -400,13 +400,16 @@ sub set_rbl_tag_data {
my ($self) = @_;
# DNS URIs
+ my $rbl_tag = $self->{tag_data}->{RBL}; # just in case, should be empty
+ $rbl_tag = '' if !defined $rbl_tag;
while (my ($dnsuri, $answers) = each %{ $self->{dnsuri} }) {
# when parsing, look for elements of \".*?\" or \S+ with ", " as separator
- $self->{tag_data}->{RBL} .= "<$dnsuri>" .
- " [" . join(", ", @{ $answers }) . "]\n";
+ $rbl_tag .= "<$dnsuri>" . " [" . join(", ", @{ $answers }) . "]\n";
+ }
+ if (defined $rbl_tag && $rbl_tag ne '') {
+ chomp $rbl_tag;
+ $self->set_tag('RBL', $rbl_tag);
}
-
- chomp $self->{tag_data}->{RBL} if defined $self->{tag_data}->{RBL};
}
###########################################################################
Modified: spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgStatus.pm
URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgStatus.pm?rev=1040623&r1=1040622&r2=1040623&view=diff
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgStatus.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgStatus.pm Tue Nov 30 16:35:15 2010
@@ -1034,8 +1034,6 @@ sub _replace_tags {
# default to leaving the original string in place, if we cannot find
# a tag for it (bug 4793)
- my $t;
- my $v;
local($1,$2,$3);
$text =~ s{(_(\w+?)(?:\((.*?)\))?_)}{
my $full = $1;
@@ -1057,6 +1055,107 @@ sub _replace_tags {
# public API for plugins
+=item $status->action_depends_on_tags($tags, $code, @args)
+
+Enqueue the supplied subroutine reference C<$code>, to become runnable when
+all the specified tags become available. The C<$tags> may be a simple
+scalar - a tag name, or a listref of tag names. The subroutine C<&$code>
+when called will be passed a C<permessagestatus> object as its first argument,
+followed by the supplied (optional) list C<@args> .
+
+=cut
+
+sub action_depends_on_tags {
+ my($self, $tags, $code, @args) = @_;
+
+ ref $code eq 'CODE'
+ or die "action_depends_on_tags: argument must be a subroutine ref";
+
+ # tag names on which the given action depends
+ my @dep_tags = !ref $tags ? uc $tags : map(uc($_),@$tags);
+
+ # @{$self->{tagrun_subs}} list of all submitted subroutines
+ # @{$self->{tagrun_actions}{$tag}} bitmask of action indices blocked by tag
+ # $self->{tagrun_tagscnt}[$action_ind] count of tags still pending
+
+ # store action details, obtain its index
+ push(@{$self->{tagrun_subs}}, [$code,@args]);
+ my $action_ind = $#{$self->{tagrun_subs}};
+
+ # list dependency tag names which are not already satistied
+ my @blocking_tags =
+ grep(!defined $self->{tag_data}{$_} || $self->{tag_data}{$_} eq '',
+ @dep_tags);
+
+ $self->{tagrun_tagscnt}[$action_ind] = scalar @blocking_tags;
+ $self->{tagrun_actions}{$_}[$action_ind] = 1 for @blocking_tags;
+
+ if (@blocking_tags) {
+ dbg("check: tagrun - action %s blocking on tags %s",
+ $action_ind, join(', ',@blocking_tags));
+ } else {
+ dbg("check: tagrun - tag %s was ready, action %s runnable immediately: %s",
+ join(', ',@dep_tags), $action_ind, join(', ',$code,@args));
+ &$code($self, @args);
+ }
+}
+
+# tag_is_ready() will be called by set_tag(), indicating that a given
+# tag just received its value, possibly unblocking an action routine
+# as declared by action_depends_on_tags().
+#
+# Well-behaving plugins should call set_tag() once when a tag is fully
+# assembled and ready. Multiple calls to set the same tag value are handled
+# gracefully, but may result in premature activation of a pending action.
+# Setting tag values by plugins should not be done directly but only through
+# the public API set_tag(), otherwise a pending action release may be missed.
+#
+sub tag_is_ready {
+ my($self, $tag) = @_;
+ $tag = uc $tag;
+
+ if (would_log('dbg', 'check')) {
+ my $tag_val = $self->{tag_data}{$tag};
+ dbg("check: tagrun - tag %s is now ready, value: %s",
+ $tag, ref $tag_val ne 'ARRAY' ? $tag_val
+ : 'ARY:['.join(',',@$tag_val).']' );
+ }
+ if (ref $self->{tagrun_actions}{$tag}) { # any action blocking on this tag?
+ my $action_ind = 0;
+ foreach my $action_pending (@{$self->{tagrun_actions}{$tag}}) {
+ if ($action_pending) {
+ $self->{tagrun_actions}{$tag}[$action_ind] = 0;
+ if ($self->{tagrun_tagscnt}[$action_ind] <= 0) {
+ # should not happen, warn and ignore
+ warn "tagrun error: count for $action_ind is ".
+ $self->{tagrun_tagscnt}[$action_ind]."\n";
+ } elsif (! --($self->{tagrun_tagscnt}[$action_ind])) {
+ my($code,@args) = @{$self->{tagrun_subs}[$action_ind]};
+ dbg("check: tagrun - tag %s unblocking the action %s: %s",
+ $tag, $action_ind, join(', ',$code,@args));
+ &$code($self, @args);
+ }
+ }
+ $action_ind++;
+ }
+ }
+}
+
+# debugging aid: show actions that are still pending, waiting for their
+# tags to receive a value
+#
+sub report_unsatisfied_actions {
+ my($self) = @_;
+ my @tags;
+ @tags = keys %{$self->{tagrun_actions}} if ref $self->{tagrun_actions};
+ for my $tag (@tags) {
+ my @pending_actions = grep($self->{tagrun_actions}{$tag}[$_],
+ (0 .. $#{$self->{tagrun_actions}{$tag}}));
+ dbg("check: tagrun - tag %s is still blocking action %s",
+ $tag, join(', ', @pending_actions)) if @pending_actions;
+ }
+}
+
=item $status->set_tag($tagname, $value)
Set a template tag, as used in C<add_header>, report templates, etc. This API
@@ -1081,11 +1180,9 @@ C<undef> will be returned if a tag by th
=cut
sub set_tag {
- my $self = shift;
- my $tag = uc shift;
- my $val = shift;
-
- $self->{tag_data}->{$tag} = $val;
+ my($self,$tag,$val) = @_;
+ $self->{tag_data}->{uc $tag} = $val;
+ $self->tag_is_ready($tag);
}
# public API for plugins
@@ -1290,10 +1387,12 @@ sub _get_tag {
if (exists $tags{$tag}) {
$data = $tags{$tag};
$data = $data->(@_) if ref $data eq 'CODE';
+ $data = join(' ',@$data) if ref $data eq 'ARRAY';
$data = "" if !defined $data;
} elsif (exists $self->{tag_data}->{$tag}) {
$data = $self->{tag_data}->{$tag};
$data = $data->(@_) if ref $data eq 'CODE';
+ $data = join(' ',@$data) if ref $data eq 'ARRAY';
$data = "" if !defined $data;
}
return $data;
@@ -1318,6 +1417,8 @@ sub finish {
permsgstatus => $self
});
+ $self->report_unsatisfied_actions;
+
# Delete out all of the members of $self. This will remove any direct
# circular references and let the memory get reclaimed while also being more
# efficient than a foreach() loop over the keys.
@@ -1370,11 +1471,11 @@ sub extract_message_metadata {
$self->{$item} = $self->{msg}->{metadata}->{$item};
}
- $self->{tag_data}->{RELAYSTRUSTED} = $self->{relays_trusted_str};
- $self->{tag_data}->{RELAYSUNTRUSTED} = $self->{relays_untrusted_str};
- $self->{tag_data}->{RELAYSINTERNAL} = $self->{relays_internal_str};
- $self->{tag_data}->{RELAYSEXTERNAL} = $self->{relays_external_str};
- $self->{tag_data}->{LANGUAGES} = $self->{msg}->get_metadata("X-Languages");
+ $self->set_tag('RELAYSTRUSTED', $self->{relays_trusted_str});
+ $self->set_tag('RELAYSUNTRUSTED', $self->{relays_untrusted_str});
+ $self->set_tag('RELAYSINTERNAL', $self->{relays_internal_str});
+ $self->set_tag('RELAYSEXTERNAL', $self->{relays_external_str});
+ $self->set_tag('LANGUAGES', $self->{msg}->get_metadata("X-Languages"));
# This should happen before we get called, but just in case.
if (!defined $self->{msg}->{metadata}->{html}) {
Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/ASN.pm
URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/ASN.pm?rev=1040623&r1=1040622&r2=1040623&view=diff
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/ASN.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/ASN.pm Tue Nov 30 16:35:15 2010
@@ -243,6 +243,8 @@ sub process_dns_result {
my $zone = $conf->{asnlookups}[$zone_index]->{zone};
my $asn_tag = $conf->{asnlookups}[$zone_index]->{asn_tag};
my $route_tag = $conf->{asnlookups}[$zone_index]->{route_tag};
+ my %asn_tag_data;
+ my %route_tag_data;
my @answer = !defined $response ? () : $response->answer;
@@ -255,23 +257,12 @@ sub process_dns_result {
"response: '%s'", $zone, $rr->txtdata);
next;
}
- unless ($scanner->{tag_data}->{$asn_tag} =~ /\b\QAS$items[0]\E\b/) {
- if ($scanner->{tag_data}->{$asn_tag}) {
- $scanner->{tag_data}->{$asn_tag} .= " AS$items[0]";
- } else {
- $scanner->{tag_data}->{$asn_tag} = "AS$items[0]";
- }
- }
- unless ($scanner->{tag_data}->{$route_tag} =~
- m{\b\Q$items[1]/$items[2]\E\b}) {
- if ($scanner->{tag_data}->{$route_tag}) {
- $scanner->{tag_data}->{$route_tag} .= " $items[1]/$items[2]";
- } else {
- $scanner->{tag_data}->{$route_tag} = "$items[1]/$items[2]";
- }
- }
+ $asn_tag_data{'AS'.$items[0]} = 1;
+ $route_tag_data{$items[1].'/'.$items[2]} = 1;
}
}
+ $scanner->set_tag($asn_tag, join(' ', keys %asn_tag_data));
+ $scanner->set_tag($route_tag, join(' ', keys %route_tag_data));
return;
}
Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DCC.pm
URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DCC.pm?rev=1040623&r1=1040622&r2=1040623&view=diff
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DCC.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DCC.pm Tue Nov 30 16:35:15 2010
@@ -614,8 +614,8 @@ sub check_dcc {
local($1,$2);
if ($response =~ /^X-DCC-(.*)-Metrics: (.*)$/) {
- $permsgstatus->{tag_data}->{DCCB} = $1;
- $permsgstatus->{tag_data}->{DCCR} = $2;
+ $permsgstatus->set_tag('DCCB', $1);
+ $permsgstatus->set_tag('DCCR', $2);
}
$response =~ s/many/999999/ig;
$response =~ s/ok\d?/0/ig;
@@ -668,7 +668,7 @@ sub check_dcc_reputation_range {
my $result = $dcc_rep >= $min && $dcc_rep <= $max ? 1 : 0;
dbg("dcc: dcc_rep %s, min %s, max %s => result=%s",
$dcc_rep, $min, $max, $result?'YES':'no');
- $permsgstatus->{tag_data}->{DCCREP} = $dcc_rep;
+ $permsgstatus->set_tag('DCCREP', $dcc_rep);
return $dcc_rep >= $min && $dcc_rep <= $max ? 1 : 0;
}
return 0;
Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DKIM.pm
URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DKIM.pm?rev=1040623&r1=1040622&r2=1040623&view=diff
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DKIM.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/DKIM.pm Tue Nov 30 16:35:15 2010
@@ -841,13 +841,21 @@ sub _check_dkim_signature {
my $sig = $valid_signatures[0];
my $sig_res = ($sig_result_supported ? $sig : $verifier)->result_detail;
dbg("dkim: signature verification result: %s", uc($sig_res));
- my(%seen1,%seen2);
- $pms->set_tag('DKIMIDENTITY',
- join(" ", grep { defined($_) && $_ ne '' && !$seen1{$_}++ }
- map { $_->identity } @valid_signatures));
- $pms->set_tag('DKIMDOMAIN',
- join(" ", grep { defined($_) && $_ ne '' && !$seen2{$_}++ }
- map { $_->domain } @valid_signatures));
+
+ # supply values for both tags
+ my(%seen1, %seen2, @identity_list, @domain_list);
+ @identity_list = grep(defined $_ && $_ ne '' && !$seen1{$_}++,
+ map($_->identity, @valid_signatures));
+ @domain_list = grep(defined $_ && $_ ne '' && !$seen2{$_}++,
+ map($_->domain, @valid_signatures));
+ $pms->set_tag('DKIMIDENTITY', !@identity_list ? ''
+ : @identity_list == 1 ? $identity_list[0]
+ : \@identity_list);
+ $pms->set_tag('DKIMDOMAIN', !@domain_list ? ''
+ : @domain_list == 1 ? $domain_list[0]
+ : \@domain_list);
+ $pms->set_tag('DKIMDOMAIN', 'test');
+
} elsif (@signatures) {
$pms->{dkim_signed} = 1;
my $sig = $signatures[0];
Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Pyzor.pm
URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Pyzor.pm?rev=1040623&r1=1040622&r2=1040623&view=diff
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Pyzor.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Pyzor.pm Tue Nov 30 16:35:15 2010
@@ -351,12 +351,8 @@ sub pyzor_lookup {
}
}
- if ($pyzor_whitelisted) {
- $permsgstatus->{tag_data}->{PYZOR} = "Whitelisted.";
- }
- else {
- $permsgstatus->{tag_data}->{PYZOR} = "Reported $pyzor_count times.";
- }
+ $permsgstatus->set_tag('PYZOR', $pyzor_whitelisted ? "Whitelisted."
+ : "Reported $pyzor_count times.");
if ($pyzor_count >= $self->{main}->{conf}->{pyzor_max}) {
dbg("pyzor: listed: COUNT=$pyzor_count/$self->{main}->{conf}->{pyzor_max} WHITELIST=$pyzor_whitelisted");