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

Filename/usr/local/lib/perl5/site_perl/Mail/SpamAssassin.pm
StatementsExecuted 42665 statements in 384ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
191012677.0ms716sMail::SpamAssassin::::call_pluginsMail::SpamAssassin::call_plugins (recurses: max depth 1, inclusive time 14.7s)
7592256.6ms75.3msMail::SpamAssassin::::find_all_addrs_in_lineMail::SpamAssassin::find_all_addrs_in_line
61409455.5ms55.5msMail::SpamAssassin::::time_methodMail::SpamAssassin::time_method
2341136.9ms4.74sMail::SpamAssassin::::parseMail::SpamAssassin::parse
11131.4ms215msMail::SpamAssassin::::BEGIN@71Mail::SpamAssassin::BEGIN@71
11127.6ms41.5msMail::SpamAssassin::::BEGIN@74Mail::SpamAssassin::BEGIN@74
2341118.1ms710sMail::SpamAssassin::::learnMail::SpamAssassin::learn
281012117.3ms17.3msMail::SpamAssassin::::CORE:substMail::SpamAssassin::CORE:subst (opcode)
5366416.2ms25.9msMail::SpamAssassin::::sed_pathMail::SpamAssassin::sed_path (recurses: max depth 1, inclusive time 28µs)
11110.8ms299msMail::SpamAssassin::::BEGIN@75Mail::SpamAssassin::BEGIN@75
11110.1ms18.2msMail::SpamAssassin::::BEGIN@77Mail::SpamAssassin::BEGIN@77
68219.80ms18.4msMail::SpamAssassin::::read_cf_fileMail::SpamAssassin::read_cf_file
470428.36ms10.7sMail::SpamAssassin::::initMail::SpamAssassin::init
1116.86ms16.4msMail::SpamAssassin::::BEGIN@84Mail::SpamAssassin::BEGIN@84
63216.40ms30.8msMail::SpamAssassin::::_read_cf_preMail::SpamAssassin::_read_cf_pre
1114.51ms9.55msMail::SpamAssassin::::BEGIN@70Mail::SpamAssassin::BEGIN@70
164114.14ms4.14msMail::SpamAssassin::::CORE:readMail::SpamAssassin::CORE:read (opcode)
1113.90ms29.4msMail::SpamAssassin::::BEGIN@69Mail::SpamAssassin::BEGIN@69
1113.73ms4.41msMail::SpamAssassin::::BEGIN@65Mail::SpamAssassin::BEGIN@65
68113.07ms3.07msMail::SpamAssassin::::CORE:openMail::SpamAssassin::CORE:open (opcode)
1554313.05ms3.05msMail::SpamAssassin::::CORE:regcompMail::SpamAssassin::CORE:regcomp (opcode)
1112.68ms6.51msMail::SpamAssassin::::BEGIN@76Mail::SpamAssassin::BEGIN@76
72312.61ms2.61msMail::SpamAssassin::::CORE:statMail::SpamAssassin::CORE:stat (opcode)
1112.52ms2.87msMail::SpamAssassin::::BEGIN@78Mail::SpamAssassin::BEGIN@78
1112.08ms2.67msMail::SpamAssassin::::BEGIN@72Mail::SpamAssassin::BEGIN@72
1112.04ms2.63msMail::SpamAssassin::::BEGIN@73Mail::SpamAssassin::BEGIN@73
61421.39ms29.0msMail::SpamAssassin::::read_cfMail::SpamAssassin::read_cf
4211.32ms2.36msMail::SpamAssassin::::_get_cf_pre_files_in_dirMail::SpamAssassin::_get_cf_pre_files_in_dir
6811864µs864µsMail::SpamAssassin::::CORE:closeMail::SpamAssassin::CORE:close (opcode)
111680µs929µsMail::SpamAssassin::::BEGIN@80Mail::SpamAssassin::BEGIN@80
5461345µs345µsMail::SpamAssassin::::CORE:matchMail::SpamAssassin::CORE:match (opcode)
111315µs11.7msMail::SpamAssassin::::create_lockerMail::SpamAssassin::create_locker
651306µs2.51msMail::SpamAssassin::::first_existing_pathMail::SpamAssassin::first_existing_path
6821290µs290µsMail::SpamAssassin::::CORE:ftfileMail::SpamAssassin::CORE:ftfile (opcode)
111276µs48.7msMail::SpamAssassin::::newMail::SpamAssassin::new
5911234µs234µsMail::SpamAssassin::::CORE:ftereadMail::SpamAssassin::CORE:fteread (opcode)
411226µs226µsMail::SpamAssassin::::CORE:readdirMail::SpamAssassin::CORE:readdir (opcode)
6521193µs193µsMail::SpamAssassin::::CORE:ftdirMail::SpamAssassin::CORE:ftdir (opcode)
111192µs930µsMail::SpamAssassin::::find_rule_support_fileMail::SpamAssassin::find_rule_support_file
5911188µs188µsMail::SpamAssassin::::CORE:ftsizeMail::SpamAssassin::CORE:ftsize (opcode)
111179µs201µsMail::SpamAssassin::::init_learnerMail::SpamAssassin::init_learner
221149µs941µsMail::SpamAssassin::::get_and_create_userstate_dirMail::SpamAssassin::get_and_create_userstate_dir
411127µs127µsMail::SpamAssassin::::CORE:open_dirMail::SpamAssassin::CORE:open_dir (opcode)
246187µs87µsMail::SpamAssassin::::CORE:substcontMail::SpamAssassin::CORE:substcont (opcode)
21167µs129µsMail::SpamAssassin::::expand_nameMail::SpamAssassin::expand_name
41155µs55µsMail::SpamAssassin::::CORE:sortMail::SpamAssassin::CORE:sort (opcode)
21154µs1.06msMail::SpamAssassin::::get_cf_files_in_dirMail::SpamAssassin::get_cf_files_in_dir
11153µs65µsMail::SpamAssassin::::BEGIN@62Mail::SpamAssassin::BEGIN@62
11147µs72µsMail::SpamAssassin::::BEGIN@85Mail::SpamAssassin::BEGIN@85
22144µs3.29msMail::SpamAssassin::::read_preMail::SpamAssassin::read_pre
21143µs1.39msMail::SpamAssassin::::get_pre_files_in_dirMail::SpamAssassin::get_pre_files_in_dir
11136µs49µsMail::SpamAssassin::::VersionMail::SpamAssassin::Version
11135µs290µsMail::SpamAssassin::::BEGIN@83Mail::SpamAssassin::BEGIN@83
11134µs690µsMail::SpamAssassin::::BEGIN@90Mail::SpamAssassin::BEGIN@90
11132µs176µsMail::SpamAssassin::::BEGIN@79Mail::SpamAssassin::BEGIN@79
11130µs5.67sMail::SpamAssassin::::finish_learnerMail::SpamAssassin::finish_learner
11129µs70µsMail::SpamAssassin::::BEGIN@88Mail::SpamAssassin::BEGIN@88
41128µs28µsMail::SpamAssassin::::CORE:closedirMail::SpamAssassin::CORE:closedir (opcode)
11125µs384µsMail::SpamAssassin::::BEGIN@86Mail::SpamAssassin::BEGIN@86
11124µs247µsMail::SpamAssassin::::BEGIN@87Mail::SpamAssassin::BEGIN@87
11123µs67µsMail::SpamAssassin::::BEGIN@63Mail::SpamAssassin::BEGIN@63
11122µs29µsMail::SpamAssassin::::BEGIN@64Mail::SpamAssassin::BEGIN@64
11121µs156µsMail::SpamAssassin::::BEGIN@82Mail::SpamAssassin::BEGIN@82
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;
62258µs278µs
# spent 65µs (53+12) within Mail::SpamAssassin::BEGIN@62 which was called: # once (53µs+12µs) by main::BEGIN@65 at line 62
use strict;
# spent 65µs making 1 call to Mail::SpamAssassin::BEGIN@62 # spent 12µs making 1 call to strict::import
63257µs2111µs
# spent 67µs (23+44) within Mail::SpamAssassin::BEGIN@63 which was called: # once (23µs+44µs) by main::BEGIN@65 at line 63
use warnings;
# spent 67µs making 1 call to Mail::SpamAssassin::BEGIN@63 # spent 44µs making 1 call to warnings::import
64269µs236µs
# spent 29µs (22+7) within Mail::SpamAssassin::BEGIN@64 which was called: # once (22µs+7µs) by main::BEGIN@65 at line 64
use bytes;
# spent 29µs making 1 call to Mail::SpamAssassin::BEGIN@64 # spent 7µs making 1 call to bytes::import
652390µs24.48ms
# spent 4.41ms (3.73+678µs) within Mail::SpamAssassin::BEGIN@65 which was called: # once (3.73ms+678µs) by main::BEGIN@65 at line 65
use re 'taint';
# spent 4.41ms making 1 call to Mail::SpamAssassin::BEGIN@65 # spent 69µs making 1 call to re::import
66
67130µsrequire 5.006_001;
68
692406µs229.6ms
# spent 29.4ms (3.90+25.5) within Mail::SpamAssassin::BEGIN@69 which was called: # once (3.90ms+25.5ms) by main::BEGIN@65 at line 69
use Mail::SpamAssassin::Logger;
# spent 29.4ms making 1 call to Mail::SpamAssassin::BEGIN@69 # spent 189µs making 1 call to Exporter::import
702308µs29.66ms
# spent 9.55ms (4.51+5.04) within Mail::SpamAssassin::BEGIN@70 which was called: # once (4.51ms+5.04ms) by main::BEGIN@65 at line 70
use Mail::SpamAssassin::Constants;
# spent 9.55ms making 1 call to Mail::SpamAssassin::BEGIN@70 # spent 109µs making 1 call to Exporter::import
712425µs1215ms
# spent 215ms (31.4+184) within Mail::SpamAssassin::BEGIN@71 which was called: # once (31.4ms+184ms) by main::BEGIN@65 at line 71
use Mail::SpamAssassin::Conf;
# spent 215ms making 1 call to Mail::SpamAssassin::BEGIN@71
722381µs12.67ms
# spent 2.67ms (2.08+590µs) within Mail::SpamAssassin::BEGIN@72 which was called: # once (2.08ms+590µs) by main::BEGIN@65 at line 72
use Mail::SpamAssassin::Conf::SQL;
# spent 2.67ms making 1 call to Mail::SpamAssassin::BEGIN@72
732354µs12.63ms
# spent 2.63ms (2.04+588µs) within Mail::SpamAssassin::BEGIN@73 which was called: # once (2.04ms+588µs) by main::BEGIN@65 at line 73
use Mail::SpamAssassin::Conf::LDAP;
# spent 2.63ms making 1 call to Mail::SpamAssassin::BEGIN@73
742394µs141.5ms
# spent 41.5ms (27.6+13.9) within Mail::SpamAssassin::BEGIN@74 which was called: # once (27.6ms+13.9ms) by main::BEGIN@65 at line 74
use Mail::SpamAssassin::PerMsgStatus;
# spent 41.5ms making 1 call to Mail::SpamAssassin::BEGIN@74
752421µs1299ms
# spent 299ms (10.8+288) within Mail::SpamAssassin::BEGIN@75 which was called: # once (10.8ms+288ms) by main::BEGIN@65 at line 75
use Mail::SpamAssassin::Message;
# spent 299ms making 1 call to Mail::SpamAssassin::BEGIN@75
762356µs16.51ms
# spent 6.51ms (2.68+3.83) within Mail::SpamAssassin::BEGIN@76 which was called: # once (2.68ms+3.83ms) by main::BEGIN@65 at line 76
use Mail::SpamAssassin::PluginHandler;
# spent 6.51ms making 1 call to Mail::SpamAssassin::BEGIN@76
772353µs118.2ms
# spent 18.2ms (10.1+8.01) within Mail::SpamAssassin::BEGIN@77 which was called: # once (10.1ms+8.01ms) by main::BEGIN@65 at line 77
use Mail::SpamAssassin::DnsResolver;
# spent 18.2ms making 1 call to Mail::SpamAssassin::BEGIN@77
782334µs12.87ms
# spent 2.87ms (2.52+352µs) within Mail::SpamAssassin::BEGIN@78 which was called: # once (2.52ms+352µs) by main::BEGIN@65 at line 78
use Mail::SpamAssassin::RegistryBoundaries;
# spent 2.87ms making 1 call to Mail::SpamAssassin::BEGIN@78
79277µs2320µs
# spent 176µs (32+144) within Mail::SpamAssassin::BEGIN@79 which was called: # once (32µs+144µs) by main::BEGIN@65 at line 79
use Mail::SpamAssassin::Util qw(untaint_var am_running_on_windows);
# spent 176µs making 1 call to Mail::SpamAssassin::BEGIN@79 # spent 144µs making 1 call to Exporter::import
802322µs1929µs
# spent 929µs (680+250) within Mail::SpamAssassin::BEGIN@80 which was called: # once (680µs+250µs) by main::BEGIN@65 at line 80
use Mail::SpamAssassin::Util::ScopedTimer;
# spent 929µs making 1 call to Mail::SpamAssassin::BEGIN@80
81
82264µs2291µs
# spent 156µs (21+135) within Mail::SpamAssassin::BEGIN@82 which was called: # once (21µs+135µs) by main::BEGIN@65 at line 82
use Errno qw(ENOENT EACCES);
# spent 156µs making 1 call to Mail::SpamAssassin::BEGIN@82 # spent 135µs making 1 call to Exporter::import
83275µs2544µs
# spent 290µs (35+255) within Mail::SpamAssassin::BEGIN@83 which was called: # once (35µs+255µs) by main::BEGIN@65 at line 83
use File::Basename;
# spent 290µs making 1 call to Mail::SpamAssassin::BEGIN@83 # spent 255µs making 1 call to Exporter::import
842354µs216.6ms
# spent 16.4ms (6.86+9.55) within Mail::SpamAssassin::BEGIN@84 which was called: # once (6.86ms+9.55ms) by main::BEGIN@65 at line 84
use File::Path;
# spent 16.4ms making 1 call to Mail::SpamAssassin::BEGIN@84 # spent 157µs making 1 call to Exporter::import
853122µs297µs
# spent 72µs (47+25) within Mail::SpamAssassin::BEGIN@85 which was called: # once (47µs+25µs) by main::BEGIN@65 at line 85
use File::Spec 0.8;
# spent 72µs making 1 call to Mail::SpamAssassin::BEGIN@85 # spent 25µs making 1 call to version::_VERSION
86275µs2742µs
# spent 384µs (25+359) within Mail::SpamAssassin::BEGIN@86 which was called: # once (25µs+359µs) by main::BEGIN@65 at line 86
use Time::HiRes qw(time);
# spent 384µs making 1 call to Mail::SpamAssassin::BEGIN@86 # spent 359µs making 1 call to Time::HiRes::import
87265µs2470µs
# spent 247µs (24+223) within Mail::SpamAssassin::BEGIN@87 which was called: # once (24µs+223µs) by main::BEGIN@65 at line 87
use Cwd;
# spent 247µs making 1 call to Mail::SpamAssassin::BEGIN@87 # spent 223µs making 1 call to Exporter::import
88297µs2111µs
# spent 70µs (29+41) within Mail::SpamAssassin::BEGIN@88 which was called: # once (29µs+41µs) by main::BEGIN@65 at line 88
use Config;
# spent 70µs making 1 call to Mail::SpamAssassin::BEGIN@88 # spent 41µs making 1 call to Config::import
89
9012µs
# spent 690µs (34+656) within Mail::SpamAssassin::BEGIN@90 which was called: # once (34µs+656µ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.9ms21.35ms};
# spent 690µs making 1 call to Mail::SpamAssassin::BEGIN@90 # spent 656µ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';
109133µ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
113122µ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 49µs (36+13) within Mail::SpamAssassin::Version which was called: # once (36µs+13µs) by Mail::SpamAssassin::new at line 407
sub Version {
127123µs113µs $VERSION =~ /^(\d+)\.(\d\d\d)(\d\d\d)$/;
# spent 13µ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
17112µ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 48.7ms (276µs+48.5) within Mail::SpamAssassin::new which was called: # once (276µs+48.5ms) by main::RUNTIME at line 227 of /usr/local/bin/sa-learn
sub new {
39013µs my $class = shift;
39113µs $class = ref($class) || $class;
392
39312µs my $self = shift;
39412µs if (!defined $self) { $self = { }; }
39512µ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
40419µ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µs260µs dbg("generic: SpamAssassin version " . Version());
# spent 49µs making 1 call to Mail::SpamAssassin::Version # spent 11µ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';
41513µs $self->{LOCAL_STATE_DIR} ||= '/var/db/spamassassin';
416541µ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 }
42719µ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
432119µs14.62ms $self->{registryboundaries} = Mail::SpamAssassin::RegistryBoundaries->new ($self);
# spent 4.62ms making 1 call to Mail::SpamAssassin::RegistryBoundaries::new
433120µs125µs $self->{plugins} = Mail::SpamAssassin::PluginHandler->new ($self);
# spent 25µ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
43819µs12.20ms Mail::SpamAssassin::Util::clean_path_in_taint_mode();
439
44017µs if (!defined $self->{username}) {
441118µs12.71ms $self->{username} = (Mail::SpamAssassin::Util::portable_getpwuid ($>))[0];
# spent 2.71ms making 1 call to Mail::SpamAssassin::Util::portable_getpwuid
442 }
443
444120µs111.7ms $self->create_locker();
# spent 11.7ms making 1 call to Mail::SpamAssassin::create_locker
445
446112µs $self;
447}
448
449
# spent 11.7ms (315µs+11.4) within Mail::SpamAssassin::create_locker which was called: # once (315µs+11.4ms) by Mail::SpamAssassin::new at line 444
sub create_locker {
45015µs my ($self) = @_;
451
45212µs my $class;
45316µ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!)
45718µ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
462114µs126µs if (am_running_on_windows()) {
# spent 26µ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;
4751196µs ' or do {
# spent 427µs executing statements in string eval
# includes 3.16ms 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
480112µ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.74s (36.9ms+4.70) within Mail::SpamAssassin::parse which was called 234 times, avg 20.2ms/call: # 234 times (36.9ms+4.70s) by main::wanted at line 566 of /usr/local/bin/sa-learn, avg 20.2ms/call
sub parse {
526234662µs my($self, $message, $parsenow, $suppl_attrib) = @_;
527
5282344.23ms2341.99ms my $start_time = time;
# spent 1.99ms making 234 calls to Time::HiRes::time, avg 9µs/call
5292342.32ms2344.25ms $self->init(1);
# spent 4.25ms making 234 calls to Mail::SpamAssassin::init, avg 18µs/call
5302341.91ms2341.89ms my $timer = $self->time_method("parse");
# spent 1.89ms making 234 calls to Mail::SpamAssassin::time_method, avg 8µs/call
531
532234457µs my $master_deadline;
533 # passed in at a function call
534234523µ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
5382341.69ms if ($self->{conf}->{time_limit}) { # defined and nonzero
539234785µs my $time_limit_deadline = $start_time + $self->{conf}->{time_limit};
540234957µs if (!defined $master_deadline || $time_limit_deadline < $master_deadline) {
541234474µs $master_deadline = $time_limit_deadline;
542 }
543 }
5442341.91ms if (defined $master_deadline) {
5452342.04ms2341.91ms dbg("config: time limit %.1f s", $master_deadline - $start_time);
# spent 1.91ms making 234 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},
5512345.50ms2344.67s master_deadline=>$master_deadline, suppl_attrib=>$suppl_attrib });
# spent 4.67s making 234 calls to Mail::SpamAssassin::Message::new, avg 20.0ms/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.
5562342.60ms23417.8ms $self->call_plugins("post_message_parse", { message => $msg });
# spent 17.8ms making 234 calls to Mail::SpamAssassin::call_plugins, avg 76µs/call
557
5582341.94ms 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 710s (18.1ms+710) within Mail::SpamAssassin::learn which was called 234 times, avg 3.03s/call: # 234 times (18.1ms+710s) by main::wanted at line 574 of /usr/local/bin/sa-learn, avg 3.03s/call
sub learn {
643234614µs my ($self, $mail_obj, $id, $isspam, $forget) = @_;
644234459µs local ($_);
645
6462341.33ms require Mail::SpamAssassin::PerMsgLearner;
6472341.86ms2343.48ms $self->init(1);
# spent 3.48ms making 234 calls to Mail::SpamAssassin::init, avg 15µs/call
6482342.57ms2348.16ms my $msg = Mail::SpamAssassin::PerMsgLearner->new($self, $mail_obj);
# spent 8.16ms making 234 calls to Mail::SpamAssassin::PerMsgLearner::new, avg 35µs/call
649
6502341.62ms if ($forget) {
651 dbg("learn: forgetting message");
652 $msg->forget($id);
653 } elsif ($isspam) {
6542341.60ms2341.53ms dbg("learn: learning spam");
# spent 1.53ms making 234 calls to Mail::SpamAssassin::Logger::dbg, avg 7µs/call
6552342.00ms234710s $msg->learn_spam($id);
# spent 710s making 234 calls to Mail::SpamAssassin::PerMsgLearner::learn_spam, avg 3.03s/call
656 } else {
657 dbg("learn: learning ham");
658 $msg->learn_ham($id);
659 }
660
6612342.40ms $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 201µs (179+22) within Mail::SpamAssassin::init_learner which was called: # once (179µs+22µ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;
70817µs16µs dbg("learn: initializing learner");
# spent 6µ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
713111µ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 ...
725140µs while( my($k,$v) = each %kv ) {
726650µs $ret{$k} = $self->{$v};
7271033µ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.67s (30µs+5.67) within Mail::SpamAssassin::finish_learner which was called: # once (30µs+5.67s) by main::RUNTIME at line 501 of /usr/local/bin/sa-learn
sub finish_learner {
75712µs my $self = shift;
758113µs15.67s $self->{bayes_scanner}->force_close(1) if $self->{bayes_scanner};
# spent 5.67s making 1 call to Mail::SpamAssassin::Bayes::force_close
759113µ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 1586 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 55.5ms within Mail::SpamAssassin::time_method which was called 6140 times, avg 9µs/call: # 2207 times (20.1ms+0s) by Mail::SpamAssassin::Plugin::TxRep::check_reputation at line 1388 of Mail/SpamAssassin/Plugin/TxRep.pm, avg 9µs/call # 2207 times (19.8ms+0s) by Mail::SpamAssassin::Plugin::TxRep::check_reputation at line 1434 of Mail/SpamAssassin/Plugin/TxRep.pm, avg 9µs/call # 468 times (4.41ms+0s) by Mail::SpamAssassin::PerMsgStatus::get_uri_detail_list at line 2236 of Mail/SpamAssassin/PerMsgStatus.pm, avg 9µs/call # 321 times (2.79ms+0s) by Mail::SpamAssassin::Plugin::TxRep::check_senders_reputation at line 1237 of Mail/SpamAssassin/Plugin/TxRep.pm, avg 9µs/call # 234 times (2.61ms+0s) by Mail::SpamAssassin::Plugin::Bayes::_learn_trapped at line 475 of Mail/SpamAssassin/Plugin/Bayes.pm, avg 11µs/call # 234 times (2.05ms+0s) by Mail::SpamAssassin::PerMsgStatus::extract_message_metadata at line 1702 of Mail/SpamAssassin/PerMsgStatus.pm, avg 9µs/call # 234 times (1.89ms+0s) by Mail::SpamAssassin::parse at line 530, avg 8µs/call # 234 times (1.89ms+0s) by Mail::SpamAssassin::Plugin::Bayes::learn_message at line 382 of Mail/SpamAssassin/Plugin/Bayes.pm, avg 8µs/call # once (10µs+0s) by Mail::SpamAssassin::init at line 1660
sub time_method {
1606614015.1ms my ($self, $name) = @_;
1607614087.3ms 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.36ms+10.7) within Mail::SpamAssassin::init which was called 470 times, avg 22.9ms/call: # 234 times (4.25ms+0s) by Mail::SpamAssassin::parse at line 529, avg 18µs/call # 234 times (3.48ms+0s) by Mail::SpamAssassin::learn at line 647, avg 15µs/call # once (612µs+10.7s) 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 {
16474701.05ms my ($self, $use_user_pref) = @_;
1648
1649 # Allow init() to be called multiple times, but only run once.
16504701.64ms if (defined $self->{_initted}) {
1651 # If the PID changes, reseed the PRNG (if permitted) and the DNS ID counter
16524692.35ms if ($self->{_initted} != $$) {
1653 $self->{_initted} = $$;
1654 srand if !$self->{skip_prng_reseeding};
1655 $self->{resolver}->reinit_post_fork();
1656 }
165746911.5ms 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()
1662161µ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
1669112µ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.
167413µs my $siterules = $self->{site_rules_filename};
1675112µs1518µs $siterules ||= $self->first_existing_path (@site_rules_path);
# spent 518µs making 1 call to Mail::SpamAssassin::first_existing_path
1676
167714µs my $sysrules = $self->{rules_filename};
167819µs1338µs $sysrules ||= $self->first_existing_path (@default_rules_path);
# spent 338µs making 1 call to Mail::SpamAssassin::first_existing_path
1679
168015µs if ($siterules) {
1681122µs12.90ms $self->{config_text} .= $self->read_pre($siterules, 'site rules pre files');
# spent 2.90ms 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) {
168819µs1388µs $self->{config_text} .= $self->read_pre($sysrules, 'sys rules pre files');
# spent 388µ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) {
1695112µs1608µs my $cftext = $self->read_cf($sysrules, 'default rules dir');
# spent 608µ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 }
169916µs $self->{config_text} .= $cftext;
1700 }
1701
170216µs if (!$self->{languages_filename}) {
1703111µs1930µs $self->{languages_filename} = $self->find_rule_support_file("languages");
# spent 930µs making 1 call to Mail::SpamAssassin::find_rule_support_file
1704 }
1705
170616µs if ($siterules && !$self->{ignore_site_cf_files}) {
1707112µs11.21ms $self->{config_text} .= $self->read_cf($siterules, 'site rules dir');
# spent 1.21ms making 1 call to Mail::SpamAssassin::read_cf
1708 }
1709
171015µs if ( $use_user_pref != 0 ) {
171118µs1646µs $self->get_and_create_userstate_dir();
# spent 646µs making 1 call to Mail::SpamAssassin::get_and_create_userstate_dir
1712
1713 # user prefs file
171413µs my $fname = $self->{userprefs_filename};
1715121µs1447µs $fname ||= $self->first_existing_path (@default_userprefs_path);
# spent 447µs making 1 call to Mail::SpamAssassin::first_existing_path
1716
171713µ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
1734111µs1386µs $self->{config_text} .= $self->read_cf($fname, 'user prefs file');
# spent 386µ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}) {
174213µs $self->{config_text} .= $self->{post_config_text};
1743 }
1744
1745118µs17µs if ($self->{config_text} !~ /\S/) {
# spent 7µ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!
175514µs $self->{conf}->{main} = $self;
175619µs117µs if (would_log('dbg', 'config_text') > 1) {
# spent 17µs making 1 call to Mail::SpamAssassin::Logger::would_log
1757 dbg('config_text: '.$self->{config_text});
1758 }
1759111µs14.68s $self->{conf}->parse_rules ($self->{config_text});
# spent 4.68s making 1 call to Mail::SpamAssassin::Conf::parse_rules
1760114µs16.01s $self->{conf}->finish_parsing(0);
# spent 6.01s making 1 call to Mail::SpamAssassin::Conf::finish_parsing
176118µs delete $self->{conf}->{main}; # to allow future GC'ing
1762
176315µs undef $self->{config_text}; # ensure it's actually freed
176413µ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}) {
177216µs require Mail::SpamAssassin::Bayes;
1773125µs130.5ms $self->{bayes_scanner} = new Mail::SpamAssassin::Bayes ($self);
# spent 30.5ms making 1 call to Mail::SpamAssassin::Bayes::new
1774 }
177518µ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};
1780112µs11.23ms $set |= 2 if $self->{bayes_scanner} && $self->{bayes_scanner}->is_scan_available();
# spent 1.23ms making 1 call to Mail::SpamAssassin::Bayes::is_scan_available
1781112µs136µs $self->{conf}->set_score_set ($set);
# spent 36µs making 1 call to Mail::SpamAssassin::Conf::set_score_set
1782
178313µs if ($self->{only_these_rules}) {
1784 $self->{conf}->trim_rules($self->{only_these_rules});
1785 }
1786
178715µ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'}}) {
1791992µ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
1796131µs16.35ms $self->{resolver} = Mail::SpamAssassin::DnsResolver->new($self);
# spent 6.35ms making 1 call to Mail::SpamAssassin::DnsResolver::new
1797
1798 # TODO -- open DNS cache etc. if necessary
1799}
1800
1801
# spent 29.0ms (1.39+27.6) within Mail::SpamAssassin::read_cf which was called 61 times, avg 475µs/call: # 58 times (1.33ms+25.4ms) by Mail::SpamAssassin::Conf::Parser::parse at line 343 of Mail/SpamAssassin/Conf/Parser.pm, avg 461µs/call # once (23µs+1.19ms) by Mail::SpamAssassin::init at line 1707 # once (20µs+587µs) by Mail::SpamAssassin::init at line 1695 # once (17µs+369µs) by Mail::SpamAssassin::init at line 1734
sub read_cf {
180261181µs my ($self, $allpaths, $desc) = @_;
1803611.11ms6127.6ms return $self->_read_cf_pre($allpaths,$desc,\&get_cf_files_in_dir);
# spent 27.6ms making 61 calls to Mail::SpamAssassin::_read_cf_pre, avg 452µs/call
1804}
1805
1806
# spent 3.29ms (44µs+3.24) within Mail::SpamAssassin::read_pre which was called 2 times, avg 1.64ms/call: # once (23µs+2.88ms) by Mail::SpamAssassin::init at line 1681 # once (21µs+367µs) by Mail::SpamAssassin::init at line 1688
sub read_pre {
180728µs my ($self, $allpaths, $desc) = @_;
1808238µs23.24ms return $self->_read_cf_pre($allpaths,$desc,\&get_pre_files_in_dir);
# spent 3.24ms making 2 calls to Mail::SpamAssassin::_read_cf_pre, avg 1.62ms/call
1809}
1810
1811
# spent 30.8ms (6.40+24.4) within Mail::SpamAssassin::_read_cf_pre which was called 63 times, avg 489µs/call: # 61 times (6.12ms+21.4ms) by Mail::SpamAssassin::read_cf at line 1803, avg 452µs/call # 2 times (283µs+2.96ms) by Mail::SpamAssassin::read_pre at line 1808, avg 1.62ms/call
sub _read_cf_pre {
181263145µs my ($self, $allpaths, $desc, $filelistmethod) = @_;
1813
181463133µs return '' unless defined ($allpaths);
1815
181663138µs my $txt = '';
181763461µs foreach my $path (split("\000", $allpaths))
1818 {
181963497µs63403µs dbg("config: using \"$path\" for $desc");
# spent 403µs making 63 calls to Mail::SpamAssassin::Logger::dbg, avg 6µs/call
1820
1821632.90ms632.41ms my $stat_errn = stat($path) ? 0 : 0+$!;
# spent 2.41ms making 63 calls to Mail::SpamAssassin::CORE:stat, avg 38µs/call
1822632.48ms240715µs if ($stat_errn == ENOENT) {
# spent 234µs making 59 calls to Mail::SpamAssassin::CORE:fteread, avg 4µs/call # spent 188µs making 59 calls to Mail::SpamAssassin::CORE:ftsize, avg 3µs/call # spent 187µs making 63 calls to Mail::SpamAssassin::CORE:ftdir, avg 3µs/call # spent 105µ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 _) {
1827452µs42.45ms foreach my $file ($self->$filelistmethod($path)) {
# spent 1.39ms making 2 calls to Mail::SpamAssassin::get_pre_files_in_dir, avg 694µs/call # spent 1.06ms making 2 calls to Mail::SpamAssassin::get_cf_files_in_dir, avg 532µs/call
18289111µs91.97ms $txt .= read_cf_file($file);
# spent 1.97ms making 9 calls to Mail::SpamAssassin::read_cf_file, avg 219µs/call
1829 }
1830 } elsif (-f _ && -s _ && -r _) {
1831591.67ms5916.4ms $txt .= read_cf_file($path);
# spent 16.4ms making 59 calls to Mail::SpamAssassin::read_cf_file, avg 279µs/call
1832 }
1833 }
1834
183563815µs return $txt;
1836}
1837
1838
1839
# spent 18.4ms (9.80+8.62) within Mail::SpamAssassin::read_cf_file which was called 68 times, avg 271µs/call: # 59 times (8.68ms+7.77ms) by Mail::SpamAssassin::_read_cf_pre at line 1831, avg 279µs/call # 9 times (1.12ms+849µs) by Mail::SpamAssassin::_read_cf_pre at line 1828, avg 219µs/call
sub read_cf_file {
184068149µs my($path) = @_;
184168185µs my $txt = '';
1842
184368397µs local *IN;
1844683.93ms683.07ms if (open (IN, "<".$path)) {
# spent 3.07ms making 68 calls to Mail::SpamAssassin::CORE:open, avg 45µs/call
1845
1846136279µs my($inbuf,$nread); $txt = '';
18471647.05ms1644.14ms while ( $nread=read(IN,$inbuf,16384) ) { $txt .= $inbuf }
# spent 4.14ms making 164 calls to Mail::SpamAssassin::CORE:read, avg 25µs/call
184868189µs defined $nread or die "error reading $path: $!";
1849681.35ms68864µs close IN or die "error closing $path: $!";
# spent 864µs making 68 calls to Mail::SpamAssassin::CORE:close, avg 13µs/call
185068206µs undef $inbuf;
1851
1852681.58ms $txt = "file start $path\n" . $txt;
1853 # add an extra \n in case file did not end in one.
185468324µs $txt .= "\nfile end $path\n";
1855
185668652µs68540µs dbg("config: read file $path");
# spent 540µ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.32ms return $txt;
1863}
1864
1865
# spent 941µs (149+792) within Mail::SpamAssassin::get_and_create_userstate_dir which was called 2 times, avg 470µs/call: # once (78µs+568µs) by Mail::SpamAssassin::init at line 1711 # once (71µs+224µ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
1872213µ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
1882220µs2512µs $fname ||= $self->first_existing_path (@default_userstate_dir);
# spent 512µs making 2 calls to Mail::SpamAssassin::first_existing_path, avg 256µ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
188825µ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
1895239µs214µs my $stat_errn = stat($fname) ? 0 : 0+$!;
# spent 14µs making 2 calls to Mail::SpamAssassin::CORE:stat, avg 7µs/call
1896227µs25µs if ($stat_errn == 0 && !-d _) {
# spent 5µ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 {
1902425µs2260µs mkpath($fname, 0, 0700); 1;
# spent 260µs making 2 calls to File::Path::mkpath, avg 130µs/call
190329µ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 930µs (192+738) within Mail::SpamAssassin::find_rule_support_file which was called: # once (192µs+738µs) by Mail::SpamAssassin::init at line 1703
sub find_rule_support_file {
192313µs my ($self, $filename) = @_;
1924
1925 return $self->first_existing_path(
192616235µs16738µs map { my $p = $_; $p =~ s{$}{/$filename}; $p } @default_rules_path );
# spent 692µs making 1 call to Mail::SpamAssassin::first_existing_path # spent 25µ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 129µs (67+62) within Mail::SpamAssassin::expand_name which was called 2 times, avg 64µs/call: # 2 times (67µs+62µs) by Mail::SpamAssassin::sed_path at line 2039, avg 64µs/call
sub expand_name {
200228µs my ($self, $name) = @_;
2003211µs my $home = $self->{user_dir} || $ENV{HOME} || '';
2004
2005215µs254µs if (am_running_on_windows()) {
# spent 54µs making 2 calls to Mail::SpamAssassin::Util::am_running_on_windows, avg 27µ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 {
2016240µs28µs return $home if ($home && $home =~ /\//o);
# spent 8µs making 2 calls to Mail::SpamAssassin::CORE:match, avg 4µs/call
2017 return (getpwnam($name))[7] if ($name ne '');
2018 return (getpwuid($>))[7];
2019 }
2020}
2021
2022
# spent 25.9ms (16.2+9.69) within Mail::SpamAssassin::sed_path which was called 536 times, avg 48µs/call: # 235 times (5.10ms+143µs) by Mail::SpamAssassin::BayesStore::DBM::tie_db_readonly at line 158 of Mail/SpamAssassin/BayesStore/DBM.pm, avg 22µs/call # 234 times (4.91ms+129µs) by Mail::SpamAssassin::BayesStore::DBM::_get_journal_filename at line 1436 of Mail/SpamAssassin/BayesStore/DBM.pm, avg 22µs/call # 58 times (5.26ms+7.80ms) by Mail::SpamAssassin::Conf::Parser::fix_path_relative_to_current_file at line 1499 of Mail/SpamAssassin/Conf/Parser.pm, avg 225µs/call # 7 times (826µs+1.16ms) by Mail::SpamAssassin::first_existing_path at line 2056, avg 284µs/call # once (109µs+450µs) by Mail::SpamAssassin::DBBasedAddrList::new_checker at line 68 of Mail/SpamAssassin/DBBasedAddrList.pm # once (34µs+0s) by Mail::SpamAssassin::BayesStore::DBM::tie_db_writable at line 256 of Mail/SpamAssassin/BayesStore/DBM.pm
sub sed_path {
20235362.39ms my ($self, $path) = @_;
20245361.27ms return if !defined $path;
2025
20265362.81ms if (exists($self->{conf}->{sed_path_cache}->{$path})) {
20274695.31ms return $self->{conf}->{sed_path_cache}->{$path};
2028 }
2029
203067153µs my $orig_path = $path;
2031
203268829µs69261µs $path =~ s/__local_rules_dir__/$self->{LOCAL_RULES_DIR} || ''/ges;
# spent 252µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 4µs/call # spent 9µs making 2 calls to Mail::SpamAssassin::CORE:substcont, avg 4µs/call
203369673µs71185µs $path =~ s/__local_state_dir__/$self->{LOCAL_STATE_DIR} || ''/ges;
# spent 174µ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
203468619µs69192µs $path =~ s/__def_rules_dir__/$self->{DEF_RULES_DIR} || ''/ges;
# spent 186µ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
203567545µs67168µs $path =~ s{__prefix__}{$self->{PREFIX} || $Config{prefix} || '/usr'}ges;
# spent 168µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call
203668655µs70543µs $path =~ s{__userstate__}{$self->get_and_create_userstate_dir() || ''}ges;
# spent 295µs making 1 call to Mail::SpamAssassin::get_and_create_userstate_dir # spent 235µ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
203767584µs67178µs $path =~ s{__perl_major_ver__}{$self->get_perl_major_version()}ges;
# spent 178µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call
203867598µs67178µs $path =~ s/__version__/${VERSION}/gs;
# spent 178µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call
203969645µs73320µs $path =~ s/^\~([^\/]*)/$self->expand_name($1)/es;
# spent 166µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 2µs/call # spent 129µs making 2 calls to Mail::SpamAssassin::expand_name, avg 64µs/call # spent 25µs making 4 calls to Mail::SpamAssassin::CORE:substcont, avg 6µs/call
2040
204167611µs677.69ms $path = Mail::SpamAssassin::Util::untaint_file_path ($path);
# spent 7.69ms making 67 calls to Mail::SpamAssassin::Util::untaint_file_path, avg 115µs/call
204267651µs $self->{conf}->{sed_path_cache}->{$orig_path} = $path;
204367625µ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.51ms (306µs+2.20) within Mail::SpamAssassin::first_existing_path which was called 6 times, avg 418µs/call: # 2 times (81µs+431µs) by Mail::SpamAssassin::get_and_create_userstate_dir at line 1882, avg 256µs/call # once (83µs+609µs) by Mail::SpamAssassin::find_rule_support_file at line 1926 # once (53µs+465µs) by Mail::SpamAssassin::init at line 1675 # once (40µs+406µs) by Mail::SpamAssassin::init at line 1715 # once (48µs+290µs) by Mail::SpamAssassin::init at line 1678
sub first_existing_path {
2053615µs my $self = shift;
2054610µs my $path;
2055622µs foreach my $p (@_) {
2056766µs71.99ms $path = $self->sed_path ($p);
# spent 2.02ms making 7 calls to Mail::SpamAssassin::sed_path, avg 288µs/call, recursion: max depth 1, sum of overlapping time 28µs
2057717µs if (defined $path) {
20587248µs7185µs my($errn) = stat($path) ? 0 : 0+$!;
# spent 185µ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" }
2061682µs else { return $path }
2062 }
2063 }
2064 return;
2065}
2066
2067###########################################################################
2068
2069
# spent 1.06ms (54µs+1.01) within Mail::SpamAssassin::get_cf_files_in_dir which was called 2 times, avg 532µs/call: # 2 times (54µs+1.01ms) by Mail::SpamAssassin::_read_cf_pre at line 1827, avg 532µs/call
sub get_cf_files_in_dir {
207025µs my ($self, $dir) = @_;
2071240µs21.01ms return $self->_get_cf_pre_files_in_dir($dir, 'cf');
# spent 1.01ms making 2 calls to Mail::SpamAssassin::_get_cf_pre_files_in_dir, avg 505µs/call
2072}
2073
2074
# spent 1.39ms (43µs+1.34) within Mail::SpamAssassin::get_pre_files_in_dir which was called 2 times, avg 694µs/call: # 2 times (43µs+1.34ms) by Mail::SpamAssassin::_read_cf_pre at line 1827, avg 694µs/call
sub get_pre_files_in_dir {
207525µs my ($self, $dir) = @_;
2076233µs21.34ms return $self->_get_cf_pre_files_in_dir($dir, 'pre');
# spent 1.34ms making 2 calls to Mail::SpamAssassin::_get_cf_pre_files_in_dir, avg 672µs/call
2077}
2078
2079
# spent 2.36ms (1.32+1.04) within Mail::SpamAssassin::_get_cf_pre_files_in_dir which was called 4 times, avg 589µs/call: # 2 times (681µs+664µs) by Mail::SpamAssassin::get_pre_files_in_dir at line 2076, avg 672µs/call # 2 times (634µs+376µs) by Mail::SpamAssassin::get_cf_files_in_dir at line 2071, avg 505µs/call
sub _get_cf_pre_files_in_dir {
2080412µs my ($self, $dir, $type) = @_;
2081
2082417µ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 {
21014161µs4127µs opendir(SA_CF_DIR, $dir) or warn "config: cannot opendir $dir: $!\n";
# spent 127µs making 4 calls to Mail::SpamAssassin::CORE:open_dir, avg 32µs/call
2102521.83ms93830µs my @cfs = grep { $_ ne '.' && $_ ne '..' &&
# spent 284µs making 40 calls to Mail::SpamAssassin::CORE:match, avg 7µs/call # spent 226µs making 4 calls to Mail::SpamAssassin::CORE:readdir, avg 56µs/call # spent 184µs making 9 calls to Mail::SpamAssassin::CORE:ftfile, avg 20µs/call # spent 136µs making 40 calls to Mail::SpamAssassin::CORE:regcomp, avg 3µs/call
2103 /\.${type}$/i && -f "$dir/$_" } readdir(SA_CF_DIR);
2104489µs428µs closedir SA_CF_DIR;
# spent 28µs making 4 calls to Mail::SpamAssassin::CORE:closedir, avg 7µs/call
2105
210626274µs455µs return map { "$dir/$_" } sort { $a cmp $b } @cfs;
# spent 55µs making 4 calls to Mail::SpamAssassin::CORE:sort, avg 14µ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 716s (77.0ms+716) within Mail::SpamAssassin::call_plugins which was called 1910 times, avg 375ms/call: # 501 times (11.2ms+156ms) by Mail::SpamAssassin::Conf::Parser::parse at line 467 of Mail/SpamAssassin/Conf/Parser.pm, avg 334µs/call # 234 times (6.89ms+710s) by Mail::SpamAssassin::Bayes::learn at line 120 of Mail/SpamAssassin/Bayes.pm, avg 3.03s/call # 234 times (9.70ms+8.12ms) by Mail::SpamAssassin::parse at line 556, avg 76µs/call # 234 times (10.2ms+-10.2ms) by Mail::SpamAssassin::Plugin::Bayes::_learn_trapped at line 488 of Mail/SpamAssassin/Plugin/Bayes.pm, avg 0s/call # 234 times (20.2ms+-20.2ms) by Mail::SpamAssassin::PerMsgStatus::extract_message_metadata at line 1750 of Mail/SpamAssassin/PerMsgStatus.pm, avg 0s/call # 234 times (9.34ms+-9.34ms) by Mail::SpamAssassin::PerMsgStatus::finish at line 1659 of Mail/SpamAssassin/PerMsgStatus.pm, avg 0s/call # 234 times (9.19ms+-9.19ms) by Mail::SpamAssassin::Message::Metadata::extract at line 106 of Mail/SpamAssassin/Message/Metadata.pm, avg 0s/call # once (42µs+5.67s) by Mail::SpamAssassin::Bayes::force_close at line 83 of Mail/SpamAssassin/Bayes.pm # once (42µs+134ms) by Mail::SpamAssassin::Conf::Parser::finish_parsing at line 942 of Mail/SpamAssassin/Conf/Parser.pm # once (42µs+30.4ms) by Mail::SpamAssassin::Bayes::new at line 61 of Mail/SpamAssassin/Bayes.pm # once (32µs+1.17ms) by Mail::SpamAssassin::Bayes::is_scan_available at line 153 of Mail/SpamAssassin/Bayes.pm # once (44µs+708µs) by Mail::SpamAssassin::Conf::Parser::finish_parsing at line 853 of Mail/SpamAssassin/Conf/Parser.pm
sub call_plugins {
212219103.96ms my $self = shift;
2123
2124 # We could potentially get called after a finish(), so just return.
212519106.23ms return unless $self->{plugins};
2126
2127 # safety net in case some plugin changes global settings, Bug 6218
2128191012.8ms local $/ = $/; # prevent underlying modules from changing the global $/
2129
213019104.18ms my $subname = shift;
2131191036.1ms1910716s return $self->{plugins}->callback($subname, @_);
# spent 730s making 1910 calls to Mail::SpamAssassin::PluginHandler::callback, avg 382ms/call, recursion: max depth 1, sum of overlapping time 14.6s
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 75.3ms (56.6+18.7) within Mail::SpamAssassin::find_all_addrs_in_line which was called 759 times, avg 99µs/call: # 758 times (56.4ms+18.6ms) by Mail::SpamAssassin::Plugin::Bayes::_pre_chew_addr_header at line 1468 of Mail/SpamAssassin/Plugin/Bayes.pm, avg 99µs/call # once (198µs+114µs) by Mail::SpamAssassin::PerMsgStatus::all_to_addrs at line 3124 of Mail/SpamAssassin/PerMsgStatus.pm
sub find_all_addrs_in_line {
21687594.25ms my ($self, $line) = @_;
2169
2170 # a more permissive pattern based on "dot-atom" as per RFC2822
21717591.65ms my $ID_PATTERN = '[-a-z0-9_\+\:\=\!\#\$\%\&\*\^\?\{\}\|\~\/\.]+';
21727591.72ms my $HOST_PATTERN = '[-a-z0-9_\+\:\/]+';
2173
21747591.31ms my @addrs;
2175 my %seen;
217675925.0ms154213.2ms while ($line =~ s/(?:mailto:)?\s*
# spent 11.5ms making 771 calls to Mail::SpamAssassin::CORE:subst, avg 15µs/call # spent 1.70ms making 771 calls to Mail::SpamAssassin::CORE:regcomp, avg 2µs/call
2177 ($ID_PATTERN \@
2178 $HOST_PATTERN(?:\.$HOST_PATTERN)+)//oix)
2179 {
21807553.63ms my $addr = $1;
21817556.81ms7552.12ms $addr =~ s/^mailto://;
# spent 2.12ms making 755 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call
218214986.33ms next if (defined ($seen{$addr})); $seen{$addr} = 1;
218374315.9ms14863.32ms push (@addrs, $addr);
# spent 2.12ms making 743 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call # spent 1.21ms making 743 calls to Mail::SpamAssassin::CORE:regcomp, avg 2µs/call
2184 }
2185
218675918.0ms 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 864µs within Mail::SpamAssassin::CORE:close which was called 68 times, avg 13µs/call: # 68 times (864µs+0s) by Mail::SpamAssassin::read_cf_file at line 1849, avg 13µs/call
sub Mail::SpamAssassin::CORE:close; # opcode
# spent 28µs within Mail::SpamAssassin::CORE:closedir which was called 4 times, avg 7µs/call: # 4 times (28µs+0s) by Mail::SpamAssassin::_get_cf_pre_files_in_dir at line 2104, avg 7µs/call
sub Mail::SpamAssassin::CORE:closedir; # opcode
# spent 193µs within Mail::SpamAssassin::CORE:ftdir which was called 65 times, avg 3µs/call: # 63 times (187µs+0s) by Mail::SpamAssassin::_read_cf_pre at line 1822, avg 3µs/call # 2 times (5µs+0s) by Mail::SpamAssassin::get_and_create_userstate_dir at line 1896, avg 3µs/call
sub Mail::SpamAssassin::CORE:ftdir; # opcode
# spent 234µs within Mail::SpamAssassin::CORE:fteread which was called 59 times, avg 4µs/call: # 59 times (234µs+0s) by Mail::SpamAssassin::_read_cf_pre at line 1822, avg 4µs/call
sub Mail::SpamAssassin::CORE:fteread; # opcode
# spent 290µs within Mail::SpamAssassin::CORE:ftfile which was called 68 times, avg 4µs/call: # 59 times (105µs+0s) by Mail::SpamAssassin::_read_cf_pre at line 1822, avg 2µs/call # 9 times (184µs+0s) by Mail::SpamAssassin::_get_cf_pre_files_in_dir at line 2102, avg 20µs/call
sub Mail::SpamAssassin::CORE:ftfile; # opcode
# spent 188µs within Mail::SpamAssassin::CORE:ftsize which was called 59 times, avg 3µs/call: # 59 times (188µs+0s) by Mail::SpamAssassin::_read_cf_pre at line 1822, avg 3µs/call
sub Mail::SpamAssassin::CORE:ftsize; # opcode
# spent 345µs within Mail::SpamAssassin::CORE:match which was called 54 times, avg 6µs/call: # 40 times (284µ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 (8µs+0s) by Mail::SpamAssassin::expand_name at line 2016, avg 4µs/call # once (13µs+0s) by Mail::SpamAssassin::Version at line 127 # once (9µs+0s) by main::BEGIN@65 at line 109 # once (7µs+0s) by Mail::SpamAssassin::init at line 1745
sub Mail::SpamAssassin::CORE:match; # opcode
# spent 3.07ms within Mail::SpamAssassin::CORE:open which was called 68 times, avg 45µs/call: # 68 times (3.07ms+0s) by Mail::SpamAssassin::read_cf_file at line 1844, avg 45µs/call
sub Mail::SpamAssassin::CORE:open; # opcode
# spent 127µs within Mail::SpamAssassin::CORE:open_dir which was called 4 times, avg 32µs/call: # 4 times (127µs+0s) by Mail::SpamAssassin::_get_cf_pre_files_in_dir at line 2101, avg 32µs/call
sub Mail::SpamAssassin::CORE:open_dir; # opcode
# spent 4.14ms within Mail::SpamAssassin::CORE:read which was called 164 times, avg 25µs/call: # 164 times (4.14ms+0s) by Mail::SpamAssassin::read_cf_file at line 1847, avg 25µs/call
sub Mail::SpamAssassin::CORE:read; # opcode
# spent 226µs within Mail::SpamAssassin::CORE:readdir which was called 4 times, avg 56µs/call: # 4 times (226µs+0s) by Mail::SpamAssassin::_get_cf_pre_files_in_dir at line 2102, avg 56µs/call
sub Mail::SpamAssassin::CORE:readdir; # opcode
# spent 3.05ms within Mail::SpamAssassin::CORE:regcomp which was called 1554 times, avg 2µs/call: # 771 times (1.70ms+0s) by Mail::SpamAssassin::find_all_addrs_in_line at line 2176, avg 2µs/call # 743 times (1.21ms+0s) by Mail::SpamAssassin::find_all_addrs_in_line at line 2183, avg 2µs/call # 40 times (136µ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 55µs within Mail::SpamAssassin::CORE:sort which was called 4 times, avg 14µs/call: # 4 times (55µs+0s) by Mail::SpamAssassin::_get_cf_pre_files_in_dir at line 2106, avg 14µs/call
sub Mail::SpamAssassin::CORE:sort; # opcode
# spent 2.61ms within Mail::SpamAssassin::CORE:stat which was called 72 times, avg 36µs/call: # 63 times (2.41ms+0s) by Mail::SpamAssassin::_read_cf_pre at line 1821, avg 38µs/call # 7 times (185µ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 17.3ms within Mail::SpamAssassin::CORE:subst which was called 2810 times, avg 6µs/call: # 771 times (11.5ms+0s) by Mail::SpamAssassin::find_all_addrs_in_line at line 2176, avg 15µs/call # 755 times (2.12ms+0s) by Mail::SpamAssassin::find_all_addrs_in_line at line 2181, avg 3µs/call # 743 times (2.12ms+0s) by Mail::SpamAssassin::find_all_addrs_in_line at line 2183, avg 3µs/call # 67 times (252µs+0s) by Mail::SpamAssassin::sed_path at line 2032, avg 4µs/call # 67 times (235µs+0s) by Mail::SpamAssassin::sed_path at line 2036, avg 4µs/call # 67 times (186µs+0s) by Mail::SpamAssassin::sed_path at line 2034, avg 3µs/call # 67 times (178µs+0s) by Mail::SpamAssassin::sed_path at line 2038, avg 3µs/call # 67 times (178µs+0s) by Mail::SpamAssassin::sed_path at line 2037, avg 3µs/call # 67 times (174µs+0s) by Mail::SpamAssassin::sed_path at line 2033, avg 3µs/call # 67 times (168µs+0s) by Mail::SpamAssassin::sed_path at line 2035, avg 3µs/call # 67 times (166µs+0s) by Mail::SpamAssassin::sed_path at line 2039, avg 2µ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 87µs within Mail::SpamAssassin::CORE:substcont which was called 24 times, avg 4µs/call: # 10 times (25µs+0s) by Mail::SpamAssassin::find_rule_support_file at line 1926, avg 2µs/call # 4 times (25µs+0s) by Mail::SpamAssassin::sed_path at line 2039, avg 6µ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 2036, avg 6µs/call # 2 times (9µs+0s) by Mail::SpamAssassin::sed_path at line 2032, avg 4µ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