line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# <@LICENSE> |
2
|
|
|
|
|
|
|
# Licensed to the Apache Software Foundation (ASF) under one or more |
3
|
|
|
|
|
|
|
# contributor license agreements. See the NOTICE file distributed with |
4
|
|
|
|
|
|
|
# this work for additional information regarding copyright ownership. |
5
|
|
|
|
|
|
|
# The ASF licenses this file to you under the Apache License, Version 2.0 |
6
|
|
|
|
|
|
|
# (the "License"); you may not use this file except in compliance with |
7
|
|
|
|
|
|
|
# the License. You may obtain a copy of the License at: |
8
|
|
|
|
|
|
|
# |
9
|
|
|
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0 |
10
|
|
|
|
|
|
|
# |
11
|
|
|
|
|
|
|
# Unless required by applicable law or agreed to in writing, software |
12
|
|
|
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS, |
13
|
|
|
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14
|
|
|
|
|
|
|
# See the License for the specific language governing permissions and |
15
|
|
|
|
|
|
|
# limitations under the License. |
16
|
|
|
|
|
|
|
# </@LICENSE> |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
use strict; |
20
|
22
|
|
|
22
|
|
162
|
use warnings; |
|
22
|
|
|
|
|
48
|
|
|
22
|
|
|
|
|
11054
|
|
21
|
22
|
|
|
22
|
|
127
|
# use bytes; |
|
22
|
|
|
|
|
43
|
|
|
22
|
|
|
|
|
6498
|
|
22
|
|
|
|
|
|
|
use re 'taint'; |
23
|
22
|
|
|
22
|
|
132
|
use Errno qw(EBADF); |
|
22
|
|
|
|
|
45
|
|
|
22
|
|
|
|
|
742
|
|
24
|
22
|
|
|
22
|
|
154
|
|
|
22
|
|
|
|
|
55
|
|
|
22
|
|
|
|
|
1162
|
|
25
|
|
|
|
|
|
|
use Mail::SpamAssassin::Plugin; |
26
|
22
|
|
|
22
|
|
131
|
use Mail::SpamAssassin::Locales; |
|
22
|
|
|
|
|
43
|
|
|
22
|
|
|
|
|
508
|
|
27
|
22
|
|
|
22
|
|
121
|
use Mail::SpamAssassin::Util qw(get_my_locales parse_rfc822_date); |
|
22
|
|
|
|
|
38
|
|
|
22
|
|
|
|
|
600
|
|
28
|
22
|
|
|
22
|
|
166
|
use Mail::SpamAssassin::Logger; |
|
22
|
|
|
|
|
53
|
|
|
22
|
|
|
|
|
1372
|
|
29
|
22
|
|
|
22
|
|
169
|
use Mail::SpamAssassin::Constants qw(:sa :ip); |
|
22
|
|
|
|
|
55
|
|
|
22
|
|
|
|
|
1239
|
|
30
|
22
|
|
|
22
|
|
169
|
|
|
22
|
|
|
|
|
51
|
|
|
22
|
|
|
|
|
81263
|
|
31
|
|
|
|
|
|
|
our @ISA = qw(Mail::SpamAssassin::Plugin); |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# constructor: register the eval rule |
34
|
|
|
|
|
|
|
my $class = shift; |
35
|
|
|
|
|
|
|
my $mailsaobject = shift; |
36
|
63
|
|
|
63
|
1
|
226
|
|
37
|
63
|
|
|
|
|
143
|
# some boilerplate... |
38
|
|
|
|
|
|
|
$class = ref($class) || $class; |
39
|
|
|
|
|
|
|
my $self = $class->SUPER::new($mailsaobject); |
40
|
63
|
|
33
|
|
|
5838
|
bless ($self, $class); |
41
|
63
|
|
|
|
|
351
|
|
42
|
63
|
|
|
|
|
156
|
# the important bit! |
43
|
|
|
|
|
|
|
$self->register_eval_rule("check_for_fake_aol_relay_in_rcvd"); |
44
|
|
|
|
|
|
|
$self->register_eval_rule("check_for_faraway_charset_in_headers"); |
45
|
63
|
|
|
|
|
6111
|
$self->register_eval_rule("check_for_unique_subject_id"); |
46
|
63
|
|
|
|
|
217
|
$self->register_eval_rule("check_illegal_chars"); |
47
|
63
|
|
|
|
|
182
|
$self->register_eval_rule("check_for_forged_hotmail_received_headers"); |
48
|
63
|
|
|
|
|
202
|
$self->register_eval_rule("check_for_no_hotmail_received_headers"); |
49
|
63
|
|
|
|
|
216
|
$self->register_eval_rule("check_for_msn_groups_headers"); |
50
|
63
|
|
|
|
|
192
|
$self->register_eval_rule("check_for_forged_eudoramail_received_headers"); |
51
|
63
|
|
|
|
|
214
|
$self->register_eval_rule("check_for_forged_yahoo_received_headers"); |
52
|
63
|
|
|
|
|
187
|
$self->register_eval_rule("check_for_forged_juno_received_headers"); |
53
|
63
|
|
|
|
|
182
|
$self->register_eval_rule("check_for_forged_gmail_received_headers"); |
54
|
63
|
|
|
|
|
242
|
$self->register_eval_rule("check_for_matching_env_and_hdr_from"); |
55
|
63
|
|
|
|
|
167
|
$self->register_eval_rule("sorted_recipients"); |
56
|
63
|
|
|
|
|
215
|
$self->register_eval_rule("similar_recipients"); |
57
|
63
|
|
|
|
|
210
|
$self->register_eval_rule("check_for_missing_to_header"); |
58
|
63
|
|
|
|
|
207
|
$self->register_eval_rule("check_for_forged_gw05_received_headers"); |
59
|
63
|
|
|
|
|
5311
|
$self->register_eval_rule("check_for_shifted_date"); |
60
|
63
|
|
|
|
|
168
|
$self->register_eval_rule("subject_is_all_caps"); |
61
|
63
|
|
|
|
|
169
|
$self->register_eval_rule("check_for_to_in_subject"); |
62
|
63
|
|
|
|
|
197
|
$self->register_eval_rule("check_outlook_message_id"); |
63
|
63
|
|
|
|
|
207
|
$self->register_eval_rule("check_messageid_not_usable"); |
64
|
63
|
|
|
|
|
208
|
$self->register_eval_rule("check_header_count_range"); |
65
|
63
|
|
|
|
|
180
|
$self->register_eval_rule("check_unresolved_template"); |
66
|
63
|
|
|
|
|
5140
|
$self->register_eval_rule("check_ratware_name_id"); |
67
|
63
|
|
|
|
|
202
|
$self->register_eval_rule("check_ratware_envelope_from"); |
68
|
63
|
|
|
|
|
158
|
$self->register_eval_rule("gated_through_received_hdr_remover"); |
69
|
63
|
|
|
|
|
184
|
$self->register_eval_rule("received_within_months"); |
70
|
63
|
|
|
|
|
173
|
$self->register_eval_rule("check_equal_from_domains"); |
71
|
63
|
|
|
|
|
180
|
|
72
|
63
|
|
|
|
|
193
|
return $self; |
73
|
|
|
|
|
|
|
} |
74
|
63
|
|
|
|
|
566
|
|
75
|
|
|
|
|
|
|
# load triplets.txt into memory |
76
|
|
|
|
|
|
|
my ($self) = @_; |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
$self->word_is_in_dictionary("aba"); |
79
|
2
|
|
|
2
|
1
|
7
|
} |
80
|
|
|
|
|
|
|
|
81
|
2
|
|
|
|
|
8
|
my ($self, $pms) = @_; |
82
|
|
|
|
|
|
|
local ($_); |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
$_ = $pms->get('Received'); |
85
|
0
|
|
|
0
|
0
|
0
|
s/\s/ /gs; |
86
|
0
|
|
|
|
|
0
|
|
87
|
|
|
|
|
|
|
# this is the hostname format used by AOL for their relays. Spammers love |
88
|
0
|
|
|
|
|
0
|
# forging it. Don't make it more specific to match aol.com only, though -- |
89
|
0
|
|
|
|
|
0
|
# there's another set of spammers who generate fake hostnames to go with |
90
|
|
|
|
|
|
|
# it! |
91
|
|
|
|
|
|
|
if (/ rly-[a-z][a-z]\d\d\./i) { |
92
|
|
|
|
|
|
|
return 0 if /\/AOL-\d+\.\d+\.\d+\)/; # via AOL mail relay |
93
|
|
|
|
|
|
|
return 0 if /ESMTP id (?:RELAY|MAILRELAY|MAILIN)/; # AOLish |
94
|
|
|
|
|
|
|
return 1; |
95
|
0
|
0
|
|
|
|
0
|
} |
96
|
0
|
0
|
|
|
|
0
|
|
97
|
0
|
0
|
|
|
|
0
|
# spam: Received: from unknown (HELO mta05bw.bigpond.com) (80.71.176.130) by |
98
|
0
|
|
|
|
|
0
|
# rly-xw01.mx.aol.com with QMQP; Sat, 15 Jun 2002 23:37:16 -0000 |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
# non: Received: from rly-xj02.mx.aol.com (rly-xj02.mail.aol.com [172.20.116.39]) by |
101
|
|
|
|
|
|
|
# omr-r05.mx.aol.com (v83.35) with ESMTP id RELAYIN7-0501132011; Wed, 01 |
102
|
|
|
|
|
|
|
# May 2002 13:20:11 -0400 |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# non: Received: from logs-tr.proxy.aol.com (logs-tr.proxy.aol.com [152.163.201.132]) |
105
|
|
|
|
|
|
|
# by rly-ip01.mx.aol.com (8.8.8/8.8.8/AOL-5.0.0) |
106
|
|
|
|
|
|
|
# with ESMTP id NAA08955 for <sapient-alumni@yahoogroups.com>; |
107
|
|
|
|
|
|
|
# Thu, 4 Apr 2002 13:11:20 -0500 (EST) |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
return 0; |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
my ($self, $pms) = @_; |
113
|
0
|
|
|
|
|
0
|
my $hdr; |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
my @locales = get_my_locales($self->{main}->{conf}->{ok_locales}); |
116
|
|
|
|
|
|
|
|
117
|
0
|
|
|
0
|
0
|
0
|
return 0 if grep { $_ eq "all" } @locales; |
118
|
0
|
|
|
|
|
0
|
|
119
|
|
|
|
|
|
|
for my $h (qw(From Subject)) { |
120
|
0
|
|
|
|
|
0
|
my @hdrs = $pms->get("$h:raw"); # ??? get() returns a scalar ??? |
121
|
|
|
|
|
|
|
if ($#hdrs >= 0) { |
122
|
0
|
0
|
|
|
|
0
|
$hdr = join(" ", @hdrs); |
|
0
|
|
|
|
|
0
|
|
123
|
|
|
|
|
|
|
} else { |
124
|
0
|
|
|
|
|
0
|
$hdr = ''; |
125
|
0
|
|
|
|
|
0
|
} |
126
|
0
|
0
|
|
|
|
0
|
while ($hdr =~ /=\?(.+?)\?.\?.*?\?=/g) { |
127
|
0
|
|
|
|
|
0
|
Mail::SpamAssassin::Locales::is_charset_ok_for_locales($1, @locales) |
128
|
|
|
|
|
|
|
or return 1; |
129
|
0
|
|
|
|
|
0
|
} |
130
|
|
|
|
|
|
|
} |
131
|
0
|
|
|
|
|
0
|
0; |
132
|
0
|
0
|
|
|
|
0
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
my ($self, $pms) = @_; |
135
|
|
|
|
|
|
|
local ($_); |
136
|
0
|
|
|
|
|
0
|
$_ = lc $pms->get('Subject'); |
137
|
|
|
|
|
|
|
study; # study is a no-op since perl 5.16.0, eliminating related bugs |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
my $id = 0; |
140
|
0
|
|
|
0
|
0
|
0
|
if (/[-_\.\s]{7,}([-a-z0-9]{4,})$/ |
141
|
0
|
|
|
|
|
0
|
|| /\s{10,}(?:\S\s)?(\S+)$/ |
142
|
0
|
|
|
|
|
0
|
|| /\s{3,}[-:\#\(\[]+([-a-z0-9]{4,})[\]\)]+$/ |
143
|
0
|
|
|
|
|
0
|
|| /\s{3,}[:\#\(\[]*([a-f0-9]{4,})[\]\)]*$/ |
144
|
|
|
|
|
|
|
|| /\s{3,}[-:\#]([a-z0-9]{5,})$/ |
145
|
0
|
|
|
|
|
0
|
|| /[\s._]{3,}([^0\s._]\d{3,})$/ |
146
|
0
|
0
|
0
|
|
|
0
|
|| /[\s._]{3,}\[(\S+)\]$/ |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# (7217vPhZ0-478TLdy5829qicU9-0@26) and similar |
149
|
|
|
|
|
|
|
|| /\(([-\w]{7,}\@\d+)\)$/ |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
# Seven or more digits at the end of a subject is almost certainly a id |
152
|
|
|
|
|
|
|
|| /\b(\d{7,})\s*$/ |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
# stuff at end of line after "!" or "?" is usually an id |
155
|
|
|
|
|
|
|
|| /[!\?]\s*(\d{4,}|\w+(-\w+)+)\s*$/ |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# 9095IPZK7-095wsvp8715rJgY8-286-28 and similar |
158
|
|
|
|
|
|
|
# excluding 'Re:', etc and the first word |
159
|
|
|
|
|
|
|
|| /(?:\w{2,3}:\s)?\w+\s+(\w{7,}-\w{7,}(-\w+)*)\s*$/ |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
# #30D7 and similar |
162
|
|
|
|
|
|
|
|| /\s#\s*([a-f0-9]{4,})\s*$/ |
163
|
|
|
|
|
|
|
) |
164
|
|
|
|
|
|
|
{ |
165
|
|
|
|
|
|
|
$id = $1; |
166
|
|
|
|
|
|
|
# exempt online purchases |
167
|
|
|
|
|
|
|
if ($id =~ /\d{5,}/ |
168
|
|
|
|
|
|
|
&& /(?:item|invoice|order|number|confirmation).{1,6}\Q$id\E\s*$/) |
169
|
|
|
|
|
|
|
{ |
170
|
|
|
|
|
|
|
$id = 0; |
171
|
0
|
|
|
|
|
0
|
} |
172
|
|
|
|
|
|
|
|
173
|
0
|
0
|
0
|
|
|
0
|
# for the "foo-bar-baz" case, otherwise it won't |
174
|
|
|
|
|
|
|
# be found in the dict: |
175
|
|
|
|
|
|
|
$id =~ s/-//; |
176
|
0
|
|
|
|
|
0
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
return ($id && !$self->word_is_in_dictionary($id)); |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
0
|
|
|
|
|
0
|
# word_is_in_dictionary() |
182
|
|
|
|
|
|
|
# |
183
|
|
|
|
|
|
|
# See if the word looks like an English word, by checking if each triplet |
184
|
0
|
|
0
|
|
|
0
|
# of letters it contains is one that can be found in the English language. |
185
|
|
|
|
|
|
|
# Does not include triplets only found in proper names, or in the Latin |
186
|
|
|
|
|
|
|
# and Greek terms that might be found in a larger dictionary |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
my %triplets; |
189
|
|
|
|
|
|
|
my $triplets_loaded = 0; |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
my ($self, $word) = @_; |
192
|
|
|
|
|
|
|
local ($_); |
193
|
|
|
|
|
|
|
local $/ = "\n"; # Ensure $/ is set appropriately |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# $word =~ tr/A-Z/a-z/; # already done by this stage |
196
|
|
|
|
|
|
|
$word =~ s/^\s+//; |
197
|
|
|
|
|
|
|
$word =~ s/\s+$//; |
198
|
2
|
|
|
2
|
0
|
6
|
|
199
|
2
|
|
|
|
|
4
|
# If it contains a digit, dash, etc, it's not a valid word. |
200
|
2
|
|
|
|
|
7
|
# Don't reject words like "can't" and "I'll" |
201
|
|
|
|
|
|
|
return 0 if ($word =~ /[^a-z\']/); |
202
|
|
|
|
|
|
|
|
203
|
2
|
|
|
|
|
8
|
# handle a few common "blah blah blah (comment)" styles |
204
|
2
|
|
|
|
|
6
|
return 1 if ($word eq "ot"); # off-topic |
205
|
|
|
|
|
|
|
return 1 if ($word =~ /(?:linux|nix|bsd)/); # not in most dicts |
206
|
|
|
|
|
|
|
return 1 if ($word =~ /(?:whew|phew|attn|tha?nx)/); # not in most dicts |
207
|
|
|
|
|
|
|
|
208
|
2
|
50
|
|
|
|
7
|
my $word_len = length($word); |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# Unique IDs probably aren't going to be only one or two letters long |
211
|
2
|
50
|
|
|
|
7
|
return 1 if ($word_len < 3); |
212
|
2
|
50
|
|
|
|
17
|
|
213
|
2
|
50
|
|
|
|
8
|
if (!$triplets_loaded) { |
214
|
|
|
|
|
|
|
# take a copy to avoid modifying the real one |
215
|
2
|
|
|
|
|
5
|
my @default_triplets_path = @Mail::SpamAssassin::default_rules_path; |
216
|
|
|
|
|
|
|
s{$}{/triplets.txt} for @default_triplets_path; |
217
|
|
|
|
|
|
|
my $filename = $self->{main}->first_existing_path (@default_triplets_path); |
218
|
2
|
50
|
|
|
|
6
|
|
219
|
|
|
|
|
|
|
if (!defined $filename) { |
220
|
2
|
50
|
|
|
|
5
|
dbg("eval: failed to locate the triplets.txt file"); |
221
|
|
|
|
|
|
|
return 1; |
222
|
2
|
|
|
|
|
9
|
} |
223
|
2
|
|
|
|
|
20
|
|
224
|
2
|
|
|
|
|
14
|
local *TRIPLETS; |
225
|
|
|
|
|
|
|
if (!open (TRIPLETS, "<$filename")) { |
226
|
2
|
50
|
|
|
|
7
|
dbg("eval: failed to open '$filename', cannot check dictionary: $!"); |
227
|
2
|
|
|
|
|
7
|
return 1; |
228
|
2
|
|
|
|
|
12
|
} |
229
|
|
|
|
|
|
|
for($!=0; <TRIPLETS>; $!=0) { |
230
|
|
|
|
|
|
|
chomp; |
231
|
0
|
|
|
|
|
0
|
$triplets{$_} = 1; |
232
|
0
|
0
|
|
|
|
0
|
} |
233
|
0
|
|
|
|
|
0
|
defined $_ || $!==0 or |
234
|
0
|
|
|
|
|
0
|
$!==EBADF ? dbg("eval: error reading from $filename: $!") |
235
|
|
|
|
|
|
|
: die "error reading from $filename: $!"; |
236
|
0
|
|
|
|
|
0
|
close(TRIPLETS) or die "error closing $filename: $!"; |
237
|
0
|
|
|
|
|
0
|
|
238
|
0
|
|
|
|
|
0
|
$triplets_loaded = 1; |
239
|
|
|
|
|
|
|
} # if (!$triplets_loaded) |
240
|
0
|
0
|
0
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
my $i; |
243
|
0
|
0
|
|
|
|
0
|
|
244
|
|
|
|
|
|
|
for ($i = 0; $i < ($word_len - 2); $i++) { |
245
|
0
|
|
|
|
|
0
|
my $triplet = substr($word, $i, 3); |
246
|
|
|
|
|
|
|
if (!$triplets{$triplet}) { |
247
|
|
|
|
|
|
|
dbg("eval: unique ID: letter triplet '$triplet' from word '$word' not valid"); |
248
|
|
|
|
|
|
|
return 0; |
249
|
0
|
|
|
|
|
0
|
} |
250
|
|
|
|
|
|
|
} # for ($i = 0; $i < ($word_len - 2); $i++) |
251
|
0
|
|
|
|
|
0
|
|
252
|
0
|
|
|
|
|
0
|
# All letter triplets in word were found to be valid |
253
|
0
|
0
|
|
|
|
0
|
return 1; |
254
|
0
|
|
|
|
|
0
|
} |
255
|
0
|
|
|
|
|
0
|
|
256
|
|
|
|
|
|
|
# look for 8-bit and other illegal characters that should be MIME |
257
|
|
|
|
|
|
|
# encoded, these might want to exempt languages that do not use |
258
|
|
|
|
|
|
|
# Latin-based alphabets, but only if the user wants it that way |
259
|
|
|
|
|
|
|
my ($self, $pms, $header, $ratio, $count) = @_; |
260
|
0
|
|
|
|
|
0
|
|
261
|
|
|
|
|
|
|
$header .= ":raw" unless $header =~ /:raw$/; |
262
|
|
|
|
|
|
|
my $str = $pms->get($header); |
263
|
|
|
|
|
|
|
return 0 if !defined $str || $str eq ''; |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
# count illegal substrings (RFC 2045) |
266
|
|
|
|
|
|
|
# (non-ASCII + C0 controls except TAB, NL, CR) |
267
|
0
|
|
|
0
|
0
|
0
|
my $illegal = $str =~ tr/\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\xff//; |
268
|
|
|
|
|
|
|
|
269
|
0
|
0
|
|
|
|
0
|
# minor exemptions for Subject |
270
|
0
|
|
|
|
|
0
|
if ($illegal > 0 && lc $header eq "subject:raw") { |
271
|
0
|
0
|
0
|
|
|
0
|
# only exempt a single cent sign, pound sign, or registered sign |
272
|
|
|
|
|
|
|
my $exempt = $str =~ tr/\xa2\xa3\xae//; |
273
|
|
|
|
|
|
|
$illegal-- if $exempt == 1; |
274
|
|
|
|
|
|
|
} |
275
|
0
|
|
|
|
|
0
|
|
276
|
|
|
|
|
|
|
return 0 if $str eq ''; |
277
|
|
|
|
|
|
|
return (($illegal / length($str)) >= $ratio && $illegal >= $count); |
278
|
0
|
0
|
0
|
|
|
0
|
} |
279
|
|
|
|
|
|
|
|
280
|
0
|
|
|
|
|
0
|
# ezmlm has a very bad habit of removing Received: headers! bad ezmlm. |
281
|
0
|
0
|
|
|
|
0
|
# |
282
|
|
|
|
|
|
|
my ($self, $pms) = @_; |
283
|
|
|
|
|
|
|
|
284
|
0
|
0
|
|
|
|
0
|
my $txt = $pms->get("Mailing-List",undef); |
285
|
0
|
|
0
|
|
|
0
|
if (defined $txt && $txt =~ /^contact \S+\@\S+\; run by ezmlm$/) { |
286
|
|
|
|
|
|
|
my $dlto = $pms->get("Delivered-To"); |
287
|
|
|
|
|
|
|
my $rcvd = $pms->get("Received"); |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
# ensure we have other indicative headers too |
290
|
|
|
|
|
|
|
if ($dlto =~ /^mailing list \S+\@\S+/ && |
291
|
0
|
|
|
0
|
0
|
0
|
$rcvd =~ /qmail \d+ invoked (?:from network|by .{3,20})\); \d+ ... \d+/) |
292
|
|
|
|
|
|
|
{ |
293
|
0
|
|
|
|
|
0
|
return 1; |
294
|
0
|
0
|
0
|
|
|
0
|
} |
295
|
0
|
|
|
|
|
0
|
} |
296
|
0
|
|
|
|
|
0
|
|
297
|
|
|
|
|
|
|
my $rcvd = $pms->get("Received",undef); |
298
|
|
|
|
|
|
|
if (!defined $rcvd) { |
299
|
0
|
0
|
0
|
|
|
0
|
# we have no Received headers! These tests cannot run in that case |
300
|
|
|
|
|
|
|
return 1; |
301
|
|
|
|
|
|
|
} |
302
|
0
|
|
|
|
|
0
|
|
303
|
|
|
|
|
|
|
# MSN groups removes Received lines. thanks MSN |
304
|
|
|
|
|
|
|
if ($rcvd =~ /from groups\.msn\.com \(\S+\.msn\.com /) { |
305
|
|
|
|
|
|
|
return 1; |
306
|
0
|
|
|
|
|
0
|
} |
307
|
0
|
0
|
|
|
|
0
|
|
308
|
|
|
|
|
|
|
return 0; |
309
|
0
|
|
|
|
|
0
|
} |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
# FORGED_HOTMAIL_RCVD |
312
|
|
|
|
|
|
|
my ($self, $pms) = @_; |
313
|
0
|
0
|
|
|
|
0
|
|
314
|
0
|
|
|
|
|
0
|
if (defined $pms->{hotmail_addr_but_no_hotmail_received}) { return; } |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
$pms->{hotmail_addr_with_forged_hotmail_received} = 0; |
317
|
0
|
|
|
|
|
0
|
$pms->{hotmail_addr_but_no_hotmail_received} = 0; |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
my $rcvd = $pms->get('Received'); |
320
|
|
|
|
|
|
|
$rcvd =~ s/\s+/ /gs; # just spaces, simplify the regexp |
321
|
|
|
|
|
|
|
|
322
|
0
|
|
|
0
|
|
0
|
return if ($rcvd =~ |
323
|
|
|
|
|
|
|
/from mail pickup service by hotmail\.com with Microsoft SMTPSVC;/); |
324
|
0
|
0
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
325
|
|
|
|
|
|
|
# Microsoft passes Hotmail mail directly to MSN Group servers. |
326
|
0
|
|
|
|
|
0
|
return if $self->check_for_msn_groups_headers($pms); |
327
|
0
|
|
|
|
|
0
|
|
328
|
|
|
|
|
|
|
my $ip = $pms->get('X-Originating-Ip',undef); |
329
|
0
|
|
|
|
|
0
|
my $IP_ADDRESS = IP_ADDRESS; |
330
|
0
|
|
|
|
|
0
|
my $orig = $pms->get('X-OriginatorOrg',undef); |
331
|
|
|
|
|
|
|
my $ORIGINATOR = 'hotmail.com'; |
332
|
0
|
0
|
|
|
|
0
|
|
333
|
|
|
|
|
|
|
if (defined $ip && $ip =~ /$IP_ADDRESS/) { $ip = 1; } else { $ip = 0; } |
334
|
|
|
|
|
|
|
if (defined $orig && $orig =~ /$ORIGINATOR/) { $orig = 1; } else { $orig = 0; } |
335
|
|
|
|
|
|
|
|
336
|
0
|
0
|
|
|
|
0
|
# Hotmail formats its received headers like this: |
337
|
|
|
|
|
|
|
# Received: from hotmail.com (f135.law8.hotmail.com [216.33.241.135]) |
338
|
0
|
|
|
|
|
0
|
# or like |
339
|
0
|
|
|
|
|
0
|
# Received: from EUR01-VE1-obe.outbound.protection.outlook.com (mail-oln040092066056.outbound.protection.outlook.com [40.92.66.56]) |
340
|
0
|
|
|
|
|
0
|
# spammers do not ;) |
341
|
0
|
|
|
|
|
0
|
|
342
|
|
|
|
|
|
|
if ($self->gated_through_received_hdr_remover($pms)) { return; } |
343
|
0
|
0
|
0
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
344
|
0
|
0
|
0
|
|
|
0
|
if ($rcvd =~ /from (?:\S*\.)?hotmail.com \(\S+\.hotmail(?:\.msn)?\.com[ \)]/ && $ip) |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
345
|
|
|
|
|
|
|
{ return; } |
346
|
|
|
|
|
|
|
if ($rcvd =~ /from \S*\.outbound\.protection\.outlook\.com \(\S+\.outbound\.protection\.outlook\.com[ \)]/ && $orig) |
347
|
|
|
|
|
|
|
{ return; } |
348
|
|
|
|
|
|
|
if ($rcvd =~ /from \S*\.hotmail.com \(\[$IP_ADDRESS\][ \):]/ && $ip) |
349
|
|
|
|
|
|
|
{ return; } |
350
|
|
|
|
|
|
|
if ($rcvd =~ /from \S+ by \S+\.hotmail(?:\.msn)?\.com with HTTP\;/ && $ip) |
351
|
|
|
|
|
|
|
{ return; } |
352
|
0
|
0
|
|
|
|
0
|
if ($rcvd =~ /from \[66\.218.\S+\] by \S+\.yahoo\.com/ && $ip) |
|
0
|
|
|
|
|
0
|
|
353
|
|
|
|
|
|
|
{ return; } |
354
|
0
|
0
|
0
|
|
|
0
|
|
355
|
0
|
|
|
|
|
0
|
if ($rcvd =~ /(?:from |HELO |helo=)\S*hotmail\.com\b/) { |
356
|
0
|
0
|
0
|
|
|
0
|
# HELO'd as hotmail.com, despite not being hotmail |
357
|
0
|
|
|
|
|
0
|
$pms->{hotmail_addr_with_forged_hotmail_received} = 1; |
358
|
0
|
0
|
0
|
|
|
0
|
} else { |
359
|
0
|
|
|
|
|
0
|
# check to see if From claimed to be @hotmail.com |
360
|
0
|
0
|
0
|
|
|
0
|
my $from = $pms->get('From:addr'); |
361
|
0
|
|
|
|
|
0
|
if ($from !~ /\bhotmail\.com$/i) { return; } |
362
|
0
|
0
|
0
|
|
|
0
|
$pms->{hotmail_addr_but_no_hotmail_received} = 1; |
363
|
0
|
|
|
|
|
0
|
} |
364
|
|
|
|
|
|
|
} |
365
|
0
|
0
|
|
|
|
0
|
|
366
|
|
|
|
|
|
|
# FORGED_HOTMAIL_RCVD |
367
|
0
|
|
|
|
|
0
|
my ($self, $pms) = @_; |
368
|
|
|
|
|
|
|
$self->_check_for_forged_hotmail_received_headers($pms); |
369
|
|
|
|
|
|
|
return $pms->{hotmail_addr_with_forged_hotmail_received}; |
370
|
0
|
|
|
|
|
0
|
} |
371
|
0
|
0
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
372
|
0
|
|
|
|
|
0
|
# SEMIFORGED_HOTMAIL_RCVD |
373
|
|
|
|
|
|
|
my ($self, $pms) = @_; |
374
|
|
|
|
|
|
|
$self->_check_for_forged_hotmail_received_headers($pms); |
375
|
|
|
|
|
|
|
return $pms->{hotmail_addr_but_no_hotmail_received}; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
|
378
|
0
|
|
|
0
|
0
|
0
|
# MSN_GROUPS |
379
|
0
|
|
|
|
|
0
|
my ($self, $pms) = @_; |
380
|
0
|
|
|
|
|
0
|
|
381
|
|
|
|
|
|
|
my $to = $pms->get('To'); |
382
|
|
|
|
|
|
|
return 0 unless $to =~ /<(\S+)\@groups\.msn\.com>/i; |
383
|
|
|
|
|
|
|
my $listname = $1; |
384
|
|
|
|
|
|
|
|
385
|
0
|
|
|
0
|
0
|
0
|
# from Theo Van Dinter, see bug 591 |
386
|
0
|
|
|
|
|
0
|
# Updated by DOS, based on messages from Bob Menschel, bug 4301 |
387
|
0
|
|
|
|
|
0
|
|
388
|
|
|
|
|
|
|
return 0 unless $pms->get('Received') =~ |
389
|
|
|
|
|
|
|
/from mail pickup service by ((?:p\d\d\.)groups\.msn\.com)\b/; |
390
|
|
|
|
|
|
|
my $server = $1; |
391
|
|
|
|
|
|
|
|
392
|
0
|
|
|
0
|
0
|
0
|
if ($listname =~ /^notifications$/) { |
393
|
|
|
|
|
|
|
return 0 unless $pms->get('Message-Id') =~ /^<\S+\@$server>/; |
394
|
0
|
|
|
|
|
0
|
} else { |
395
|
0
|
0
|
|
|
|
0
|
return 0 unless $pms->get('Message-Id') =~ /^<$listname-\S+\@groups\.msn\.com>/; |
396
|
0
|
|
|
|
|
0
|
return 0 unless $pms->get('EnvelopeFrom:addr') =~ /$listname-bounce\@groups\.msn\.com/; |
397
|
|
|
|
|
|
|
} |
398
|
|
|
|
|
|
|
return 1; |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
# MSN Groups |
401
|
0
|
0
|
|
|
|
0
|
# Return-path: <ListName-bounce@groups.msn.com> |
402
|
|
|
|
|
|
|
# Received: from groups.msn.com (tk2dcpuba02.msn.com [65.54.195.210]) by |
403
|
0
|
|
|
|
|
0
|
# dogma.slashnull.org (8.11.6/8.11.6) with ESMTP id g72K35v10457 for |
404
|
|
|
|
|
|
|
# <zzzzzzzzzzzz@jmason.org>; Fri, 2 Aug 2002 21:03:05 +0100 |
405
|
0
|
0
|
|
|
|
0
|
# Received: from mail pickup service by groups.msn.com with Microsoft |
406
|
0
|
0
|
|
|
|
0
|
# SMTPSVC; Fri, 2 Aug 2002 13:01:30 -0700 |
407
|
|
|
|
|
|
|
# Message-id: <ListName-1392@groups.msn.com> |
408
|
0
|
0
|
|
|
|
0
|
# X-loop: notifications@groups.msn.com |
409
|
0
|
0
|
|
|
|
0
|
# Reply-to: "List Full Name" <ListName@groups.msn.com> |
410
|
|
|
|
|
|
|
# To: "List Full Name" <ListName@groups.msn.com> |
411
|
0
|
|
|
|
|
0
|
|
412
|
|
|
|
|
|
|
# Return-path: <ListName-bounce@groups.msn.com> |
413
|
|
|
|
|
|
|
# Received: from p04.groups.msn.com ([65.54.195.216]) etc... |
414
|
|
|
|
|
|
|
# Received: from mail pickup service by p04.groups.msn.com with Microsoft SMTPSVC; |
415
|
|
|
|
|
|
|
# Thu, 5 May 2005 20:30:37 -0700 |
416
|
|
|
|
|
|
|
# X-Originating-Ip: 207.68.170.30 |
417
|
|
|
|
|
|
|
# From: =?iso-8859-1?B?IqSj4/D9pEbzeN9s9vLw6qQiIA==?=<zzzzzzzz@hotmail.com> |
418
|
|
|
|
|
|
|
# To: "Managers of List Name" <notifications@groups.msn.com> |
419
|
|
|
|
|
|
|
# Subject: =?iso-8859-1?Q?APPROVAL_NEEDED:_=A4=A3=E3=F0=FD=A4F=F3x=DFl?= |
420
|
|
|
|
|
|
|
# =?iso-8859-1?Q?=F6=F2=F0=EA=A4_applied_to_join_List_Name=2C?= |
421
|
|
|
|
|
|
|
# =?iso-8859-1?Q?_an_MSN_Group?= |
422
|
|
|
|
|
|
|
# Date: Thu, 5 May 2005 20:30:37 -0700 |
423
|
|
|
|
|
|
|
# MIME-Version: 1.0 |
424
|
|
|
|
|
|
|
# Content-Type: multipart/alternative; |
425
|
|
|
|
|
|
|
# boundary="----=_NextPart_000_333944_01C551B1.4BBA02B0" |
426
|
|
|
|
|
|
|
# X-MimeOLE: Produced By Microsoft MimeOLE V5.50.4927.1200 |
427
|
|
|
|
|
|
|
# Message-ID: <TK2DCPUBA042cv0aGlt00020aa3@p04.groups.msn.com> |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
# Return-path: <ListName-bounce@groups.msn.com> |
430
|
|
|
|
|
|
|
# Received: from [65.54.208.83] (helo=p05.groups.msn.com) etc... |
431
|
|
|
|
|
|
|
# Received: from mail pickup service by p05.groups.msn.com with Microsoft SMTPSVC; |
432
|
|
|
|
|
|
|
# Fri, 6 May 2005 14:59:25 -0700 |
433
|
|
|
|
|
|
|
# X-Originating-Ip: 207.68.170.30 |
434
|
|
|
|
|
|
|
# Message-Id: <ListName-101@groups.msn.com> |
435
|
|
|
|
|
|
|
# Reply-To: "List Name" <ListName@groups.msn.com> |
436
|
|
|
|
|
|
|
# From: "whoever" <zzzzzzzzzz@hotmail.com> |
437
|
|
|
|
|
|
|
# To: "List Name" <ListName@groups.msn.com> |
438
|
|
|
|
|
|
|
# Subject: whatever |
439
|
|
|
|
|
|
|
# Date: Fri, 6 May 2005 14:59:25 -0700 |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
########################################################################### |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
my ($self, $pms) = @_; |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
my $from = $pms->get('From:addr'); |
448
|
|
|
|
|
|
|
if ($from !~ /\beudoramail\.com$/i) { return 0; } |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
my $rcvd = $pms->get('Received'); |
451
|
|
|
|
|
|
|
$rcvd =~ s/\s+/ /gs; # just spaces, simplify the regexp |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
my $ip = $pms->get('X-Sender-Ip',undef); |
454
|
|
|
|
|
|
|
my $IP_ADDRESS = IP_ADDRESS; |
455
|
|
|
|
|
|
|
if (defined $ip && $ip =~ /$IP_ADDRESS/) { $ip = 1; } else { $ip = 0; } |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
# Eudoramail formats its received headers like this: |
458
|
|
|
|
|
|
|
# Received: from Unknown/Local ([?.?.?.?]) by shared1-mail.whowhere.com; |
459
|
0
|
|
|
0
|
0
|
0
|
# Thu Nov 29 13:44:25 2001 |
460
|
|
|
|
|
|
|
# Message-Id: <JGDHDEHPPJECDAAA@shared1-mail.whowhere.com> |
461
|
0
|
|
|
|
|
0
|
# Organization: QUALCOMM Eudora Web-Mail (http://www.eudoramail.com:80) |
462
|
0
|
0
|
|
|
|
0
|
# X-Sender-Ip: 192.175.21.146 |
|
0
|
|
|
|
|
0
|
|
463
|
|
|
|
|
|
|
# X-Mailer: MailCity Service |
464
|
0
|
|
|
|
|
0
|
|
465
|
0
|
|
|
|
|
0
|
if ($self->gated_through_received_hdr_remover($pms)) { return 0; } |
466
|
|
|
|
|
|
|
|
467
|
0
|
|
|
|
|
0
|
if ($rcvd =~ /by \S*whowhere.com\;/ && $ip) { return 0; } |
468
|
0
|
|
|
|
|
0
|
|
469
|
0
|
0
|
0
|
|
|
0
|
return 1; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
470
|
|
|
|
|
|
|
} |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
########################################################################### |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
my ($self, $pms) = @_; |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
my $from = $pms->get('From:addr'); |
477
|
|
|
|
|
|
|
if ($from !~ /\byahoo\.com$/i) { return 0; } |
478
|
|
|
|
|
|
|
|
479
|
0
|
0
|
|
|
|
0
|
my $rcvd = $pms->get('Received'); |
|
0
|
|
|
|
|
0
|
|
480
|
|
|
|
|
|
|
|
481
|
0
|
0
|
0
|
|
|
0
|
if ($pms->get("Resent-From") ne '' && $pms->get("Resent-To") ne '') { |
|
0
|
|
|
|
|
0
|
|
482
|
|
|
|
|
|
|
my $xrcvd = $pms->get("X-Received"); |
483
|
0
|
|
|
|
|
0
|
$rcvd = $xrcvd if $xrcvd ne ''; |
484
|
|
|
|
|
|
|
} |
485
|
|
|
|
|
|
|
$rcvd =~ s/\s+/ /gs; # just spaces, simplify the regexp |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
# not sure about this |
488
|
|
|
|
|
|
|
#if ($rcvd !~ /from \S*yahoo\.com/) { return 0; } |
489
|
81
|
|
|
81
|
0
|
188
|
|
490
|
|
|
|
|
|
|
if ($self->gated_through_received_hdr_remover($pms)) { return 0; } |
491
|
81
|
|
|
|
|
278
|
|
492
|
81
|
50
|
|
|
|
345
|
# bug 3740: ignore bounces from Yahoo!. only honoured if the |
|
81
|
|
|
|
|
1220
|
|
493
|
|
|
|
|
|
|
# correct rDNS shows up in the trusted relay list, or first untrusted relay |
494
|
0
|
|
|
|
|
0
|
# |
495
|
|
|
|
|
|
|
# bug 4528: [ ip=68.142.202.54 rdns=mta122.mail.mud.yahoo.com |
496
|
0
|
0
|
0
|
|
|
0
|
# helo=mta122.mail.mud.yahoo.com by=eclectic.kluge.net ident= |
497
|
0
|
|
|
|
|
0
|
# envfrom= intl=0 id=49F2EAF13B auth= ] |
498
|
0
|
0
|
|
|
|
0
|
# |
499
|
|
|
|
|
|
|
if ($pms->{relays_trusted_str} =~ / rdns=\S+\.yahoo\.com / |
500
|
0
|
|
|
|
|
0
|
|| $pms->{relays_untrusted_str} =~ /^[^\]]+ rdns=\S+\.yahoo\.com /) |
501
|
|
|
|
|
|
|
{ return 0; } |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
if ($rcvd =~ /by web\S+\.mail\S*\.yahoo\.com via HTTP/) { return 0; } |
504
|
|
|
|
|
|
|
if ($rcvd =~ /by sonic\S+\.consmr\.mail\S*\.yahoo\.com with HTTP/) { return 0; } |
505
|
0
|
0
|
|
|
|
0
|
if ($rcvd =~ /by smtp\S+\.yahoo\.com with SMTP/) { return 0; } |
|
0
|
|
|
|
|
0
|
|
506
|
|
|
|
|
|
|
my $IP_ADDRESS = IP_ADDRESS; |
507
|
|
|
|
|
|
|
if ($rcvd =~ |
508
|
|
|
|
|
|
|
/from \[$IP_ADDRESS\] by \S+\.(?:groups|scd|dcn)\.yahoo\.com with NNFMP/) { |
509
|
|
|
|
|
|
|
return 0; |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
# used in "forward this news item to a friend" links. There's no better |
513
|
|
|
|
|
|
|
# received hdrs to match on, unfortunately. I'm not sure if the next test is |
514
|
0
|
0
|
0
|
|
|
0
|
# still useful, as a result. |
515
|
|
|
|
|
|
|
# |
516
|
0
|
|
|
|
|
0
|
# search for msgid <20020929140301.451A92940A9@xent.com>, subject "Yahoo! |
517
|
|
|
|
|
|
|
# News Story - Top Stories", date Sep 29 2002 on |
518
|
0
|
0
|
|
|
|
0
|
# <http://xent.com/pipermail/fork/> for an example. |
|
0
|
|
|
|
|
0
|
|
519
|
0
|
0
|
|
|
|
0
|
# |
|
0
|
|
|
|
|
0
|
|
520
|
0
|
0
|
|
|
|
0
|
if ($rcvd =~ /\bmailer\d+\.bulk\.scd\.yahoo\.com\b/ |
|
0
|
|
|
|
|
0
|
|
521
|
0
|
|
|
|
|
0
|
&& $from =~ /\@reply\.yahoo\.com$/i) { return 0; } |
522
|
0
|
0
|
|
|
|
0
|
|
523
|
|
|
|
|
|
|
if ($rcvd =~ /by \w+\.\w+\.yahoo\.com \(\d+\.\d+\.\d+\/\d+\.\d+\.\d+\)(?: with ESMTP)? id \w+/) { |
524
|
0
|
|
|
|
|
0
|
# possibly sent from "mail this story to a friend" |
525
|
|
|
|
|
|
|
return 0; |
526
|
|
|
|
|
|
|
} |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
return 1; |
529
|
|
|
|
|
|
|
} |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
my ($self, $pms) = @_; |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
my $from = $pms->get('From:addr'); |
534
|
|
|
|
|
|
|
if ($from !~ /\bjuno\.com$/i) { return 0; } |
535
|
0
|
0
|
0
|
|
|
0
|
|
536
|
0
|
|
|
|
|
0
|
if ($self->gated_through_received_hdr_remover($pms)) { return 0; } |
537
|
|
|
|
|
|
|
|
538
|
0
|
0
|
|
|
|
0
|
my $xorig = $pms->get('X-Originating-IP'); |
539
|
|
|
|
|
|
|
my $xmailer = $pms->get('X-Mailer'); |
540
|
0
|
|
|
|
|
0
|
my $rcvd = $pms->get('Received'); |
541
|
|
|
|
|
|
|
my $IP_ADDRESS = IP_ADDRESS; |
542
|
|
|
|
|
|
|
|
543
|
0
|
|
|
|
|
0
|
if ($xorig ne '') { |
544
|
|
|
|
|
|
|
# New style Juno has no X-Originating-IP header, and other changes |
545
|
|
|
|
|
|
|
if($rcvd !~ /from.*\b(?:juno|untd)\.com.*[\[\(]$IP_ADDRESS[\]\)].*by/ |
546
|
|
|
|
|
|
|
&& $rcvd !~ / cookie\.(?:juno|untd)\.com /) { return 1; } |
547
|
0
|
|
|
0
|
0
|
0
|
if($xmailer !~ /Juno /) { return 1; } |
548
|
|
|
|
|
|
|
} else { |
549
|
0
|
|
|
|
|
0
|
if($rcvd =~ /from.*\bmail\.com.*\[$IP_ADDRESS\].*by/) { |
550
|
0
|
0
|
|
|
|
0
|
if($xmailer !~ /\bmail\.com/) { return 1; } |
|
0
|
|
|
|
|
0
|
|
551
|
|
|
|
|
|
|
} elsif($rcvd =~ /from (webmail\S+\.untd\.com) \(\1 \[$IP_ADDRESS\]\) by/) { |
552
|
0
|
0
|
|
|
|
0
|
if($xmailer !~ /^Webmail Version \d/) { return 1; } |
|
0
|
|
|
|
|
0
|
|
553
|
|
|
|
|
|
|
} else { |
554
|
0
|
|
|
|
|
0
|
return 1; |
555
|
0
|
|
|
|
|
0
|
} |
556
|
0
|
|
|
|
|
0
|
if($xorig !~ /$IP_ADDRESS/) { return 1; } |
557
|
0
|
|
|
|
|
0
|
} |
558
|
|
|
|
|
|
|
|
559
|
0
|
0
|
|
|
|
0
|
return 0; |
560
|
|
|
|
|
|
|
} |
561
|
0
|
0
|
0
|
|
|
0
|
|
562
|
0
|
|
|
|
|
0
|
my ($self, $pms) = @_; |
563
|
0
|
0
|
|
|
|
0
|
use constant GOOGLE_MESSAGE_STATE_LENGTH_MIN => 60; |
|
0
|
|
|
|
|
0
|
|
564
|
|
|
|
|
|
|
use constant GOOGLE_SMTP_SOURCE_LENGTH_MIN => 60; |
565
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
566
|
0
|
0
|
|
|
|
0
|
my $from = $pms->get('From:addr'); |
|
0
|
|
|
|
|
0
|
|
567
|
|
|
|
|
|
|
if ($from !~ /\bgmail\.com$/i) { return 0; } |
568
|
0
|
0
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
569
|
|
|
|
|
|
|
my $xgms = $pms->get('X-Gm-Message-State'); |
570
|
0
|
|
|
|
|
0
|
my $xss = $pms->get('X-Google-Smtp-Source'); |
571
|
|
|
|
|
|
|
my $xreceived = $pms->get('X-Received'); |
572
|
0
|
0
|
|
|
|
0
|
my $received = $pms->get('Received'); |
|
0
|
|
|
|
|
0
|
|
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
if ($xreceived =~ /by 10\.\S+ with SMTP id \S+/) { return 0; } |
575
|
0
|
|
|
|
|
0
|
if ($xreceived =~ /by 2002\:a\d\d\:\w+\:\S+ with SMTP id \S+/) { return 0; } |
576
|
|
|
|
|
|
|
if ($received =~ /by smtp\.googlemail\.com with ESMTPSA id \S+/) { |
577
|
|
|
|
|
|
|
return 0; |
578
|
|
|
|
|
|
|
} |
579
|
0
|
|
|
0
|
0
|
0
|
if ( (length($xgms) >= GOOGLE_MESSAGE_STATE_LENGTH_MIN) && |
580
|
22
|
|
|
22
|
|
246
|
(length($xss) >= GOOGLE_SMTP_SOURCE_LENGTH_MIN)) { |
|
22
|
|
|
|
|
79
|
|
|
22
|
|
|
|
|
7345
|
|
581
|
22
|
|
|
22
|
|
159
|
return 0; |
|
22
|
|
|
|
|
54
|
|
|
22
|
|
|
|
|
8375
|
|
582
|
|
|
|
|
|
|
} |
583
|
0
|
|
|
|
|
0
|
|
584
|
0
|
0
|
|
|
|
0
|
return 1; |
|
0
|
|
|
|
|
0
|
|
585
|
|
|
|
|
|
|
} |
586
|
0
|
|
|
|
|
0
|
|
587
|
0
|
|
|
|
|
0
|
my ($self, $pms) =@_; |
588
|
0
|
|
|
|
|
0
|
# two blank headers match so don't bother checking |
589
|
0
|
|
|
|
|
0
|
return (lc $pms->get('EnvelopeFrom:addr') eq lc $pms->get('From:addr')); |
590
|
|
|
|
|
|
|
} |
591
|
0
|
0
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
592
|
0
|
0
|
|
|
|
0
|
my ($self, $pms) = @_; |
|
0
|
|
|
|
|
0
|
|
593
|
0
|
0
|
|
|
|
0
|
|
594
|
0
|
|
|
|
|
0
|
if (!exists $pms->{tocc_sorted}) { |
595
|
|
|
|
|
|
|
$self->_check_recipients($pms); |
596
|
0
|
0
|
0
|
|
|
0
|
} |
597
|
|
|
|
|
|
|
return $pms->{tocc_sorted}; |
598
|
0
|
|
|
|
|
0
|
} |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
my ($self, $pms, $min, $max) = @_; |
601
|
0
|
|
|
|
|
0
|
|
602
|
|
|
|
|
|
|
if (!exists $pms->{tocc_similar}) { |
603
|
|
|
|
|
|
|
$self->_check_recipients($pms); |
604
|
|
|
|
|
|
|
} |
605
|
0
|
|
|
0
|
0
|
0
|
return (($min eq 'undef' || $pms->{tocc_similar} >= $min) && |
606
|
|
|
|
|
|
|
($max eq 'undef' || $pms->{tocc_similar} < $max)); |
607
|
0
|
|
|
|
|
0
|
} |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
# best experimentally derived values |
610
|
|
|
|
|
|
|
use constant TOCC_SORTED_COUNT => 7; |
611
|
81
|
|
|
81
|
0
|
196
|
use constant TOCC_SIMILAR_COUNT => 5; |
612
|
|
|
|
|
|
|
use constant TOCC_SIMILAR_LENGTH => 2; |
613
|
81
|
100
|
|
|
|
248
|
|
614
|
41
|
|
|
|
|
153
|
my ($self, $pms) = @_; |
615
|
|
|
|
|
|
|
|
616
|
81
|
|
|
|
|
1351
|
my @inputs; |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
# ToCc: pseudo-header works best, but sometimes Bcc: is better |
619
|
|
|
|
|
|
|
for ('ToCc', 'Bcc') { |
620
|
81
|
|
|
81
|
0
|
381
|
my $to = $pms->get($_); # get recipients |
621
|
|
|
|
|
|
|
$to =~ s/\(.*?\)//g; # strip out the (comments) |
622
|
81
|
100
|
|
|
|
285
|
push(@inputs, ($to =~ m/([\w.=-]+\@\w+(?:[\w.-]+\.)+\w+)/g)); |
623
|
40
|
|
|
|
|
156
|
last if scalar(@inputs) >= TOCC_SIMILAR_COUNT; |
624
|
|
|
|
|
|
|
} |
625
|
|
|
|
|
|
|
|
626
|
81
|
|
33
|
|
|
2031
|
# remove duplicate addresses only when they appear next to each other |
627
|
|
|
|
|
|
|
my @address; |
628
|
|
|
|
|
|
|
my $previous = ''; |
629
|
|
|
|
|
|
|
while (my $current = shift @inputs) { |
630
|
22
|
|
|
22
|
|
198
|
push(@address, ($previous = $current)) if lc($current) ne lc($previous); |
|
22
|
|
|
|
|
45
|
|
|
22
|
|
|
|
|
1194
|
|
631
|
22
|
|
|
22
|
|
135
|
last if @address == 256; |
|
22
|
|
|
|
|
48
|
|
|
22
|
|
|
|
|
1040
|
|
632
|
22
|
|
|
22
|
|
161
|
} |
|
22
|
|
|
|
|
59
|
|
|
22
|
|
|
|
|
68449
|
|
633
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
# ideas that had both poor S/O ratios and poor hit rates: |
635
|
81
|
|
|
81
|
|
222
|
# - testing for reverse sorted recipient lists |
636
|
|
|
|
|
|
|
# - testing To: and Cc: headers separately |
637
|
81
|
|
|
|
|
131
|
$pms->{tocc_sorted} = (scalar(@address) >= TOCC_SORTED_COUNT && |
638
|
|
|
|
|
|
|
join(',', @address) eq (join(',', sort @address))); |
639
|
|
|
|
|
|
|
|
640
|
81
|
|
|
|
|
179
|
# a good S/O ratio and hit rate is achieved by comparing 2-byte |
641
|
161
|
|
|
|
|
367
|
# substrings and requiring 5 or more addresses |
642
|
161
|
|
|
|
|
302
|
$pms->{tocc_similar} = 0; |
643
|
161
|
|
|
|
|
442
|
if (scalar (@address) >= TOCC_SIMILAR_COUNT) { |
644
|
161
|
100
|
|
|
|
493
|
my @user = map { substr($_,0,TOCC_SIMILAR_LENGTH) } @address; |
645
|
|
|
|
|
|
|
my @fqhn = map { m/\@(.*)/ } @address; |
646
|
|
|
|
|
|
|
my @host = map { substr($_,0,TOCC_SIMILAR_LENGTH) } @fqhn; |
647
|
|
|
|
|
|
|
my $hits = 0; |
648
|
81
|
|
|
|
|
133
|
my $combinations = 0; |
649
|
81
|
|
|
|
|
152
|
for (my $i = 0; $i <= $#address; $i++) { |
650
|
81
|
|
|
|
|
259
|
for (my $j = $i+1; $j <= $#address; $j++) { |
651
|
55
|
50
|
|
|
|
163
|
$hits++ if $user[$i] eq $user[$j]; |
652
|
55
|
50
|
|
|
|
177
|
$hits++ if $host[$i] eq $host[$j] && $fqhn[$i] ne $fqhn[$j]; |
653
|
|
|
|
|
|
|
$combinations++; |
654
|
|
|
|
|
|
|
} |
655
|
|
|
|
|
|
|
} |
656
|
|
|
|
|
|
|
$pms->{tocc_similar} = $hits / $combinations; |
657
|
|
|
|
|
|
|
} |
658
|
81
|
|
66
|
|
|
371
|
} |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
my ($self, $pms) = @_; |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
my $hdr = $pms->get('To'); |
663
|
81
|
|
|
|
|
207
|
$hdr = $pms->get('Apparently-To') if $hdr eq ''; |
664
|
81
|
100
|
|
|
|
265
|
return 1 if $hdr eq ''; |
665
|
1
|
|
|
|
|
4
|
|
|
48
|
|
|
|
|
111
|
|
666
|
1
|
|
|
|
|
9
|
return 0; |
|
48
|
|
|
|
|
155
|
|
667
|
1
|
|
|
|
|
21
|
} |
|
48
|
|
|
|
|
101
|
|
668
|
1
|
|
|
|
|
7
|
|
669
|
1
|
|
|
|
|
3
|
my ($self, $pms) = @_; |
670
|
1
|
|
|
|
|
5
|
local ($_); |
671
|
48
|
|
|
|
|
87
|
|
672
|
1128
|
100
|
|
|
|
1737
|
my $rcv = $pms->get('Received'); |
673
|
1128
|
100
|
100
|
|
|
2780
|
|
674
|
1128
|
|
|
|
|
2035
|
# e.g. |
675
|
|
|
|
|
|
|
# Received: from mail3.icytundra.com by gw05 with ESMTP; Thu, 21 Jun 2001 02:28:32 -0400 |
676
|
|
|
|
|
|
|
my ($h1, $h2) = ($rcv =~ |
677
|
1
|
|
|
|
|
22
|
m/\nfrom\s(\S+)\sby\s(\S+)\swith\sESMTP\;\s+\S\S\S,\s+\d+\s+\S\S\S\s+ |
678
|
|
|
|
|
|
|
\d{4}\s+\d\d:\d\d:\d\d\s+[-+]*\d{4}\n$/xs); |
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
if (defined ($h1) && defined ($h2) && $h2 !~ /\./) { |
681
|
|
|
|
|
|
|
return 1; |
682
|
0
|
|
|
0
|
0
|
0
|
} |
683
|
|
|
|
|
|
|
|
684
|
0
|
|
|
|
|
0
|
0; |
685
|
0
|
0
|
|
|
|
0
|
} |
686
|
0
|
0
|
|
|
|
0
|
|
687
|
|
|
|
|
|
|
########################################################################### |
688
|
0
|
|
|
|
|
0
|
|
689
|
|
|
|
|
|
|
my ($self, $pms, $min, $max) = @_; |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
if (!exists $pms->{date_diff}) { |
692
|
0
|
|
|
0
|
0
|
0
|
$self->_check_date_diff($pms); |
693
|
0
|
|
|
|
|
0
|
} |
694
|
|
|
|
|
|
|
return (($min eq 'undef' || $pms->{date_diff} >= (3600 * $min)) && |
695
|
0
|
|
|
|
|
0
|
($max eq 'undef' || $pms->{date_diff} < (3600 * $max))); |
696
|
|
|
|
|
|
|
} |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
# filters out some false positives in old corpus mail - Allen |
699
|
0
|
|
|
|
|
0
|
my ($self,$pms,$min,$max) = @_; |
700
|
|
|
|
|
|
|
|
701
|
|
|
|
|
|
|
if (!exists($pms->{date_received})) { |
702
|
|
|
|
|
|
|
$self->_check_date_received($pms); |
703
|
0
|
0
|
0
|
|
|
0
|
} |
|
|
|
0
|
|
|
|
|
704
|
0
|
|
|
|
|
0
|
my $diff = time() - $pms->{date_received}; |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
# 365.2425 * 24 * 60 * 60 = 31556952 = seconds in year (including leap) |
707
|
0
|
|
|
|
|
0
|
|
708
|
|
|
|
|
|
|
if (((! defined($min)) || ($min eq 'undef') || |
709
|
|
|
|
|
|
|
($diff >= (31556952 * ($min/12)))) && |
710
|
|
|
|
|
|
|
((! defined($max)) || ($max eq 'undef') || |
711
|
|
|
|
|
|
|
($diff < (31556952 * ($max/12))))) { |
712
|
|
|
|
|
|
|
return 1; |
713
|
0
|
|
|
0
|
0
|
0
|
} else { |
714
|
|
|
|
|
|
|
return 0; |
715
|
0
|
0
|
|
|
|
0
|
} |
716
|
0
|
|
|
|
|
0
|
} |
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
my ($self, $pms) = @_; |
719
|
0
|
|
0
|
|
|
0
|
|
720
|
|
|
|
|
|
|
my $time; |
721
|
|
|
|
|
|
|
# a Resent-Date: header takes precedence over any Date: header |
722
|
|
|
|
|
|
|
DATE: for my $header ('Resent-Date', 'Date') { |
723
|
|
|
|
|
|
|
my @dates = $pms->{msg}->get_header($header); |
724
|
0
|
|
|
0
|
0
|
0
|
for my $date (@dates) { |
725
|
|
|
|
|
|
|
if (defined($date) && length($date)) { |
726
|
0
|
0
|
|
|
|
0
|
chomp($date); |
727
|
0
|
|
|
|
|
0
|
$time = parse_rfc822_date($date); |
728
|
|
|
|
|
|
|
} |
729
|
0
|
|
|
|
|
0
|
last DATE if defined($time); |
730
|
|
|
|
|
|
|
} |
731
|
|
|
|
|
|
|
} |
732
|
|
|
|
|
|
|
if (defined($time)) { |
733
|
0
|
0
|
0
|
|
|
0
|
$pms->{date_header_time} = $time; |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
734
|
|
|
|
|
|
|
} |
735
|
|
|
|
|
|
|
else { |
736
|
|
|
|
|
|
|
$pms->{date_header_time} = undef; |
737
|
0
|
|
|
|
|
0
|
} |
738
|
|
|
|
|
|
|
} |
739
|
0
|
|
|
|
|
0
|
|
740
|
|
|
|
|
|
|
my ($self, $pms) = @_; |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
$pms->{received_header_times} = [ () ]; |
743
|
|
|
|
|
|
|
$pms->{received_fetchmail_time} = undef; |
744
|
0
|
|
|
0
|
|
0
|
|
745
|
|
|
|
|
|
|
my (@received); |
746
|
0
|
|
|
|
|
0
|
my $received = $pms->get('Received'); |
747
|
|
|
|
|
|
|
if ($received ne '') { |
748
|
0
|
|
|
|
|
0
|
@received = grep {$_ =~ m/\S/} (split(/\n/,$received)); |
749
|
0
|
|
|
|
|
0
|
} |
750
|
0
|
|
|
|
|
0
|
# if we have no Received: headers, chances are we're archived mail |
751
|
0
|
0
|
0
|
|
|
0
|
# with a limited set of headers |
752
|
0
|
|
|
|
|
0
|
if (!scalar(@received)) { |
753
|
0
|
|
|
|
|
0
|
return; |
754
|
|
|
|
|
|
|
} |
755
|
0
|
0
|
|
|
|
0
|
|
756
|
|
|
|
|
|
|
# handle fetchmail headers |
757
|
|
|
|
|
|
|
my (@local); |
758
|
0
|
0
|
|
|
|
0
|
if (($received[0] =~ |
759
|
0
|
|
|
|
|
0
|
m/\bfrom (?:localhost\s|(?:\S+ ){1,2}\S*\b127\.0\.0\.1\b)/) || |
760
|
|
|
|
|
|
|
($received[0] =~ m/qmail \d+ invoked by uid \d+/)) { |
761
|
|
|
|
|
|
|
push @local, (shift @received); |
762
|
0
|
|
|
|
|
0
|
} |
763
|
|
|
|
|
|
|
if (scalar(@received) && |
764
|
|
|
|
|
|
|
($received[0] =~ m/\bby localhost with \w+ \(fetchmail-[\d.]+/)) { |
765
|
|
|
|
|
|
|
push @local, (shift @received); |
766
|
|
|
|
|
|
|
} |
767
|
0
|
|
|
0
|
|
0
|
elsif (scalar(@local)) { |
768
|
|
|
|
|
|
|
unshift @received, (shift @local); |
769
|
0
|
|
|
|
|
0
|
} |
770
|
0
|
|
|
|
|
0
|
|
771
|
|
|
|
|
|
|
if (scalar(@local)) { |
772
|
0
|
|
|
|
|
0
|
my (@fetchmail_times); |
773
|
0
|
|
|
|
|
0
|
foreach my $rcvd (@local) { |
774
|
0
|
0
|
|
|
|
0
|
if ($rcvd =~ m/(\s.?\d+ \S\S\S \d+ \d+:\d+:\d+ \S+)/) { |
775
|
0
|
|
|
|
|
0
|
my $date = $1; |
|
0
|
|
|
|
|
0
|
|
776
|
|
|
|
|
|
|
dbg2("eval: trying Received fetchmail header date for real time: $date"); |
777
|
|
|
|
|
|
|
my $time = parse_rfc822_date($date); |
778
|
|
|
|
|
|
|
if (defined($time) && (time() >= $time)) { |
779
|
0
|
0
|
|
|
|
0
|
dbg2("eval: time_t from date=$time, rcvd=$date"); |
780
|
0
|
|
|
|
|
0
|
push @fetchmail_times, $time; |
781
|
|
|
|
|
|
|
} |
782
|
|
|
|
|
|
|
} |
783
|
|
|
|
|
|
|
} |
784
|
0
|
|
|
|
|
0
|
if (scalar(@fetchmail_times) > 1) { |
785
|
0
|
0
|
0
|
|
|
0
|
$pms->{received_fetchmail_time} = |
786
|
|
|
|
|
|
|
(sort {$b <=> $a} (@fetchmail_times))[0]; |
787
|
|
|
|
|
|
|
} elsif (scalar(@fetchmail_times)) { |
788
|
0
|
|
|
|
|
0
|
$pms->{received_fetchmail_time} = $fetchmail_times[0]; |
789
|
|
|
|
|
|
|
} |
790
|
0
|
0
|
0
|
|
|
0
|
} |
|
|
0
|
|
|
|
|
|
791
|
|
|
|
|
|
|
|
792
|
0
|
|
|
|
|
0
|
my (@header_times); |
793
|
|
|
|
|
|
|
foreach my $rcvd (@received) { |
794
|
|
|
|
|
|
|
if ($rcvd =~ m/(\s.?\d+ \S\S\S \d+ \d+:\d+:\d+ \S+)/) { |
795
|
0
|
|
|
|
|
0
|
my $date = $1; |
796
|
|
|
|
|
|
|
dbg2("eval: trying Received header date for real time: $date"); |
797
|
|
|
|
|
|
|
my $time = parse_rfc822_date($date); |
798
|
0
|
0
|
|
|
|
0
|
if (defined($time)) { |
799
|
0
|
|
|
|
|
0
|
dbg2("eval: time_t from date=$time, rcvd=$date"); |
800
|
0
|
|
|
|
|
0
|
push @header_times, $time; |
801
|
0
|
0
|
|
|
|
0
|
} |
802
|
0
|
|
|
|
|
0
|
} |
803
|
0
|
|
|
|
|
0
|
} |
804
|
0
|
|
|
|
|
0
|
|
805
|
0
|
0
|
0
|
|
|
0
|
if (scalar(@header_times)) { |
806
|
0
|
|
|
|
|
0
|
$pms->{received_header_times} = [ @header_times ]; |
807
|
0
|
|
|
|
|
0
|
} else { |
808
|
|
|
|
|
|
|
dbg("eval: no dates found in Received headers"); |
809
|
|
|
|
|
|
|
} |
810
|
|
|
|
|
|
|
} |
811
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
812
|
|
|
|
|
|
|
my ($self, $pms) = @_; |
813
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
814
|
|
|
|
|
|
|
my (@dates_poss); |
815
|
0
|
|
|
|
|
0
|
|
816
|
|
|
|
|
|
|
$pms->{date_received} = 0; |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
if (!exists($pms->{date_header_time})) { |
819
|
0
|
|
|
|
|
0
|
$self->_get_date_header_time($pms); |
820
|
0
|
|
|
|
|
0
|
} |
821
|
0
|
0
|
|
|
|
0
|
|
822
|
0
|
|
|
|
|
0
|
if (defined($pms->{date_header_time})) { |
823
|
0
|
|
|
|
|
0
|
push @dates_poss, $pms->{date_header_time}; |
824
|
0
|
|
|
|
|
0
|
} |
825
|
0
|
0
|
|
|
|
0
|
|
826
|
0
|
|
|
|
|
0
|
if (!exists($pms->{received_header_times})) { |
827
|
0
|
|
|
|
|
0
|
$self->_get_received_header_times($pms); |
828
|
|
|
|
|
|
|
} |
829
|
|
|
|
|
|
|
my (@received_header_times) = @{ $pms->{received_header_times} }; |
830
|
|
|
|
|
|
|
if (scalar(@received_header_times)) { |
831
|
|
|
|
|
|
|
push @dates_poss, $received_header_times[0]; |
832
|
0
|
0
|
|
|
|
0
|
} |
833
|
0
|
|
|
|
|
0
|
if (defined($pms->{received_fetchmail_time})) { |
834
|
|
|
|
|
|
|
push @dates_poss, $pms->{received_fetchmail_time}; |
835
|
0
|
|
|
|
|
0
|
} |
836
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
if (defined($pms->{date_header_time}) && scalar(@received_header_times)) { |
838
|
|
|
|
|
|
|
if (!exists($pms->{date_diff})) { |
839
|
|
|
|
|
|
|
$self->_check_date_diff($pms); |
840
|
0
|
|
|
0
|
|
0
|
} |
841
|
|
|
|
|
|
|
push @dates_poss, $pms->{date_header_time} - $pms->{date_diff}; |
842
|
0
|
|
|
|
|
0
|
} |
843
|
|
|
|
|
|
|
|
844
|
0
|
|
|
|
|
0
|
if (scalar(@dates_poss)) { # use median |
845
|
|
|
|
|
|
|
$pms->{date_received} = (sort {$b <=> $a} |
846
|
0
|
0
|
|
|
|
0
|
(@dates_poss))[int($#dates_poss/2)]; |
847
|
0
|
|
|
|
|
0
|
dbg("eval: date chosen from message: " . |
848
|
|
|
|
|
|
|
scalar(localtime($pms->{date_received}))); |
849
|
|
|
|
|
|
|
} else { |
850
|
0
|
0
|
|
|
|
0
|
dbg("eval: no dates found in message"); |
851
|
0
|
|
|
|
|
0
|
} |
852
|
|
|
|
|
|
|
} |
853
|
|
|
|
|
|
|
|
854
|
0
|
0
|
|
|
|
0
|
my ($self, $pms) = @_; |
855
|
0
|
|
|
|
|
0
|
|
856
|
|
|
|
|
|
|
$pms->{date_diff} = 0; |
857
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
858
|
0
|
0
|
|
|
|
0
|
if (!exists($pms->{date_header_time})) { |
859
|
0
|
|
|
|
|
0
|
$self->_get_date_header_time($pms); |
860
|
|
|
|
|
|
|
} |
861
|
0
|
0
|
|
|
|
0
|
|
862
|
0
|
|
|
|
|
0
|
if (!defined($pms->{date_header_time})) { |
863
|
|
|
|
|
|
|
return; # already have tests for this |
864
|
|
|
|
|
|
|
} |
865
|
0
|
0
|
0
|
|
|
0
|
|
866
|
0
|
0
|
|
|
|
0
|
if (!exists($pms->{received_header_times})) { |
867
|
0
|
|
|
|
|
0
|
$self->_get_received_header_times($pms); |
868
|
|
|
|
|
|
|
} |
869
|
0
|
|
|
|
|
0
|
my (@header_times) = @{ $pms->{received_header_times} }; |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
if (!scalar(@header_times)) { |
872
|
0
|
0
|
|
|
|
0
|
return; # archived mail? |
873
|
0
|
|
|
|
|
0
|
} |
|
0
|
|
|
|
|
0
|
|
874
|
|
|
|
|
|
|
|
875
|
|
|
|
|
|
|
my (@diffs) = map {$pms->{date_header_time} - $_} (@header_times); |
876
|
0
|
|
|
|
|
0
|
|
877
|
|
|
|
|
|
|
# if the last Received: header has no difference, then we choose to |
878
|
0
|
|
|
|
|
0
|
# exclude it |
879
|
|
|
|
|
|
|
if ($#diffs > 0 && $diffs[$#diffs] == 0) { |
880
|
|
|
|
|
|
|
pop(@diffs); |
881
|
|
|
|
|
|
|
} |
882
|
|
|
|
|
|
|
|
883
|
0
|
|
|
0
|
|
0
|
# use the date with the smallest absolute difference |
884
|
|
|
|
|
|
|
# (experimentally, this results in the fewest false positives) |
885
|
0
|
|
|
|
|
0
|
@diffs = sort { abs($a) <=> abs($b) } @diffs; |
886
|
|
|
|
|
|
|
$pms->{date_diff} = $diffs[0]; |
887
|
0
|
0
|
|
|
|
0
|
} |
888
|
0
|
|
|
|
|
0
|
|
889
|
|
|
|
|
|
|
|
890
|
|
|
|
|
|
|
my ($self, $pms) = @_; |
891
|
0
|
0
|
|
|
|
0
|
my $subject = $pms->get('Subject'); |
892
|
0
|
|
|
|
|
0
|
|
893
|
|
|
|
|
|
|
$subject =~ s/^\s+//; |
894
|
|
|
|
|
|
|
$subject =~ s/\s+$//; |
895
|
0
|
0
|
|
|
|
0
|
$subject =~ s/^(?:(?:Re|Fwd|Fw|Aw|Antwort|WG|SV|VB|VS|VL):\s*)+//i; # Bug 6805 |
896
|
0
|
|
|
|
|
0
|
return 0 if $subject !~ /\s/; # don't match one word subjects |
897
|
|
|
|
|
|
|
return 0 if (length $subject < 10); # don't match short subjects |
898
|
0
|
|
|
|
|
0
|
$subject =~ s/[^a-zA-Z]//g; # only look at letters |
|
0
|
|
|
|
|
0
|
|
899
|
|
|
|
|
|
|
|
900
|
0
|
0
|
|
|
|
0
|
# now, check to see if the subject is encoded using a non-ASCII charset. |
901
|
0
|
|
|
|
|
0
|
# If so, punt on this test to avoid FPs. We just list the known charsets |
902
|
|
|
|
|
|
|
# this test will FP on, here. |
903
|
|
|
|
|
|
|
my $subjraw = $pms->get('Subject:raw'); |
904
|
0
|
|
|
|
|
0
|
my $CLTFAC = Mail::SpamAssassin::Constants::CHARSETS_LIKELY_TO_FP_AS_CAPS; |
|
0
|
|
|
|
|
0
|
|
905
|
|
|
|
|
|
|
if ($subjraw =~ /=\?${CLTFAC}\?/i) { |
906
|
|
|
|
|
|
|
return 0; |
907
|
|
|
|
|
|
|
} |
908
|
0
|
0
|
0
|
|
|
0
|
|
909
|
0
|
|
|
|
|
0
|
return length($subject) && ($subject eq uc($subject)); |
910
|
|
|
|
|
|
|
} |
911
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
my ($self, $pms, $test) = @_; |
913
|
|
|
|
|
|
|
|
914
|
0
|
|
|
|
|
0
|
my $full_to = $pms->get('To:addr'); |
|
0
|
|
|
|
|
0
|
|
915
|
0
|
|
|
|
|
0
|
return 0 unless $full_to ne ''; |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
my $subject = $pms->get('Subject'); |
918
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
if ($test eq "address") { |
920
|
0
|
|
|
0
|
0
|
0
|
return $subject =~ /\b\Q$full_to\E\b/i; # "user@domain.com" |
921
|
0
|
|
|
|
|
0
|
} |
922
|
|
|
|
|
|
|
elsif ($test eq "user") { |
923
|
0
|
|
|
|
|
0
|
my $to = $full_to; |
924
|
0
|
|
|
|
|
0
|
$to =~ s/\@.*//; |
925
|
0
|
|
|
|
|
0
|
my $subj = $subject; |
926
|
0
|
0
|
|
|
|
0
|
$subj =~ s/^\s+//; |
927
|
0
|
0
|
|
|
|
0
|
$subj =~ s/\s+$//; |
928
|
0
|
|
|
|
|
0
|
|
929
|
|
|
|
|
|
|
return $subject =~ /^(?: |
930
|
|
|
|
|
|
|
(?:re|fw):\s*(?:\w+\s+)?\Q$to\E$ |
931
|
|
|
|
|
|
|
|(?-i:\Q$to\E)\s*[,:;!?-](?:$|\s) |
932
|
|
|
|
|
|
|
|\Q$to\E$ |
933
|
0
|
|
|
|
|
0
|
|,\s*\Q$to\E[,:;!?-]$ |
934
|
0
|
|
|
|
|
0
|
)/ix; |
935
|
0
|
0
|
|
|
|
0
|
} |
936
|
0
|
|
|
|
|
0
|
return 0; |
937
|
|
|
|
|
|
|
} |
938
|
|
|
|
|
|
|
|
939
|
0
|
|
0
|
|
|
0
|
my ($self, $pms) = @_; |
940
|
|
|
|
|
|
|
local ($_); |
941
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
my $id = $pms->get('MESSAGEID'); |
943
|
0
|
|
|
0
|
0
|
0
|
return 0 if $id !~ /^<[0-9a-f]{4}([0-9a-f]{8})\$[0-9a-f]{8}\$[0-9a-f]{8}\@/; |
944
|
|
|
|
|
|
|
|
945
|
0
|
|
|
|
|
0
|
my $timetoken = hex($1); |
946
|
0
|
0
|
|
|
|
0
|
my $x = 0.0023283064365387; |
947
|
|
|
|
|
|
|
my $y = 27111902.8329849; |
948
|
0
|
|
|
|
|
0
|
|
949
|
|
|
|
|
|
|
my $fudge = 250; |
950
|
0
|
0
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
|
951
|
0
|
|
|
|
|
0
|
$_ = $pms->get('Date'); |
952
|
|
|
|
|
|
|
$_ = parse_rfc822_date($_) || 0; |
953
|
|
|
|
|
|
|
my $expected = int (($_ * $x) + $y); |
954
|
0
|
|
|
|
|
0
|
my $diff = $timetoken - $expected; |
955
|
0
|
|
|
|
|
0
|
return 0 if (abs($diff) < $fudge); |
956
|
0
|
|
|
|
|
0
|
|
957
|
0
|
|
|
|
|
0
|
$_ = $pms->get('Received'); |
958
|
0
|
|
|
|
|
0
|
/(\s.?\d+ \S\S\S \d+ \d+:\d+:\d+ \S+).*?$/; |
959
|
|
|
|
|
|
|
$_ = parse_rfc822_date($_) || 0; |
960
|
0
|
|
|
|
|
0
|
$expected = int(($_ * $x) + $y); |
961
|
|
|
|
|
|
|
$diff = $timetoken - $expected; |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
return (abs($diff) >= $fudge); |
964
|
|
|
|
|
|
|
} |
965
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
my ($self, $pms) = @_; |
967
|
0
|
|
|
|
|
0
|
local ($_); |
968
|
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
# Lyris eats message-ids. also some ezmlm, I think :( |
970
|
|
|
|
|
|
|
$_ = $pms->get("List-Unsubscribe"); |
971
|
81
|
|
|
81
|
0
|
180
|
return 1 if (/<mailto:(?:leave-\S+|\S+-unsubscribe)\@\S+>$/i); |
972
|
81
|
|
|
|
|
140
|
|
973
|
|
|
|
|
|
|
# ezmlm again |
974
|
81
|
|
|
|
|
220
|
if($self->gated_through_received_hdr_remover($pms)) { return 1; } |
975
|
81
|
100
|
|
|
|
1415
|
|
976
|
|
|
|
|
|
|
# Allen notes this as 'Wacky sendmail version?' |
977
|
1
|
|
|
|
|
7
|
$_ = $pms->get("Received"); |
978
|
1
|
|
|
|
|
2
|
return 1 if /\/CWT\/DCE\)/; |
979
|
1
|
|
|
|
|
2
|
|
980
|
|
|
|
|
|
|
# Apr 2 2003 jm: iPlanet rewrites lots of stuff, including Message-IDs |
981
|
1
|
|
|
|
|
3
|
return 1 if /iPlanet Messaging Server/; |
982
|
|
|
|
|
|
|
|
983
|
1
|
|
|
|
|
4
|
return 0; |
984
|
1
|
|
50
|
|
|
16
|
} |
985
|
1
|
|
|
|
|
5
|
|
986
|
1
|
|
|
|
|
4
|
# Return true if the count of $hdr headers are within the given range |
987
|
1
|
50
|
|
|
|
4
|
my ($self, $pms, $hdr, $min, $max) = @_; |
988
|
|
|
|
|
|
|
my %uniq; |
989
|
1
|
|
|
|
|
5
|
my @hdrs = grep(!$uniq{$_}++, $pms->{msg}->get_header ($hdr)); |
990
|
1
|
|
|
|
|
35
|
return (scalar @hdrs >= $min && scalar @hdrs <= $max); |
991
|
1
|
|
50
|
|
|
4
|
} |
992
|
1
|
|
|
|
|
6
|
|
993
|
1
|
|
|
|
|
2
|
my ($self, $pms) = @_; |
994
|
|
|
|
|
|
|
|
995
|
1
|
|
|
|
|
23
|
my $all = $pms->get('ALL'); # cached access |
996
|
|
|
|
|
|
|
|
997
|
|
|
|
|
|
|
for my $header (split(/\n/, $all)) { |
998
|
|
|
|
|
|
|
# slightly faster to test in this order |
999
|
0
|
|
|
0
|
0
|
|
if ($header =~ /%[A-Z][A-Z_-]/ && |
1000
|
0
|
|
|
|
|
|
$header !~ /^(?:X-VMS-To|X-UIDL|X-Face|To|Cc|From|Subject|References|In-Reply-To|(?:X-|Resent-|X-Original-)?Message-Id):/i) |
1001
|
|
|
|
|
|
|
{ |
1002
|
|
|
|
|
|
|
return 1; |
1003
|
0
|
|
|
|
|
|
} |
1004
|
0
|
0
|
|
|
|
|
} |
1005
|
|
|
|
|
|
|
return 0; |
1006
|
|
|
|
|
|
|
} |
1007
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
my ($self, $pms) = @_; |
1009
|
|
|
|
|
|
|
|
1010
|
0
|
|
|
|
|
|
my $mid = $pms->get('MESSAGEID'); |
1011
|
0
|
0
|
|
|
|
|
my $from = $pms->get('From'); |
1012
|
|
|
|
|
|
|
if ($mid =~ m/<[A-Z]{28}\.([^>]+?)>/) { |
1013
|
|
|
|
|
|
|
if ($from =~ m/\"[^\"]+\"\s*<\Q$1\E>/) { |
1014
|
0
|
0
|
|
|
|
|
return 1; |
1015
|
|
|
|
|
|
|
} |
1016
|
0
|
|
|
|
|
|
} |
1017
|
|
|
|
|
|
|
return 0; |
1018
|
|
|
|
|
|
|
} |
1019
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
my ($self, $pms) = @_; |
1021
|
0
|
|
|
0
|
0
|
|
|
1022
|
0
|
|
|
|
|
|
my $to = $pms->get('To:addr'); |
1023
|
0
|
|
|
|
|
|
my $from = $pms->get('EnvelopeFrom:addr'); |
1024
|
0
|
|
0
|
|
|
|
|
1025
|
|
|
|
|
|
|
return 0 if $from eq '' || $to eq ''; |
1026
|
|
|
|
|
|
|
return 0 if $from =~ /^SRS\d[=+-]/i; |
1027
|
|
|
|
|
|
|
|
1028
|
0
|
|
|
0
|
0
|
|
if ($to =~ /^([^@]+)\@(.+)$/) { |
1029
|
|
|
|
|
|
|
my($user,$dom) = ($1,$2); |
1030
|
0
|
|
|
|
|
|
$dom = $self->{main}->{registryboundaries}->trim_domain($dom); |
1031
|
|
|
|
|
|
|
return unless |
1032
|
0
|
|
|
|
|
|
($self->{main}->{registryboundaries}->is_domain_valid($dom)); |
1033
|
|
|
|
|
|
|
|
1034
|
0
|
0
|
0
|
|
|
|
return 1 if ($from =~ /\b\Q$dom\E.\Q$user\E@/i); |
1035
|
|
|
|
|
|
|
} |
1036
|
|
|
|
|
|
|
|
1037
|
0
|
|
|
|
|
|
return 0; |
1038
|
|
|
|
|
|
|
} |
1039
|
|
|
|
|
|
|
|
1040
|
0
|
|
|
|
|
|
# ADDED FROM BUG 6487 |
1041
|
|
|
|
|
|
|
my ($self, $pms) = @_; |
1042
|
|
|
|
|
|
|
|
1043
|
|
|
|
|
|
|
my $from = $pms->get('From:addr'); |
1044
|
0
|
|
|
0
|
0
|
|
my $envfrom = $pms->get('EnvelopeFrom:addr'); |
1045
|
|
|
|
|
|
|
|
1046
|
0
|
|
|
|
|
|
local $1; |
1047
|
0
|
|
|
|
|
|
my $fromdomain = ''; |
1048
|
0
|
0
|
|
|
|
|
#Revised regexp from 6487 comment 3 |
1049
|
0
|
0
|
|
|
|
|
$fromdomain = $1 if $from =~ /\@([^@]*)\z/; |
1050
|
0
|
|
|
|
|
|
$fromdomain =~ s/^.+\.([^\.]+\.[^\.]+)$/$1/; |
1051
|
|
|
|
|
|
|
return 0 if $fromdomain eq ''; |
1052
|
|
|
|
|
|
|
|
1053
|
0
|
|
|
|
|
|
my $envfromdomain = ''; |
1054
|
|
|
|
|
|
|
$envfromdomain = $1 if $envfrom =~ /\@([^@]*)\z/; |
1055
|
|
|
|
|
|
|
$envfromdomain =~ s/^.+\.([^\.]+\.[^\.]+)$/$1/; |
1056
|
|
|
|
|
|
|
return 0 if $envfromdomain eq ''; |
1057
|
0
|
|
|
0
|
0
|
|
|
1058
|
|
|
|
|
|
|
dbg("eval: From 2nd level domain: $fromdomain, EnvelopeFrom 2nd level domain: $envfromdomain"); |
1059
|
0
|
|
|
|
|
|
|
1060
|
0
|
|
|
|
|
|
return 1 if lc($fromdomain) ne lc($envfromdomain); |
1061
|
|
|
|
|
|
|
|
1062
|
0
|
0
|
0
|
|
|
|
return 0; |
1063
|
0
|
0
|
|
|
|
|
} |
1064
|
|
|
|
|
|
|
|
1065
|
0
|
0
|
|
|
|
|
|
1066
|
0
|
|
|
|
|
|
########################################################################### |
1067
|
0
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
# support eval-test verbose debugs using "-Deval" |
1069
|
0
|
0
|
|
|
|
|
if (would_log('dbg', 'eval') == 2) { |
1070
|
|
|
|
|
|
|
dbg(@_); |
1071
|
0
|
0
|
|
|
|
|
} |
1072
|
|
|
|
|
|
|
} |
1073
|
|
|
|
|
|
|
|
1074
|
0
|
|
|
|
|
|
1; |