line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# |
2
|
|
|
|
|
|
|
# Mail::SPF::Record |
3
|
|
|
|
|
|
|
# Abstract base class for SPF records. |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# (C) 2005-2012 Julian Mehnle |
6
|
|
|
|
|
|
|
# 2005 Shevek |
7
|
|
|
|
|
|
|
# $Id: Record.pm 57 2012-01-30 08:15:31Z julian $ |
8
|
|
|
|
|
|
|
# |
9
|
|
|
|
|
|
|
############################################################################## |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
package Mail::SPF::Record; |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
=head1 NAME |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
Mail::SPF::Record - Abstract base class for SPF records |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=cut |
18
|
|
|
|
|
|
|
|
19
|
3
|
|
|
3
|
|
18
|
use warnings; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
117
|
|
20
|
3
|
|
|
3
|
|
20
|
use strict; |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
100
|
|
21
|
|
|
|
|
|
|
|
22
|
3
|
|
|
3
|
|
17
|
use utf8; # Hack to keep Perl 5.6 from whining about /[\p{}]/. |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
20
|
|
23
|
|
|
|
|
|
|
|
24
|
3
|
|
|
3
|
|
72
|
use base 'Mail::SPF::Base'; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
296
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
use overload |
27
|
3
|
|
|
|
|
29
|
'""' => 'stringify', |
28
|
3
|
|
|
3
|
|
18
|
fallback => 1; |
|
3
|
|
|
|
|
6
|
|
29
|
|
|
|
|
|
|
|
30
|
3
|
|
|
3
|
|
240
|
use Error ':try'; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
22
|
|
31
|
|
|
|
|
|
|
|
32
|
3
|
|
|
3
|
|
532
|
use constant TRUE => (0 == 0); |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
243
|
|
33
|
3
|
|
|
3
|
|
15
|
use constant FALSE => not TRUE; |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
174
|
|
34
|
|
|
|
|
|
|
|
35
|
3
|
|
|
3
|
|
18
|
use constant default_qualifier => '+'; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
182
|
|
36
|
|
|
|
|
|
|
|
37
|
3
|
|
|
|
|
4667
|
use constant results_by_qualifier => { |
38
|
|
|
|
|
|
|
'' => 'pass', |
39
|
|
|
|
|
|
|
'+' => 'pass', |
40
|
|
|
|
|
|
|
'-' => 'fail', |
41
|
|
|
|
|
|
|
'~' => 'softfail', |
42
|
|
|
|
|
|
|
'?' => 'neutral' |
43
|
3
|
|
|
3
|
|
85
|
}; |
|
3
|
|
|
|
|
5
|
|
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# Interface: |
46
|
|
|
|
|
|
|
############################################################################## |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
=head1 SYNOPSIS |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=head2 Creating a record from a string |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
use Mail::SPF::v1::Record; |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
my $record = Mail::SPF::v1::Record->new_from_string("v=spf1 a mx -all"); |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=head2 Creating a record synthetically |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
use Mail::SPF::v2::Record; |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
my $record = Mail::SPF::v2::Record->new( |
61
|
|
|
|
|
|
|
scopes => ['mfrom', 'pra'], |
62
|
|
|
|
|
|
|
terms => [ |
63
|
|
|
|
|
|
|
Mail::SPF::Mech::A->new(), |
64
|
|
|
|
|
|
|
Mail::SPF::Mech::MX->new(), |
65
|
|
|
|
|
|
|
Mail::SPF::Mech::All->new(qualifier => '-') |
66
|
|
|
|
|
|
|
], |
67
|
|
|
|
|
|
|
global_mods => [ |
68
|
|
|
|
|
|
|
Mail::SPF::Mod::Exp->new(domain_spec => 'spf-exp.example.com') |
69
|
|
|
|
|
|
|
] |
70
|
|
|
|
|
|
|
); |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=cut |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
# Implementation: |
75
|
|
|
|
|
|
|
############################################################################## |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=head1 DESCRIPTION |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
B is an abstract base class for SPF records. It cannot be |
80
|
|
|
|
|
|
|
instantiated directly. Create an instance of a concrete sub-class instead. |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=head2 Constructor |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
The following constructors are provided: |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=over |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
=item B: returns I |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
Creates a new SPF record object. |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
%options is a list of key/value pairs representing any of the following |
93
|
|
|
|
|
|
|
options: |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=over |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=item B |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
A I denoting the unparsed text of the record. |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=item B |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
A reference to an I of Is denoting the scopes that are covered |
104
|
|
|
|
|
|
|
by the record (see the description of the C option of |
105
|
|
|
|
|
|
|
L constructor|Mail::SPF::Request/new>). |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=item B |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
A reference to an I of I (i.e. I or |
110
|
|
|
|
|
|
|
I) objects that make up the record. I |
111
|
|
|
|
|
|
|
objects must not be included here, but should be specified using the |
112
|
|
|
|
|
|
|
C option instead. |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=item B |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
A reference to an I of I objects that are global |
117
|
|
|
|
|
|
|
modifiers of the record. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=back |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=cut |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
sub new { |
124
|
0
|
|
|
0
|
1
|
|
my ($self, %options) = @_; |
125
|
0
|
0
|
|
|
|
|
$self->class ne __PACKAGE__ |
126
|
|
|
|
|
|
|
or throw Mail::SPF::EAbstractClass; |
127
|
0
|
|
|
|
|
|
$self = $self->SUPER::new(%options); |
128
|
0
|
0
|
|
|
|
|
$self->{parse_text} = $self->{text} if not defined($self->{parse_text}); |
129
|
0
|
|
0
|
|
|
|
$self->{terms} ||= []; |
130
|
0
|
|
0
|
|
|
|
$self->{global_mods} ||= {}; |
131
|
0
|
|
|
|
|
|
return $self; |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=item B: returns I; |
135
|
|
|
|
|
|
|
throws I, I, |
136
|
|
|
|
|
|
|
I |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
Creates a new SPF record object by parsing the string and any options given. |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=cut |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
sub new_from_string { |
143
|
0
|
|
|
0
|
1
|
|
my ($self, $text, %options) = @_; |
144
|
0
|
|
|
|
|
|
$self = $self->new(%options, text => $text); |
145
|
0
|
|
|
|
|
|
$self->parse(); |
146
|
0
|
|
|
|
|
|
return $self; |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=back |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=head2 Class methods |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
The following class methods are provided: |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=over |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=item B: returns I |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
I. Returns a regular expression that matches a legal version tag. |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
This method is abstract and must be implemented by sub-classes of |
162
|
|
|
|
|
|
|
Mail::SPF::Record. |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=item B: returns I |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
Returns the default qualifier, i.e. B<'+'>. |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=item B: returns I of I |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
Returns a reference to a hash that maps qualifiers to result codes as follows: |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
Qualifier | Result code |
173
|
|
|
|
|
|
|
-----------+------------- |
174
|
|
|
|
|
|
|
+ | pass |
175
|
|
|
|
|
|
|
- | fail |
176
|
|
|
|
|
|
|
~ | softfail |
177
|
|
|
|
|
|
|
? | neutral |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=back |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=head2 Instance methods |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
The following instance methods are provided: |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
=over |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=cut |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
sub parse { |
190
|
0
|
|
|
0
|
0
|
|
my ($self) = @_; |
191
|
0
|
0
|
|
|
|
|
defined($self->{parse_text}) |
192
|
|
|
|
|
|
|
or throw Mail::SPF::ENothingToParse('Nothing to parse for record'); |
193
|
0
|
|
|
|
|
|
$self->parse_version_tag(); |
194
|
0
|
|
|
|
|
|
$self->parse_term() while length($self->{parse_text}); |
195
|
0
|
|
|
|
|
|
$self->parse_end(); |
196
|
0
|
|
|
|
|
|
return; |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
sub parse_version_tag { |
200
|
0
|
|
|
0
|
0
|
|
my ($self) = @_; |
201
|
0
|
0
|
|
|
|
|
if (not $self->{parse_text} =~ s/^${\$self->version_tag_pattern}(?:\x20+|$)//) { |
|
0
|
|
|
|
|
|
|
202
|
0
|
|
|
|
|
|
throw Mail::SPF::EInvalidRecordVersion( |
203
|
|
|
|
|
|
|
"Not a '" . $self->version_tag . "' record: '" . $self->text . "'"); |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
sub parse_term { |
208
|
0
|
|
|
0
|
0
|
|
my ($self) = @_; |
209
|
0
|
0
|
|
|
|
|
if ( |
|
|
0
|
|
|
|
|
|
210
|
|
|
|
|
|
|
$self->{parse_text} =~ s/ |
211
|
|
|
|
|
|
|
^ |
212
|
|
|
|
|
|
|
( |
213
|
0
|
|
|
|
|
|
${\Mail::SPF::Mech->qualifier_pattern}? |
|
0
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
(${\Mail::SPF::Mech->name_pattern}) |
215
|
|
|
|
|
|
|
[^\x20]* |
216
|
|
|
|
|
|
|
) |
217
|
|
|
|
|
|
|
(?: \x20+ | $ ) |
218
|
|
|
|
|
|
|
//x |
219
|
|
|
|
|
|
|
) { |
220
|
|
|
|
|
|
|
# Looks like a mechanism: |
221
|
0
|
|
|
|
|
|
my ($mech_text, $mech_name) = ($1, lc($2)); |
222
|
0
|
|
|
|
|
|
my $mech_class = $self->mech_classes->{$mech_name}; |
223
|
0
|
0
|
|
|
|
|
throw Mail::SPF::EInvalidMech("Unknown mechanism type '$mech_name' in '" . $self->version_tag . "' record") |
224
|
|
|
|
|
|
|
if not defined($mech_class); |
225
|
0
|
|
|
|
|
|
my $mech = $mech_class->new_from_string($mech_text); |
226
|
0
|
|
|
|
|
|
push(@{$self->{terms}}, $mech); |
|
0
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
elsif ( |
229
|
|
|
|
|
|
|
$self->{parse_text} =~ s/ |
230
|
|
|
|
|
|
|
^ |
231
|
|
|
|
|
|
|
( |
232
|
0
|
|
|
|
|
|
(${\Mail::SPF::Mod->name_pattern}) = |
233
|
|
|
|
|
|
|
[^\x20]* |
234
|
|
|
|
|
|
|
) |
235
|
|
|
|
|
|
|
(?: \x20+ | $ ) |
236
|
|
|
|
|
|
|
//x |
237
|
|
|
|
|
|
|
) { |
238
|
|
|
|
|
|
|
# Looks like a modifier: |
239
|
0
|
|
|
|
|
|
my ($mod_text, $mod_name) = ($1, lc($2)); |
240
|
0
|
|
|
|
|
|
my $mod_class = $self->mod_classes->{$mod_name}; |
241
|
0
|
0
|
|
|
|
|
if (defined($mod_class)) { |
242
|
|
|
|
|
|
|
# Known modifier. |
243
|
0
|
|
|
|
|
|
my $mod = $mod_class->new_from_string($mod_text); |
244
|
0
|
0
|
|
|
|
|
if ($mod->isa('Mail::SPF::GlobalMod')) { |
|
|
0
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# Global modifier. |
246
|
0
|
0
|
|
|
|
|
not defined($self->{global_mods}->{$mod_name}) or |
247
|
|
|
|
|
|
|
throw Mail::SPF::EDuplicateGlobalMod("Duplicate global modifier '$mod_name' encountered"); |
248
|
0
|
|
|
|
|
|
$self->{global_mods}->{$mod_name} = $mod; |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
elsif ($mod->isa('Mail::SPF::PositionalMod')) { |
251
|
|
|
|
|
|
|
# Positional modifier, queue normally: |
252
|
0
|
|
|
|
|
|
push(@{$self->{terms}}, $mod); |
|
0
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
else { |
255
|
|
|
|
|
|
|
# Huh? This should not happen. |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
else { |
259
|
|
|
|
|
|
|
# Unknown modifier. |
260
|
0
|
|
|
|
|
|
my $mod = Mail::SPF::UnknownMod->new_from_string($mod_text); |
261
|
0
|
|
|
|
|
|
push(@{$self->{terms}}, $mod); |
|
0
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
else { |
265
|
0
|
|
|
|
|
|
throw Mail::SPF::EJunkInRecord("Junk encountered in record '" . $self->text . "'"); |
266
|
|
|
|
|
|
|
} |
267
|
0
|
|
|
|
|
|
return; |
268
|
|
|
|
|
|
|
} |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
sub parse_end { |
271
|
0
|
|
|
0
|
0
|
|
my ($self) = @_; |
272
|
0
|
0
|
|
|
|
|
throw Mail::SPF::EJunkInRecord("Junk encountered in record '" . $self->text . "'") |
273
|
|
|
|
|
|
|
if $self->{parse_text} ne ''; |
274
|
0
|
|
|
|
|
|
delete($self->{parse_text}); |
275
|
0
|
|
|
|
|
|
return; |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
=item B: returns I; throws I |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
Returns the unparsed text of the record. Throws a I |
281
|
|
|
|
|
|
|
exception if the record was created synthetically instead of being parsed, and |
282
|
|
|
|
|
|
|
no text was provided. |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
=cut |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
sub text { |
287
|
0
|
|
|
0
|
1
|
|
my ($self) = @_; |
288
|
0
|
0
|
|
|
|
|
defined($self->{text}) |
289
|
|
|
|
|
|
|
or throw Mail::SPF::ENoUnparsedText; |
290
|
0
|
|
|
|
|
|
return $self->{text}; |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=item B: returns I |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
I. Returns the version tag of the record. |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
This method is abstract and must be implemented by sub-classes of |
298
|
|
|
|
|
|
|
Mail::SPF::Record. |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=item B: returns I of I |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
Returns a list of the scopes that are covered by the record. See the |
303
|
|
|
|
|
|
|
description of the L constructor's C option. |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
=cut |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
sub scopes { |
308
|
0
|
|
|
0
|
1
|
|
my ($self) = @_; |
309
|
0
|
|
|
|
|
|
return @{$self->{scopes}}; |
|
0
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
=item B: returns I of I |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
Returns a list of the terms that make up the record, excluding any global |
315
|
|
|
|
|
|
|
modifiers, which are returned by the C method. See the |
316
|
|
|
|
|
|
|
description of the L constructor's C option. |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
=cut |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
sub terms { |
321
|
0
|
|
|
0
|
1
|
|
my ($self) = @_; |
322
|
0
|
|
|
|
|
|
return @{$self->{terms}}; |
|
0
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
=item B: returns I of I |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
Returns a list of the global modifiers of the record, ordered ascending by |
328
|
|
|
|
|
|
|
modifier precedence. See the description of the L constructor's |
329
|
|
|
|
|
|
|
C option. |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=cut |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
sub global_mods { |
334
|
0
|
|
|
0
|
1
|
|
my ($self) = @_; |
335
|
0
|
|
|
|
|
|
return sort { $a->precedence <=> $b->precedence } values(%{$self->{global_mods}}); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
=item B: returns I |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
Returns the global modifier of the given name if it is present in the record. |
341
|
|
|
|
|
|
|
Returns B otherwise. Use this method if you wish to retrieve a specific |
342
|
|
|
|
|
|
|
global modifier as opposed to getting all of them. |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
=cut |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
sub global_mod { |
347
|
0
|
|
|
0
|
1
|
|
my ($self, $mod_name) = @_; |
348
|
0
|
|
|
|
|
|
return $self->{global_mods}->{$mod_name}; |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
=item B: returns I |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
Returns the record's version tag and terms (including the global modifiers) |
354
|
|
|
|
|
|
|
formatted as a string. You can simply use a Mail::SPF::Record object as a |
355
|
|
|
|
|
|
|
string for the same effect, see L<"OVERLOADING">. |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
=cut |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
sub stringify { |
360
|
0
|
|
|
0
|
0
|
|
my ($self) = @_; |
361
|
0
|
|
|
|
|
|
return join(' ', $self->version_tag, $self->terms, $self->global_mods); |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
=item B: throws I |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
Evaluates the SPF record in the context of the request parameters represented |
367
|
|
|
|
|
|
|
by the given I object. The given I |
368
|
|
|
|
|
|
|
object is used for performing DNS look-ups. Throws a I |
369
|
|
|
|
|
|
|
object matching the outcome of the evaluation; see L. See |
370
|
|
|
|
|
|
|
RFC 4408, 4.6 and 4.7, for the exact algorithm used. |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
=cut |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
sub eval { |
375
|
0
|
|
|
0
|
1
|
|
my ($self, $server, $request) = @_; |
376
|
|
|
|
|
|
|
|
377
|
0
|
0
|
|
|
|
|
defined($server) |
378
|
|
|
|
|
|
|
or throw Mail::SPF::EOptionRequired('Mail::SPF server object required for record evaluation'); |
379
|
0
|
0
|
|
|
|
|
defined($request) |
380
|
|
|
|
|
|
|
or throw Mail::SPF::EOptionRequired('Request object required for record evaluation'); |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
try { |
383
|
0
|
|
|
0
|
|
|
foreach my $term ($self->terms) { |
384
|
0
|
0
|
|
|
|
|
if ($term->isa('Mail::SPF::Mech')) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
385
|
|
|
|
|
|
|
# Term is a mechanism. |
386
|
0
|
|
|
|
|
|
my $mech = $term; |
387
|
0
|
0
|
|
|
|
|
if ($mech->match($server, $request)) { |
388
|
0
|
|
|
|
|
|
my $result_name = $self->results_by_qualifier->{$mech->qualifier}; |
389
|
0
|
|
|
|
|
|
my $result_class = $server->result_class($result_name); |
390
|
0
|
|
|
|
|
|
my $result = $result_class->new($server, $request, "Mechanism '$term' matched"); |
391
|
0
|
|
|
|
|
|
$mech->explain($server, $request, $result); |
392
|
0
|
|
|
|
|
|
$result->throw(); |
393
|
|
|
|
|
|
|
} |
394
|
|
|
|
|
|
|
} |
395
|
|
|
|
|
|
|
elsif ($term->isa('Mail::SPF::PositionalMod')) { |
396
|
|
|
|
|
|
|
# Term is a positional modifier. |
397
|
0
|
|
|
|
|
|
my $mod = $term; |
398
|
0
|
|
|
|
|
|
$mod->process($server, $request); |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
elsif ($term->isa('Mail::SPF::UnknownMod')) { |
401
|
|
|
|
|
|
|
# Term is an unknown modifier. Ignore it (RFC 4408, 6/3). |
402
|
|
|
|
|
|
|
} |
403
|
|
|
|
|
|
|
else { |
404
|
|
|
|
|
|
|
# Invalid term object encountered: |
405
|
0
|
|
|
|
|
|
throw Mail::SPF::EUnexpectedTermObject( |
406
|
|
|
|
|
|
|
"Unexpected term object '$term' encountered"); |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
} |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
# Default result when "falling off" the end of the record (RFC 4408, 4.7/1): |
411
|
0
|
|
|
|
|
|
$server->throw_result('neutral-by-default', $request, |
412
|
|
|
|
|
|
|
'Default neutral result due to no mechanism matches'); |
413
|
|
|
|
|
|
|
} |
414
|
|
|
|
|
|
|
catch Mail::SPF::Result with { |
415
|
0
|
|
|
0
|
|
|
my ($result) = @_; |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
# Process global modifiers in ascending order of precedence: |
418
|
0
|
|
|
|
|
|
foreach my $global_mod ($self->global_mods) { |
419
|
0
|
|
|
|
|
|
$global_mod->process($server, $request, $result); |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
|
422
|
0
|
|
|
|
|
|
$result->throw(); |
423
|
0
|
|
|
|
|
|
}; |
424
|
|
|
|
|
|
|
} |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
=back |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
=head1 OVERLOADING |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
If a Mail::SPF::Record object is used as a I, the C method |
431
|
|
|
|
|
|
|
is used to convert the object into a string. |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
=head1 SEE ALSO |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
L, L, L, |
436
|
|
|
|
|
|
|
L, L, L |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
L |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
For availability, support, and license information, see the README file |
441
|
|
|
|
|
|
|
included with Mail::SPF. |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=head1 AUTHORS |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
Julian Mehnle , Shevek |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=cut |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
TRUE; |