File Coverage

blib/lib/Mail/Message/Head/SpamGroup.pm
Criterion Covered Total %
statement 72 76 94.7
branch 31 40 77.5
condition 3 6 50.0
subroutine 11 12 91.6
pod 7 7 100.0
total 124 141 87.9


line stmt bran cond sub pod time code
1             # This code is part of Perl distribution Mail-Message version 4.04.
2             # The POD got stripped from this file by OODoc version 3.06.
3             # For contributors see file ChangeLog.
4              
5             # This software is copyright (c) 2001-2026 by Mark Overmeer.
6              
7             # This is free software; you can redistribute it and/or modify it under
8             # the same terms as the Perl 5 programming language system itself.
9             # SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later
10              
11              
12             package Mail::Message::Head::SpamGroup;{
13             our $VERSION = '4.04';
14             }
15              
16 2     2   2364 use parent 'Mail::Message::Head::FieldGroup';
  2         7  
  2         18  
17              
18 2     2   153 use strict;
  2         4  
  2         55  
19 2     2   10 use warnings;
  2         5  
  2         135  
20              
21 2     2   12 use Log::Report 'mail-message', import => [ qw/panic/ ];
  2         5  
  2         18  
22              
23             #--------------------
24              
25             my %fighters;
26             my $fighterfields; # one regexp for all fields
27              
28 25     25 1 102 sub knownFighters() { keys %fighters }
29              
30              
31             sub fighter($;@)
32 134     134 1 431 { my ($thing, $name) = (shift, shift);
33              
34 134 100       355 if(@_)
35 6         23 { my %args = @_;
36 6 50       19 defined $args{fields} or panic "requires fields";
37 6 50       15 defined $args{isspam} or panic "requires isspam";
38 6         17 $fighters{$name} = \%args;
39              
40 6         38 my @fields = map $_->{fields}, values %fighters;
41 6         13 local $" = '|';
42 6         168 $fighterfields = qr/@fields/;
43             }
44              
45 134         252 %{$fighters{$name}};
  134         2337  
46             }
47              
48              
49             BEGIN
50             { __PACKAGE__->fighter( SpamAssassin =>
51             fields => qr/^X-Spam-/i,
52             isspam => sub {
53 8         19 my ($sg, $head) = @_;
54 8   66     23 my $f = $head->get('X-Spam-Flag') || $head->get('X-Spam-Status');
55 8 50       21 $f ? $f =~ m/^yes\b/i : 0;
56             },
57             version => sub {
58 8         19 my ($sg, $head) = @_;
59 8 50       26 my $assin = $head->get('X-Spam-Checker-Version') or return ();
60 8         24 my ($software, $version) = $assin =~ m/^(.*)\s+(.*?)\s*$/;
61 8         36 ($software, $version);
62             },
63 2     2   1893 );
64              
65             __PACKAGE__->fighter( 'Habeas-SWE' =>
66             fields => qr/^X-Habeas-SWE/i,
67             isspam => sub {
68 26         59 my ($sg, $head) = @_;
69 26         73 not $sg->habeasSweFieldsCorrect;
70             },
71 2         16 );
72              
73             __PACKAGE__->fighter( MailScanner =>
74             fields => qr/^X-MailScanner/i,
75             isspam => sub {
76 0         0 my ($sg, $head) = @_;
77 0         0 my $subject = $head->get('subject');
78 0         0 $subject =~ m/^\{ (?:spam|virus)/xi;
79             },
80 2         15 );
81             }
82              
83              
84             sub from($@)
85 44     44 1 520 { my ($class, $from, %args) = @_;
86 44 50       253 my $head = $from->isa('Mail::Message::Head') ? $from : $from->head;
87 44         106 my ($self, @detected);
88              
89 44 100       163 my @types = defined $args{types} ? @{$args{types}} : $class->knownFighters;
  19         54  
90              
91 44         137 foreach my $type (@types)
92 94 100       407 { $self = $class->new(head => $head) unless defined $self;
93 94 100       249 $self->collectFields($type) or next;
94              
95 34         93 my %fighter = $self->fighter($type);
96 34 100       118 my ($software, $version) = defined $fighter{version} ? $fighter{version}->($self, $head) : ();
97              
98 34         118 $self->detected($type, $software, $version);
99 34         88 $self->spamDetected( $fighter{isspam}->($self, $head) );
100              
101 34         97 push @detected, $self;
102 34         115 undef $self; # create a new one
103             }
104              
105 44         217 @detected;
106             }
107              
108             sub collectFields($)
109 94     94 1 225 { my ($self, $set) = @_;
110 94 50       239 my %fighter = $self->fighter($set) or panic "no spam set $set";
111              
112 94         324 my @names = map $_->name, $self->head->grepNames($fighter{fields});
113 94 100       436 $self->addFields(@names) if @names;
114 94         443 @names;
115             }
116              
117              
118 0     0 1 0 sub isSpamGroupFieldName($) { $_[1] =~ $fighterfields }
119              
120              
121             my @habeas_lines = (
122             'winter into spring', 'brightly anticipated', 'like Habeas SWE (tm)',
123             'Copyright 2002 Habeas (tm)',
124             'Sender Warranted Email (SWE) (tm). The sender of this',
125             'email in exchange for a license for this Habeas',
126             'warrant mark warrants that this is a Habeas Compliant',
127             'Message (HCM) and not spam. Please report use of this',
128             'mark in spam to .',
129             );
130              
131             sub habeasSweFieldsCorrect(;$)
132 46     46 1 91 { my $self;
133              
134 46 100       128 if(@_ > 1)
135 12         32 { my ($class, $thing) = @_;
136 12 100       124 my $head = $thing->isa('Mail::Message::Head') ? $thing : $thing->head;
137 12 50       44 $self = $head->spamGroups('Habeas-SWE') or return;
138             }
139             else
140 34         63 { $self = shift;
141 34         100 my $type = $self->type;
142 34 50 33     187 defined $type && $type eq 'Habeas-SWE' or return;
143             }
144              
145 46         134 my $head = $self->head;
146 46 100       123 $self->fields == @habeas_lines or return;
147              
148 32         144 for(my $nr=1; $nr <= $#habeas_lines; $nr++)
149 228 50       827 { my $f = $head->get("X-Habeas-SWE-$nr") or return;
150 228 100       754 $f->unfoldedBody eq $habeas_lines[$nr-1] or return;
151             }
152              
153 25         130 1;
154             }
155              
156              
157             sub spamDetected(;$)
158 34     34 1 56 { my $self = shift;
159 34 100       115 @_? ($self->{MMFS_spam} = shift) : $self->{MMFS_spam};
160             }
161              
162             1;