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;