You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spamassassin.apache.org by fe...@apache.org on 2006/08/04 01:55:39 UTC

svn commit: r428576 - in /spamassassin/rules/trunk/sandbox/felicity: 70_imageinfo.cf ImageInfo.pm

Author: felicity
Date: Thu Aug  3 16:55:38 2006
New Revision: 428576

URL: http://svn.apache.org/viewvc?rev=428576&view=rev
Log:
test plugin and rules from Dallas L. Engelken ala the dev list

Added:
    spamassassin/rules/trunk/sandbox/felicity/70_imageinfo.cf
    spamassassin/rules/trunk/sandbox/felicity/ImageInfo.pm

Added: spamassassin/rules/trunk/sandbox/felicity/70_imageinfo.cf
URL: http://svn.apache.org/viewvc/spamassassin/rules/trunk/sandbox/felicity/70_imageinfo.cf?rev=428576&view=auto
==============================================================================
--- spamassassin/rules/trunk/sandbox/felicity/70_imageinfo.cf (added)
+++ spamassassin/rules/trunk/sandbox/felicity/70_imageinfo.cf Thu Aug  3 16:55:38 2006
@@ -0,0 +1,59 @@
+# SpamAssassin rules file: Image information tests
+#
+# Please don't modify this file as your changes will be overwritten with
+# the next update. Use @@LOCAL_RULES_DIR@@/local.cf instead.
+# See 'perldoc Mail::SpamAssassin::Conf' for details.
+#
+# <@LICENSE>
+# Copyright 2004 Apache Software Foundation
+#
+# Licensed 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>
+#
+###########################################################################
+
+loadplugin Mail::SpamAssassin::Plugin::ImageInfo ImageInfo.pm
+
+ifplugin Mail::SpamAssassin::Plugin::ImageInfo
+
+meta            __HTML_IMG_ONLY         ( HTML_IMAGE_ONLY_04 || HTML_IMAGE_ONLY_08 || HTML_IMAGE_ONLY_12 || HTML_IMAGE_ONLY_16 || HTML_IMAGE_ONLY_20 || HTML_IMAGE_ONLY_24 || HTML_IMAGE_ONLY_28 )
+
+body            __GIF_ATTACH_1          eval:image_count('gif','1','1')
+body            __GIF_ATTACH_4P         eval:image_count('gif','4')
+body            __GIF_AREA_180K         eval:pixel_coverage('gif','180000','400000')
+
+body            __PNG_ATTACH_1          eval:image_count('png','1','1')
+body            __PNG_ATTACH_4P         eval:image_count('png','4')
+body            __PNG_AREA_180K         eval:pixel_coverage('png','180000','400000')
+
+meta            DC_GIF_UNO_LARGO        ( __GIF_ATTACH_1 && __GIF_AREA_180K )
+describe        DC_GIF_UNO_LARGO        Message contains a single large inline gif
+score           DC_GIF_UNO_LARGO        3.00
+
+meta            DC_GIF_MULTI_LARGO      ( GIF_ATTACH_4P && __GIF_AREA_180K )
+describe        DC_GIF_MULTI_LARGO       Message contains 4 or more inline gif that cover alot of area
+score           DC_GIF_MULTI_LARGO      4.00
+
+meta            DC_PNG_UNO_LARGO        ( __PNG_ATTACH_1 && __PNG_AREA_180K )
+describe        DC_PNG_UNO_LARGO        Message contains a single large inline gif
+score           DC_PNG_UNO_LARGO        3.00
+
+meta            DC_PNG_MULTI_LARGO      ( __PNG_ATTACH_4P && __PNG_AREA_180K )
+describe        DC_PNG_MULTI_LARGO      Message contains 4 or more inline png that cover alot of area
+score           DC_PNG_MULTI_LARGO      4.00
+
+meta            DC_IMAGE_SPAM           ( __HTML_IMG_ONLY && ( DC_GIF_UNO_LARGO || DC_PNG_UNO_LARGO || DC_GIF_MULTI_LARGO || DC_PNG_MULTI_LARGO ))
+describe        DC_IMAGE_SPAM           Possible Image-only spam
+score           DC_IMAGE_SPAM           3.00
+
+endif

Added: spamassassin/rules/trunk/sandbox/felicity/ImageInfo.pm
URL: http://svn.apache.org/viewvc/spamassassin/rules/trunk/sandbox/felicity/ImageInfo.pm?rev=428576&view=auto
==============================================================================
--- spamassassin/rules/trunk/sandbox/felicity/ImageInfo.pm (added)
+++ spamassassin/rules/trunk/sandbox/felicity/ImageInfo.pm Thu Aug  3 16:55:38 2006
@@ -0,0 +1,170 @@
+# <@LICENSE>
+# Copyright 2004 Apache Software Foundation
+# 
+# Licensed 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::Plugin::ImageInfo;
+
+use Mail::SpamAssassin::Plugin;
+use Mail::SpamAssassin::Logger;
+use strict;
+use warnings;
+use bytes;
+
+use vars qw(@ISA);
+@ISA = qw(Mail::SpamAssassin::Plugin);
+
+# constructor: register the eval rule
+sub new {
+  my $class = shift;
+  my $mailsaobject = shift;
+
+  # some boilerplate...
+  $class = ref($class) || $class;
+  my $self = $class->SUPER::new($mailsaobject);
+  bless ($self, $class);
+
+  $self->register_eval_rule ("image_count");
+  $self->register_eval_rule ("pixel_coverage");
+
+  return $self;
+}
+
+# -----------------------------------------
+
+my %get_details = (
+  'gif' => sub {
+    my ($pms, $part) = @_;
+    my $header = $part->decode(13);
+
+    # make sure this is actually a valid gif..
+    return unless $header =~ s/^GIF(8[79]a)//;
+    my $version = $1;
+
+    my ($width, $height, $packed, $bgcolor, $aspect) = unpack("vvCCC", $header);
+    my $area = $width * $height;
+    my $color_table_size = 1 << (($packed & 0x07) + 1);
+    #my $global_color_table = $packed & 0x80;
+    #my $has_global_color_table = $global_color_table ? 1 : 0;
+    #my $sorted_colors = ($packed & 0x08)?1:0;
+    #my $resolution = ((($packed & 0x70) >> 4) + 1);
+
+    $pms->{imageinfo}->{pc_gif} += $area;
+
+    dbg("imageinfo: gif image ".($part->{'name'} ? $part->{'name'} : '')." is $height x $width pixels ($area pixels sq.), with $color_table_size color table");
+  },
+
+  'png' => sub {
+    my ($pms, $part) = @_;
+    my $data = $part->decode();
+
+    return unless (substr($data, 0, 8) eq "\x89PNG\x0d\x0a\x1a\x0a");
+
+    my $datalen = length $data;
+    my $pos = 8;
+    my $chunksize = 8;
+    my ($width, $height) = ( 0, 0 );
+    my ($depth, $ctype, $compression, $filter, $interlace);
+  
+    while ($pos < $datalen) {
+      my ($len, $type) = unpack("Na4", substr($data, $pos, $chunksize));
+      $pos += $chunksize;
+ 
+      last if $type eq "IEND";  # end of png image.
+
+      next unless ( $type eq "IHDR" && $len == 13 );
+      
+      my $bytes = substr($data, $pos, $len + 4);
+      my $crc = unpack("N", substr($bytes, -4, 4, ""));
+
+      if ($type eq "IHDR" && $len == 13) {
+        ($width, $height, $depth, $ctype, $compression, $filter, $interlace) = unpack("NNCCCCC", $bytes);
+        last;
+      }
+    }
+
+    my $area = $width * $height;
+    $pms->{imageinfo}->{pc_png} += $area;
+
+    dbg("imageinfo: png image ".($part->{'name'} ? $part->{'name'} : '')." is $height x $width pixels ($area pixels sq.)");
+  },
+);
+
+sub _get_images {
+  my ($self,$pms) = @_;
+  my $result = 0;
+
+  foreach my $type ( 'all', keys %get_details ) {
+    $pms->{'imageinfo'}->{"pc_$type"} = 0;
+    $pms->{'imageinfo'}->{"count_$type"} = 0;
+  }
+
+  foreach my $p ($pms->{msg}->find_parts(qr@^image/(?:gif|png)$@, 1)) {
+    # make sure its base64 encoded
+    my $cte = lc $p->get_header('content-transfer-encoding') || '';
+    next if ($cte !~ /^base64$/);
+
+    my ($type) = $p->{'type'} =~ m@/(\w+)$@;
+    if ($type && exists $get_details{$type}) {
+       $get_details{$type}->($pms,$p);
+       $pms->{'imageinfo'}->{"count_$type"} ++;
+    }
+  }
+
+  foreach my $type ( keys %get_details ) {
+    $pms->{'imageinfo'}->{'pc_all'} += $pms->{'imageinfo'}->{"pc_$type"};
+    $pms->{'imageinfo'}->{'count_all'} += $pms->{'imageinfo'}->{"count_$type"};
+  }
+}
+
+# -----------------------------------------
+
+sub image_count {
+  my ($self,$pms,$body,$type,$min,$max) = @_;
+  
+  return unless defined $min;
+
+  # make sure we have image data read in.
+  if (!exists $pms->{'imageinfo'}) {
+    $self->_get_images($pms);
+  }
+
+  return result_check($min, $max, $pms->{'imageinfo'}->{"count_$type"});
+}
+
+# -----------------------------------------
+
+sub pixel_coverage {
+  my ($self,$pms,$body,$type,$min,$max) = @_;
+
+  return unless (defined $type && defined $min);
+
+  # make sure we have image data read in.
+  if (!exists $pms->{'imageinfo'}) {
+    $self->_get_images($pms);
+  }
+  
+  return result_check($min, $max, $pms->{'imageinfo'}->{"pc_$type"});
+}
+
+# -----------------------------------------
+
+sub result_check {
+  my ($min, $max, $value) = @_;
+  return 0 if ($value < $min);
+  return 0 if (defined $max && $value > $max);
+  return 1;
+}
+
+1;