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");