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 2013/03/06 17:31:48 UTC

svn commit: r1453407 - in /spamassassin/trunk/lib/Mail/SpamAssassin: Message.pm Message/Metadata.pm PerMsgStatus.pm Plugin/Bayes.pm Plugin/RelayCountry.pm

Author: mmartinec
Date: Wed Mar  6 16:31:47 2013
New Revision: 1453407

URL: http://svn.apache.org/r1453407
Log:
Bug 6915: PerMsgStatus::get_tag() enhancement and optimization

Modified:
    spamassassin/trunk/lib/Mail/SpamAssassin/Message.pm
    spamassassin/trunk/lib/Mail/SpamAssassin/Message/Metadata.pm
    spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgStatus.pm
    spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Bayes.pm
    spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/RelayCountry.pm

Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Message.pm
URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Message.pm?rev=1453407&r1=1453406&r2=1453407&view=diff
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Message.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Message.pm Wed Mar  6 16:31:47 2013
@@ -498,7 +498,7 @@ sub extract_message_metadata {
   my ($self, $permsgstatus) = @_;
 
   # do this only once per message, it can be expensive
-  if ($self->{already_extracted_metadata}) { return; }
+  return  if $self->{already_extracted_metadata};
   $self->{already_extracted_metadata} = 1;
 
   $self->{metadata}->extract ($self, $permsgstatus);

Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Message/Metadata.pm
URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Message/Metadata.pm?rev=1453407&r1=1453406&r2=1453407&view=diff
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Message/Metadata.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Message/Metadata.pm Wed Mar  6 16:31:47 2013
@@ -84,16 +84,21 @@ sub extract {
   # pre-chew Received headers
   $self->parse_received_headers ($permsgstatus, $msg);
 
-  foreach ( [$self->{relays_external},  'LASTEXTERNALREVIP'],
-            [$self->{relays_untrusted}, 'FIRSTTRUSTEDREVIP'] ) {
-    my($rly, $tag) = @$_;
-    if (ref $rly && @$rly) {
-      my($r0, $ip, $revip);
-      $r0 = $rly->[0];
-      $ip = $r0->{ip}  if ref $r0 && !$r0->{ip_private};
+  foreach my $tuple (
+      [$self->{relays_trusted},   'RELAYSTRUSTEDREVIP'  ],
+      [$self->{relays_untrusted}, 'RELAYSUNTRUSTEDREVIP'],
+      [$self->{relays_internal},  'RELAYSINTERNALREVIP' ],
+      [$self->{relays_external},  'RELAYSEXTERNALREVIP' ])
+  { my($rly, $tag) = @$tuple;
+    my @revips;
+    @revips = map {
+      my($ip,$revip);
+      $ip = $_->{ip}  if ref $_ && !$_->{ip_private};
       $revip = reverse_ip_address($ip)  if defined $ip && $ip ne '';
-      $permsgstatus->set_tag($tag,$revip)  if defined $revip && $revip ne '';
-    }
+      defined $revip && $revip ne '' ? $revip : ();
+    } @$rly  if $rly;
+    $permsgstatus->set_tag($tag,
+                           @revips == 1 ? $revips[0] : \@revips) if @revips;
   }
 
   $permsgstatus->{main}->call_plugins("extract_metadata",

Modified: spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgStatus.pm
URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgStatus.pm?rev=1453407&r1=1453406&r2=1453407&view=diff
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgStatus.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/PerMsgStatus.pm Wed Mar  6 16:31:47 2013
@@ -78,6 +78,181 @@ use vars qw{
 
 ###########################################################################
 
+use vars qw( %common_tags );
+
+BEGIN {
+  %common_tags = (
+
+    YESNO => sub {
+      my $pms = shift;
+      $pms->_get_tag_value_for_yesno(@_);
+    },
+  
+    YESNOCAPS => sub {
+      my $pms = shift;
+      uc $pms->_get_tag_value_for_yesno(@_);
+    },
+
+    SCORE => sub {
+      my $pms = shift;
+      $pms->_get_tag_value_for_score(@_);
+    },
+
+    HITS => sub {
+      my $pms = shift;
+      $pms->_get_tag_value_for_score(@_);
+    },
+
+    REQD => sub {
+      my $pms = shift;
+      $pms->_get_tag_value_for_required_score(@_);
+    },
+
+    VERSION => \&Mail::SpamAssassin::Version,
+
+    SUBVERSION => sub { $Mail::SpamAssassin::SUB_VERSION },
+
+    RULESVERSION => sub {
+      my $pms = shift;
+      my $conf = $pms->{conf};
+      my @fnames;
+      @fnames =
+        keys %{$conf->{update_version}}  if $conf->{update_version};
+      @fnames = sort @fnames  if @fnames > 1;
+      join(',', map($conf->{update_version}{$_}, @fnames));
+    },
+
+    HOSTNAME => sub {
+      my $pms = shift;
+      $pms->{conf}->{report_hostname} ||
+        Mail::SpamAssassin::Util::fq_hostname();
+    },
+
+    REMOTEHOSTNAME => sub {
+      my $pms = shift;
+      $pms->{tag_data}->{'REMOTEHOSTNAME'} || "localhost";
+    },
+
+    REMOTEHOSTADDR => sub {
+      my $pms = shift;
+      $pms->{tag_data}->{'REMOTEHOSTADDR'} || "127.0.0.1";
+    },
+
+    LASTEXTERNALIP => sub {
+      my $pms = shift;
+      my $lasthop = $pms->{relays_external}->[0];
+      $lasthop ? $lasthop->{ip} : '';
+    },
+
+    LASTEXTERNALRDNS => sub {
+      my $pms = shift;
+      my $lasthop = $pms->{relays_external}->[0];
+      $lasthop ? $lasthop->{rdns} : '';
+    },
+
+    LASTEXTERNALHELO => sub {
+      my $pms = shift;
+      my $lasthop = $pms->{relays_external}->[0];
+      $lasthop ? $lasthop->{helo} : '';
+    },
+
+    CONTACTADDRESS => sub {
+      my $pms = shift;
+      $pms->{conf}->{report_contact};
+    },
+
+    BAYES => sub {
+      my $pms = shift;
+      defined $pms->{bayes_score} ? sprintf("%3.4f", $pms->{bayes_score})
+                                   : "0.5";
+    },
+
+    DATE => \&Mail::SpamAssassin::Util::time_to_rfc822_date,
+
+    STARS => sub {
+      my $pms = shift;
+      my $arg = (shift || "*");
+      my $length = int($pms->{score});
+      $length = 50 if $length > 50;
+      $arg x $length;
+    },
+
+    AUTOLEARN => sub {
+      my $pms = shift;
+      $pms->get_autolearn_status();
+    },
+
+    AUTOLEARNSCORE => sub {
+      my $pms = shift;
+      $pms->get_autolearn_points();
+    },
+
+    TESTS => sub {
+      my $pms = shift;
+      my $arg = (shift || ',');
+      join($arg, sort(@{$pms->{test_names_hit}})) || "none";
+    },
+
+    SUBTESTS => sub {
+      my $pms = shift;
+      my $arg = (shift || ',');
+      join($arg, sort(@{$pms->{subtest_names_hit}})) || "none";
+    },
+
+    TESTSSCORES => sub {
+      my $pms = shift;
+      my $arg = (shift || ",");
+      my $line = '';
+      foreach my $test (sort @{$pms->{test_names_hit}}) {
+        my $score = $pms->{conf}->{scores}->{$test};
+        $score = '0'  if !defined $score;
+        $line .= $arg  if $line ne '';
+        $line .= $test . "=" . $score;
+      }
+      $line ne '' ? $line : 'none';
+    },
+
+    PREVIEW => sub {
+      my $pms = shift;
+      $pms->get_content_preview();
+    },
+
+    REPORT => sub {
+      my $pms = shift;
+      "\n" . ($pms->{tag_data}->{REPORT} || "");
+    },
+
+    HEADER => sub {
+      my $pms = shift;
+      my $hdr = shift;
+      return if !$hdr;
+      $pms->get($hdr,undef);
+    },
+
+    TIMING => sub {
+      my $pms = shift;
+      $pms->{main}->timer_report();
+    },
+
+    ADDEDHEADERHAM => sub {
+      my $pms = shift;
+      $pms->_get_added_headers('headers_ham');
+    },
+
+    ADDEDHEADERSPAM => sub {
+      my $pms = shift;
+      $pms->_get_added_headers('headers_spam');
+    },
+
+    ADDEDHEADER => sub {
+      my $pms = shift;
+      $pms->_get_added_headers(
+                $pms->{is_spam} ? 'headers_spam' : 'headers_ham');
+    },
+
+  );
+}
+
 sub new {
   my $class = shift;
   $class = ref($class) || $class;
@@ -1074,7 +1249,8 @@ sub _replace_tags {
           # Bug 6278: break infinite recursion through _get_added_headers and
           # _get_tag on an attempt to use such tag in add_header template
         } else {
-          $result = $self->_get_tag($tag,$3);
+          $result = $self->get_tag_raw($tag,$3);
+          $result = join(' ',@$result)  if ref $result eq 'ARRAY';
         }
         defined $result ? $result : $full;
       }ge;
@@ -1190,22 +1366,31 @@ sub report_unsatisfied_actions {
 
 =item $status->set_tag($tagname, $value)
 
-Set a template tag, as used in C<add_header>, report templates, etc. This API
-is intended for use by plugins.   Tag names will be converted to an
-all-uppercase representation internally.
-
-C<$value> can be a subroutine reference, which will be evaluated each time
-the template is expanded.  Note that perl supports closures, which means
-that variables set in the caller's scope can be accessed inside this C<sub>.
-For example:
+Set a template tag, as used in C<add_header>, report templates, etc.
+This API is intended for use by plugins.  Tag names will be converted
+to an all-uppercase representation internally.
+
+C<$value> can be a simple scalar (string or number), or a reference to an
+array, in which case the public method get_tag will join array elements
+using a space as a separator, returning a single string for backward
+compatibility.
+
+C<$value> can also be a subroutine reference, which will be evaluated
+each time the template is expanded. The first argument passed by get_tag
+to a called subroutine will be a PerMsgStatus object (this module's object),
+followed by optional arguments provided by a caller to get_tag.
+
+Note that perl supports closures, which means that variables set in the
+caller's scope can be accessed inside this C<sub>. For example:
 
     my $text = "hello world!";
     $status->set_tag("FOO", sub {
+              my $pms = shift;
               return $text;
             });
 
-See C<Mail::SpamAssassin::Conf>'s C<TEMPLATE TAGS> section for more details on
-how template tags are used.
+See C<Mail::SpamAssassin::Conf>'s C<TEMPLATE TAGS> section for more details
+on how template tags are used.
 
 C<undef> will be returned if a tag by that name has not been defined.
 
@@ -1232,8 +1417,51 @@ C<undef> will be returned if a tag by th
 =cut
 
 sub get_tag {
-  # expose this previously-private API
-  return shift->_get_tag(uc shift);
+  my($self, $tag, @args) = @_;
+
+  return if !defined $tag;
+  $tag = uc $tag;
+  my $data;
+  if (exists $common_tags{$tag}) {
+    # tag data from traditional pre-defined tag subroutines
+    $data = $common_tags{$tag};
+    $data = $data->($self,@args)  if ref $data eq 'CODE';
+    $data = join(' ',@$data)  if ref $data eq 'ARRAY';
+    $data = ""  if !defined $data;
+  } elsif (exists $self->{tag_data}->{$tag}) {
+    # tag data comes from $self->{tag_data}->{TAG}, typically from plugins
+    $data = $self->{tag_data}->{$tag};
+    $data = $data->($self,@args)  if ref $data eq 'CODE';
+    $data = join(' ',@$data)  if ref $data eq 'ARRAY';
+    $data = ""  if !defined $data;
+  }
+  return $data;
+}
+
+=item $string = $status->get_tag_raw($tagname, @args)
+
+Similar to C<get_tag>, but keeps a tag name unchanged (does not uppercase it),
+and does not convert arrayref tag values into a single string.
+
+=cut
+
+sub get_tag_raw {
+  my($self, $tag, @args) = @_;
+
+  return if !defined $tag;
+  my $data;
+  if (exists $common_tags{$tag}) {
+    # tag data from traditional pre-defined tag subroutines
+    $data = $common_tags{$tag};
+    $data = $data->($self,@args)  if ref $data eq 'CODE';
+    $data = ""  if !defined $data;
+  } elsif (exists $self->{tag_data}->{$tag}) {
+    # tag data comes from $self->{tag_data}->{TAG}, typically from plugins
+    $data = $self->{tag_data}->{$tag};
+    $data = $data->($self,@args)  if ref $data eq 'CODE';
+    $data = ""  if !defined $data;
+  }
+  return $data;
 }
 
 ###########################################################################
@@ -1306,145 +1534,10 @@ sub _get_tag_value_for_score {
 }
 
 sub _get_tag_value_for_required_score {
-  my $self  = shift;
+  my $self = shift;
   return sprintf("%2.1f", $self->{conf}->{required_score});
 }
 
-sub _get_tag {
-  my $self = shift;
-  my $tag = shift;
-  my %tags;
-
-  # tag data also comes from $self->{tag_data}->{TAG}
-
-  $tag = "" unless defined $tag; # can be "0", so use a defined test
-
-  %tags = ( YESNO     => sub {    $self->_get_tag_value_for_yesno(@_) },
-  
-            YESNOCAPS => sub { uc $self->_get_tag_value_for_yesno(@_) },
-
-            SCORE => sub { $self->_get_tag_value_for_score(shift) },
-            HITS  => sub { $self->_get_tag_value_for_score(shift) },
-
-            REQD  => sub { $self->_get_tag_value_for_required_score() },
-
-            VERSION => \&Mail::SpamAssassin::Version,
-
-            SUBVERSION => sub { $Mail::SpamAssassin::SUB_VERSION },
-
-            RULESVERSION => sub {
-              my @fnames;  my $conf = $self->{conf};
-              @fnames =
-                keys %{$conf->{update_version}}  if $conf->{update_version};
-              @fnames = sort @fnames  if @fnames > 1;
-              join(',', map($conf->{update_version}{$_}, @fnames));
-            },
-
-            HOSTNAME => sub {
-	      $self->{conf}->{report_hostname} ||
-	      Mail::SpamAssassin::Util::fq_hostname();
-	    },
-
-	    REMOTEHOSTNAME => sub {
-	      $self->{tag_data}->{'REMOTEHOSTNAME'} || "localhost";
-	    },
-	    REMOTEHOSTADDR => sub {
-	      $self->{tag_data}->{'REMOTEHOSTADDR'} || "127.0.0.1";
-	    },
-
-            LASTEXTERNALIP => sub {
-              my $lasthop = $self->{relays_external}->[0];
-              $lasthop ? $lasthop->{ip} : '';
-            },
-
-            LASTEXTERNALRDNS => sub {
-              my $lasthop = $self->{relays_external}->[0];
-              $lasthop ? $lasthop->{rdns} : '';
-            },
-
-            LASTEXTERNALHELO => sub {
-              my $lasthop = $self->{relays_external}->[0];
-              $lasthop ? $lasthop->{helo} : '';
-            },
-
-            CONTACTADDRESS => sub { $self->{conf}->{report_contact} },
-
-            BAYES => sub {
-              defined($self->{bayes_score}) ?
-                        sprintf("%3.4f", $self->{bayes_score}) : "0.5"
-            },
-
-            DATE => \&Mail::SpamAssassin::Util::time_to_rfc822_date,
-
-            STARS => sub {
-              my $arg = (shift || "*");
-              my $length = int($self->{score});
-              $length = 50 if $length > 50;
-              $arg x $length;
-            },
-
-            AUTOLEARN => sub { $self->get_autolearn_status() },
-
-            AUTOLEARNSCORE => sub { $self->get_autolearn_points() },
-
-            TESTS => sub {
-              my $arg = (shift || ',');
-              join($arg, sort(@{$self->{test_names_hit}})) || "none";
-            },
-
-            SUBTESTS => sub {
-              my $arg = (shift || ',');
-              join($arg, sort(@{$self->{subtest_names_hit}})) || "none";
-            },
-
-            TESTSSCORES => sub {
-              my $arg = (shift || ",");
-              my $line = '';
-              foreach my $test (sort @{$self->{test_names_hit}}) {
-                my $score = $self->{conf}->{scores}->{$test};
-                $score = '0'  if !defined $score;
-                $line .= $arg  if $line ne '';
-                $line .= $test . "=" . $score;
-              }
-              $line ne '' ? $line : 'none';
-            },
-
-            PREVIEW => sub { $self->get_content_preview() },
-
-            REPORT => sub { "\n" . ($self->{tag_data}->{REPORT} || "") },
-
-	    HEADER => sub {
-	      my $hdr = shift || return;
-	      $self->get($hdr,undef);
-	    },
-
-            TIMING => sub { $self->{main}->timer_report() },
-
-            ADDEDHEADERHAM => sub { $self->_get_added_headers('headers_ham') },
-
-            ADDEDHEADERSPAM=> sub { $self->_get_added_headers('headers_spam') },
-
-            ADDEDHEADER => sub {
-              $self->_get_added_headers(
-                        $self->{is_spam} ? 'headers_spam' : 'headers_ham');
-            },
-
-          );
-
-  my $data;
-  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;
-}
 
 ###########################################################################
 

Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Bayes.pm
URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Bayes.pm?rev=1453407&r1=1453406&r2=1453407&view=diff
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Bayes.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/Bayes.pm Wed Mar  6 16:31:47 2013
@@ -836,27 +836,30 @@ skip:
   $permsgstatus->set_tag ('BAYESTC', $tcount_total);
 
   $permsgstatus->set_tag ('HAMMYTOKENS', sub {
+              my $pms = shift;
               $self->bayes_report_make_list
-                ($permsgstatus, $permsgstatus->{bayes_token_info_hammy}, shift);
+                ($pms, $pms->{bayes_token_info_hammy}, shift);
             });
 
   $permsgstatus->set_tag ('SPAMMYTOKENS', sub {
+              my $pms = shift;
               $self->bayes_report_make_list
-                ($permsgstatus, $permsgstatus->{bayes_token_info_spammy}, shift);
+                ($pms, $pms->{bayes_token_info_spammy}, shift);
             });
 
   $permsgstatus->set_tag ('TOKENSUMMARY', sub {
-              if ( defined $permsgstatus->{tag_data}{BAYESTC} )
+              my $pms = shift;
+              if ( defined $pms->{tag_data}{BAYESTC} )
                 {
-                  my $tcount_neutral = $permsgstatus->{tag_data}{BAYESTCLEARNED}
-                                    - $permsgstatus->{tag_data}{BAYESTCSPAMMY}
-                                    - $permsgstatus->{tag_data}{BAYESTCHAMMY};
-                  my $tcount_new = $permsgstatus->{tag_data}{BAYESTC}
-                                    - $permsgstatus->{tag_data}{BAYESTCLEARNED};
+                  my $tcount_neutral = $pms->{tag_data}{BAYESTCLEARNED}
+                                     - $pms->{tag_data}{BAYESTCSPAMMY}
+                                     - $pms->{tag_data}{BAYESTCHAMMY};
+                  my $tcount_new = $pms->{tag_data}{BAYESTC}
+                                 - $pms->{tag_data}{BAYESTCLEARNED};
                   "Tokens: new, $tcount_new; "
-                    ."hammy, $permsgstatus->{tag_data}{BAYESTCHAMMY}; "
+                    ."hammy, $pms->{tag_data}{BAYESTCHAMMY}; "
                     ."neutral, $tcount_neutral; "
-                    ."spammy, $permsgstatus->{tag_data}{BAYESTCSPAMMY}."
+                    ."spammy, $pms->{tag_data}{BAYESTCSPAMMY}."
                 } else {
                   "Bayes not run.";
                 }

Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/RelayCountry.pm
URL: http://svn.apache.org/viewvc/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/RelayCountry.pm?rev=1453407&r1=1453406&r2=1453407&view=diff
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/RelayCountry.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/RelayCountry.pm Wed Mar  6 16:31:47 2013
@@ -137,8 +137,11 @@ sub parsed_metadata {
 
   return 1 unless $db;
 
-  $opts->{permsgstatus}->set_tag ("RELAYCOUNTRY",
-          $opts->{permsgstatus}->get_message->get_metadata('X-Relay-Countries'));
+  my $countries =
+    $opts->{permsgstatus}->get_message->get_metadata('X-Relay-Countries');
+  my @c_list = split(' ', $countries);
+  $opts->{permsgstatus}->set_tag("RELAYCOUNTRY",
+                                 @c_list == 1 ? $c_list[0] : \@c_list);
   return 1;
 }