← Index
NYTProf Performance Profile   « line view »
For /usr/local/bin/sa-learn
  Run on Tue Nov 7 05:38:10 2017
Reported on Tue Nov 7 06:16:00 2017

Filename/usr/local/lib/perl5/site_perl/Mail/SpamAssassin.pm
StatementsExecuted 46676 statements in 412ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
80929474.1ms74.1msMail::SpamAssassin::::time_methodMail::SpamAssassin::time_method
7622266.7ms84.9msMail::SpamAssassin::::find_all_addrs_in_lineMail::SpamAssassin::find_all_addrs_in_line
191612665.8ms1576sMail::SpamAssassin::::call_pluginsMail::SpamAssassin::call_plugins (recurses: max depth 1, inclusive time 14.4s)
11131.4ms217msMail::SpamAssassin::::BEGIN@71Mail::SpamAssassin::BEGIN@71
2351129.9ms4.61sMail::SpamAssassin::::parseMail::SpamAssassin::parse
11127.7ms41.6msMail::SpamAssassin::::BEGIN@74Mail::SpamAssassin::BEGIN@74
2351125.0ms1570sMail::SpamAssassin::::learnMail::SpamAssassin::learn
5396423.0ms33.1msMail::SpamAssassin::::sed_pathMail::SpamAssassin::sed_path (recurses: max depth 1, inclusive time 26µs)
281912116.9ms16.9msMail::SpamAssassin::::CORE:substMail::SpamAssassin::CORE:subst (opcode)
11110.7ms289msMail::SpamAssassin::::BEGIN@75Mail::SpamAssassin::BEGIN@75
11110.1ms18.0msMail::SpamAssassin::::BEGIN@77Mail::SpamAssassin::BEGIN@77
68219.53ms17.8msMail::SpamAssassin::::read_cf_fileMail::SpamAssassin::read_cf_file
472428.30ms10.7sMail::SpamAssassin::::initMail::SpamAssassin::init
1116.82ms16.3msMail::SpamAssassin::::BEGIN@84Mail::SpamAssassin::BEGIN@84
63215.95ms29.6msMail::SpamAssassin::::_read_cf_preMail::SpamAssassin::_read_cf_pre
1114.41ms9.55msMail::SpamAssassin::::BEGIN@70Mail::SpamAssassin::BEGIN@70
164114.05ms4.05msMail::SpamAssassin::::CORE:readMail::SpamAssassin::CORE:read (opcode)
1113.99ms29.8msMail::SpamAssassin::::BEGIN@69Mail::SpamAssassin::BEGIN@69
1113.46ms4.12msMail::SpamAssassin::::BEGIN@65Mail::SpamAssassin::BEGIN@65
1560313.23ms3.23msMail::SpamAssassin::::CORE:regcompMail::SpamAssassin::CORE:regcomp (opcode)
68112.88ms2.88msMail::SpamAssassin::::CORE:openMail::SpamAssassin::CORE:open (opcode)
1112.66ms6.44msMail::SpamAssassin::::BEGIN@76Mail::SpamAssassin::BEGIN@76
1112.57ms2.93msMail::SpamAssassin::::BEGIN@78Mail::SpamAssassin::BEGIN@78
72312.52ms2.52msMail::SpamAssassin::::CORE:statMail::SpamAssassin::CORE:stat (opcode)
1112.11ms2.69msMail::SpamAssassin::::BEGIN@72Mail::SpamAssassin::BEGIN@72
1112.00ms2.58msMail::SpamAssassin::::BEGIN@73Mail::SpamAssassin::BEGIN@73
61421.34ms27.8msMail::SpamAssassin::::read_cfMail::SpamAssassin::read_cf
4211.28ms2.35msMail::SpamAssassin::::_get_cf_pre_files_in_dirMail::SpamAssassin::_get_cf_pre_files_in_dir
6811815µs815µsMail::SpamAssassin::::CORE:closeMail::SpamAssassin::CORE:close (opcode)
111684µs920µsMail::SpamAssassin::::BEGIN@80Mail::SpamAssassin::BEGIN@80
6821345µs345µsMail::SpamAssassin::::CORE:ftfileMail::SpamAssassin::CORE:ftfile (opcode)
5461343µs343µsMail::SpamAssassin::::CORE:matchMail::SpamAssassin::CORE:match (opcode)
651293µs2.56msMail::SpamAssassin::::first_existing_pathMail::SpamAssassin::first_existing_path
111278µs47.7msMail::SpamAssassin::::newMail::SpamAssassin::new
411231µs231µsMail::SpamAssassin::::CORE:readdirMail::SpamAssassin::CORE:readdir (opcode)
5911225µs225µsMail::SpamAssassin::::CORE:ftereadMail::SpamAssassin::CORE:fteread (opcode)
111200µs11.6msMail::SpamAssassin::::create_lockerMail::SpamAssassin::create_locker
111197µs228µsMail::SpamAssassin::::init_learnerMail::SpamAssassin::init_learner
6521185µs185µsMail::SpamAssassin::::CORE:ftdirMail::SpamAssassin::CORE:ftdir (opcode)
111176µs898µsMail::SpamAssassin::::find_rule_support_fileMail::SpamAssassin::find_rule_support_file
221152µs1.01msMail::SpamAssassin::::get_and_create_userstate_dirMail::SpamAssassin::get_and_create_userstate_dir
5911141µs141µsMail::SpamAssassin::::CORE:ftsizeMail::SpamAssassin::CORE:ftsize (opcode)
411104µs104µsMail::SpamAssassin::::CORE:open_dirMail::SpamAssassin::CORE:open_dir (opcode)
246194µs94µsMail::SpamAssassin::::CORE:substcontMail::SpamAssassin::CORE:substcont (opcode)
21184µs143µsMail::SpamAssassin::::expand_nameMail::SpamAssassin::expand_name
11166µs74µsMail::SpamAssassin::::BEGIN@62Mail::SpamAssassin::BEGIN@62
41164µs64µsMail::SpamAssassin::::CORE:sortMail::SpamAssassin::CORE:sort (opcode)
22146µs3.25msMail::SpamAssassin::::read_preMail::SpamAssassin::read_pre
41145µs45µsMail::SpamAssassin::::CORE:closedirMail::SpamAssassin::CORE:closedir (opcode)
21142µs1.44msMail::SpamAssassin::::get_pre_files_in_dirMail::SpamAssassin::get_pre_files_in_dir
11139µs64µsMail::SpamAssassin::::BEGIN@85Mail::SpamAssassin::BEGIN@85
11136µs178µsMail::SpamAssassin::::BEGIN@79Mail::SpamAssassin::BEGIN@79
11136µs48µsMail::SpamAssassin::::VersionMail::SpamAssassin::Version
21135µs989µsMail::SpamAssassin::::get_cf_files_in_dirMail::SpamAssassin::get_cf_files_in_dir
11133µs71µsMail::SpamAssassin::::BEGIN@63Mail::SpamAssassin::BEGIN@63
11132µs657µsMail::SpamAssassin::::BEGIN@90Mail::SpamAssassin::BEGIN@90
11132µs286µsMail::SpamAssassin::::BEGIN@83Mail::SpamAssassin::BEGIN@83
11130µs5.48sMail::SpamAssassin::::finish_learnerMail::SpamAssassin::finish_learner
11130µs36µsMail::SpamAssassin::::BEGIN@64Mail::SpamAssassin::BEGIN@64
11128µs252µsMail::SpamAssassin::::BEGIN@87Mail::SpamAssassin::BEGIN@87
11126µs164µsMail::SpamAssassin::::BEGIN@82Mail::SpamAssassin::BEGIN@82
11126µs67µsMail::SpamAssassin::::BEGIN@88Mail::SpamAssassin::BEGIN@88
11126µs374µsMail::SpamAssassin::::BEGIN@86Mail::SpamAssassin::BEGIN@86
11110µs10µsMail::SpamAssassin::::set_persistent_address_list_factoryMail::SpamAssassin::set_persistent_address_list_factory
0000s0sMail::SpamAssassin::::add_address_to_blacklistMail::SpamAssassin::add_address_to_blacklist
0000s0sMail::SpamAssassin::::add_address_to_whitelistMail::SpamAssassin::add_address_to_whitelist
0000s0sMail::SpamAssassin::::add_all_addresses_to_blacklistMail::SpamAssassin::add_all_addresses_to_blacklist
0000s0sMail::SpamAssassin::::add_all_addresses_to_whitelistMail::SpamAssassin::add_all_addresses_to_whitelist
0000s0sMail::SpamAssassin::::checkMail::SpamAssassin::check
0000s0sMail::SpamAssassin::::check_message_textMail::SpamAssassin::check_message_text
0000s0sMail::SpamAssassin::::compile_nowMail::SpamAssassin::compile_now
0000s0sMail::SpamAssassin::::copy_configMail::SpamAssassin::copy_config
0000s0sMail::SpamAssassin::::create_default_prefsMail::SpamAssassin::create_default_prefs
0000s0sMail::SpamAssassin::::debug_diagnosticsMail::SpamAssassin::debug_diagnostics
0000s0sMail::SpamAssassin::::dump_bayes_dbMail::SpamAssassin::dump_bayes_db
0000s0sMail::SpamAssassin::::find_all_addrs_in_mailMail::SpamAssassin::find_all_addrs_in_mail
0000s0sMail::SpamAssassin::::finishMail::SpamAssassin::finish
0000s0sMail::SpamAssassin::::get_loaded_plugins_listMail::SpamAssassin::get_loaded_plugins_list
0000s0sMail::SpamAssassin::::get_perl_major_versionMail::SpamAssassin::get_perl_major_version
0000s0sMail::SpamAssassin::::have_pluginMail::SpamAssassin::have_plugin
0000s0sMail::SpamAssassin::::lint_rulesMail::SpamAssassin::lint_rules
0000s0sMail::SpamAssassin::::load_scoreonly_ldapMail::SpamAssassin::load_scoreonly_ldap
0000s0sMail::SpamAssassin::::load_scoreonly_sqlMail::SpamAssassin::load_scoreonly_sql
0000s0sMail::SpamAssassin::::read_scoreonly_configMail::SpamAssassin::read_scoreonly_config
0000s0sMail::SpamAssassin::::rebuild_learner_cachesMail::SpamAssassin::rebuild_learner_caches
0000s0sMail::SpamAssassin::::remove_address_from_whitelistMail::SpamAssassin::remove_address_from_whitelist
0000s0sMail::SpamAssassin::::remove_all_addresses_from_whitelistMail::SpamAssassin::remove_all_addresses_from_whitelist
0000s0sMail::SpamAssassin::::remove_spamassassin_markupMail::SpamAssassin::remove_spamassassin_markup
0000s0sMail::SpamAssassin::::report_as_spamMail::SpamAssassin::report_as_spam
0000s0sMail::SpamAssassin::::revoke_as_spamMail::SpamAssassin::revoke_as_spam
0000s0sMail::SpamAssassin::::sa_dieMail::SpamAssassin::sa_die
0000s0sMail::SpamAssassin::::signal_user_changedMail::SpamAssassin::signal_user_changed
0000s0sMail::SpamAssassin::::timer_disableMail::SpamAssassin::timer_disable
0000s0sMail::SpamAssassin::::timer_enableMail::SpamAssassin::timer_enable
0000s0sMail::SpamAssassin::::timer_endMail::SpamAssassin::timer_end
0000s0sMail::SpamAssassin::::timer_reportMail::SpamAssassin::timer_report
0000s0sMail::SpamAssassin::::timer_resetMail::SpamAssassin::timer_reset
0000s0sMail::SpamAssassin::::timer_startMail::SpamAssassin::timer_start
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 - Spam detector and markup engine
21
22=head1 SYNOPSIS
23
24 my $spamtest = Mail::SpamAssassin->new();
25 my $mail = $spamtest->parse($message);
26 my $status = $spamtest->check($mail);
27
28 if ($status->is_spam()) {
29 $message = $status->rewrite_mail();
30 }
31 else {
32 ...
33 }
34 ...
35
36 $status->finish();
37 $mail->finish();
38 $spamtest->finish();
39
40=head1 DESCRIPTION
41
42Mail::SpamAssassin is a module to identify spam using several methods
43including text analysis, internet-based realtime blacklists, statistical
44analysis, and internet-based hashing algorithms.
45
46Using its rule base, it uses a wide range of heuristic tests on mail
47headers and body text to identify "spam", also known as unsolicited bulk
48email. Once identified as spam, the mail can then be tagged as spam for
49later filtering using the user's own mail user agent application or at
50the mail transfer agent.
51
52If you wish to use a command-line filter tool, try the C<spamassassin>
53or the C<spamd>/C<spamc> tools provided.
54
55=head1 METHODS
56
57=over 4
58
59=cut
60
61package Mail::SpamAssassin;
62274µs283µs
# spent 74µs (66+9) within Mail::SpamAssassin::BEGIN@62 which was called: # once (66µs+9µs) by main::BEGIN@65 at line 62
use strict;
# spent 74µs making 1 call to Mail::SpamAssassin::BEGIN@62 # spent 9µs making 1 call to strict::import
63281µs2108µs
# spent 71µs (33+38) within Mail::SpamAssassin::BEGIN@63 which was called: # once (33µs+38µs) by main::BEGIN@65 at line 63
use warnings;
# spent 71µs making 1 call to Mail::SpamAssassin::BEGIN@63 # spent 38µs making 1 call to warnings::import
64281µs243µs
# spent 36µs (30+7) within Mail::SpamAssassin::BEGIN@64 which was called: # once (30µs+7µs) by main::BEGIN@65 at line 64
use bytes;
# spent 36µs making 1 call to Mail::SpamAssassin::BEGIN@64 # spent 7µs making 1 call to bytes::import
652342µs24.22ms
# spent 4.12ms (3.46+658µs) within Mail::SpamAssassin::BEGIN@65 which was called: # once (3.46ms+658µs) by main::BEGIN@65 at line 65
use re 'taint';
# spent 4.12ms making 1 call to Mail::SpamAssassin::BEGIN@65 # spent 101µs making 1 call to re::import
66
67130µsrequire 5.006_001;
68
692352µs230.0ms
# spent 29.8ms (3.99+25.8) within Mail::SpamAssassin::BEGIN@69 which was called: # once (3.99ms+25.8ms) by main::BEGIN@65 at line 69
use Mail::SpamAssassin::Logger;
# spent 29.8ms making 1 call to Mail::SpamAssassin::BEGIN@69 # spent 195µs making 1 call to Exporter::import
702312µs29.66ms
# spent 9.55ms (4.41+5.14) within Mail::SpamAssassin::BEGIN@70 which was called: # once (4.41ms+5.14ms) by main::BEGIN@65 at line 70
use Mail::SpamAssassin::Constants;
# spent 9.55ms making 1 call to Mail::SpamAssassin::BEGIN@70 # spent 110µs making 1 call to Exporter::import
712449µs1217ms
# spent 217ms (31.4+185) within Mail::SpamAssassin::BEGIN@71 which was called: # once (31.4ms+185ms) by main::BEGIN@65 at line 71
use Mail::SpamAssassin::Conf;
# spent 217ms making 1 call to Mail::SpamAssassin::BEGIN@71
722371µs12.69ms
# spent 2.69ms (2.11+577µs) within Mail::SpamAssassin::BEGIN@72 which was called: # once (2.11ms+577µs) by main::BEGIN@65 at line 72
use Mail::SpamAssassin::Conf::SQL;
# spent 2.69ms making 1 call to Mail::SpamAssassin::BEGIN@72
732357µs12.58ms
# spent 2.58ms (2.00+586µs) within Mail::SpamAssassin::BEGIN@73 which was called: # once (2.00ms+586µs) by main::BEGIN@65 at line 73
use Mail::SpamAssassin::Conf::LDAP;
# spent 2.58ms making 1 call to Mail::SpamAssassin::BEGIN@73
742434µs141.6ms
# spent 41.6ms (27.7+14.0) within Mail::SpamAssassin::BEGIN@74 which was called: # once (27.7ms+14.0ms) by main::BEGIN@65 at line 74
use Mail::SpamAssassin::PerMsgStatus;
# spent 41.6ms making 1 call to Mail::SpamAssassin::BEGIN@74
752430µs1289ms
# spent 289ms (10.7+278) within Mail::SpamAssassin::BEGIN@75 which was called: # once (10.7ms+278ms) by main::BEGIN@65 at line 75
use Mail::SpamAssassin::Message;
# spent 289ms making 1 call to Mail::SpamAssassin::BEGIN@75
762376µs16.44ms
# spent 6.44ms (2.66+3.79) within Mail::SpamAssassin::BEGIN@76 which was called: # once (2.66ms+3.79ms) by main::BEGIN@65 at line 76
use Mail::SpamAssassin::PluginHandler;
# spent 6.44ms making 1 call to Mail::SpamAssassin::BEGIN@76
772346µs118.0ms
# spent 18.0ms (10.1+7.91) within Mail::SpamAssassin::BEGIN@77 which was called: # once (10.1ms+7.91ms) by main::BEGIN@65 at line 77
use Mail::SpamAssassin::DnsResolver;
# spent 18.0ms making 1 call to Mail::SpamAssassin::BEGIN@77
782349µs12.93ms
# spent 2.93ms (2.57+359µs) within Mail::SpamAssassin::BEGIN@78 which was called: # once (2.57ms+359µs) by main::BEGIN@65 at line 78
use Mail::SpamAssassin::RegistryBoundaries;
# spent 2.93ms making 1 call to Mail::SpamAssassin::BEGIN@78
79263µs2320µs
# spent 178µs (36+142) within Mail::SpamAssassin::BEGIN@79 which was called: # once (36µs+142µs) by main::BEGIN@65 at line 79
use Mail::SpamAssassin::Util qw(untaint_var am_running_on_windows);
# spent 178µs making 1 call to Mail::SpamAssassin::BEGIN@79 # spent 142µs making 1 call to Exporter::import
802302µs1920µs
# spent 920µs (684+236) within Mail::SpamAssassin::BEGIN@80 which was called: # once (684µs+236µs) by main::BEGIN@65 at line 80
use Mail::SpamAssassin::Util::ScopedTimer;
# spent 920µs making 1 call to Mail::SpamAssassin::BEGIN@80
81
82261µs2301µs
# spent 164µs (26+137) within Mail::SpamAssassin::BEGIN@82 which was called: # once (26µs+137µs) by main::BEGIN@65 at line 82
use Errno qw(ENOENT EACCES);
# spent 164µs making 1 call to Mail::SpamAssassin::BEGIN@82 # spent 137µs making 1 call to Exporter::import
83273µs2540µs
# spent 286µs (32+254) within Mail::SpamAssassin::BEGIN@83 which was called: # once (32µs+254µs) by main::BEGIN@65 at line 83
use File::Basename;
# spent 286µs making 1 call to Mail::SpamAssassin::BEGIN@83 # spent 254µs making 1 call to Exporter::import
842368µs216.5ms
# spent 16.3ms (6.82+9.50) within Mail::SpamAssassin::BEGIN@84 which was called: # once (6.82ms+9.50ms) by main::BEGIN@65 at line 84
use File::Path;
# spent 16.3ms making 1 call to Mail::SpamAssassin::BEGIN@84 # spent 173µs making 1 call to Exporter::import
853127µs289µs
# spent 64µs (39+25) within Mail::SpamAssassin::BEGIN@85 which was called: # once (39µs+25µs) by main::BEGIN@65 at line 85
use File::Spec 0.8;
# spent 64µs making 1 call to Mail::SpamAssassin::BEGIN@85 # spent 25µs making 1 call to version::_VERSION
86279µs2721µs
# spent 374µs (26+348) within Mail::SpamAssassin::BEGIN@86 which was called: # once (26µs+348µs) by main::BEGIN@65 at line 86
use Time::HiRes qw(time);
# spent 374µs making 1 call to Mail::SpamAssassin::BEGIN@86 # spent 348µs making 1 call to Time::HiRes::import
87271µs2476µs
# spent 252µs (28+224) within Mail::SpamAssassin::BEGIN@87 which was called: # once (28µs+224µs) by main::BEGIN@65 at line 87
use Cwd;
# spent 252µs making 1 call to Mail::SpamAssassin::BEGIN@87 # spent 224µs making 1 call to Exporter::import
88299µs2107µs
# spent 67µs (26+40) within Mail::SpamAssassin::BEGIN@88 which was called: # once (26µs+40µs) by main::BEGIN@65 at line 88
use Config;
# spent 67µs making 1 call to Mail::SpamAssassin::BEGIN@88 # spent 40µs making 1 call to Config::import
89
9012µs
# spent 657µs (32+625) within Mail::SpamAssassin::BEGIN@90 which was called: # once (32µs+625µs) by main::BEGIN@65 at line 95
use vars qw{
91 @ISA $VERSION $SUB_VERSION @EXTRA_VERSION $IS_DEVEL_BUILD $HOME_URL
92 @default_rules_path @default_prefs_path
93 @default_userprefs_path @default_userstate_dir
94 @site_rules_path
95116.7ms21.28ms};
# spent 657µs making 1 call to Mail::SpamAssassin::BEGIN@90 # spent 625µs making 1 call to vars::import
96
9712µs$VERSION = "3.004001"; # update after release (same format as perl $])
98#$IS_DEVEL_BUILD = 1; # change for release versions
99
100# Used during the prerelease/release-candidate part of the official release
101# process. If you hacked up your SA, you should add a version_tag to your .cf
102# files; this variable should not be modified.
103#@EXTRA_VERSION = qw();
104
105120µs@ISA = qw();
106
107# SUB_VERSION is now just <yyyy>-<mm>-<dd>
10812µs$SUB_VERSION = 'svnunknown';
109138µs19µsif ('$LastChangedDate: 2015-04-28 03:54:45 -0400 (Tue, 28 Apr 2015) $' =~ ':') {
# spent 9µs making 1 call to Mail::SpamAssassin::CORE:match
110 # Subversion keyword "$LastChangedDate: 2015-04-28 03:54:45 -0400 (Tue, 28 Apr 2015) $" has been successfully expanded.
111 # Doesn't happen with automated launchpad builds:
112 # https://bugs.launchpad.net/launchpad/+bug/780916
113123µs $SUB_VERSION = (split(/\s+/,'$LastChangedDate: 2015-04-28 03:54:45 -0400 (Tue, 28 Apr 2015) $ updated by SVN'))[1];
114}
115
116
11712µsif (defined $IS_DEVEL_BUILD && $IS_DEVEL_BUILD) {
118 if ('$LastChangedRevision: 1676427 $' =~ ':') {
119 # Subversion keyword "$LastChangedRevision: 1676427 $" has been successfully expanded.
120 push(@EXTRA_VERSION, ('r' . qw{$LastChangedRevision: 1676427 $ updated by SVN}[1]));
121 } else {
122 push(@EXTRA_VERSION, ('r' . 'svnunknown'));
123 }
124}
125
126
# spent 48µs (36+12) within Mail::SpamAssassin::Version which was called: # once (36µs+12µs) by Mail::SpamAssassin::new at line 407
sub Version {
127122µs112µs $VERSION =~ /^(\d+)\.(\d\d\d)(\d\d\d)$/;
# spent 12µs making 1 call to Mail::SpamAssassin::CORE:match
128128µs return join('-', sprintf("%d.%d.%d", $1, $2, $3), @EXTRA_VERSION);
129}
130
13112µs$HOME_URL = "http://spamassassin.apache.org/";
132
133# note that the CWD takes priority. This is required in case a user
134# is testing a new version of SpamAssassin on a machine with an older
135# version installed. Unless you can come up with a fix for this that
136# allows "make test" to work, don't change this.
13714µs@default_rules_path = (
138 '__local_state_dir__/__version__',
139 '__def_rules_dir__',
140 '__prefix__/share/spamassassin',
141 '/usr/local/share/spamassassin',
142 '/usr/share/spamassassin',
143);
144
145# first 3 are BSDish, latter 2 Linuxish
14614µs@site_rules_path = (
147 '__local_rules_dir__',
148 '__prefix__/etc/mail/spamassassin',
149 '__prefix__/etc/spamassassin',
150 '/usr/local/etc/spamassassin',
151 '/usr/pkg/etc/spamassassin',
152 '/usr/etc/spamassassin',
153 '/etc/mail/spamassassin',
154 '/etc/spamassassin',
155);
156
15714µs@default_prefs_path = (
158 '__local_rules_dir__/user_prefs.template',
159 '__prefix__/etc/mail/spamassassin/user_prefs.template',
160 '__prefix__/share/spamassassin/user_prefs.template',
161 '/etc/spamassassin/user_prefs.template',
162 '/etc/mail/spamassassin/user_prefs.template',
163 '/usr/local/share/spamassassin/user_prefs.template',
164 '/usr/share/spamassassin/user_prefs.template',
165);
166
16712µs@default_userprefs_path = (
168 '~/.spamassassin/user_prefs',
169);
170
17114µs@default_userstate_dir = (
172 '~/.spamassassin',
173);
174
175###########################################################################
176
177=item $t = Mail::SpamAssassin->new( { opt => val, ... } )
178
179Constructs a new C<Mail::SpamAssassin> object. You may pass a hash
180reference to the constructor which may contain the following attribute-
181value pairs.
182
183=over 4
184
185=item debug
186
187This is the debug options used to determine logging level. It exists to
188allow sections of debug messages (called "facilities") to be enabled or
189disabled. If this is a string, it is treated as a comma-delimited list
190of the debug facilities. If it's a hash reference, then the keys are
191treated as the list of debug facilities and if it's a array reference,
192then the elements are treated as the list of debug facilities.
193
194There are also two special cases: (1) if the special case of "info" is
195passed as a debug facility, then all informational messages are enabled;
196(2) if the special case of "all" is passed as a debug facility, then all
197debugging facilities are enabled.
198
199=item rules_filename
200
201The filename/directory to load spam-identifying rules from. (optional)
202
203=item site_rules_filename
204
205The filename/directory to load site-specific spam-identifying rules from.
206(optional)
207
208=item userprefs_filename
209
210The filename to load preferences from. (optional)
211
212=item userstate_dir
213
214The directory user state is stored in. (optional)
215
216=item config_tree_recurse
217
218Set to C<1> to recurse through directories when reading configuration
219files, instead of just reading a single level. (optional, default 0)
220
221=item config_text
222
223The text of all rules and preferences. If you prefer not to load the rules
224from files, read them in yourself and set this instead. As a result, this will
225override the settings for C<rules_filename>, C<site_rules_filename>,
226and C<userprefs_filename>.
227
228=item pre_config_text
229
230Similar to C<config_text>, this text is placed before config_text to allow an
231override of config files.
232
233=item post_config_text
234
235Similar to C<config_text>, this text is placed after config_text to allow an
236override of config files.
237
238=item force_ipv4
239
240If set to 1, DNS or other network tests will prefer IPv4 and not attempt
241to use IPv6. Use if the existing tests for IPv6 availability produce
242incorrect results or crashes.
243
244=item force_ipv6
245
246For symmetry with force_ipv4: if set to 1, DNS or other network tests
247will prefer IPv6 and not attempt to use IPv4. Some plugins may disregard
248this setting and use whatever protocol family they are comfortable with.
249
250=item require_rules
251
252If set to 1, init() will die if no valid rules could be loaded. This is the
253default behaviour when called by C<spamassassin> or C<spamd>.
254
255=item languages_filename
256
257If you want to be able to use the language-guessing rule
258C<UNWANTED_LANGUAGE_BODY>, and are using C<config_text> instead of
259C<rules_filename>, C<site_rules_filename>, and C<userprefs_filename>, you will
260need to set this. It should be the path to the B<languages> file normally
261found in the SpamAssassin B<rules> directory.
262
263=item local_tests_only
264
265If set to 1, no tests that require internet access will be performed. (default:
2660)
267
268=item need_tags
269
270The option provides a way to avoid more expensive processing when it is known
271in advance that some information will not be needed by a caller.
272
273A value of the option can either be a string (a comma-delimited list of tag
274names), or a reference to a list of individual tag names. A caller may provide
275the list in advance, specifying his intention to later collect the information
276through $pms->get_tag() calls. If a name of a tag starts with a 'NO' (case
277insensitive), it shows that a caller will not be interested in such tag,
278although there is no guarantee it would save any resources, nor that a tag
279value will be empty. Currently no built-in tags start with 'NO'. A later
280entry overrides previous one, e.g. ASN,NOASN,ASN,TIMING,NOASN is equivalent
281to TIMING,NOASN.
282
283For backward compatibility, all tags available as of version 3.2.4 will
284be available by default (unless disabled by NOtag), even if not requested
285through need_tags option. Future versions may provide new tags conditionally
286available.
287
288Currently the only tag that needs to be explicitly requested is 'TIMING'.
289Not requesting it can save a millisecond or two - it mostly serves to
290illustrate the usage of need_tags.
291
292Example:
293 need_tags => 'TIMING,noLANGUAGES,RELAYCOUNTRY,ASN,noASNCIDR',
294or:
295 need_tags => [qw(TIMING noLANGUAGES RELAYCOUNTRY ASN noASNCIDR)],
296
297=item ignore_site_cf_files
298
299If set to 1, any rule files found in the C<site_rules_filename> directory will
300be ignored. *.pre files (used for loading plugins) found in the
301C<site_rules_filename> directory will still be used. (default: 0)
302
303=item dont_copy_prefs
304
305If set to 1, the user preferences file will not be created if it doesn't
306already exist. (default: 0)
307
308=item save_pattern_hits
309
310If set to 1, the patterns hit can be retrieved from the
311C<Mail::SpamAssassin::PerMsgStatus> object. Used for debugging.
312
313=item home_dir_for_helpers
314
315If set, the B<HOME> environment variable will be set to this value
316when using test applications that require their configuration data,
317such as Razor, Pyzor and DCC.
318
319=item username
320
321If set, the C<username> attribute will use this as the current user's name.
322Otherwise, the default is taken from the runtime environment (ie. this process'
323effective UID under UNIX).
324
325=item skip_prng_reseeding
326
327If skip_prng_reseeding is set to true, the SpamAssassin library will B<not>
328call srand() to reseed a pseudo-random number generator (PRNG). The srand()
329Perl function should be called during initialization of each child process,
330soon after forking.
331
332Prior to version 3.4.0, calling srand() was handled by the SpamAssassin
333library.
334
335This setting requires the caller to decide when to call srand().
336This choice may be desired to preserve the entropy of a PRNG. The default
337value of skip_prng_reseeding is false to maintain backward compatibility.
338
339This option should only be set by a caller if it calls srand() upon spawning
340child processes. Unless you are certain you need it, leave this setting as
341false.
342
343NOTE: The skip_prng_reseeding feature is implemented in spamd as of 3.4.0
344which allows spamd to call srand() right after forking a child process.
345
346=back
347
348If none of C<rules_filename>, C<site_rules_filename>, C<userprefs_filename>, or
349C<config_text> is set, the C<Mail::SpamAssassin> module will search for the
350configuration files in the usual installed locations using the below variable
351definitions which can be passed in.
352
353=over 4
354
355=item PREFIX
356
357Used as the root for certain directory paths such as:
358
359 '__prefix__/etc/mail/spamassassin'
360 '__prefix__/etc/spamassassin'
361
362Defaults to "/usr/local".
363
364=item DEF_RULES_DIR
365
366Location where the default rules are installed. Defaults to
367"/usr/local/share/spamassassin".
368
369=item LOCAL_RULES_DIR
370
371Location where the local site rules are installed. Defaults to
372"/usr/local/etc/mail/spamassassin".
373
374=item LOCAL_STATE_DIR
375
376Location of the local state directory, mainly used for installing updates via
377C<sa-update> and compiling rulesets to native code. Defaults to
378"/var/db/spamassassin".
379
380=back
381
382
383=cut
384
385# undocumented ctor settings:
386#
387# - keep_config_parsing_metadata: used by build/listpromotable, default 0
388
389
# spent 47.7ms (278µs+47.4) within Mail::SpamAssassin::new which was called: # once (278µs+47.4ms) by main::RUNTIME at line 227 of /usr/local/bin/sa-learn
sub new {
39012µs my $class = shift;
39113µs $class = ref($class) || $class;
392
39312µs my $self = shift;
39412µs if (!defined $self) { $self = { }; }
39513µs bless ($self, $class);
396
397 # basic backward compatibility; debug used to be a boolean.
398 # translate that into 'all', which is what it meant before 3.1.0.
399110µs if ($self->{debug} && $self->{debug} eq '1') {
400 $self->{debug} = 'all';
401 }
402
403 # enable or disable debugging
404110µs124µs Mail::SpamAssassin::Logger::add_facilities($self->{debug});
# spent 24µs making 1 call to Mail::SpamAssassin::Logger::add_facilities
405
406 # first debugging information possibly printed should be the version
407117µs258µs dbg("generic: SpamAssassin version " . Version());
# spent 48µs making 1 call to Mail::SpamAssassin::Version # spent 10µs making 1 call to Mail::SpamAssassin::Logger::dbg
408
409 # if the libs are installed in an alternate location, and the caller
410 # didn't set PREFIX, we should have an estimated guess ready, values
411 # substituted at 'make' time
41212µs $self->{PREFIX} ||= '/usr/local';
41312µs $self->{DEF_RULES_DIR} ||= '/usr/local/share/spamassassin';
41412µs $self->{LOCAL_RULES_DIR} ||= '/usr/local/etc/mail/spamassassin';
41514µs $self->{LOCAL_STATE_DIR} ||= '/var/db/spamassassin';
416537µs16µs dbg("generic: Perl %s, %s", $], join(", ", map { $_ . '=' . $self->{$_} }
# spent 6µs making 1 call to Mail::SpamAssassin::Logger::dbg
417 qw(PREFIX DEF_RULES_DIR LOCAL_RULES_DIR LOCAL_STATE_DIR)));
418
41914µs $self->{needed_tags} = {};
42026µs { my $ntags = $self->{need_tags};
42112µs if (defined $ntags) {
422 for my $t (ref $ntags ? @$ntags : split(/[, \s]+/,$ntags)) {
423 $self->{needed_tags}->{$2} = !defined($1) if $t =~ /^(NO)?(.+)\z/si;
424 }
425 }
426 }
427113µs116µs if (would_log('dbg','timing') || $self->{needed_tags}->{TIMING}) {
# spent 16µs making 1 call to Mail::SpamAssassin::Logger::would_log
428 $self->timer_enable();
429 }
430
431115µs127.1ms $self->{conf} ||= new Mail::SpamAssassin::Conf ($self);
# spent 27.1ms making 1 call to Mail::SpamAssassin::Conf::new
432118µs15.19ms $self->{registryboundaries} = Mail::SpamAssassin::RegistryBoundaries->new ($self);
# spent 5.19ms making 1 call to Mail::SpamAssassin::RegistryBoundaries::new
433122µs128µs $self->{plugins} = Mail::SpamAssassin::PluginHandler->new ($self);
# spent 28µs making 1 call to Mail::SpamAssassin::PluginHandler::new
434
43513µs $self->{save_pattern_hits} ||= 0;
436
437 # Make sure that we clean $PATH if we're tainted
438110µs12.19ms Mail::SpamAssassin::Util::clean_path_in_taint_mode();
439
44016µs if (!defined $self->{username}) {
441112µs11.21ms $self->{username} = (Mail::SpamAssassin::Util::portable_getpwuid ($>))[0];
# spent 1.21ms making 1 call to Mail::SpamAssassin::Util::portable_getpwuid
442 }
443
444110µs111.6ms $self->create_locker();
# spent 11.6ms making 1 call to Mail::SpamAssassin::create_locker
445
446111µs $self;
447}
448
449
# spent 11.6ms (200µs+11.4) within Mail::SpamAssassin::create_locker which was called: # once (200µs+11.4ms) by Mail::SpamAssassin::new at line 444
sub create_locker {
45013µs my ($self) = @_;
451
45212µs my $class;
45314µs my $m = $self->{conf}->{lock_method};
454
455 # let people choose what they want -- even if they may not work on their
456 # OS. (they could be using cygwin!)
45716µs if ($m eq 'win32') { $class = 'Win32'; }
458 elsif ($m eq 'flock') { $class = 'Flock'; }
459 elsif ($m eq 'nfssafe') { $class = 'UnixNFSSafe'; }
460 else {
461 # OS-specific defaults
462110µs121µs if (am_running_on_windows()) {
# spent 21µs making 1 call to Mail::SpamAssassin::Util::am_running_on_windows
463 $class = 'Win32';
464 } else {
46512µs $class = 'UnixNFSSafe';
466 }
467 }
468
469 # this could probably be made a little faster; for now I'm going
470 # for slow but safe, by keeping in quotes
471 eval '
472 use Mail::SpamAssassin::Locker::'.$class.';
473 $self->{locker} = new Mail::SpamAssassin::Locker::'.$class.' ($self);
474 1;
475189µs ' or do {
# spent 386µs executing statements in string eval
# includes 3.23ms spent executing 1 call to 1 sub defined therein.
476 my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat;
477 die "Mail::SpamAssassin::Locker::$class error: $eval_stat\n";
478 };
479
480111µs if (!defined $self->{locker}) { die "locker: oops! no locker"; }
481}
482
483###########################################################################
484
485=item parse($message, $parse_now [, $suppl_attrib])
486
487Parse will return a Mail::SpamAssassin::Message object with just the
488headers parsed. When calling this function, there are two optional
489parameters that can be passed in: $message is either undef (which
490will use STDIN), a scalar - a string containing an entire message,
491a reference to such string, an array reference of the message with
492one line per array element, or either a file glob or an IO::File object
493which holds the entire contents of the message; and $parse_now, which
494specifies whether or not to create a MIME tree at parse time or later
495as necessary.
496
497The I<$parse_now> option, by default, is set to false (0). This
498allows SpamAssassin to not have to generate the tree of internal
499data nodes if the information is not going to be used. This is
500handy, for instance, when running C<spamassassin -d>, which only
501needs the pristine header and body which is always parsed and stored
502by this function.
503
504The optional last argument I<$suppl_attrib> provides a way for a caller
505to pass additional information about a message to SpamAssassin. It is
506either undef, or a ref to a hash where each key/value pair provides some
507supplementary attribute of the message, typically information that cannot
508be deduced from the message itself, or is hard to do so reliably, or would
509represent unnecessary work for SpamAssassin to obtain it. The argument will
510be stored to a Mail::SpamAssassin::Message object as 'suppl_attrib', thus
511made available to the rest of the code as well as to plugins. The exact list
512of attributes will evolve through time, any unknown attribute should be
513ignored. Possible examples are: SMTP envelope information, a flag indicating
514that a message as supplied by a caller was truncated due to size limit, an
515already verified list of DKIM signature objects, or perhaps a list of rule
516hits predetermined by a caller, which makes another possible way for a
517caller to provide meta information (instead of having to insert made-up
518header fields in order to pass information), or maybe just plain rule hits.
519
520For more information, please see the C<Mail::SpamAssassin::Message>
521and C<Mail::SpamAssassin::Message::Node> POD.
522
523=cut
524
525
# spent 4.61s (29.9ms+4.58) within Mail::SpamAssassin::parse which was called 235 times, avg 19.6ms/call: # 235 times (29.9ms+4.58s) by main::wanted at line 566 of /usr/local/bin/sa-learn, avg 19.6ms/call
sub parse {
526235653µs my($self, $message, $parsenow, $suppl_attrib) = @_;
527
5282353.97ms2351.98ms my $start_time = time;
# spent 1.98ms making 235 calls to Time::HiRes::time, avg 8µs/call
5292352.31ms2354.18ms $self->init(1);
# spent 4.18ms making 235 calls to Mail::SpamAssassin::init, avg 18µs/call
5302351.86ms2352.12ms my $timer = $self->time_method("parse");
# spent 2.12ms making 235 calls to Mail::SpamAssassin::time_method, avg 9µs/call
531
532235450µs my $master_deadline;
533 # passed in at a function call
534235503µs if (ref $suppl_attrib && exists $suppl_attrib->{master_deadline}) {
535 $master_deadline = $suppl_attrib->{master_deadline}; # may be undef
536 }
537 # found in a config file - overrides passed-in number if lower
5382351.71ms if ($self->{conf}->{time_limit}) { # defined and nonzero
539235746µs my $time_limit_deadline = $start_time + $self->{conf}->{time_limit};
540235940µs if (!defined $master_deadline || $time_limit_deadline < $master_deadline) {
541235466µs $master_deadline = $time_limit_deadline;
542 }
543 }
5442351.97ms if (defined $master_deadline) {
5452351.93ms2351.81ms dbg("config: time limit %.1f s", $master_deadline - $start_time);
# spent 1.81ms making 235 calls to Mail::SpamAssassin::Logger::dbg, avg 8µs/call
546 }
547
548 my $msg = Mail::SpamAssassin::Message->new({
549 message=>$message, parsenow=>$parsenow,
550 normalize=>$self->{conf}->{normalize_charset},
5512355.22ms2354.55s master_deadline=>$master_deadline, suppl_attrib=>$suppl_attrib });
# spent 4.55s making 235 calls to Mail::SpamAssassin::Message::new, avg 19.4ms/call
552
553 # bug 5069: The goal here is to get rendering plugins to do things
554 # like OCR, convert doc and pdf to text, etc, though it could be anything
555 # that wants to process the message after it's been parsed.
5562352.54ms23517.7ms $self->call_plugins("post_message_parse", { message => $msg });
# spent 17.7ms making 235 calls to Mail::SpamAssassin::call_plugins, avg 75µs/call
557
5582351.89ms return $msg;
559}
560
561
562###########################################################################
563
564=item $status = $f->check ($mail)
565
566Check a mail, encapsulated in a C<Mail::SpamAssassin::Message> object,
567to determine if it is spam or not.
568
569Returns a C<Mail::SpamAssassin::PerMsgStatus> object which can be
570used to test or manipulate the mail message.
571
572Note that the C<Mail::SpamAssassin> object can be re-used for further messages
573without affecting this check; in OO terminology, the C<Mail::SpamAssassin>
574object is a "factory". However, if you do this, be sure to call the
575C<finish()> method on the status objects when you're done with them.
576
577=cut
578
579sub check {
580 my ($self, $mail_obj) = @_;
581
582 $self->init(1);
583 my $pms = Mail::SpamAssassin::PerMsgStatus->new($self, $mail_obj);
584 $pms->check();
585 dbg("timing: " . $self->timer_report()) if $self->{timer_enabled};
586 $pms;
587}
588
589=item $status = $f->check_message_text ($mailtext)
590
591Check a mail, encapsulated in a plain string C<$mailtext>, to determine if it
592is spam or not.
593
594Otherwise identical to C<check()> above.
595
596=cut
597
598sub check_message_text {
599 my ($self, $mailtext) = @_;
600 my $msg = $self->parse($mailtext, 1);
601 my $result = $self->check($msg);
602
603 # Kill off the metadata ...
604 # Do _NOT_ call normal finish() here. PerMsgStatus has a copy of
605 # the message. So killing it here will cause things like
606 # rewrite_message() to fail. <grrr>
607 #
608 $msg->finish_metadata();
609
610 return $result;
611}
612
613###########################################################################
614
615=item $status = $f->learn ($mail, $id, $isspam, $forget)
616
617Learn from a mail, encapsulated in a C<Mail::SpamAssassin::Message> object.
618
619If C<$isspam> is set, the mail is assumed to be spam, otherwise it will
620be learnt as non-spam.
621
622If C<$forget> is set, the attributes of the mail will be removed from
623both the non-spam and spam learning databases.
624
625C<$id> is an optional message-identification string, used internally
626to tag the message. If it is C<undef>, the Message-Id of the message
627will be used. It should be unique to that message.
628
629Returns a C<Mail::SpamAssassin::PerMsgLearner> object which can be used to
630manipulate the learning process for each mail.
631
632Note that the C<Mail::SpamAssassin> object can be re-used for further messages
633without affecting this check; in OO terminology, the C<Mail::SpamAssassin>
634object is a "factory". However, if you do this, be sure to call the
635C<finish()> method on the learner objects when you're done with them.
636
637C<learn()> and C<check()> can be run using the same factory. C<init_learner()>
638must be called before using this method.
639
640=cut
641
642
# spent 1570s (25.0ms+1570) within Mail::SpamAssassin::learn which was called 235 times, avg 6.68s/call: # 235 times (25.0ms+1570s) by main::wanted at line 574 of /usr/local/bin/sa-learn, avg 6.68s/call
sub learn {
643235611µs my ($self, $mail_obj, $id, $isspam, $forget) = @_;
644235489µs local ($_);
645
6462351.31ms require Mail::SpamAssassin::PerMsgLearner;
6472351.77ms2353.51ms $self->init(1);
# spent 3.51ms making 235 calls to Mail::SpamAssassin::init, avg 15µs/call
6482352.48ms2357.89ms my $msg = Mail::SpamAssassin::PerMsgLearner->new($self, $mail_obj);
# spent 7.89ms making 235 calls to Mail::SpamAssassin::PerMsgLearner::new, avg 34µs/call
649
6502351.56ms if ($forget) {
651 dbg("learn: forgetting message");
652 $msg->forget($id);
653 } elsif ($isspam) {
6542351.60ms2351.52ms dbg("learn: learning spam");
# spent 1.52ms making 235 calls to Mail::SpamAssassin::Logger::dbg, avg 6µs/call
6552351.97ms2351570s $msg->learn_spam($id);
# spent 1570s making 235 calls to Mail::SpamAssassin::PerMsgLearner::learn_spam, avg 6.68s/call
656 } else {
657 dbg("learn: learning ham");
658 $msg->learn_ham($id);
659 }
660
6612358.71ms $msg;
662}
663
664###########################################################################
665
666=item $f->init_learner ( [ { opt => val, ... } ] )
667
668Initialise learning. You may pass the following attribute-value pairs to this
669method.
670
671=over 4
672
673=item caller_will_untie
674
675Whether or not the code calling this method will take care of untie'ing
676from the Bayes databases (by calling C<finish_learner()>) (optional, default 0).
677
678=item force_expire
679
680Should an expiration run be forced to occur immediately? (optional, default 0).
681
682=item learn_to_journal
683
684Should learning data be written to the journal, instead of directly to the
685databases? (optional, default 0).
686
687=item wait_for_lock
688
689Whether or not to wait a long time for locks to complete (optional, default 0).
690
691=item opportunistic_expire_check_only
692
693During the opportunistic journal sync and expire check, don't actually do the
694expire but report back whether or not it should occur (optional, default 0).
695
696=item no_relearn
697
698If doing a learn operation, and the message has already been learned as
699the opposite type, don't re-learn the message.
700
701=back
702
703=cut
704
705
# spent 228µs (197+31) within Mail::SpamAssassin::init_learner which was called: # once (197µs+31µs) by main::RUNTIME at line 349 of /usr/local/bin/sa-learn
sub init_learner {
70612µs my $self = shift;
70712µs my $opts = shift;
708115µs115µs dbg("learn: initializing learner");
# spent 15µs making 1 call to Mail::SpamAssassin::Logger::dbg
709
710 # Make sure we're already initialized ...
71119µs116µs $self->init(1);
# spent 16µs making 1 call to Mail::SpamAssassin::init
712
713112µs my %kv = (
714 'force_expire' => 'learn_force_expire',
715 'learn_to_journal' => 'learn_to_journal',
716 'caller_will_untie' => 'learn_caller_will_untie',
717 'wait_for_lock' => 'learn_wait_for_lock',
718 'opportunistic_expire_check_only' => 'opportunistic_expire_check_only',
719 'no_relearn' => 'learn_no_relearn',
720 );
721
72212µs my %ret;
723
724 # Set any other options that need setting ...
725147µs while( my($k,$v) = each %kv ) {
726649µs $ret{$k} = $self->{$v};
7271042µs if (exists $opts->{$k}) { $self->{$v} = $opts->{$k}; }
728 }
729
730112µs return \%ret;
731}
732
733###########################################################################
734
735=item $f->rebuild_learner_caches ({ opt => val })
736
737Rebuild any cache databases; should be called after the learning process.
738Options include: C<verbose>, which will output diagnostics to C<stdout>
739if set to 1.
740
741=cut
742
743sub rebuild_learner_caches {
744 my $self = shift;
745 my $opts = shift;
746 $self->{bayes_scanner}->sync(1,1,$opts) if $self->{bayes_scanner};
747 1;
748}
749
750=item $f->finish_learner ()
751
752Finish learning.
753
754=cut
755
756
# spent 5.48s (30µs+5.48) within Mail::SpamAssassin::finish_learner which was called: # once (30µs+5.48s) by main::RUNTIME at line 501 of /usr/local/bin/sa-learn
sub finish_learner {
75713µs my $self = shift;
758114µs15.48s $self->{bayes_scanner}->force_close(1) if $self->{bayes_scanner};
# spent 5.48s making 1 call to Mail::SpamAssassin::Bayes::force_close
759114µs 1;
760}
761
762=item $f->dump_bayes_db()
763
764Dump the contents of the Bayes DB
765
766=cut
767
768sub dump_bayes_db {
769 my($self,@opts) = @_;
770 $self->{bayes_scanner}->dump_bayes_db(@opts) if $self->{bayes_scanner};
771}
772
773=item $f->signal_user_changed ( [ { opt => val, ... } ] )
774
775Signals that the current user has changed (possibly using C<setuid>), meaning
776that SpamAssassin should close any per-user databases it has open, and re-open
777using ones appropriate for the new user.
778
779Note that this should be called I<after> reading any per-user configuration, as
780that data may override some paths opened in this method. You may pass the
781following attribute-value pairs:
782
783=over 4
784
785=item username
786
787The username of the user. This will be used for the C<username> attribute.
788
789=item user_dir
790
791A directory to use as a 'home directory' for the current user's data,
792overriding the system default. This directory must be readable and writable by
793the process. Note that the resulting C<userstate_dir> will be the
794C<.spamassassin> subdirectory of this dir.
795
796=item userstate_dir
797
798A directory to use as a directory for the current user's data, overriding the
799system default. This directory must be readable and writable by the process.
800The default is C<user_dir/.spamassassin>.
801
802=back
803
804=cut
805
806sub signal_user_changed {
807 my $self = shift;
808 my $opts = shift;
809 my $set = 0;
810
811 my $timer = $self->time_method("signal_user_changed");
812 dbg("info: user has changed");
813
814 if (defined $opts && $opts->{username}) {
815 $self->{username} = $opts->{username};
816 } else {
817 undef $self->{username};
818 }
819 if (defined $opts && $opts->{user_dir}) {
820 $self->{user_dir} = $opts->{user_dir};
821 } else {
822 undef $self->{user_dir};
823 }
824 if (defined $opts && $opts->{userstate_dir}) {
825 $self->{userstate_dir} = $opts->{userstate_dir};
826 } else {
827 undef $self->{userstate_dir};
828 }
829
830 # reopen bayes dbs for this user
831 $self->{bayes_scanner}->finish() if $self->{bayes_scanner};
832 if ($self->{conf}->{use_bayes}) {
833 require Mail::SpamAssassin::Bayes;
834 $self->{bayes_scanner} = new Mail::SpamAssassin::Bayes ($self);
835 } else {
836 delete $self->{bayes_scanner} if $self->{bayes_scanner};
837 }
838
839 # this user may have a different learn_to_journal setting, so reset appropriately
840 $self->{'learn_to_journal'} = $self->{conf}->{bayes_learn_to_journal};
841
842 $set |= 1 unless $self->{local_tests_only};
843 $set |= 2 if $self->{bayes_scanner} && $self->{bayes_scanner}->is_scan_available();
844
845 $self->{conf}->set_score_set ($set);
846
847 $self->call_plugins("signal_user_changed", {
848 username => $self->{username},
849 userstate_dir => $self->{userstate_dir},
850 user_dir => $self->{user_dir},
851 });
852
853 1;
854}
855
856###########################################################################
857
858=item $f->report_as_spam ($mail, $options)
859
860Report a mail, encapsulated in a C<Mail::SpamAssassin::Message> object, as
861human-verified spam. This will submit the mail message to live,
862collaborative, spam-blocker databases, allowing other users to block this
863message.
864
865It will also submit the mail to SpamAssassin's Bayesian learner.
866
867Options is an optional reference to a hash of options. Currently these
868can be:
869
870=over 4
871
872=item dont_report_to_dcc
873
874Inhibits reporting of the spam to DCC.
875
876=item dont_report_to_pyzor
877
878Inhibits reporting of the spam to Pyzor.
879
880=item dont_report_to_razor
881
882Inhibits reporting of the spam to Razor.
883
884=item dont_report_to_spamcop
885
886Inhibits reporting of the spam to SpamCop.
887
888=back
889
890=cut
891
892sub report_as_spam {
893 my ($self, $mail, $options) = @_;
894 local ($_);
895
896 $self->init(1);
897 my $timer = $self->time_method("report_as_spam");
898
899 # learn as spam if enabled
900 if ( $self->{conf}->{bayes_learn_during_report} ) {
901 $self->learn ($mail, undef, 1, 0);
902 }
903
904 require Mail::SpamAssassin::Reporter;
905 $mail = Mail::SpamAssassin::Reporter->new($self, $mail, $options);
906 $mail->report();
907}
908
909###########################################################################
910
911=item $f->revoke_as_spam ($mail, $options)
912
913Revoke a mail, encapsulated in a C<Mail::SpamAssassin::Message> object, as
914human-verified ham (non-spam). This will revoke the mail message from live,
915collaborative, spam-blocker databases, allowing other users to block this
916message.
917
918It will also submit the mail to SpamAssassin's Bayesian learner as nonspam.
919
920Options is an optional reference to a hash of options. Currently these
921can be:
922
923=over 4
924
925=item dont_report_to_razor
926
927Inhibits revoking of the spam to Razor.
928
929
930=back
931
932=cut
933
934sub revoke_as_spam {
935 my ($self, $mail, $options) = @_;
936 local ($_);
937
938 $self->init(1);
939 my $timer = $self->time_method("revoke_as_spam");
940
941 # learn as nonspam
942 $self->learn ($mail, undef, 0, 0);
943
944 require Mail::SpamAssassin::Reporter;
945 $mail = Mail::SpamAssassin::Reporter->new($self, $mail, $options);
946 $mail->revoke ();
947}
948
949###########################################################################
950
951=item $f->add_address_to_whitelist ($addr, $cli_p)
952
953Given a string containing an email address, add it to the automatic
954whitelist database.
955
956If $cli_p is set then underlying plugin may give visual feedback on additions/failures.
957
958=cut
959
960sub add_address_to_whitelist {
961 my ($self, $addr, $cli_p) = @_;
962
963 $self->call_plugins("whitelist_address", { address => $addr,
964 cli_p => $cli_p });
965}
966
967###########################################################################
968
969=item $f->add_all_addresses_to_whitelist ($mail, $cli_p)
970
971Given a mail message, find as many addresses in the usual headers (To, Cc, From
972etc.), and the message body, and add them to the automatic whitelist database.
973
974If $cli_p is set then underlying plugin may give visual feedback on additions/failures.
975
976=cut
977
978sub add_all_addresses_to_whitelist {
979 my ($self, $mail_obj, $cli_p) = @_;
980
981 foreach my $addr ($self->find_all_addrs_in_mail ($mail_obj)) {
982 $self->call_plugins("whitelist_address", { address => $addr,
983 cli_p => $cli_p });
984 }
985}
986
987###########################################################################
988
989=item $f->remove_address_from_whitelist ($addr, $cli_p)
990
991Given a string containing an email address, remove it from the automatic
992whitelist database.
993
994If $cli_p is set then underlying plugin may give visual feedback on additions/failures.
995
996=cut
997
998sub remove_address_from_whitelist {
999 my ($self, $addr, $cli_p) = @_;
1000
1001 $self->call_plugins("remove_address", { address => $addr,
1002 cli_p => $cli_p });
1003}
1004
1005###########################################################################
1006
1007=item $f->remove_all_addresses_from_whitelist ($mail, $cli_p)
1008
1009Given a mail message, find as many addresses in the usual headers (To, Cc, From
1010etc.), and the message body, and remove them from the automatic whitelist
1011database.
1012
1013If $cli_p is set then underlying plugin may give visual feedback on additions/failures.
1014
1015=cut
1016
1017sub remove_all_addresses_from_whitelist {
1018 my ($self, $mail_obj, $cli_p) = @_;
1019
1020 foreach my $addr ($self->find_all_addrs_in_mail ($mail_obj)) {
1021 $self->call_plugins("remove_address", { address => $addr,
1022 cli_p => $cli_p });
1023 }
1024}
1025
1026###########################################################################
1027
1028=item $f->add_address_to_blacklist ($addr, $cli_p)
1029
1030Given a string containing an email address, add it to the automatic
1031whitelist database with a high score, effectively blacklisting them.
1032
1033If $cli_p is set then underlying plugin may give visual feedback on additions/failures.
1034
1035=cut
1036
1037sub add_address_to_blacklist {
1038 my ($self, $addr, $cli_p) = @_;
1039 $self->call_plugins("blacklist_address", { address => $addr,
1040 cli_p => $cli_p });
1041}
1042
1043###########################################################################
1044
1045=item $f->add_all_addresses_to_blacklist ($mail, $cli_p)
1046
1047Given a mail message, find addresses in the From headers and add them to the
1048automatic whitelist database with a high score, effectively blacklisting them.
1049
1050Note that To and Cc addresses are not used.
1051
1052If $cli_p is set then underlying plugin may give visual feedback on additions/failures.
1053
1054=cut
1055
1056sub add_all_addresses_to_blacklist {
1057 my ($self, $mail_obj, $cli_p) = @_;
1058
1059 $self->init(1);
1060
1061 my @addrlist;
1062 my @hdrs = $mail_obj->get_header('From');
1063 if ($#hdrs >= 0) {
1064 push (@addrlist, $self->find_all_addrs_in_line (join (" ", @hdrs)));
1065 }
1066
1067 foreach my $addr (@addrlist) {
1068 $self->call_plugins("blacklist_address", { address => $addr,
1069 cli_p => $cli_p });
1070 }
1071
1072}
1073
1074###########################################################################
1075
1076=item $text = $f->remove_spamassassin_markup ($mail)
1077
1078Returns the text of the message, with any SpamAssassin-added text (such
1079as the report, or X-Spam-Status headers) stripped.
1080
1081Note that the B<$mail> object is not modified.
1082
1083Warning: if the input message in B<$mail> contains a mixture of CR-LF
1084(Windows-style) and LF (UNIX-style) line endings, it will be "canonicalized"
1085to use one or the other consistently throughout.
1086
1087=cut
1088
1089sub remove_spamassassin_markup {
1090 my ($self, $mail_obj) = @_;
1091 local ($_);
1092
1093 my $timer = $self->time_method("remove_spamassassin_markup");
1094 my $mbox = $mail_obj->get_mbox_separator() || '';
1095
1096 dbg("markup: removing markup");
1097
1098 # Go looking for a "report_safe" encapsulated message. Abort out ASAP
1099 # if we have definitive proof it's not an encapsulated message.
1100 my $ct = $mail_obj->get_header("Content-Type") || '';
1101 if ( $ct =~ m!^\s*multipart/mixed;\s+boundary\s*=\s*["']?(.+?)["']?(?:;|$)!i ) {
1102
1103 # Ok, this is a possible encapsulated message, search for the
1104 # appropriate mime part and deal with it if necessary.
1105 my $boundary = "\Q$1\E";
1106 my @msg = split(/^/,$mail_obj->get_pristine_body());
1107
1108 my $flag = 0;
1109 $ct = '';
1110 my $cd = '';
1111 for ( my $i = 0 ; $i <= $#msg ; $i++ ) {
1112 # only look at mime part headers
1113 next unless ( $msg[$i] =~ /^--$boundary\r?$/ || $flag );
1114
1115 if ( $msg[$i] =~ /^\s*$/ ) { # end of mime header
1116
1117 # Ok, we found the encapsulated piece ...
1118 if ($ct =~ m@^(?:message/rfc822|text/plain);\s+x-spam-type=original@ ||
1119 ($ct eq "message/rfc822" &&
1120 $cd eq $self->{conf}->{'encapsulated_content_description'}))
1121 {
1122 splice @msg, 0, $i+1; # remove the front part, including the blank line
1123
1124 # find the end and chop it off
1125 for ( $i = 0 ; $i <= $#msg ; $i++ ) {
1126 if ( $msg[$i] =~ /^--$boundary/ ) {
1127 splice @msg, ($msg[$i-1] =~ /\S/ ? $i : $i-1);
1128 # will remove the blank line (not sure it'll always be
1129 # there) and everything below. don't worry, the splice
1130 # guarantees the for will stop ...
1131 }
1132 }
1133
1134 # Ok, we're done. Return the rewritten message.
1135 return join('', $mbox, @msg);
1136 }
1137
1138 $flag = 0;
1139 $ct = '';
1140 $cd = '';
1141 next;
1142 }
1143
1144 # Ok, we're in the mime header ... Capture the appropriate headers...
1145 $flag = 1;
1146 if ( $msg[$i] =~ /^Content-Type:\s+(.+?)\s*$/i ) {
1147 $ct = $1;
1148 }
1149 elsif ( $msg[$i] =~ /^Content-Description:\s+(.+?)\s*$/i ) {
1150 $cd = $1;
1151 }
1152 }
1153 }
1154
1155 # Ok, if we got here, the message wasn't a report_safe encapsulated message.
1156 # So treat it like a "report_safe 0" message.
1157 my $hdrs = $mail_obj->get_pristine_header();
1158 my $body = $mail_obj->get_pristine_body();
1159
1160 # remove DOS line endings
1161 $hdrs =~ s/\r//gs;
1162
1163 # unfold SA added headers, but not X-Spam-Prev headers ...
1164 $hdrs = "\n".$hdrs; # simplifies regexp below
1165 1 while $hdrs =~ s/(\nX-Spam-(?!Prev).+?)\n[ \t]+(\S.*\n)/$1 $2/g;
1166 $hdrs =~ s/^\n//;
1167
1168###########################################################################
1169 # Backward Compatibilty, pre 3.0.x.
1170
1171 # deal with rewritten headers w/out X-Spam-Prev- versions ...
1172 $self->init(1);
1173 foreach my $header ( keys %{$self->{conf}->{rewrite_header}} ) {
1174 # let the 3.0 decoding do it...
1175 next if ($hdrs =~ /^X-Spam-Prev-$header:/im);
1176
1177 dbg("markup: removing markup in $header");
1178 if ($header eq 'Subject') {
1179 my $tag = $self->{conf}->{rewrite_header}->{'Subject'};
1180 $tag = quotemeta($tag);
1181 $tag =~ s/_HITS_/\\d{2}\\.\\d{2}/g;
1182 $tag =~ s/_SCORE_/\\d{2}\\.\\d{2}/g;
1183 $tag =~ s/_REQD_/\\d{2}\\.\\d{2}/g;
1184 1 while $hdrs =~ s/^Subject: ${tag} /Subject: /gm;
1185 } else {
1186 $hdrs =~ s/^(${header}:[ \t].*?)\t\([^)]*\)$/$1/gm;
1187 }
1188 }
1189
1190 # Now deal with report cleansing from 2.4x and previous.
1191 # possibly a blank line, "SPAM: ----.+", followed by "SPAM: stuff" lines,
1192 # followed by another "SPAM: ----.+" line, followed by a blank line.
1193 1 while ($body =~ s/^\n?SPAM: ----.+\n(?:SPAM:.*\n)*SPAM: ----.+\n\n//);
1194###########################################################################
1195
1196 # 3.0 version -- support for previously-nonexistent Subject hdr.
1197 # ensure the Subject line didn't *really* contain "(nonexistent)" in
1198 # the original message!
1199 if ($hdrs =~ /^X-Spam-Prev-Subject:\s*\(nonexistent\)$/m
1200 && $hdrs !~ /^Subject:.*\(nonexistent\).*$/m)
1201 {
1202 $hdrs =~ s/(^|\n)X-Spam-Prev-Subject:\s*\(nonexistent\)\n/$1\n/s;
1203 $hdrs =~ s/(^|\n)Subject:\s*[ \t]*.*\n(?:\s+\S.*\n)*/$1\n/s;
1204 }
1205
1206 # 3.0 version -- revert from X-Spam-Prev to original ...
1207 while ($hdrs =~ s/^X-Spam-Prev-(([^:]+:)[ \t]*.*\n(?:\s+\S.*\n)*)//m) {
1208 my($hdr, $name) = ($1,$2);
1209
1210 # If the rewritten version doesn't exist, we should deal with it anyway...
1211 unless ($hdrs =~ s/^$name[ \t]*.*\n(?:\s+\S.*\n)*/$hdr/m) {
1212 $hdrs =~ s/\n\n/\n$hdr\n/;
1213 }
1214 }
1215
1216 # remove any other X-Spam headers we added, will be unfolded
1217 $hdrs = "\n".$hdrs; # simplifies regexp below
1218 1 while $hdrs =~ s/\nX-Spam-.*\n/\n/g;
1219 $hdrs =~ s/^\n//;
1220
1221 # re-add DOS line endings
1222 if ($mail_obj->{line_ending} ne "\n") {
1223 $hdrs =~ s/\r?\n/$mail_obj->{line_ending}/gs;
1224 }
1225
1226 # Put the whole thing back together ...
1227 return join ('', $mbox, $hdrs, $body);
1228}
1229
1230###########################################################################
1231
1232=item $f->read_scoreonly_config ($filename)
1233
1234Read a configuration file and parse user preferences from it.
1235
1236User preferences are as defined in the C<Mail::SpamAssassin::Conf> manual page.
1237In other words, they include scoring options, scores, whitelists and
1238blacklists, and so on, but do not include rule definitions, privileged
1239settings, etc. unless C<allow_user_rules> is enabled; and they never include
1240the administrator settings.
1241
1242=cut
1243
1244sub read_scoreonly_config {
1245 my ($self, $filename) = @_;
1246
1247 my $timer = $self->time_method("read_scoreonly_config");
1248 local *IN;
1249 if (!open(IN,"<$filename")) {
1250 # the file may not exist; this should not be verbose
1251 dbg("config: read_scoreonly_config: cannot open \"$filename\": $!");
1252 return;
1253 }
1254
1255 my($inbuf,$nread,$text); $text = '';
1256 while ( $nread=read(IN,$inbuf,16384) ) { $text .= $inbuf }
1257 defined $nread or die "error reading $filename: $!";
1258 close IN or die "error closing $filename: $!";
1259 undef $inbuf;
1260
1261 $text = "file start $filename\n" . $text;
1262 # add an extra \n in case file did not end in one.
1263 $text .= "\nfile end $filename\n";
1264
1265 $self->{conf}->{main} = $self;
1266 $self->{conf}->parse_scores_only ($text);
1267 $self->{conf}->finish_parsing(1);
1268
1269 delete $self->{conf}->{main}; # to allow future GC'ing
1270}
1271
1272###########################################################################
1273
1274=item $f->load_scoreonly_sql ($username)
1275
1276Read configuration paramaters from SQL database and parse scores from it. This
1277will only take effect if the perl C<DBI> module is installed, and the
1278configuration parameters C<user_scores_dsn>, C<user_scores_sql_username>, and
1279C<user_scores_sql_password> are set correctly.
1280
1281The username in C<$username> will also be used for the C<username> attribute of
1282the Mail::SpamAssassin object.
1283
1284=cut
1285
1286sub load_scoreonly_sql {
1287 my ($self, $username) = @_;
1288
1289 my $timer = $self->time_method("load_scoreonly_sql");
1290 my $src = Mail::SpamAssassin::Conf::SQL->new ($self);
1291 $self->{username} = $username;
1292 unless ($src->load($username)) {
1293 return 0;
1294 }
1295 return 1;
1296}
1297
1298###########################################################################
1299
1300=item $f->load_scoreonly_ldap ($username)
1301
1302Read configuration paramaters from an LDAP server and parse scores from it.
1303This will only take effect if the perl C<Net::LDAP> and C<URI> modules are
1304installed, and the configuration parameters C<user_scores_dsn>,
1305C<user_scores_ldap_username>, and C<user_scores_ldap_password> are set
1306correctly.
1307
1308The username in C<$username> will also be used for the C<username> attribute of
1309the Mail::SpamAssassin object.
1310
1311=cut
1312
1313sub load_scoreonly_ldap {
1314 my ($self, $username) = @_;
1315
1316 dbg("config: load_scoreonly_ldap($username)");
1317 my $timer = $self->time_method("load_scoreonly_ldap");
1318 my $src = Mail::SpamAssassin::Conf::LDAP->new ($self);
1319 $self->{username} = $username;
1320 $src->load($username);
1321}
1322
1323###########################################################################
1324
1325=item $f->set_persistent_address_list_factory ($factoryobj)
1326
1327Set the persistent address list factory, used to create objects for the
1328automatic whitelist algorithm's persistent-storage back-end. See
1329C<Mail::SpamAssassin::PersistentAddrList> for the API these factory objects
1330must implement, and the API the objects they produce must implement.
1331
1332=cut
1333
1334
# spent 10µs within Mail::SpamAssassin::set_persistent_address_list_factory which was called: # once (10µs+0s) by Mail::SpamAssassin::Plugin::TxRep::open_storages at line 1650 of Mail/SpamAssassin/Plugin/TxRep.pm
sub set_persistent_address_list_factory {
133512µs my ($self, $fac) = @_;
1336112µs $self->{pers_addr_list_factory} = $fac;
1337}
1338
1339###########################################################################
1340
1341=item $f->compile_now ($use_user_prefs, $keep_userstate)
1342
1343Compile all patterns, load all configuration files, and load all
1344possibly-required Perl modules.
1345
1346Normally, Mail::SpamAssassin uses lazy evaluation where possible, but if you
1347plan to fork() or start a new perl interpreter thread to process a message,
1348this is suboptimal, as each process/thread will have to perform these actions.
1349
1350Call this function in the master thread or process to perform the actions
1351straightaway, so that the sub-processes will not have to.
1352
1353If C<$use_user_prefs> is 0, this will initialise the SpamAssassin
1354configuration without reading the per-user configuration file and it will
1355assume that you will call C<read_scoreonly_config> at a later point.
1356
1357If C<$keep_userstate> is true, compile_now() will revert any configuration
1358options which have a default with I<__userstate__> in it post-init(),
1359and then re-change the option before returning. This lets you change
1360I<$ENV{'HOME'}> to a temp directory, have compile_now() and create any
1361files there as necessary without disturbing the actual files as changed
1362by a configuration option. By default, this is disabled.
1363
1364=cut
1365
1366sub compile_now {
1367 my ($self, $use_user_prefs, $deal_with_userstate) = @_;
1368
1369 my $timer = $self->time_method("compile_now");
1370
1371 # Backup default values which deal with userstate.
1372 # This is done so we can create any new files in, presumably, a temp dir.
1373 # see bug 2762 for more details.
1374 my %backup;
1375 if (defined $deal_with_userstate && $deal_with_userstate) {
1376 while(my($k,$v) = each %{$self->{conf}}) {
1377 $backup{$k} = $v if (defined $v && !ref($v) && $v =~/__userstate__/);
1378 }
1379 }
1380
1381 $self->init($use_user_prefs);
1382
1383 # if init() didn't change the value from default, forget about it.
1384 # if the value is different, remember the new version, and reset the default.
1385 while(my($k,$v) = each %backup) {
1386 if ($self->{conf}->{$k} eq $v) {
1387 delete $backup{$k};
1388 }
1389 else {
1390 my $backup = $backup{$k};
1391 $backup{$k} = $self->{conf}->{$k};
1392 $self->{conf}->{$k} = $backup;
1393 }
1394 }
1395
1396 dbg("ignore: test message to precompile patterns and load modules");
1397
1398 # tell plugins we are about to send a message for compiling purposes
1399 $self->call_plugins("compile_now_start",
1400 { use_user_prefs => $use_user_prefs,
1401 keep_userstate => $deal_with_userstate});
1402
1403 # note: this may incur network access. Good. We want to make sure
1404 # as much as possible is preloaded!
1405 my @testmsg = ("From: ignore\@compiling.spamassassin.taint.org\n",
1406 "Message-Id: <".time."\@spamassassin_spamd_init>\n", "\n",
1407 "I need to make this message body somewhat long so TextCat preloads\n"x20);
1408
1409 my $mail = $self->parse(\@testmsg, 1, { master_deadline => undef });
1410 my $status = Mail::SpamAssassin::PerMsgStatus->new($self, $mail,
1411 { disable_auto_learning => 1 } );
1412
1413 # We want to turn off the bayes rules for this test msg
1414 my $use_bayes_rules_value = $self->{conf}->{use_bayes_rules};
1415 $self->{conf}->{use_bayes_rules} = 0;
1416 $status->check();
1417 $self->{conf}->{use_bayes_rules} = $use_bayes_rules_value;
1418 $status->finish();
1419 $mail->finish();
1420 $self->finish_learner();
1421
1422 $self->{conf}->free_uncompiled_rule_source();
1423
1424 # load SQL modules now as well
1425 my $dsn = $self->{conf}->{user_scores_dsn};
1426 if ($dsn ne '') {
1427 if ($dsn =~ /^ldap:/i) {
1428 Mail::SpamAssassin::Conf::LDAP::load_modules();
1429 } else {
1430 Mail::SpamAssassin::Conf::SQL::load_modules();
1431 }
1432 }
1433
1434 # make sure things are ready for scanning
1435 $self->{bayes_scanner}->force_close() if $self->{bayes_scanner};
1436 $self->call_plugins("compile_now_finish",
1437 { use_user_prefs => $use_user_prefs,
1438 keep_userstate => $deal_with_userstate});
1439
1440 # Reset any non-default values to the post-init() version.
1441 while(my($k,$v) = each %backup) {
1442 $self->{conf}->{$k} = $v;
1443 }
1444
1445 # clear sed_path_cache
1446 delete $self->{conf}->{sed_path_cache};
1447
1448 1;
1449}
1450
1451###########################################################################
1452
1453=item $f->debug_diagnostics ()
1454
1455Output some diagnostic information, useful for debugging SpamAssassin
1456problems.
1457
1458=cut
1459
1460sub debug_diagnostics {
1461 my ($self) = @_;
1462
1463 # load this class lazily, to avoid overhead when this method isn't
1464 # called.
1465 eval {
1466 require Mail::SpamAssassin::Util::DependencyInfo;
1467 dbg(Mail::SpamAssassin::Util::DependencyInfo::debug_diagnostics($self));
1468 };
1469}
1470
1471###########################################################################
1472
1473=item $failed = $f->lint_rules ()
1474
1475Syntax-check the current set of rules. Returns the number of
1476syntax errors discovered, or 0 if the configuration is valid.
1477
1478=cut
1479
1480sub lint_rules {
1481 my ($self) = @_;
1482
1483 dbg("ignore: using a test message to lint rules");
1484 my @testmsg = ("From: ignore\@compiling.spamassassin.taint.org\n",
1485 "Subject: \n",
1486 "Message-Id: <".CORE::time()."\@lint_rules>\n", "\n",
1487 "I need to make this message body somewhat long so TextCat preloads\n"x20);
1488
1489 $self->{lint_rules} = $self->{conf}->{lint_rules} = 1;
1490 $self->{syntax_errors} = 0;
1491
1492 my $olddcp = $self->{dont_copy_prefs};
1493 $self->{dont_copy_prefs} = 1;
1494
1495 $self->init(1);
1496 $self->{syntax_errors} += $self->{conf}->{errors};
1497
1498 $self->{dont_copy_prefs} = $olddcp; # revert back to previous
1499
1500 # bug 5048: override settings to ensure a faster lint
1501 $self->{'conf'}->{'use_auto_whitelist'} = 0;
1502 $self->{'conf'}->{'bayes_auto_learn'} = 0;
1503
1504 my $mail = $self->parse(\@testmsg, 1, { master_deadline => undef });
1505 my $status = Mail::SpamAssassin::PerMsgStatus->new($self, $mail,
1506 { disable_auto_learning => 1 } );
1507 $status->check();
1508
1509 $self->{syntax_errors} += $status->{rule_errors};
1510 $status->finish();
1511 $mail->finish();
1512 dbg("timing: " . $self->timer_report()) if $self->{timer_enabled};
1513 return ($self->{syntax_errors});
1514}
1515
1516###########################################################################
1517
1518=item $f->finish()
1519
1520Destroy this object, so that it will be garbage-collected once it
1521goes out of scope. The object will no longer be usable after this
1522method is called.
1523
1524=cut
1525
1526sub finish {
1527 my ($self) = @_;
1528
1529 $self->timer_start("finish");
1530 $self->call_plugins("finish_tests", { conf => $self->{conf},
1531 main => $self });
1532
1533 $self->{conf}->finish(); delete $self->{conf};
1534 $self->{plugins}->finish(); delete $self->{plugins};
1535
1536 if ($self->{bayes_scanner}) {
1537 $self->{bayes_scanner}->finish();
1538 delete $self->{bayes_scanner};
1539 }
1540
1541 $self->{resolver}->finish() if $self->{resolver};
1542
1543 $self->timer_end("finish");
1544 %{$self} = ();
1545}
1546
1547###########################################################################
1548# timers: bug 5356
1549
1550sub timer_enable {
1551 my ($self) = @_;
1552 dbg("config: timing enabled") if !$self->{timer_enabled};
1553 $self->{timer_enabled} = 1;
1554}
1555
1556sub timer_disable {
1557 my ($self) = @_;
1558 dbg("config: timing disabled") if $self->{timer_enabled};
1559 $self->{timer_enabled} = 0;
1560}
1561
1562# discard all timers, start afresh
1563sub timer_reset {
1564 my ($self) = @_;
1565 delete $self->{timers};
1566 delete $self->{timers_order};
1567}
1568
1569sub timer_start {
1570 my ($self, $name) = @_;
1571
1572 return unless $self->{timer_enabled};
1573# dbg("timing: '$name' starting");
1574
1575 if (!exists $self->{timers}->{$name}) {
1576 push @{$self->{timers_order}}, $name;
1577 }
1578
1579 $self->{timers}->{$name}->{start} = Time::HiRes::time();
1580 # note that this will reset any existing, unstopped timer of that name;
1581 # that's ok
1582}
1583
1584sub timer_end {
1585 my ($self, $name) = @_;
1586 return unless $self->{timer_enabled};
1587
1588 my $t = $self->{timers}->{$name};
1589 $t->{end} = time;
1590
1591 if (!$t->{start}) {
1592 warn "timer_end('$name') with no timer_start";
1593 return;
1594 }
1595
1596 # add to any existing elapsed time for this event, since
1597 # we may call the same timer name multiple times -- this is ok,
1598 # as long as they are not nested
1599 my $dt = $t->{end} - $t->{start};
1600 $dt = 0 if $dt < 0; # tolerate clock jumps, just in case
1601 if (defined $t->{elapsed}) { $t->{elapsed} += $dt }
1602 else { $t->{elapsed} = $dt }
1603}
1604
1605
# spent 74.1ms within Mail::SpamAssassin::time_method which was called 8092 times, avg 9µs/call: # 3114 times (29.1ms+0s) by Mail::SpamAssassin::Plugin::TxRep::check_reputation at line 1444 of Mail/SpamAssassin/Plugin/TxRep.pm, avg 9µs/call # 3114 times (28.0ms+0s) by Mail::SpamAssassin::Plugin::TxRep::check_reputation at line 1495 of Mail/SpamAssassin/Plugin/TxRep.pm, avg 9µs/call # 470 times (4.31ms+0s) by Mail::SpamAssassin::PerMsgStatus::get_uri_detail_list at line 2236 of Mail/SpamAssassin/PerMsgStatus.pm, avg 9µs/call # 453 times (3.69ms+0s) by Mail::SpamAssassin::Plugin::TxRep::check_senders_reputation at line 1247 of Mail/SpamAssassin/Plugin/TxRep.pm, avg 8µs/call # 235 times (2.70ms+0s) by Mail::SpamAssassin::Plugin::Bayes::_learn_trapped at line 475 of Mail/SpamAssassin/Plugin/Bayes.pm, avg 12µs/call # 235 times (2.12ms+0s) by Mail::SpamAssassin::parse at line 530, avg 9µs/call # 235 times (2.09ms+0s) by Mail::SpamAssassin::Plugin::Bayes::learn_message at line 382 of Mail/SpamAssassin/Plugin/Bayes.pm, avg 9µs/call # 235 times (2.07ms+0s) by Mail::SpamAssassin::PerMsgStatus::extract_message_metadata at line 1702 of Mail/SpamAssassin/PerMsgStatus.pm, avg 9µs/call # once (10µs+0s) by Mail::SpamAssassin::init at line 1660
sub time_method {
1606809219.9ms my ($self, $name) = @_;
1607809282.4ms return unless $self->{timer_enabled};
1608 return Mail::SpamAssassin::Util::ScopedTimer->new($self, $name);
1609}
1610
1611sub timer_report {
1612 my ($self) = @_;
1613
1614 my $earliest;
1615 my $latest;
1616
1617 while (my($name,$h) = each(%{$self->{timers}})) {
1618 # dbg("timing: %s - %s", $name, join(", ",
1619 # map { sprintf("%s => %s", $_, $h->{$_}) } keys(%$h)));
1620 my $start = $h->{start};
1621 if (defined $start && (!defined $earliest || $earliest > $start)) {
1622 $earliest = $start;
1623 }
1624 my $end = $h->{end};
1625 if (defined $end && (!defined $latest || $latest < $end)) {
1626 $latest = $end;
1627 }
1628 dbg("timing: start but no end: $name") if defined $start && !defined $end;
1629 }
1630 my $total =
1631 (!defined $latest || !defined $earliest) ? 0 : $latest - $earliest;
1632 my @str;
1633 foreach my $name (@{$self->{timers_order}}) {
1634 my $elapsed = $self->{timers}->{$name}->{elapsed} || 0;
1635 my $pc = $total <= 0 || $elapsed >= $total ? 100 : ($elapsed/$total)*100;
1636 my $fmt = $elapsed >= 0.005 ? "%.0f" : $elapsed >= 0.002 ? "%.1f" : "%.2f";
1637 push @str, sprintf("%s: $fmt (%.1f%%)", $name, $elapsed*1000, $pc);
1638 }
1639
1640 return sprintf("total %.0f ms - %s", $total*1000, join(", ", @str));
1641}
1642
1643###########################################################################
1644# non-public methods.
1645
1646
# spent 10.7s (8.30ms+10.6) within Mail::SpamAssassin::init which was called 472 times, avg 22.6ms/call: # 235 times (4.18ms+0s) by Mail::SpamAssassin::parse at line 529, avg 18µs/call # 235 times (3.51ms+0s) by Mail::SpamAssassin::learn at line 647, avg 15µs/call # once (589µs+10.6s) by main::RUNTIME at line 236 of /usr/local/bin/sa-learn # once (16µs+0s) by Mail::SpamAssassin::init_learner at line 711
sub init {
16474721.12ms my ($self, $use_user_pref) = @_;
1648
1649 # Allow init() to be called multiple times, but only run once.
16504721.37ms if (defined $self->{_initted}) {
1651 # If the PID changes, reseed the PRNG (if permitted) and the DNS ID counter
16524712.52ms if ($self->{_initted} != $$) {
1653 $self->{_initted} = $$;
1654 srand if !$self->{skip_prng_reseeding};
1655 $self->{resolver}->reinit_post_fork();
1656 }
16574714.60ms return;
1658 }
1659
1660110µs110µs my $timer = $self->time_method("init");
# spent 10µs making 1 call to Mail::SpamAssassin::time_method
1661 # Note that this PID has run init()
1662128µs $self->{_initted} = $$;
1663
1664 #fix spamd reading root prefs file
166512µs if (!defined $use_user_pref) {
1666 $use_user_pref = 1;
1667 }
1668
166915µs if (!defined $self->{config_text}) {
167014µs $self->{config_text} = '';
1671
1672 # read a file called "init.pre" in site rules dir *before* all others;
1673 # even the system config.
167412µs my $siterules = $self->{site_rules_filename};
1675112µs1486µs $siterules ||= $self->first_existing_path (@site_rules_path);
# spent 486µs making 1 call to Mail::SpamAssassin::first_existing_path
1676
167713µs my $sysrules = $self->{rules_filename};
167818µs1332µs $sysrules ||= $self->first_existing_path (@default_rules_path);
# spent 332µs making 1 call to Mail::SpamAssassin::first_existing_path
1679
168014µs if ($siterules) {
1681114µs12.82ms $self->{config_text} .= $self->read_pre($siterules, 'site rules pre files');
# spent 2.82ms making 1 call to Mail::SpamAssassin::read_pre
1682 }
1683 else {
1684 warn "config: could not find site rules directory\n";
1685 }
1686
168714µs if ($sysrules) {
1688110µs1424µs $self->{config_text} .= $self->read_pre($sysrules, 'sys rules pre files');
# spent 424µs making 1 call to Mail::SpamAssassin::read_pre
1689 }
1690 else {
1691 warn "config: could not find sys rules directory\n";
1692 }
1693
169414µs if ($sysrules) {
1695111µs1637µs my $cftext = $self->read_cf($sysrules, 'default rules dir');
# spent 637µs making 1 call to Mail::SpamAssassin::read_cf
169613µs if ($self->{require_rules} && $cftext !~ /\S/) {
1697 die "config: no rules were found! Do you need to run 'sa-update'?\n";
1698 }
169915µs $self->{config_text} .= $cftext;
1700 }
1701
170216µs if (!$self->{languages_filename}) {
1703110µs1898µs $self->{languages_filename} = $self->find_rule_support_file("languages");
# spent 898µs making 1 call to Mail::SpamAssassin::find_rule_support_file
1704 }
1705
170616µs if ($siterules && !$self->{ignore_site_cf_files}) {
1707125µs11.05ms $self->{config_text} .= $self->read_cf($siterules, 'site rules dir');
# spent 1.05ms making 1 call to Mail::SpamAssassin::read_cf
1708 }
1709
171019µs if ( $use_user_pref != 0 ) {
171119µs1700µs $self->get_and_create_userstate_dir();
# spent 700µs making 1 call to Mail::SpamAssassin::get_and_create_userstate_dir
1712
1713 # user prefs file
171413µs my $fname = $self->{userprefs_filename};
171519µs1485µs $fname ||= $self->first_existing_path (@default_userprefs_path);
# spent 485µs making 1 call to Mail::SpamAssassin::first_existing_path
1716
171712µs if (!$self->{dont_copy_prefs}) {
1718 # bug 4932: if the userprefs path doesn't exist, we need to make it, so
1719 # just use the last entry in the array as the default path.
1720 $fname ||= $self->sed_path($default_userprefs_path[-1]);
1721
1722 my $stat_errn = stat($fname) ? 0 : 0+$!;
1723 if ($stat_errn == 0 && -f _) {
1724 # exists and is a regular file, nothing to do
1725 } elsif ($stat_errn == 0) {
1726 warn "config: default user preference file $fname is not a regular file\n";
1727 } elsif ($stat_errn != ENOENT) {
1728 warn "config: default user preference file $fname not accessible: $!\n";
1729 } elsif (!$self->create_default_prefs($fname)) {
1730 warn "config: failed to create default user preference file $fname\n";
1731 }
1732 }
1733
1734120µs1345µs $self->{config_text} .= $self->read_cf($fname, 'user prefs file');
# spent 345µs making 1 call to Mail::SpamAssassin::read_cf
1735 }
1736 }
1737
173813µs if ($self->{pre_config_text}) {
1739 $self->{config_text} = $self->{pre_config_text} . $self->{config_text};
1740 }
174114µs if ($self->{post_config_text}) {
174214µs $self->{config_text} .= $self->{post_config_text};
1743 }
1744
1745116µs16µs if ($self->{config_text} !~ /\S/) {
# spent 6µs making 1 call to Mail::SpamAssassin::CORE:match
1746 my $m = "config: no configuration text or files found! do you need to run 'sa-update'?\n";
1747 if ($self->{require_rules}) {
1748 die $m;
1749 } else {
1750 warn $m;
1751 }
1752 }
1753
1754 # Go and parse the config!
175513µs $self->{conf}->{main} = $self;
175618µs115µs if (would_log('dbg', 'config_text') > 1) {
# spent 15µs making 1 call to Mail::SpamAssassin::Logger::would_log
1757 dbg('config_text: '.$self->{config_text});
1758 }
1759111µs14.55s $self->{conf}->parse_rules ($self->{config_text});
# spent 4.55s making 1 call to Mail::SpamAssassin::Conf::parse_rules
1760114µs16.05s $self->{conf}->finish_parsing(0);
# spent 6.05s making 1 call to Mail::SpamAssassin::Conf::finish_parsing
176115µs delete $self->{conf}->{main}; # to allow future GC'ing
1762
176315µs undef $self->{config_text}; # ensure it's actually freed
176414µs delete $self->{config_text};
1765
176613µs if ($self->{require_rules} && !$self->{conf}->found_any_rules()) {
1767 die "config: no rules were found! Do you need to run 'sa-update'?\n";
1768 }
1769
1770 # Initialize the Bayes subsystem
177116µs if ($self->{conf}->{use_bayes}) {
177217µs require Mail::SpamAssassin::Bayes;
1773126µs134.4ms $self->{bayes_scanner} = new Mail::SpamAssassin::Bayes ($self);
# spent 34.4ms making 1 call to Mail::SpamAssassin::Bayes::new
1774 }
1775111µs $self->{'learn_to_journal'} = $self->{conf}->{bayes_learn_to_journal};
1776
1777 # Figure out/set our initial scoreset
177812µs my $set = 0;
177914µs $set |= 1 unless $self->{local_tests_only};
1780113µs11.43ms $set |= 2 if $self->{bayes_scanner} && $self->{bayes_scanner}->is_scan_available();
# spent 1.43ms making 1 call to Mail::SpamAssassin::Bayes::is_scan_available
1781112µs148µs $self->{conf}->set_score_set ($set);
# spent 48µs making 1 call to Mail::SpamAssassin::Conf::set_score_set
1782
178312µs if ($self->{only_these_rules}) {
1784 $self->{conf}->trim_rules($self->{only_these_rules});
1785 }
1786
178716µs if (!$self->{timer_enabled}) {
1788 # enable timing implicitly if _TIMING_ is used in add_header templates
1789213µs foreach my $hf_ref (@{$self->{conf}->{'headers_ham'}},
179014µs @{$self->{conf}->{'headers_spam'}}) {
17919118µs924µs if ($hf_ref->[1] =~ /_TIMING_/) { $self->timer_enable(); last }
# spent 24µs making 9 calls to Mail::SpamAssassin::CORE:match, avg 3µs/call
1792 }
1793 }
1794
1795 # should be called only after configuration has been parsed
1796128µs16.69ms $self->{resolver} = Mail::SpamAssassin::DnsResolver->new($self);
# spent 6.69ms making 1 call to Mail::SpamAssassin::DnsResolver::new
1797
1798 # TODO -- open DNS cache etc. if necessary
1799}
1800
1801
# spent 27.8ms (1.34+26.4) within Mail::SpamAssassin::read_cf which was called 61 times, avg 455µs/call: # 58 times (1.27ms+24.5ms) by Mail::SpamAssassin::Conf::Parser::parse at line 343 of Mail/SpamAssassin/Conf/Parser.pm, avg 444µs/call # once (22µs+1.03ms) by Mail::SpamAssassin::init at line 1707 # once (28µs+609µs) by Mail::SpamAssassin::init at line 1695 # once (14µs+332µs) by Mail::SpamAssassin::init at line 1734
sub read_cf {
180261192µs my ($self, $allpaths, $desc) = @_;
1803618.01ms6126.4ms return $self->_read_cf_pre($allpaths,$desc,\&get_cf_files_in_dir);
# spent 26.4ms making 61 calls to Mail::SpamAssassin::_read_cf_pre, avg 433µs/call
1804}
1805
1806
# spent 3.25ms (46µs+3.20) within Mail::SpamAssassin::read_pre which was called 2 times, avg 1.62ms/call: # once (25µs+2.80ms) by Mail::SpamAssassin::init at line 1681 # once (21µs+403µs) by Mail::SpamAssassin::init at line 1688
sub read_pre {
180728µs my ($self, $allpaths, $desc) = @_;
1808236µs23.20ms return $self->_read_cf_pre($allpaths,$desc,\&get_pre_files_in_dir);
# spent 3.20ms making 2 calls to Mail::SpamAssassin::_read_cf_pre, avg 1.60ms/call
1809}
1810
1811
# spent 29.6ms (5.95+23.7) within Mail::SpamAssassin::_read_cf_pre which was called 63 times, avg 470µs/call: # 61 times (5.70ms+20.7ms) by Mail::SpamAssassin::read_cf at line 1803, avg 433µs/call # 2 times (254µs+2.95ms) by Mail::SpamAssassin::read_pre at line 1808, avg 1.60ms/call
sub _read_cf_pre {
181263168µs my ($self, $allpaths, $desc, $filelistmethod) = @_;
1813
181463115µs return '' unless defined ($allpaths);
1815
181663138µs my $txt = '';
181763418µs foreach my $path (split("\000", $allpaths))
1818 {
181963468µs63393µs dbg("config: using \"$path\" for $desc");
# spent 393µs making 63 calls to Mail::SpamAssassin::Logger::dbg, avg 6µs/call
1820
1821632.79ms632.33ms my $stat_errn = stat($path) ? 0 : 0+$!;
# spent 2.33ms making 63 calls to Mail::SpamAssassin::CORE:stat, avg 37µs/call
1822632.32ms240679µs if ($stat_errn == ENOENT) {
# spent 225µs making 59 calls to Mail::SpamAssassin::CORE:fteread, avg 4µs/call # spent 179µs making 63 calls to Mail::SpamAssassin::CORE:ftdir, avg 3µs/call # spent 141µs making 59 calls to Mail::SpamAssassin::CORE:ftsize, avg 2µs/call # spent 133µs making 59 calls to Mail::SpamAssassin::CORE:ftfile, avg 2µs/call
1823 # no file or directory
1824 } elsif ($stat_errn != 0) {
1825 dbg("config: file or directory $path not accessible: $!");
1826 } elsif (-d _) {
1827438µs42.43ms foreach my $file ($self->$filelistmethod($path)) {
# spent 1.44ms making 2 calls to Mail::SpamAssassin::get_pre_files_in_dir, avg 720µs/call # spent 989µs making 2 calls to Mail::SpamAssassin::get_cf_files_in_dir, avg 495µs/call
1828997µs91.89ms $txt .= read_cf_file($file);
# spent 1.89ms making 9 calls to Mail::SpamAssassin::read_cf_file, avg 210µs/call
1829 }
1830 } elsif (-f _ && -s _ && -r _) {
1831591.57ms5916.0ms $txt .= read_cf_file($path);
# spent 16.0ms making 59 calls to Mail::SpamAssassin::read_cf_file, avg 271µs/call
1832 }
1833 }
1834
183563760µs return $txt;
1836}
1837
1838
1839
# spent 17.8ms (9.53+8.32) within Mail::SpamAssassin::read_cf_file which was called 68 times, avg 262µs/call: # 59 times (8.47ms+7.49ms) by Mail::SpamAssassin::_read_cf_pre at line 1831, avg 271µs/call # 9 times (1.06ms+824µs) by Mail::SpamAssassin::_read_cf_pre at line 1828, avg 210µs/call
sub read_cf_file {
184068167µs my($path) = @_;
184168183µs my $txt = '';
1842
184368339µs local *IN;
1844683.69ms682.88ms if (open (IN, "<".$path)) {
# spent 2.88ms making 68 calls to Mail::SpamAssassin::CORE:open, avg 42µs/call
1845
1846136248µs my($inbuf,$nread); $txt = '';
18471646.91ms1644.05ms while ( $nread=read(IN,$inbuf,16384) ) { $txt .= $inbuf }
# spent 4.05ms making 164 calls to Mail::SpamAssassin::CORE:read, avg 25µs/call
184868172µs defined $nread or die "error reading $path: $!";
1849681.31ms68815µs close IN or die "error closing $path: $!";
# spent 815µs making 68 calls to Mail::SpamAssassin::CORE:close, avg 12µs/call
185068217µs undef $inbuf;
1851
1852681.51ms $txt = "file start $path\n" . $txt;
1853 # add an extra \n in case file did not end in one.
185468309µs $txt .= "\nfile end $path\n";
1855
185668659µs68571µs dbg("config: read file $path");
# spent 571µs making 68 calls to Mail::SpamAssassin::Logger::dbg, avg 8µs/call
1857 }
1858 else {
1859 warn "config: cannot open \"$path\": $!\n";
1860 }
1861
1862681.21ms return $txt;
1863}
1864
1865
# spent 1.01ms (152µs+853µs) within Mail::SpamAssassin::get_and_create_userstate_dir which was called 2 times, avg 503µs/call: # once (77µs+623µs) by Mail::SpamAssassin::init at line 1711 # once (76µs+230µs) by Mail::SpamAssassin::sed_path at line 2036
sub get_and_create_userstate_dir {
186625µs my ($self, $dir) = @_;
1867
186824µs my $fname;
1869
1870 # If vpopmail is enabled then set fname to virtual homedir
1871 # precedence: dir, userstate_dir, derive from user_dir, system default
1872210µs if (defined $dir) {
1873 $fname = File::Spec->catdir ($dir, ".spamassassin");
1874 }
1875 elsif (defined $self->{userstate_dir}) {
1876 $fname = $self->{userstate_dir};
1877 }
1878 elsif (defined $self->{user_dir}) {
1879 $fname = File::Spec->catdir ($self->{user_dir}, ".spamassassin");
1880 }
1881
1882221µs2576µs $fname ||= $self->first_existing_path (@default_userstate_dir);
# spent 576µs making 2 calls to Mail::SpamAssassin::first_existing_path, avg 288µs/call
1883
1884 # bug 4932: use the last default_userstate_dir entry if none of the others
1885 # already exist
188624µs $fname ||= $self->sed_path($default_userstate_dir[-1]);
1887
188826µs if (!$self->{dont_copy_prefs}) {
1889 dbg("config: using \"$fname\" for user state dir");
1890 }
1891
1892 # if this is not a dir, not readable, or we are unable to create the dir,
1893 # this is not (yet) a serious error; in fact, it's not even worth
1894 # a warning at all times, so use dbg(). see bug 6268
1895245µs214µs my $stat_errn = stat($fname) ? 0 : 0+$!;
# spent 14µs making 2 calls to Mail::SpamAssassin::CORE:stat, avg 7µs/call
1896228µs26µs if ($stat_errn == 0 && !-d _) {
# spent 6µs making 2 calls to Mail::SpamAssassin::CORE:ftdir, avg 3µs/call
1897 dbg("config: $fname exists but is not a directory");
1898 } elsif ($stat_errn != 0 && $stat_errn != ENOENT) {
1899 dbg("config: error accessing $fname: $!");
1900 } else { # does not exist, create it
1901 eval {
1902426µs2256µs mkpath($fname, 0, 0700); 1;
# spent 256µs making 2 calls to File::Path::mkpath, avg 128µs/call
190328µs } or do {
1904 my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat;
1905 dbg("config: mkdir $fname failed: $eval_stat");
1906 };
1907 }
1908
1909218µs $fname;
1910}
1911
1912=item $fullpath = $f->find_rule_support_file ($filename)
1913
1914Find a rule-support file, such as C<languages> or C<triplets.txt>,
1915in the system-wide rules directory, and return its full path if
1916it exists, or undef if it doesn't exist.
1917
1918(This API was added in SpamAssassin 3.1.1.)
1919
1920=cut
1921
1922
# spent 898µs (176+722) within Mail::SpamAssassin::find_rule_support_file which was called: # once (176µs+722µs) by Mail::SpamAssassin::init at line 1703
sub find_rule_support_file {
192312µs my ($self, $filename) = @_;
1924
1925 return $self->first_existing_path(
192616218µs16722µs map { my $p = $_; $p =~ s{$}{/$filename}; $p } @default_rules_path );
# spent 677µs making 1 call to Mail::SpamAssassin::first_existing_path # spent 23µs making 10 calls to Mail::SpamAssassin::CORE:substcont, avg 2µs/call # spent 22µs making 5 calls to Mail::SpamAssassin::CORE:subst, avg 4µs/call
1927}
1928
1929=item $f->create_default_prefs ($filename, $username [ , $userdir ] )
1930
1931Copy default preferences file into home directory for later use and
1932modification, if it does not already exist and C<dont_copy_prefs> is
1933not set.
1934
1935=cut
1936
1937sub create_default_prefs {
1938 # $userdir will only exist if vpopmail config is enabled thru spamd
1939 # Its value will be the virtual user's maildir
1940 #
1941 my ($self, $fname, $user, $userdir) = @_;
1942
1943 if ($self->{dont_copy_prefs}) {
1944 return(0);
1945 }
1946
1947# if ($userdir && $userdir ne $self->{user_dir}) {
1948# warn "config: hooray! user_dirs don't match! '$userdir' vs '$self->{user_dir}'\n";
1949# }
1950
1951 my $stat_errn = stat($fname) ? 0 : 0+$!;
1952 if ($stat_errn == 0) {
1953 # fine, it already exists
1954 } elsif ($stat_errn != ENOENT) {
1955 dbg("config: cannot access user preferences file $fname: $!");
1956 } else {
1957 # Pass on the value of $userdir for virtual users in vpopmail
1958 # otherwise it is empty and the user's normal homedir is used
1959 $self->get_and_create_userstate_dir($userdir);
1960
1961 # copy in the default one for later editing
1962 my $defprefs =
1963 $self->first_existing_path(@Mail::SpamAssassin::default_prefs_path);
1964
1965 local(*IN,*OUT);
1966 $fname = Mail::SpamAssassin::Util::untaint_file_path($fname);
1967 if (!defined $defprefs) {
1968 warn "config: can not determine default prefs path\n";
1969 } elsif (!open(IN, "<$defprefs")) {
1970 warn "config: cannot open $defprefs: $!\n";
1971 } elsif (!open(OUT, ">$fname")) {
1972 warn "config: cannot create user preferences file $fname: $!\n";
1973 } else {
1974 # former code skipped lines beginning with '#* ', the following copy
1975 # procedure no longer does so, as it avoids reading line-by-line
1976 my($inbuf,$nread);
1977 while ( $nread=read(IN,$inbuf,16384) ) {
1978 print OUT $inbuf or die "cannot write to $fname: $!";
1979 }
1980 defined $nread or die "error reading $defprefs: $!";
1981 undef $inbuf;
1982 close OUT or die "error closing $fname: $!";
1983 close IN or die "error closing $defprefs: $!";
1984
1985 if (($< == 0) && ($> == 0) && defined($user)) { # chown it
1986 my ($uid,$gid) = (getpwnam(untaint_var($user)))[2,3];
1987 unless (chown($uid, $gid, $fname)) {
1988 warn "config: couldn't chown $fname to $uid:$gid for $user: $!\n";
1989 }
1990 }
1991 warn "config: created user preferences file: $fname\n";
1992 return(1);
1993 }
1994 }
1995
1996 return(0);
1997}
1998
1999###########################################################################
2000
2001
# spent 143µs (84+58) within Mail::SpamAssassin::expand_name which was called 2 times, avg 71µs/call: # 2 times (84µs+58µs) by Mail::SpamAssassin::sed_path at line 2039, avg 71µs/call
sub expand_name {
2002216µs my ($self, $name) = @_;
2003210µs my $home = $self->{user_dir} || $ENV{HOME} || '';
2004
2005213µs252µs if (am_running_on_windows()) {
# spent 52µs making 2 calls to Mail::SpamAssassin::Util::am_running_on_windows, avg 26µs/call
2006 my $userprofile = $ENV{USERPROFILE} || '';
2007
2008 return $userprofile if ($userprofile && $userprofile =~ m/^[a-z]\:[\/\\]/oi);
2009 return $userprofile if ($userprofile =~ m/^\\\\/o);
2010
2011 return $home if ($home && $home =~ m/^[a-z]\:[\/\\]/oi);
2012 return $home if ($home =~ m/^\\\\/o);
2013
2014 return '';
2015 } else {
2016257µs26µs return $home if ($home && $home =~ /\//o);
# spent 6µs making 2 calls to Mail::SpamAssassin::CORE:match, avg 3µs/call
2017 return (getpwnam($name))[7] if ($name ne '');
2018 return (getpwuid($>))[7];
2019 }
2020}
2021
2022
# spent 33.1ms (23.0+10.0) within Mail::SpamAssassin::sed_path which was called 539 times, avg 61µs/call: # 236 times (5.37ms+153µs) by Mail::SpamAssassin::BayesStore::DBM::tie_db_readonly at line 158 of Mail/SpamAssassin/BayesStore/DBM.pm, avg 23µs/call # 235 times (5.02ms+132µs) by Mail::SpamAssassin::BayesStore::DBM::_get_journal_filename at line 1436 of Mail/SpamAssassin/BayesStore/DBM.pm, avg 22µs/call # 58 times (11.7ms+8.03ms) by Mail::SpamAssassin::Conf::Parser::fix_path_relative_to_current_file at line 1499 of Mail/SpamAssassin/Conf/Parser.pm, avg 340µs/call # 7 times (798µs+1.26ms) by Mail::SpamAssassin::first_existing_path at line 2056, avg 294µs/call # 2 times (44µs+0s) by Mail::SpamAssassin::BayesStore::DBM::tie_db_writable at line 256 of Mail/SpamAssassin/BayesStore/DBM.pm, avg 22µs/call # once (113µs+469µs) by Mail::SpamAssassin::DBBasedAddrList::new_checker at line 68 of Mail/SpamAssassin/DBBasedAddrList.pm
sub sed_path {
20235392.38ms my ($self, $path) = @_;
20245391.23ms return if !defined $path;
2025
20265393.26ms if (exists($self->{conf}->{sed_path_cache}->{$path})) {
20274725.43ms return $self->{conf}->{sed_path_cache}->{$path};
2028 }
2029
203067154µs my $orig_path = $path;
2031
203268817µs69310µs $path =~ s/__local_rules_dir__/$self->{LOCAL_RULES_DIR} || ''/ges;
# spent 297µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 4µs/call # spent 13µs making 2 calls to Mail::SpamAssassin::CORE:substcont, avg 6µs/call
203369655µs71199µs $path =~ s/__local_state_dir__/$self->{LOCAL_STATE_DIR} || ''/ges;
# spent 188µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call # spent 11µs making 4 calls to Mail::SpamAssassin::CORE:substcont, avg 3µs/call
203468625µs69203µs $path =~ s/__def_rules_dir__/$self->{DEF_RULES_DIR} || ''/ges;
# spent 198µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call # spent 6µs making 2 calls to Mail::SpamAssassin::CORE:substcont, avg 3µs/call
203567588µs67195µs $path =~ s{__prefix__}{$self->{PREFIX} || $Config{prefix} || '/usr'}ges;
# spent 195µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call
203668648µs70553µs $path =~ s{__userstate__}{$self->get_and_create_userstate_dir() || ''}ges;
# spent 306µs making 1 call to Mail::SpamAssassin::get_and_create_userstate_dir # spent 236µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 4µs/call # spent 12µs making 2 calls to Mail::SpamAssassin::CORE:substcont, avg 6µs/call
203767601µs67223µs $path =~ s{__perl_major_ver__}{$self->get_perl_major_version()}ges;
# spent 223µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call
2038677.11ms67208µs $path =~ s/__version__/${VERSION}/gs;
# spent 208µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call
203969717µs73407µs $path =~ s/^\~([^\/]*)/$self->expand_name($1)/es;
# spent 234µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call # spent 143µs making 2 calls to Mail::SpamAssassin::expand_name, avg 71µs/call # spent 30µs making 4 calls to Mail::SpamAssassin::CORE:substcont, avg 7µs/call
2040
204167649µs677.78ms $path = Mail::SpamAssassin::Util::untaint_file_path ($path);
# spent 7.78ms making 67 calls to Mail::SpamAssassin::Util::untaint_file_path, avg 116µs/call
204267625µs $self->{conf}->{sed_path_cache}->{$orig_path} = $path;
204367633µs return $path;
2044}
2045
2046sub get_perl_major_version {
2047 my $self = shift;
2048 $] =~ /^(\d\.\d\d\d)/ or die "bad perl ver $]";
2049 return $1;
2050}
2051
2052
# spent 2.56ms (293µs+2.26) within Mail::SpamAssassin::first_existing_path which was called 6 times, avg 426µs/call: # 2 times (83µs+493µs) by Mail::SpamAssassin::get_and_create_userstate_dir at line 1882, avg 288µs/call # once (69µs+608µs) by Mail::SpamAssassin::find_rule_support_file at line 1926 # once (48µs+438µs) by Mail::SpamAssassin::init at line 1675 # once (52µs+433µs) by Mail::SpamAssassin::init at line 1715 # once (40µs+292µs) by Mail::SpamAssassin::init at line 1678
sub first_existing_path {
2053615µs my $self = shift;
2054610µs my $path;
2055614µs foreach my $p (@_) {
2056754µs72.06ms $path = $self->sed_path ($p);
# spent 2.08ms making 7 calls to Mail::SpamAssassin::sed_path, avg 298µs/call, recursion: max depth 1, sum of overlapping time 26µs
2057716µs if (defined $path) {
20587252µs7179µs my($errn) = stat($path) ? 0 : 0+$!;
# spent 179µs making 7 calls to Mail::SpamAssassin::CORE:stat, avg 26µs/call
2059718µs if ($errn == ENOENT) { } # does not exist
2060 elsif ($errn) { warn "config: path \"$path\" is inaccessible: $!\n" }
2061676µs else { return $path }
2062 }
2063 }
2064 return;
2065}
2066
2067###########################################################################
2068
2069
# spent 989µs (35+954) within Mail::SpamAssassin::get_cf_files_in_dir which was called 2 times, avg 495µs/call: # 2 times (35µs+954µs) by Mail::SpamAssassin::_read_cf_pre at line 1827, avg 495µs/call
sub get_cf_files_in_dir {
207024µs my ($self, $dir) = @_;
2071226µs2954µs return $self->_get_cf_pre_files_in_dir($dir, 'cf');
# spent 954µs making 2 calls to Mail::SpamAssassin::_get_cf_pre_files_in_dir, avg 477µs/call
2072}
2073
2074
# spent 1.44ms (42µs+1.40) within Mail::SpamAssassin::get_pre_files_in_dir which was called 2 times, avg 720µs/call: # 2 times (42µs+1.40ms) by Mail::SpamAssassin::_read_cf_pre at line 1827, avg 720µs/call
sub get_pre_files_in_dir {
207524µs my ($self, $dir) = @_;
2076233µs21.40ms return $self->_get_cf_pre_files_in_dir($dir, 'pre');
# spent 1.40ms making 2 calls to Mail::SpamAssassin::_get_cf_pre_files_in_dir, avg 699µs/call
2077}
2078
2079
# spent 2.35ms (1.28+1.07) within Mail::SpamAssassin::_get_cf_pre_files_in_dir which was called 4 times, avg 588µs/call: # 2 times (706µs+692µs) by Mail::SpamAssassin::get_pre_files_in_dir at line 2076, avg 699µs/call # 2 times (575µs+379µs) by Mail::SpamAssassin::get_cf_files_in_dir at line 2071, avg 477µs/call
sub _get_cf_pre_files_in_dir {
2080417µs my ($self, $dir, $type) = @_;
2081
208249µs if ($self->{config_tree_recurse}) {
2083 my @cfs;
2084
2085 # use "eval" to avoid loading File::Find unless this is specified
2086 eval ' use File::Find qw();
2087 File::Find::find(
2088 { untaint => 1,
2089 follow => 1,
2090 wanted =>
2091 sub { push(@cfs, $File::Find::name) if /\.\Q$type\E$/i && -f $_ }
2092 }, $dir); 1;
2093 ' or do {
2094 my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat;
2095 die "_get_cf_pre_files_in_dir error: $eval_stat";
2096 };
2097 @cfs = sort { $a cmp $b } @cfs;
2098 return @cfs;
2099 }
2100 else {
21014142µs4104µs opendir(SA_CF_DIR, $dir) or warn "config: cannot opendir $dir: $!\n";
# spent 104µs making 4 calls to Mail::SpamAssassin::CORE:open_dir, avg 26µs/call
2102521.87ms93858µs my @cfs = grep { $_ ne '.' && $_ ne '..' &&
# spent 286µs making 40 calls to Mail::SpamAssassin::CORE:match, avg 7µs/call # spent 231µs making 4 calls to Mail::SpamAssassin::CORE:readdir, avg 58µs/call # spent 212µs making 9 calls to Mail::SpamAssassin::CORE:ftfile, avg 24µs/call # spent 130µs making 40 calls to Mail::SpamAssassin::CORE:regcomp, avg 3µs/call
2103 /\.${type}$/i && -f "$dir/$_" } readdir(SA_CF_DIR);
2104485µs445µs closedir SA_CF_DIR;
# spent 45µs making 4 calls to Mail::SpamAssassin::CORE:closedir, avg 11µs/call
2105
210626241µs464µs return map { "$dir/$_" } sort { $a cmp $b } @cfs;
# spent 64µs making 4 calls to Mail::SpamAssassin::CORE:sort, avg 16µs/call
2107 }
2108}
2109
2110###########################################################################
2111
2112sub have_plugin {
2113 my ($self, $subname) = @_;
2114
2115 # We could potentially get called after a finish(), so just return.
2116 return unless $self->{plugins};
2117
2118 return $self->{plugins}->have_callback ($subname);
2119}
2120
2121
# spent 1576s (65.8ms+1576) within Mail::SpamAssassin::call_plugins which was called 1916 times, avg 822ms/call: # 501 times (11.3ms+144ms) by Mail::SpamAssassin::Conf::Parser::parse at line 467 of Mail/SpamAssassin/Conf/Parser.pm, avg 310µs/call # 235 times (6.66ms+1570s) by Mail::SpamAssassin::Bayes::learn at line 120 of Mail/SpamAssassin/Bayes.pm, avg 6.68s/call # 235 times (9.41ms+8.30ms) by Mail::SpamAssassin::parse at line 556, avg 75µs/call # 235 times (9.11ms+-9.11ms) by Mail::SpamAssassin::Message::Metadata::extract at line 106 of Mail/SpamAssassin/Message/Metadata.pm, avg 0s/call # 235 times (9.95ms+-9.95ms) by Mail::SpamAssassin::Plugin::Bayes::_learn_trapped at line 488 of Mail/SpamAssassin/Plugin/Bayes.pm, avg 0s/call # 235 times (9.46ms+-9.46ms) by Mail::SpamAssassin::PerMsgStatus::extract_message_metadata at line 1750 of Mail/SpamAssassin/PerMsgStatus.pm, avg 0s/call # 235 times (9.68ms+-9.68ms) by Mail::SpamAssassin::PerMsgStatus::finish at line 1659 of Mail/SpamAssassin/PerMsgStatus.pm, avg 0s/call # once (40µs+5.48s) by Mail::SpamAssassin::Bayes::force_close at line 83 of Mail/SpamAssassin/Bayes.pm # once (42µs+136ms) by Mail::SpamAssassin::Conf::Parser::finish_parsing at line 942 of Mail/SpamAssassin/Conf/Parser.pm # once (44µs+34.3ms) by Mail::SpamAssassin::Bayes::new at line 61 of Mail/SpamAssassin/Bayes.pm # once (44µs+1.36ms) by Mail::SpamAssassin::Bayes::is_scan_available at line 153 of Mail/SpamAssassin/Bayes.pm # once (40µs+813µs) by Mail::SpamAssassin::Conf::Parser::finish_parsing at line 853 of Mail/SpamAssassin/Conf/Parser.pm
sub call_plugins {
212219163.88ms my $self = shift;
2123
2124 # We could potentially get called after a finish(), so just return.
212519165.91ms return unless $self->{plugins};
2126
2127 # safety net in case some plugin changes global settings, Bug 6218
2128191613.1ms local $/ = $/; # prevent underlying modules from changing the global $/
2129
213019164.13ms my $subname = shift;
2131191644.5ms19161576s return $self->{plugins}->callback($subname, @_);
# spent 1590s making 1916 calls to Mail::SpamAssassin::PluginHandler::callback, avg 830ms/call, recursion: max depth 1, sum of overlapping time 14.4s
2132}
2133
2134###########################################################################
2135
2136sub find_all_addrs_in_mail {
2137 my ($self, $mail_obj) = @_;
2138
2139 $self->init(1);
2140
2141 my @addrlist;
2142 foreach my $header (qw(To From Cc Reply-To Sender
2143 Errors-To Mail-Followup-To))
2144 {
2145 my @hdrs = $mail_obj->get_header($header);
2146 if ($#hdrs < 0) { next; }
2147 push (@addrlist, $self->find_all_addrs_in_line(join (" ", @hdrs)));
2148 }
2149
2150 # find addrs in body, too
2151 foreach my $line (@{$mail_obj->get_body()}) {
2152 push (@addrlist, $self->find_all_addrs_in_line($line));
2153 }
2154
2155 my @ret;
2156 my %done;
2157
2158 foreach (@addrlist) {
2159 s/^mailto://; # from Outlook "forwarded" message
2160 next if defined ($done{$_}); $done{$_} = 1;
2161 push (@ret, $_);
2162 }
2163
2164 @ret;
2165}
2166
2167
# spent 84.9ms (66.7+18.2) within Mail::SpamAssassin::find_all_addrs_in_line which was called 762 times, avg 111µs/call: # 761 times (66.5ms+18.1ms) by Mail::SpamAssassin::Plugin::Bayes::_pre_chew_addr_header at line 1468 of Mail/SpamAssassin/Plugin/Bayes.pm, avg 111µs/call # once (177µs+138µs) by Mail::SpamAssassin::PerMsgStatus::all_to_addrs at line 3124 of Mail/SpamAssassin/PerMsgStatus.pm
sub find_all_addrs_in_line {
21687623.42ms my ($self, $line) = @_;
2169
2170 # a more permissive pattern based on "dot-atom" as per RFC2822
21717621.70ms my $ID_PATTERN = '[-a-z0-9_\+\:\=\!\#\$\%\&\*\^\?\{\}\|\~\/\.]+';
21727621.69ms my $HOST_PATTERN = '[-a-z0-9_\+\:\/]+';
2173
21747621.32ms my @addrs;
2175 my %seen;
217676224.4ms154813.0ms while ($line =~ s/(?:mailto:)?\s*
# spent 11.0ms making 774 calls to Mail::SpamAssassin::CORE:subst, avg 14µs/call # spent 1.91ms making 774 calls to Mail::SpamAssassin::CORE:regcomp, avg 2µs/call
2177 ($ID_PATTERN \@
2178 $HOST_PATTERN(?:\.$HOST_PATTERN)+)//oix)
2179 {
21807582.56ms my $addr = $1;
21817586.85ms7582.09ms $addr =~ s/^mailto://;
# spent 2.09ms making 758 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call
218215046.08ms next if (defined ($seen{$addr})); $seen{$addr} = 1;
218374629.3ms14923.18ms push (@addrs, $addr);
# spent 1.99ms making 746 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call # spent 1.19ms making 746 calls to Mail::SpamAssassin::CORE:regcomp, avg 2µs/call
2184 }
2185
218676216.8ms return @addrs;
2187}
2188
2189###########################################################################
2190
2191# sa_die -- used to die with a useful exit code.
2192
2193sub sa_die {
2194 my $exitcode = shift;
2195 warn @_;
2196 exit $exitcode;
2197}
2198
2199###########################################################################
2200
2201=item $f->copy_config ( [ $source ], [ $dest ] )
2202
2203Used for daemons to keep a persistent Mail::SpamAssassin object's
2204configuration correct if switching between users. Pass an associative
2205array reference as either $source or $dest, and set the other to 'undef'
2206so that the object will use its current configuration. i.e.:
2207
2208 # create object w/ configuration
2209 my $spamtest = Mail::SpamAssassin->new( ... );
2210
2211 # backup configuration to %conf_backup
2212 my %conf_backup;
2213 $spamtest->copy_config(undef, \%conf_backup) ||
2214 die "config: error returned from copy_config!\n";
2215
2216 ... do stuff, perhaps modify the config, etc ...
2217
2218 # reset the configuration back to the original
2219 $spamtest->copy_config(\%conf_backup, undef) ||
2220 die "config: error returned from copy_config!\n";
2221
2222Note that the contents of the associative arrays should be considered
2223opaque by calling code.
2224
2225=cut
2226
2227sub copy_config {
2228 my ($self, $source, $dest) = @_;
2229
2230 # At least one of either source or dest needs to be a hash reference ...
2231 unless ((defined $source && ref($source) eq 'HASH') ||
2232 (defined $dest && ref($dest) eq 'HASH'))
2233 {
2234 return 0;
2235 }
2236
2237 my $timer = $self->time_method("copy_config");
2238
2239 # let the Conf object itself do all the heavy lifting. It's better
2240 # than having this class know all about that class' internals...
2241 if (defined $source) {
2242 dbg ("config: copying current conf from backup");
2243 }
2244 else {
2245 dbg ("config: copying current conf to backup");
2246 }
2247 return $self->{conf}->clone($source, $dest);
2248}
2249
2250###########################################################################
2251
2252=item @plugins = $f->get_loaded_plugins_list ( )
2253
2254Return the list of plugins currently loaded by this SpamAssassin object's
2255configuration; each entry in the list is an object of type
2256C<Mail::SpamAssassin::Plugin>.
2257
2258(This API was added in SpamAssassin 3.2.0.)
2259
2260=cut
2261
2262sub get_loaded_plugins_list {
2263 my ($self) = @_;
2264 return $self->{plugins}->get_loaded_plugins_list();
2265}
2266
2267141µs1;
2268__END__
 
# spent 815µs within Mail::SpamAssassin::CORE:close which was called 68 times, avg 12µs/call: # 68 times (815µs+0s) by Mail::SpamAssassin::read_cf_file at line 1849, avg 12µs/call
sub Mail::SpamAssassin::CORE:close; # opcode
# spent 45µs within Mail::SpamAssassin::CORE:closedir which was called 4 times, avg 11µs/call: # 4 times (45µs+0s) by Mail::SpamAssassin::_get_cf_pre_files_in_dir at line 2104, avg 11µs/call
sub Mail::SpamAssassin::CORE:closedir; # opcode
# spent 185µs within Mail::SpamAssassin::CORE:ftdir which was called 65 times, avg 3µs/call: # 63 times (179µs+0s) by Mail::SpamAssassin::_read_cf_pre at line 1822, avg 3µs/call # 2 times (6µs+0s) by Mail::SpamAssassin::get_and_create_userstate_dir at line 1896, avg 3µs/call
sub Mail::SpamAssassin::CORE:ftdir; # opcode
# spent 225µs within Mail::SpamAssassin::CORE:fteread which was called 59 times, avg 4µs/call: # 59 times (225µs+0s) by Mail::SpamAssassin::_read_cf_pre at line 1822, avg 4µs/call
sub Mail::SpamAssassin::CORE:fteread; # opcode
# spent 345µs within Mail::SpamAssassin::CORE:ftfile which was called 68 times, avg 5µs/call: # 59 times (133µs+0s) by Mail::SpamAssassin::_read_cf_pre at line 1822, avg 2µs/call # 9 times (212µs+0s) by Mail::SpamAssassin::_get_cf_pre_files_in_dir at line 2102, avg 24µs/call
sub Mail::SpamAssassin::CORE:ftfile; # opcode
# spent 141µs within Mail::SpamAssassin::CORE:ftsize which was called 59 times, avg 2µs/call: # 59 times (141µs+0s) by Mail::SpamAssassin::_read_cf_pre at line 1822, avg 2µs/call
sub Mail::SpamAssassin::CORE:ftsize; # opcode
# spent 343µs within Mail::SpamAssassin::CORE:match which was called 54 times, avg 6µs/call: # 40 times (286µs+0s) by Mail::SpamAssassin::_get_cf_pre_files_in_dir at line 2102, avg 7µs/call # 9 times (24µs+0s) by Mail::SpamAssassin::init at line 1791, avg 3µs/call # 2 times (6µs+0s) by Mail::SpamAssassin::expand_name at line 2016, avg 3µs/call # once (12µs+0s) by Mail::SpamAssassin::Version at line 127 # once (9µs+0s) by main::BEGIN@65 at line 109 # once (6µs+0s) by Mail::SpamAssassin::init at line 1745
sub Mail::SpamAssassin::CORE:match; # opcode
# spent 2.88ms within Mail::SpamAssassin::CORE:open which was called 68 times, avg 42µs/call: # 68 times (2.88ms+0s) by Mail::SpamAssassin::read_cf_file at line 1844, avg 42µs/call
sub Mail::SpamAssassin::CORE:open; # opcode
# spent 104µs within Mail::SpamAssassin::CORE:open_dir which was called 4 times, avg 26µs/call: # 4 times (104µs+0s) by Mail::SpamAssassin::_get_cf_pre_files_in_dir at line 2101, avg 26µs/call
sub Mail::SpamAssassin::CORE:open_dir; # opcode
# spent 4.05ms within Mail::SpamAssassin::CORE:read which was called 164 times, avg 25µs/call: # 164 times (4.05ms+0s) by Mail::SpamAssassin::read_cf_file at line 1847, avg 25µs/call
sub Mail::SpamAssassin::CORE:read; # opcode
# spent 231µs within Mail::SpamAssassin::CORE:readdir which was called 4 times, avg 58µs/call: # 4 times (231µs+0s) by Mail::SpamAssassin::_get_cf_pre_files_in_dir at line 2102, avg 58µs/call
sub Mail::SpamAssassin::CORE:readdir; # opcode
# spent 3.23ms within Mail::SpamAssassin::CORE:regcomp which was called 1560 times, avg 2µs/call: # 774 times (1.91ms+0s) by Mail::SpamAssassin::find_all_addrs_in_line at line 2176, avg 2µs/call # 746 times (1.19ms+0s) by Mail::SpamAssassin::find_all_addrs_in_line at line 2183, avg 2µs/call # 40 times (130µs+0s) by Mail::SpamAssassin::_get_cf_pre_files_in_dir at line 2102, avg 3µs/call
sub Mail::SpamAssassin::CORE:regcomp; # opcode
# spent 64µs within Mail::SpamAssassin::CORE:sort which was called 4 times, avg 16µs/call: # 4 times (64µs+0s) by Mail::SpamAssassin::_get_cf_pre_files_in_dir at line 2106, avg 16µs/call
sub Mail::SpamAssassin::CORE:sort; # opcode
# spent 2.52ms within Mail::SpamAssassin::CORE:stat which was called 72 times, avg 35µs/call: # 63 times (2.33ms+0s) by Mail::SpamAssassin::_read_cf_pre at line 1821, avg 37µs/call # 7 times (179µs+0s) by Mail::SpamAssassin::first_existing_path at line 2058, avg 26µs/call # 2 times (14µs+0s) by Mail::SpamAssassin::get_and_create_userstate_dir at line 1895, avg 7µs/call
sub Mail::SpamAssassin::CORE:stat; # opcode
# spent 16.9ms within Mail::SpamAssassin::CORE:subst which was called 2819 times, avg 6µs/call: # 774 times (11.0ms+0s) by Mail::SpamAssassin::find_all_addrs_in_line at line 2176, avg 14µs/call # 758 times (2.09ms+0s) by Mail::SpamAssassin::find_all_addrs_in_line at line 2181, avg 3µs/call # 746 times (1.99ms+0s) by Mail::SpamAssassin::find_all_addrs_in_line at line 2183, avg 3µs/call # 67 times (297µs+0s) by Mail::SpamAssassin::sed_path at line 2032, avg 4µs/call # 67 times (236µs+0s) by Mail::SpamAssassin::sed_path at line 2036, avg 4µs/call # 67 times (234µs+0s) by Mail::SpamAssassin::sed_path at line 2039, avg 3µs/call # 67 times (223µs+0s) by Mail::SpamAssassin::sed_path at line 2037, avg 3µs/call # 67 times (208µs+0s) by Mail::SpamAssassin::sed_path at line 2038, avg 3µs/call # 67 times (198µs+0s) by Mail::SpamAssassin::sed_path at line 2034, avg 3µs/call # 67 times (195µs+0s) by Mail::SpamAssassin::sed_path at line 2035, avg 3µs/call # 67 times (188µs+0s) by Mail::SpamAssassin::sed_path at line 2033, avg 3µs/call # 5 times (22µs+0s) by Mail::SpamAssassin::find_rule_support_file at line 1926, avg 4µs/call
sub Mail::SpamAssassin::CORE:subst; # opcode
# spent 94µs within Mail::SpamAssassin::CORE:substcont which was called 24 times, avg 4µs/call: # 10 times (23µs+0s) by Mail::SpamAssassin::find_rule_support_file at line 1926, avg 2µs/call # 4 times (30µs+0s) by Mail::SpamAssassin::sed_path at line 2039, avg 7µs/call # 4 times (11µs+0s) by Mail::SpamAssassin::sed_path at line 2033, avg 3µs/call # 2 times (13µs+0s) by Mail::SpamAssassin::sed_path at line 2032, avg 6µs/call # 2 times (12µs+0s) by Mail::SpamAssassin::sed_path at line 2036, avg 6µs/call # 2 times (6µs+0s) by Mail::SpamAssassin::sed_path at line 2034, avg 3µs/call
sub Mail::SpamAssassin::CORE:substcont; # opcode