← Index
NYTProf Performance Profile   « line view »
For /usr/local/bin/sa-learn
  Run on Sun Nov 5 02:36:06 2017
Reported on Sun Nov 5 02:56:21 2017

Filename/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Plugin/SpamCop.pm
StatementsExecuted 40 statements in 3.95ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
11150.9ms145msMail::SpamAssassin::Plugin::SpamCop::::BEGIN@53Mail::SpamAssassin::Plugin::SpamCop::BEGIN@53
11188µs368µsMail::SpamAssassin::Plugin::SpamCop::::newMail::SpamAssassin::Plugin::SpamCop::new
11153µs244µsMail::SpamAssassin::Plugin::SpamCop::::set_configMail::SpamAssassin::Plugin::SpamCop::set_config
11143µs250µsMail::SpamAssassin::Plugin::SpamCop::::BEGIN@52Mail::SpamAssassin::Plugin::SpamCop::BEGIN@52
11138µs38µsMail::SpamAssassin::Plugin::SpamCop::::BEGIN@44Mail::SpamAssassin::Plugin::SpamCop::BEGIN@44
11135µs6.28msMail::SpamAssassin::Plugin::SpamCop::::BEGIN@46Mail::SpamAssassin::Plugin::SpamCop::BEGIN@46
11134µs47µsMail::SpamAssassin::Plugin::SpamCop::::BEGIN@47Mail::SpamAssassin::Plugin::SpamCop::BEGIN@47
11132µs69µsMail::SpamAssassin::Plugin::SpamCop::::BEGIN@48Mail::SpamAssassin::Plugin::SpamCop::BEGIN@48
11131µs234µsMail::SpamAssassin::Plugin::SpamCop::::BEGIN@45Mail::SpamAssassin::Plugin::SpamCop::BEGIN@45
11126µs95µsMail::SpamAssassin::Plugin::SpamCop::::BEGIN@50Mail::SpamAssassin::Plugin::SpamCop::BEGIN@50
11125µs105µsMail::SpamAssassin::Plugin::SpamCop::::BEGIN@55Mail::SpamAssassin::Plugin::SpamCop::BEGIN@55
11125µs32µsMail::SpamAssassin::Plugin::SpamCop::::BEGIN@49Mail::SpamAssassin::Plugin::SpamCop::BEGIN@49
0000s0sMail::SpamAssassin::Plugin::SpamCop::::__ANON__[:112]Mail::SpamAssassin::Plugin::SpamCop::__ANON__[:112]
0000s0sMail::SpamAssassin::Plugin::SpamCop::::__ANON__[:140]Mail::SpamAssassin::Plugin::SpamCop::__ANON__[:140]
0000s0sMail::SpamAssassin::Plugin::SpamCop::::plugin_reportMail::SpamAssassin::Plugin::SpamCop::plugin_report
0000s0sMail::SpamAssassin::Plugin::SpamCop::::smtp_dbgMail::SpamAssassin::Plugin::SpamCop::smtp_dbg
0000s0sMail::SpamAssassin::Plugin::SpamCop::::spamcop_reportMail::SpamAssassin::Plugin::SpamCop::spamcop_report
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
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=head1 NAME
19
20Mail::SpamAssassin::Plugin::SpamCop - perform SpamCop reporting of messages
21
22=head1 SYNOPSIS
23
24 loadplugin Mail::SpamAssassin::Plugin::SpamCop
25
26=head1 DESCRIPTION
27
28SpamCop is a service for reporting spam. SpamCop determines the origin
29of unwanted email and reports it to the relevant Internet service
30providers. By reporting spam, you have a positive impact on the
31problem. Reporting unsolicited email also helps feed spam filtering
32systems, including, but not limited to, the SpamCop blacklist used in
33SpamAssassin as a DNSBL.
34
35Note that spam reports sent by this plugin to SpamCop each include the
36entire spam message.
37
38See http://www.spamcop.net/ for more information about SpamCop.
39
40=cut
41
42package Mail::SpamAssassin::Plugin::SpamCop;
43
44283µs138µs
# spent 38µs within Mail::SpamAssassin::Plugin::SpamCop::BEGIN@44 which was called: # once (38µs+0s) by Mail::SpamAssassin::PluginHandler::load_plugin at line 44
use Mail::SpamAssassin::Plugin;
# spent 38µs making 1 call to Mail::SpamAssassin::Plugin::SpamCop::BEGIN@44
45290µs2436µs
# spent 234µs (31+202) within Mail::SpamAssassin::Plugin::SpamCop::BEGIN@45 which was called: # once (31µs+202µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 45
use Mail::SpamAssassin::Logger;
# spent 234µs making 1 call to Mail::SpamAssassin::Plugin::SpamCop::BEGIN@45 # spent 202µs making 1 call to Exporter::import
462101µs212.5ms
# spent 6.28ms (35µs+6.24) within Mail::SpamAssassin::Plugin::SpamCop::BEGIN@46 which was called: # once (35µs+6.24ms) by Mail::SpamAssassin::PluginHandler::load_plugin at line 46
use IO::Socket;
# spent 6.28ms making 1 call to Mail::SpamAssassin::Plugin::SpamCop::BEGIN@46 # spent 6.24ms making 1 call to IO::Socket::import
47298µs260µs
# spent 47µs (34+13) within Mail::SpamAssassin::Plugin::SpamCop::BEGIN@47 which was called: # once (34µs+13µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 47
use strict;
# spent 47µs making 1 call to Mail::SpamAssassin::Plugin::SpamCop::BEGIN@47 # spent 13µs making 1 call to strict::import
48282µs2106µs
# spent 69µs (32+37) within Mail::SpamAssassin::Plugin::SpamCop::BEGIN@48 which was called: # once (32µs+37µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 48
use warnings;
# spent 69µs making 1 call to Mail::SpamAssassin::Plugin::SpamCop::BEGIN@48 # spent 37µs making 1 call to warnings::import
49281µs240µs
# spent 32µs (25+7) within Mail::SpamAssassin::Plugin::SpamCop::BEGIN@49 which was called: # once (25µs+7µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 49
use bytes;
# spent 32µs making 1 call to Mail::SpamAssassin::Plugin::SpamCop::BEGIN@49 # spent 8µs making 1 call to bytes::import
502102µs2163µs
# spent 95µs (26+68) within Mail::SpamAssassin::Plugin::SpamCop::BEGIN@50 which was called: # once (26µs+68µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 50
use re 'taint';
# spent 95µs making 1 call to Mail::SpamAssassin::Plugin::SpamCop::BEGIN@50 # spent 68µs making 1 call to re::import
51
523115µs2458µs
# spent 250µs (43+207) within Mail::SpamAssassin::Plugin::SpamCop::BEGIN@52 which was called: # once (43µs+207µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 52
use constant HAS_NET_DNS => eval { require Net::DNS; };
# spent 250µs making 1 call to Mail::SpamAssassin::Plugin::SpamCop::BEGIN@52 # spent 207µs making 1 call to constant::import
533406µs2145ms
# spent 145ms (50.9+93.8) within Mail::SpamAssassin::Plugin::SpamCop::BEGIN@53 which was called: # once (50.9ms+93.8ms) by Mail::SpamAssassin::PluginHandler::load_plugin at line 53
use constant HAS_NET_SMTP => eval { require Net::SMTP; };
# spent 145ms making 1 call to Mail::SpamAssassin::Plugin::SpamCop::BEGIN@53 # spent 195µs making 1 call to constant::import
54
5522.63ms2185µs
# spent 105µs (25+80) within Mail::SpamAssassin::Plugin::SpamCop::BEGIN@55 which was called: # once (25µs+80µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 55
use vars qw(@ISA);
# spent 105µs making 1 call to Mail::SpamAssassin::Plugin::SpamCop::BEGIN@55 # spent 80µs making 1 call to vars::import
56115µs@ISA = qw(Mail::SpamAssassin::Plugin);
57
58
# spent 368µs (88+280) within Mail::SpamAssassin::Plugin::SpamCop::new which was called: # once (88µs+280µs) by Mail::SpamAssassin::PluginHandler::load_plugin at line 1 of (eval 71)[Mail/SpamAssassin/PluginHandler.pm:129]
sub new {
5912µs my $class = shift;
6012µs my $mailsaobject = shift;
61
6212µs $class = ref($class) || $class;
63113µs125µs my $self = $class->SUPER::new($mailsaobject);
# spent 25µs making 1 call to Mail::SpamAssassin::Plugin::new
6412µs bless ($self, $class);
65
66 # are network tests enabled?
6717µs if (!$mailsaobject->{local_tests_only} && HAS_NET_DNS && HAS_NET_SMTP) {
68110µs $self->{spamcop_available} = 1;
6918µs111µs dbg("reporter: network tests on, attempting SpamCop");
# spent 11µs making 1 call to Mail::SpamAssassin::Logger::dbg
70 }
71 else {
72 $self->{spamcop_available} = 0;
73 dbg("reporter: local tests only, disabling SpamCop");
74 }
75
76120µs1244µs $self->set_config($mailsaobject->{conf});
# spent 244µs making 1 call to Mail::SpamAssassin::Plugin::SpamCop::set_config
77
78113µs return $self;
79}
80
81
# spent 244µs (53+192) within Mail::SpamAssassin::Plugin::SpamCop::set_config which was called: # once (53µs+192µs) by Mail::SpamAssassin::Plugin::SpamCop::new at line 76
sub set_config {
8212µs my($self, $conf) = @_;
8312µs my @cmds;
84
85=head1 USER OPTIONS
86
87=over 4
88
89=item spamcop_from_address user@example.com (default: none)
90
91This address is used during manual reports to SpamCop as the From:
92address. You can use your normal email address. If this is not set, a
93guess will be used as the From: address in SpamCop reports.
94
95=cut
96
97 push (@cmds, {
98 setting => 'spamcop_from_address',
99 default => '',
100 type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
101 code => sub {
102 my ($self, $key, $value, $line) = @_;
103 if ($value =~ /([^<\s]+\@[^>\s]+)/) {
104 $self->{spamcop_from_address} = $1;
105 }
106 elsif ($value =~ /^$/) {
107 return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
108 }
109 else {
110 return $Mail::SpamAssassin::Conf::INVALID_VALUE;
111 }
112 },
113117µs });
114
115=item spamcop_to_address user@example.com (default: generic reporting address)
116
117Your customized SpamCop report submission address. You need to obtain
118this address by registering at C<http://www.spamcop.net/>. If this is
119not set, SpamCop reports will go to a generic reporting address for
120SpamAssassin users and your reports will probably have less weight in
121the SpamCop system.
122
123=cut
124
125 push (@cmds, {
126 setting => 'spamcop_to_address',
127 default => 'spamassassin-submit@spam.spamcop.net',
128 type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
129 code => sub {
130 my ($self, $key, $value, $line) = @_;
131 if ($value =~ /([^<\s]+\@[^>\s]+)/) {
132 $self->{spamcop_to_address} = $1;
133 }
134 elsif ($value =~ /^$/) {
135 return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
136 }
137 else {
138 return $Mail::SpamAssassin::Conf::INVALID_VALUE;
139 }
140 },
14117µs });
142
143=item spamcop_max_report_size (default: 50)
144
145Messages larger than this size (in kilobytes) will be truncated in
146report messages sent to SpamCop. The default setting is the maximum
147size that SpamCop will accept at the time of release.
148
149=cut
150
15116µs push (@cmds, {
152 setting => 'spamcop_max_report_size',
153 default => 50,
154 type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
155 });
156
157119µs1192µs $conf->{parser}->register_commands(\@cmds);
158}
159
160sub plugin_report {
161 my ($self, $options) = @_;
162
163 return unless $self->{spamcop_available};
164
165 if (!$options->{report}->{options}->{dont_report_to_spamcop}) {
166 if ($self->spamcop_report($options)) {
167 $options->{report}->{report_available} = 1;
168 info("reporter: spam reported to SpamCop");
169 $options->{report}->{report_return} = 1;
170 }
171 else {
172 info("reporter: could not report spam to SpamCop");
173 }
174 }
175}
176
177sub smtp_dbg {
178 my ($command, $smtp) = @_;
179
180 dbg("reporter: SpamCop sent $command");
181 my $code = $smtp->code();
182 my $message = $smtp->message();
183 my $debug;
184 $debug .= $code if $code;
185 $debug .= ($code ? " " : "") . $message if $message;
186 chomp $debug;
187 dbg("reporter: SpamCop received $debug");
188 return 1;
189}
190
191sub spamcop_report {
192 my ($self, $options) = @_;
193
194 # original text
195 my $original = ${$options->{text}};
196
197 # check date
198 my $header = $original;
199 $header =~ s/\r?\n\r?\n.*//s;
200 my $date = Mail::SpamAssassin::Util::receive_date($header);
201 if ($date && $date < time - 2*86400) {
202 warn("reporter: SpamCop message older than 2 days, not reporting\n");
203 return 0;
204 }
205
206 # message variables
207 my $boundary = "----------=_" . sprintf("%08X.%08X",time,int(rand(2**32)));
208 while ($original =~ /^\Q${boundary}\E$/m) {
209 $boundary .= "/".sprintf("%08X",int(rand(2**32)));
210 }
211 my $description = "spam report via " . Mail::SpamAssassin::Version();
212 my $trusted = $options->{msg}->{metadata}->{relays_trusted_str};
213 my $untrusted = $options->{msg}->{metadata}->{relays_untrusted_str};
214 my $user = $options->{report}->{main}->{'username'} || 'unknown';
215 my $host = Mail::SpamAssassin::Util::fq_hostname() || 'unknown';
216 my $from = $options->{report}->{conf}->{spamcop_from_address} || "$user\@$host";
217
218 # message data
219 my %head = (
220 'To' => $options->{report}->{conf}->{spamcop_to_address},
221 'From' => $from,
222 'Subject' => 'report spam',
223 'Date' => Mail::SpamAssassin::Util::time_to_rfc822_date(),
224 'Message-Id' =>
225 sprintf("<%08X.%08X@%s>",time,int(rand(2**32)),$host),
226 'MIME-Version' => '1.0',
227 'Content-Type' => "multipart/mixed; boundary=\"$boundary\"",
228 );
229
230 # truncate message
231 if (length($original) > $self->{main}->{conf}->{spamcop_max_report_size} * 1024) {
232 substr($original, ($self->{main}->{conf}->{spamcop_max_report_size} * 1024)) =
233 "\n[truncated by SpamAssassin]\n";
234 }
235
236 my $body = <<"EOM";
237This is a multi-part message in MIME format.
238
239--$boundary
240Content-Type: message/rfc822; x-spam-type=report
241Content-Description: $description
242Content-Disposition: attachment
243Content-Transfer-Encoding: 8bit
244X-Spam-Relays-Trusted: $trusted
245X-Spam-Relays-Untrusted: $untrusted
246
247$original
248--$boundary--
249
250EOM
251
252 # compose message
253 my $message;
254 while (my ($k, $v) = each %head) {
255 $message .= "$k: $v\n";
256 }
257 $message .= "\n" . $body;
258
259 # send message
260 my $failure;
261 my $mx = $head{To};
262 my $hello = Mail::SpamAssassin::Util::fq_hostname() || $from;
263 $mx =~ s/.*\@//;
264 $hello =~ s/.*\@//;
265 for my $rr (Net::DNS::mx($mx)) {
266 my $exchange = Mail::SpamAssassin::Util::untaint_hostname($rr->exchange);
267 next unless $exchange;
268 my $smtp;
269 if ($smtp = Net::SMTP->new($exchange,
270 Hello => $hello,
271 Port => 587,
272 Timeout => 10))
273 {
274 if ($smtp->mail($from) && smtp_dbg("FROM $from", $smtp) &&
275 $smtp->recipient($head{To}) && smtp_dbg("TO $head{To}", $smtp) &&
276 $smtp->data($message) && smtp_dbg("DATA", $smtp) &&
277 $smtp->quit() && smtp_dbg("QUIT", $smtp))
278 {
279 # tell user we succeeded after first attempt if we previously failed
280 warn("reporter: SpamCop report to $exchange succeeded\n") if defined $failure;
281 return 1;
282 }
283 my $code = $smtp->code();
284 my $text = $smtp->message();
285 $failure = "$code $text" if ($code && $text);
286 }
287 $failure ||= "Net::SMTP error";
288 chomp $failure;
289 warn("reporter: SpamCop report to $exchange failed: $failure\n");
290 }
291
292 return 0;
293}
294
295114µs1;
296
297=back
298
299=cut