Filename | /usr/local/lib/perl5/site_perl/Mail/SpamAssassin.pm |
Statements | Executed 59855 statements in 500ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
8305 | 9 | 4 | 91.2ms | 91.2ms | time_method | Mail::SpamAssassin::
759 | 2 | 2 | 78.1ms | 99.3ms | find_all_addrs_in_line | Mail::SpamAssassin::
3751 | 6 | 4 | 77.7ms | 96.8ms | sed_path (recurses: max depth 1, inclusive time 30µs) | Mail::SpamAssassin::
1910 | 12 | 6 | 75.1ms | 96432s | call_plugins (recurses: max depth 1, inclusive time 15.2s) | Mail::SpamAssassin::
1 | 1 | 1 | 29.2ms | 188ms | BEGIN@71 | Mail::SpamAssassin::
234 | 1 | 1 | 29.0ms | 4.85s | parse | Mail::SpamAssassin::
1 | 1 | 1 | 25.8ms | 37.9ms | BEGIN@74 | Mail::SpamAssassin::
234 | 1 | 1 | 24.5ms | 96432s | learn | Mail::SpamAssassin::
2810 | 12 | 1 | 19.3ms | 19.3ms | CORE:subst (opcode) | Mail::SpamAssassin::
1 | 1 | 1 | 10.1ms | 248ms | BEGIN@75 | Mail::SpamAssassin::
1 | 1 | 1 | 9.39ms | 14.2ms | BEGIN@77 | Mail::SpamAssassin::
470 | 4 | 2 | 9.23ms | 12.8s | init | Mail::SpamAssassin::
68 | 2 | 1 | 9.23ms | 18.6ms | read_cf_file | Mail::SpamAssassin::
63 | 2 | 1 | 6.49ms | 32.8ms | _read_cf_pre | Mail::SpamAssassin::
1 | 1 | 1 | 6.16ms | 14.7ms | BEGIN@84 | Mail::SpamAssassin::
72 | 3 | 1 | 4.92ms | 4.92ms | CORE:stat (opcode) | Mail::SpamAssassin::
164 | 1 | 1 | 4.22ms | 4.22ms | CORE:read (opcode) | Mail::SpamAssassin::
1 | 1 | 1 | 3.95ms | 7.47ms | BEGIN@70 | Mail::SpamAssassin::
68 | 1 | 1 | 3.72ms | 3.72ms | CORE:open (opcode) | Mail::SpamAssassin::
1554 | 3 | 1 | 3.64ms | 3.64ms | CORE:regcomp (opcode) | Mail::SpamAssassin::
1 | 1 | 1 | 3.58ms | 25.8ms | BEGIN@69 | Mail::SpamAssassin::
1 | 1 | 1 | 3.19ms | 3.73ms | BEGIN@65 | Mail::SpamAssassin::
1 | 1 | 1 | 2.42ms | 5.80ms | BEGIN@76 | Mail::SpamAssassin::
1 | 1 | 1 | 2.23ms | 2.54ms | BEGIN@78 | Mail::SpamAssassin::
1 | 1 | 1 | 2.00ms | 2.58ms | BEGIN@72 | Mail::SpamAssassin::
1 | 1 | 1 | 1.90ms | 2.34ms | BEGIN@73 | Mail::SpamAssassin::
61 | 4 | 2 | 1.37ms | 31.6ms | read_cf | Mail::SpamAssassin::
68 | 1 | 1 | 858µs | 858µs | CORE:close (opcode) | Mail::SpamAssassin::
4 | 2 | 1 | 855µs | 1.81ms | _get_cf_pre_files_in_dir | Mail::SpamAssassin::
1 | 1 | 1 | 588µs | 774µs | BEGIN@80 | Mail::SpamAssassin::
68 | 2 | 1 | 425µs | 425µs | CORE:ftfile (opcode) | Mail::SpamAssassin::
1 | 1 | 1 | 258µs | 47.0ms | new | Mail::SpamAssassin::
6 | 5 | 1 | 245µs | 1.95ms | first_existing_path | Mail::SpamAssassin::
1 | 1 | 1 | 222µs | 8.74ms | create_locker | Mail::SpamAssassin::
4 | 1 | 1 | 220µs | 220µs | CORE:readdir (opcode) | Mail::SpamAssassin::
59 | 1 | 1 | 208µs | 208µs | CORE:fteread (opcode) | Mail::SpamAssassin::
1 | 1 | 1 | 205µs | 228µs | init_learner | Mail::SpamAssassin::
65 | 2 | 1 | 194µs | 194µs | CORE:ftdir (opcode) | Mail::SpamAssassin::
54 | 6 | 1 | 187µs | 187µs | CORE:match (opcode) | Mail::SpamAssassin::
59 | 1 | 1 | 174µs | 174µs | CORE:ftsize (opcode) | Mail::SpamAssassin::
2 | 2 | 1 | 144µs | 811µs | get_and_create_userstate_dir | Mail::SpamAssassin::
1 | 1 | 1 | 123µs | 709µs | find_rule_support_file | Mail::SpamAssassin::
4 | 1 | 1 | 94µs | 94µs | CORE:open_dir (opcode) | Mail::SpamAssassin::
24 | 6 | 1 | 75µs | 75µs | CORE:substcont (opcode) | Mail::SpamAssassin::
2 | 1 | 1 | 65µs | 107µs | expand_name | Mail::SpamAssassin::
4 | 1 | 1 | 50µs | 50µs | CORE:sort (opcode) | Mail::SpamAssassin::
1 | 1 | 1 | 38µs | 46µs | BEGIN@62 | Mail::SpamAssassin::
1 | 1 | 1 | 37µs | 74µs | BEGIN@63 | Mail::SpamAssassin::
1 | 1 | 1 | 36µs | 45µs | Version | Mail::SpamAssassin::
1 | 1 | 1 | 36µs | 60µs | BEGIN@85 | Mail::SpamAssassin::
2 | 1 | 1 | 36µs | 1.16ms | get_pre_files_in_dir | Mail::SpamAssassin::
2 | 2 | 1 | 36µs | 2.55ms | read_pre | Mail::SpamAssassin::
2 | 1 | 1 | 33µs | 718µs | get_cf_files_in_dir | Mail::SpamAssassin::
4 | 1 | 1 | 30µs | 30µs | CORE:closedir (opcode) | Mail::SpamAssassin::
1 | 1 | 1 | 29µs | 188µs | BEGIN@83 | Mail::SpamAssassin::
1 | 1 | 1 | 28µs | 3.09ms | finish_learner | Mail::SpamAssassin::
1 | 1 | 1 | 26µs | 127µs | BEGIN@82 | Mail::SpamAssassin::
1 | 1 | 1 | 26µs | 65µs | BEGIN@88 | Mail::SpamAssassin::
1 | 1 | 1 | 24µs | 147µs | BEGIN@79 | Mail::SpamAssassin::
1 | 1 | 1 | 24µs | 356µs | BEGIN@86 | Mail::SpamAssassin::
1 | 1 | 1 | 22µs | 29µs | BEGIN@64 | Mail::SpamAssassin::
1 | 1 | 1 | 20µs | 150µs | BEGIN@87 | Mail::SpamAssassin::
1 | 1 | 1 | 19µs | 415µs | BEGIN@90 | Mail::SpamAssassin::
1 | 1 | 1 | 11µs | 11µs | set_persistent_address_list_factory | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | add_address_to_blacklist | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | add_address_to_whitelist | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | add_all_addresses_to_blacklist | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | add_all_addresses_to_whitelist | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | check | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | check_message_text | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | compile_now | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | copy_config | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | create_default_prefs | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | debug_diagnostics | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | dump_bayes_db | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | find_all_addrs_in_mail | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | finish | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | get_loaded_plugins_list | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | get_perl_major_version | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | have_plugin | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | lint_rules | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | load_scoreonly_ldap | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | load_scoreonly_sql | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | read_scoreonly_config | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | rebuild_learner_caches | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | remove_address_from_whitelist | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | remove_all_addresses_from_whitelist | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | remove_spamassassin_markup | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | report_as_spam | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | revoke_as_spam | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | sa_die | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | signal_user_changed | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | timer_disable | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | timer_enable | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | timer_end | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | timer_report | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | timer_reset | Mail::SpamAssassin::
0 | 0 | 0 | 0s | 0s | timer_start | Mail::SpamAssassin::
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 | |||||
20 | Mail::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 | |||||
42 | Mail::SpamAssassin is a module to identify spam using several methods | ||||
43 | including text analysis, internet-based realtime blacklists, statistical | ||||
44 | analysis, and internet-based hashing algorithms. | ||||
45 | |||||
46 | Using its rule base, it uses a wide range of heuristic tests on mail | ||||
47 | headers and body text to identify "spam", also known as unsolicited bulk | ||||
48 | email. Once identified as spam, the mail can then be tagged as spam for | ||||
49 | later filtering using the user's own mail user agent application or at | ||||
50 | the mail transfer agent. | ||||
51 | |||||
52 | If you wish to use a command-line filter tool, try the C<spamassassin> | ||||
53 | or the C<spamd>/C<spamc> tools provided. | ||||
54 | |||||
55 | =head1 METHODS | ||||
56 | |||||
57 | =over 4 | ||||
58 | |||||
59 | =cut | ||||
60 | |||||
61 | package Mail::SpamAssassin; | ||||
62 | 2 | 65µs | 2 | 55µs | # spent 46µs (38+8) within Mail::SpamAssassin::BEGIN@62 which was called:
# once (38µs+8µs) by main::BEGIN@65 at line 62 # spent 46µs making 1 call to Mail::SpamAssassin::BEGIN@62
# spent 8µs making 1 call to strict::import |
63 | 2 | 63µs | 2 | 112µs | # spent 74µs (37+38) within Mail::SpamAssassin::BEGIN@63 which was called:
# once (37µs+38µs) by main::BEGIN@65 at line 63 # spent 74µs making 1 call to Mail::SpamAssassin::BEGIN@63
# spent 38µs making 1 call to warnings::import |
64 | 2 | 65µs | 2 | 36µ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 # spent 29µs making 1 call to Mail::SpamAssassin::BEGIN@64
# spent 7µs making 1 call to bytes::import |
65 | 2 | 314µs | 2 | 3.80ms | # spent 3.73ms (3.19+535µs) within Mail::SpamAssassin::BEGIN@65 which was called:
# once (3.19ms+535µs) by main::BEGIN@65 at line 65 # spent 3.73ms making 1 call to Mail::SpamAssassin::BEGIN@65
# spent 73µs making 1 call to re::import |
66 | |||||
67 | 1 | 27µs | require 5.006_001; | ||
68 | |||||
69 | 2 | 338µs | 2 | 25.9ms | # spent 25.8ms (3.58+22.2) within Mail::SpamAssassin::BEGIN@69 which was called:
# once (3.58ms+22.2ms) by main::BEGIN@65 at line 69 # spent 25.8ms making 1 call to Mail::SpamAssassin::BEGIN@69
# spent 154µs making 1 call to Exporter::import |
70 | 2 | 332µs | 2 | 7.54ms | # spent 7.47ms (3.95+3.51) within Mail::SpamAssassin::BEGIN@70 which was called:
# once (3.95ms+3.51ms) by main::BEGIN@65 at line 70 # spent 7.47ms making 1 call to Mail::SpamAssassin::BEGIN@70
# spent 72µs making 1 call to Exporter::import |
71 | 2 | 414µs | 1 | 188ms | # spent 188ms (29.2+158) within Mail::SpamAssassin::BEGIN@71 which was called:
# once (29.2ms+158ms) by main::BEGIN@65 at line 71 # spent 188ms making 1 call to Mail::SpamAssassin::BEGIN@71 |
72 | 2 | 443µs | 1 | 2.58ms | # spent 2.58ms (2.00+574µs) within Mail::SpamAssassin::BEGIN@72 which was called:
# once (2.00ms+574µs) by main::BEGIN@65 at line 72 # spent 2.58ms making 1 call to Mail::SpamAssassin::BEGIN@72 |
73 | 2 | 356µs | 1 | 2.34ms | # spent 2.34ms (1.90+441µs) within Mail::SpamAssassin::BEGIN@73 which was called:
# once (1.90ms+441µs) by main::BEGIN@65 at line 73 # spent 2.34ms making 1 call to Mail::SpamAssassin::BEGIN@73 |
74 | 2 | 412µs | 1 | 37.9ms | # spent 37.9ms (25.8+12.1) within Mail::SpamAssassin::BEGIN@74 which was called:
# once (25.8ms+12.1ms) by main::BEGIN@65 at line 74 # spent 37.9ms making 1 call to Mail::SpamAssassin::BEGIN@74 |
75 | 2 | 452µs | 1 | 248ms | # spent 248ms (10.1+238) within Mail::SpamAssassin::BEGIN@75 which was called:
# once (10.1ms+238ms) by main::BEGIN@65 at line 75 # spent 248ms making 1 call to Mail::SpamAssassin::BEGIN@75 |
76 | 2 | 402µs | 1 | 5.80ms | # spent 5.80ms (2.42+3.38) within Mail::SpamAssassin::BEGIN@76 which was called:
# once (2.42ms+3.38ms) by main::BEGIN@65 at line 76 # spent 5.80ms making 1 call to Mail::SpamAssassin::BEGIN@76 |
77 | 2 | 344µs | 1 | 14.2ms | # spent 14.2ms (9.39+4.81) within Mail::SpamAssassin::BEGIN@77 which was called:
# once (9.39ms+4.81ms) by main::BEGIN@65 at line 77 # spent 14.2ms making 1 call to Mail::SpamAssassin::BEGIN@77 |
78 | 2 | 344µs | 1 | 2.54ms | # spent 2.54ms (2.23+305µs) within Mail::SpamAssassin::BEGIN@78 which was called:
# once (2.23ms+305µs) by main::BEGIN@65 at line 78 # spent 2.54ms making 1 call to Mail::SpamAssassin::BEGIN@78 |
79 | 2 | 64µs | 2 | 270µs | # spent 147µs (24+123) within Mail::SpamAssassin::BEGIN@79 which was called:
# once (24µs+123µs) by main::BEGIN@65 at line 79 # spent 147µs making 1 call to Mail::SpamAssassin::BEGIN@79
# spent 123µs making 1 call to Exporter::import |
80 | 2 | 295µs | 1 | 774µs | # spent 774µs (588+187) within Mail::SpamAssassin::BEGIN@80 which was called:
# once (588µs+187µs) by main::BEGIN@65 at line 80 # spent 774µs making 1 call to Mail::SpamAssassin::BEGIN@80 |
81 | |||||
82 | 2 | 59µs | 2 | 227µs | # spent 127µs (26+100) within Mail::SpamAssassin::BEGIN@82 which was called:
# once (26µs+100µs) by main::BEGIN@65 at line 82 # spent 127µs making 1 call to Mail::SpamAssassin::BEGIN@82
# spent 100µs making 1 call to Exporter::import |
83 | 2 | 64µs | 2 | 346µs | # spent 188µs (29+158) within Mail::SpamAssassin::BEGIN@83 which was called:
# once (29µs+158µs) by main::BEGIN@65 at line 83 # spent 188µs making 1 call to Mail::SpamAssassin::BEGIN@83
# spent 158µs making 1 call to Exporter::import |
84 | 2 | 348µs | 2 | 14.8ms | # spent 14.7ms (6.16+8.53) within Mail::SpamAssassin::BEGIN@84 which was called:
# once (6.16ms+8.53ms) by main::BEGIN@65 at line 84 # spent 14.7ms making 1 call to Mail::SpamAssassin::BEGIN@84
# spent 117µs making 1 call to Exporter::import |
85 | 3 | 103µs | 2 | 83µs | # spent 60µs (36+23) within Mail::SpamAssassin::BEGIN@85 which was called:
# once (36µs+23µs) by main::BEGIN@65 at line 85 # spent 60µs making 1 call to Mail::SpamAssassin::BEGIN@85
# spent 23µs making 1 call to version::_VERSION |
86 | 2 | 61µs | 2 | 688µs | # spent 356µs (24+332) within Mail::SpamAssassin::BEGIN@86 which was called:
# once (24µs+332µs) by main::BEGIN@65 at line 86 # spent 356µs making 1 call to Mail::SpamAssassin::BEGIN@86
# spent 332µs making 1 call to Time::HiRes::import |
87 | 2 | 55µs | 2 | 280µs | # spent 150µs (20+130) within Mail::SpamAssassin::BEGIN@87 which was called:
# once (20µs+130µs) by main::BEGIN@65 at line 87 # spent 150µs making 1 call to Mail::SpamAssassin::BEGIN@87
# spent 130µs making 1 call to Exporter::import |
88 | 2 | 82µs | 2 | 104µs | # spent 65µs (26+39) within Mail::SpamAssassin::BEGIN@88 which was called:
# once (26µs+39µs) by main::BEGIN@65 at line 88 # spent 65µs making 1 call to Mail::SpamAssassin::BEGIN@88
# spent 39µs making 1 call to Config::import |
89 | |||||
90 | 1 | 2µs | # spent 415µs (19+397) within Mail::SpamAssassin::BEGIN@90 which was called:
# once (19µs+397µs) by main::BEGIN@65 at line 95 | ||
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 | ||||
95 | 1 | 15.7ms | 2 | 812µs | }; # spent 415µs making 1 call to Mail::SpamAssassin::BEGIN@90
# spent 397µs making 1 call to vars::import |
96 | |||||
97 | 1 | 2µ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 | |||||
105 | 1 | 20µs | @ISA = qw(); | ||
106 | |||||
107 | # SUB_VERSION is now just <yyyy>-<mm>-<dd> | ||||
108 | 1 | 2µs | $SUB_VERSION = 'svnunknown'; | ||
109 | 1 | 37µs | 1 | 12µs | if ('$LastChangedDate: 2015-04-28 03:54:45 -0400 (Tue, 28 Apr 2015) $' =~ ':') { # spent 12µ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 | ||||
113 | 1 | 22µs | $SUB_VERSION = (split(/\s+/,'$LastChangedDate: 2015-04-28 03:54:45 -0400 (Tue, 28 Apr 2015) $ updated by SVN'))[1]; | ||
114 | } | ||||
115 | |||||
116 | |||||
117 | 1 | 2µs | if (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 45µs (36+8) within Mail::SpamAssassin::Version which was called:
# once (36µs+8µs) by Mail::SpamAssassin::new at line 407 | ||||
127 | 1 | 19µs | 1 | 8µs | $VERSION =~ /^(\d+)\.(\d\d\d)(\d\d\d)$/; # spent 8µs making 1 call to Mail::SpamAssassin::CORE:match |
128 | 1 | 28µs | return join('-', sprintf("%d.%d.%d", $1, $2, $3), @EXTRA_VERSION); | ||
129 | } | ||||
130 | |||||
131 | 1 | 2µ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. | ||||
137 | 1 | 4µ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 | ||||
146 | 1 | 4µ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 | |||||
157 | 1 | 4µ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 | |||||
167 | 1 | 3µs | @default_userprefs_path = ( | ||
168 | '~/.spamassassin/user_prefs', | ||||
169 | ); | ||||
170 | |||||
171 | 1 | 2µs | @default_userstate_dir = ( | ||
172 | '~/.spamassassin', | ||||
173 | ); | ||||
174 | |||||
175 | ########################################################################### | ||||
176 | |||||
177 | =item $t = Mail::SpamAssassin->new( { opt => val, ... } ) | ||||
178 | |||||
179 | Constructs a new C<Mail::SpamAssassin> object. You may pass a hash | ||||
180 | reference to the constructor which may contain the following attribute- | ||||
181 | value pairs. | ||||
182 | |||||
183 | =over 4 | ||||
184 | |||||
185 | =item debug | ||||
186 | |||||
187 | This is the debug options used to determine logging level. It exists to | ||||
188 | allow sections of debug messages (called "facilities") to be enabled or | ||||
189 | disabled. If this is a string, it is treated as a comma-delimited list | ||||
190 | of the debug facilities. If it's a hash reference, then the keys are | ||||
191 | treated as the list of debug facilities and if it's a array reference, | ||||
192 | then the elements are treated as the list of debug facilities. | ||||
193 | |||||
194 | There are also two special cases: (1) if the special case of "info" is | ||||
195 | passed 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 | ||||
197 | debugging facilities are enabled. | ||||
198 | |||||
199 | =item rules_filename | ||||
200 | |||||
201 | The filename/directory to load spam-identifying rules from. (optional) | ||||
202 | |||||
203 | =item site_rules_filename | ||||
204 | |||||
205 | The filename/directory to load site-specific spam-identifying rules from. | ||||
206 | (optional) | ||||
207 | |||||
208 | =item userprefs_filename | ||||
209 | |||||
210 | The filename to load preferences from. (optional) | ||||
211 | |||||
212 | =item userstate_dir | ||||
213 | |||||
214 | The directory user state is stored in. (optional) | ||||
215 | |||||
216 | =item config_tree_recurse | ||||
217 | |||||
218 | Set to C<1> to recurse through directories when reading configuration | ||||
219 | files, instead of just reading a single level. (optional, default 0) | ||||
220 | |||||
221 | =item config_text | ||||
222 | |||||
223 | The text of all rules and preferences. If you prefer not to load the rules | ||||
224 | from files, read them in yourself and set this instead. As a result, this will | ||||
225 | override the settings for C<rules_filename>, C<site_rules_filename>, | ||||
226 | and C<userprefs_filename>. | ||||
227 | |||||
228 | =item pre_config_text | ||||
229 | |||||
230 | Similar to C<config_text>, this text is placed before config_text to allow an | ||||
231 | override of config files. | ||||
232 | |||||
233 | =item post_config_text | ||||
234 | |||||
235 | Similar to C<config_text>, this text is placed after config_text to allow an | ||||
236 | override of config files. | ||||
237 | |||||
238 | =item force_ipv4 | ||||
239 | |||||
240 | If set to 1, DNS or other network tests will prefer IPv4 and not attempt | ||||
241 | to use IPv6. Use if the existing tests for IPv6 availability produce | ||||
242 | incorrect results or crashes. | ||||
243 | |||||
244 | =item force_ipv6 | ||||
245 | |||||
246 | For symmetry with force_ipv4: if set to 1, DNS or other network tests | ||||
247 | will prefer IPv6 and not attempt to use IPv4. Some plugins may disregard | ||||
248 | this setting and use whatever protocol family they are comfortable with. | ||||
249 | |||||
250 | =item require_rules | ||||
251 | |||||
252 | If set to 1, init() will die if no valid rules could be loaded. This is the | ||||
253 | default behaviour when called by C<spamassassin> or C<spamd>. | ||||
254 | |||||
255 | =item languages_filename | ||||
256 | |||||
257 | If you want to be able to use the language-guessing rule | ||||
258 | C<UNWANTED_LANGUAGE_BODY>, and are using C<config_text> instead of | ||||
259 | C<rules_filename>, C<site_rules_filename>, and C<userprefs_filename>, you will | ||||
260 | need to set this. It should be the path to the B<languages> file normally | ||||
261 | found in the SpamAssassin B<rules> directory. | ||||
262 | |||||
263 | =item local_tests_only | ||||
264 | |||||
265 | If set to 1, no tests that require internet access will be performed. (default: | ||||
266 | 0) | ||||
267 | |||||
268 | =item need_tags | ||||
269 | |||||
270 | The option provides a way to avoid more expensive processing when it is known | ||||
271 | in advance that some information will not be needed by a caller. | ||||
272 | |||||
273 | A value of the option can either be a string (a comma-delimited list of tag | ||||
274 | names), or a reference to a list of individual tag names. A caller may provide | ||||
275 | the list in advance, specifying his intention to later collect the information | ||||
276 | through $pms->get_tag() calls. If a name of a tag starts with a 'NO' (case | ||||
277 | insensitive), it shows that a caller will not be interested in such tag, | ||||
278 | although there is no guarantee it would save any resources, nor that a tag | ||||
279 | value will be empty. Currently no built-in tags start with 'NO'. A later | ||||
280 | entry overrides previous one, e.g. ASN,NOASN,ASN,TIMING,NOASN is equivalent | ||||
281 | to TIMING,NOASN. | ||||
282 | |||||
283 | For backward compatibility, all tags available as of version 3.2.4 will | ||||
284 | be available by default (unless disabled by NOtag), even if not requested | ||||
285 | through need_tags option. Future versions may provide new tags conditionally | ||||
286 | available. | ||||
287 | |||||
288 | Currently the only tag that needs to be explicitly requested is 'TIMING'. | ||||
289 | Not requesting it can save a millisecond or two - it mostly serves to | ||||
290 | illustrate the usage of need_tags. | ||||
291 | |||||
292 | Example: | ||||
293 | need_tags => 'TIMING,noLANGUAGES,RELAYCOUNTRY,ASN,noASNCIDR', | ||||
294 | or: | ||||
295 | need_tags => [qw(TIMING noLANGUAGES RELAYCOUNTRY ASN noASNCIDR)], | ||||
296 | |||||
297 | =item ignore_site_cf_files | ||||
298 | |||||
299 | If set to 1, any rule files found in the C<site_rules_filename> directory will | ||||
300 | be ignored. *.pre files (used for loading plugins) found in the | ||||
301 | C<site_rules_filename> directory will still be used. (default: 0) | ||||
302 | |||||
303 | =item dont_copy_prefs | ||||
304 | |||||
305 | If set to 1, the user preferences file will not be created if it doesn't | ||||
306 | already exist. (default: 0) | ||||
307 | |||||
308 | =item save_pattern_hits | ||||
309 | |||||
310 | If set to 1, the patterns hit can be retrieved from the | ||||
311 | C<Mail::SpamAssassin::PerMsgStatus> object. Used for debugging. | ||||
312 | |||||
313 | =item home_dir_for_helpers | ||||
314 | |||||
315 | If set, the B<HOME> environment variable will be set to this value | ||||
316 | when using test applications that require their configuration data, | ||||
317 | such as Razor, Pyzor and DCC. | ||||
318 | |||||
319 | =item username | ||||
320 | |||||
321 | If set, the C<username> attribute will use this as the current user's name. | ||||
322 | Otherwise, the default is taken from the runtime environment (ie. this process' | ||||
323 | effective UID under UNIX). | ||||
324 | |||||
325 | =item skip_prng_reseeding | ||||
326 | |||||
327 | If skip_prng_reseeding is set to true, the SpamAssassin library will B<not> | ||||
328 | call srand() to reseed a pseudo-random number generator (PRNG). The srand() | ||||
329 | Perl function should be called during initialization of each child process, | ||||
330 | soon after forking. | ||||
331 | |||||
332 | Prior to version 3.4.0, calling srand() was handled by the SpamAssassin | ||||
333 | library. | ||||
334 | |||||
335 | This setting requires the caller to decide when to call srand(). | ||||
336 | This choice may be desired to preserve the entropy of a PRNG. The default | ||||
337 | value of skip_prng_reseeding is false to maintain backward compatibility. | ||||
338 | |||||
339 | This option should only be set by a caller if it calls srand() upon spawning | ||||
340 | child processes. Unless you are certain you need it, leave this setting as | ||||
341 | false. | ||||
342 | |||||
343 | NOTE: The skip_prng_reseeding feature is implemented in spamd as of 3.4.0 | ||||
344 | which allows spamd to call srand() right after forking a child process. | ||||
345 | |||||
346 | =back | ||||
347 | |||||
348 | If none of C<rules_filename>, C<site_rules_filename>, C<userprefs_filename>, or | ||||
349 | C<config_text> is set, the C<Mail::SpamAssassin> module will search for the | ||||
350 | configuration files in the usual installed locations using the below variable | ||||
351 | definitions which can be passed in. | ||||
352 | |||||
353 | =over 4 | ||||
354 | |||||
355 | =item PREFIX | ||||
356 | |||||
357 | Used as the root for certain directory paths such as: | ||||
358 | |||||
359 | '__prefix__/etc/mail/spamassassin' | ||||
360 | '__prefix__/etc/spamassassin' | ||||
361 | |||||
362 | Defaults to "/usr/local". | ||||
363 | |||||
364 | =item DEF_RULES_DIR | ||||
365 | |||||
366 | Location where the default rules are installed. Defaults to | ||||
367 | "/usr/local/share/spamassassin". | ||||
368 | |||||
369 | =item LOCAL_RULES_DIR | ||||
370 | |||||
371 | Location where the local site rules are installed. Defaults to | ||||
372 | "/usr/local/etc/mail/spamassassin". | ||||
373 | |||||
374 | =item LOCAL_STATE_DIR | ||||
375 | |||||
376 | Location of the local state directory, mainly used for installing updates via | ||||
377 | C<sa-update> and compiling rulesets to native code. Defaults to | ||||
378 | "/var/db/spamassassin". | ||||
379 | |||||
380 | =back | ||||
381 | |||||
382 | |||||
383 | =cut | ||||
384 | |||||
385 | # undocumented ctor settings: | ||||
386 | # | ||||
387 | # - keep_config_parsing_metadata: used by build/listpromotable, default 0 | ||||
388 | |||||
389 | # spent 47.0ms (258µs+46.8) within Mail::SpamAssassin::new which was called:
# once (258µs+46.8ms) by main::RUNTIME at line 227 of /usr/local/bin/sa-learn | ||||
390 | 1 | 2µs | my $class = shift; | ||
391 | 1 | 2µs | $class = ref($class) || $class; | ||
392 | |||||
393 | 1 | 2µs | my $self = shift; | ||
394 | 1 | 2µs | if (!defined $self) { $self = { }; } | ||
395 | 1 | 2µ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. | ||||
399 | 1 | 9µs | if ($self->{debug} && $self->{debug} eq '1') { | ||
400 | $self->{debug} = 'all'; | ||||
401 | } | ||||
402 | |||||
403 | # enable or disable debugging | ||||
404 | 1 | 10µs | 1 | 22µs | Mail::SpamAssassin::Logger::add_facilities($self->{debug}); # spent 22µs making 1 call to Mail::SpamAssassin::Logger::add_facilities |
405 | |||||
406 | # first debugging information possibly printed should be the version | ||||
407 | 1 | 18µs | 2 | 55µs | dbg("generic: SpamAssassin version " . Version()); # spent 45µs making 1 call to Mail::SpamAssassin::Version
# spent 10µs making 1 call to Mail::SpamAssassin::Logger::dbg |
408 | |||||
409 | # if the libs are installed in an alternate location, and the caller | ||||
410 | # didn't set PREFIX, we should have an estimated guess ready, values | ||||
411 | # substituted at 'make' time | ||||
412 | 1 | 2µs | $self->{PREFIX} ||= '/usr/local'; | ||
413 | 1 | 2µs | $self->{DEF_RULES_DIR} ||= '/usr/local/share/spamassassin'; | ||
414 | 1 | 2µs | $self->{LOCAL_RULES_DIR} ||= '/usr/local/etc/mail/spamassassin'; | ||
415 | 1 | 4µs | $self->{LOCAL_STATE_DIR} ||= '/var/db/spamassassin'; | ||
416 | 5 | 36µs | 1 | 6µ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 | |||||
419 | 1 | 3µs | $self->{needed_tags} = {}; | ||
420 | 2 | 6µs | { my $ntags = $self->{need_tags}; | ||
421 | 1 | 2µ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 | } | ||||
427 | 1 | 10µs | 1 | 16µ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 | |||||
431 | 1 | 18µs | 1 | 29.8ms | $self->{conf} ||= new Mail::SpamAssassin::Conf ($self); # spent 29.8ms making 1 call to Mail::SpamAssassin::Conf::new |
432 | 1 | 19µs | 1 | 4.52ms | $self->{registryboundaries} = Mail::SpamAssassin::RegistryBoundaries->new ($self); # spent 4.52ms making 1 call to Mail::SpamAssassin::RegistryBoundaries::new |
433 | 1 | 22µs | 1 | 24µs | $self->{plugins} = Mail::SpamAssassin::PluginHandler->new ($self); # spent 24µs making 1 call to Mail::SpamAssassin::PluginHandler::new |
434 | |||||
435 | 1 | 3µs | $self->{save_pattern_hits} ||= 0; | ||
436 | |||||
437 | # Make sure that we clean $PATH if we're tainted | ||||
438 | 1 | 8µs | 1 | 1.70ms | Mail::SpamAssassin::Util::clean_path_in_taint_mode(); # spent 1.70ms making 1 call to Mail::SpamAssassin::Util::clean_path_in_taint_mode |
439 | |||||
440 | 1 | 7µs | if (!defined $self->{username}) { | ||
441 | 1 | 13µs | 1 | 1.88ms | $self->{username} = (Mail::SpamAssassin::Util::portable_getpwuid ($>))[0]; # spent 1.88ms making 1 call to Mail::SpamAssassin::Util::portable_getpwuid |
442 | } | ||||
443 | |||||
444 | 1 | 11µs | 1 | 8.74ms | $self->create_locker(); # spent 8.74ms making 1 call to Mail::SpamAssassin::create_locker |
445 | |||||
446 | 1 | 12µs | $self; | ||
447 | } | ||||
448 | |||||
449 | # spent 8.74ms (222µs+8.51) within Mail::SpamAssassin::create_locker which was called:
# once (222µs+8.51ms) by Mail::SpamAssassin::new at line 444 | ||||
450 | 1 | 2µs | my ($self) = @_; | ||
451 | |||||
452 | 1 | 2µs | my $class; | ||
453 | 1 | 5µ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!) | ||||
457 | 1 | 7µ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 | ||||
462 | 1 | 11µs | 1 | 21µs | if (am_running_on_windows()) { # spent 21µs making 1 call to Mail::SpamAssassin::Util::am_running_on_windows |
463 | $class = 'Win32'; | ||||
464 | } else { | ||||
465 | 1 | 2µ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; | ||||
475 | 1 | 102µs | ' or do { # spent 446µs executing statements in string eval # includes 2.89ms 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 | |||||
480 | 1 | 13µs | if (!defined $self->{locker}) { die "locker: oops! no locker"; } | ||
481 | } | ||||
482 | |||||
483 | ########################################################################### | ||||
484 | |||||
485 | =item parse($message, $parse_now [, $suppl_attrib]) | ||||
486 | |||||
487 | Parse will return a Mail::SpamAssassin::Message object with just the | ||||
488 | headers parsed. When calling this function, there are two optional | ||||
489 | parameters that can be passed in: $message is either undef (which | ||||
490 | will use STDIN), a scalar - a string containing an entire message, | ||||
491 | a reference to such string, an array reference of the message with | ||||
492 | one line per array element, or either a file glob or an IO::File object | ||||
493 | which holds the entire contents of the message; and $parse_now, which | ||||
494 | specifies whether or not to create a MIME tree at parse time or later | ||||
495 | as necessary. | ||||
496 | |||||
497 | The I<$parse_now> option, by default, is set to false (0). This | ||||
498 | allows SpamAssassin to not have to generate the tree of internal | ||||
499 | data nodes if the information is not going to be used. This is | ||||
500 | handy, for instance, when running C<spamassassin -d>, which only | ||||
501 | needs the pristine header and body which is always parsed and stored | ||||
502 | by this function. | ||||
503 | |||||
504 | The optional last argument I<$suppl_attrib> provides a way for a caller | ||||
505 | to pass additional information about a message to SpamAssassin. It is | ||||
506 | either undef, or a ref to a hash where each key/value pair provides some | ||||
507 | supplementary attribute of the message, typically information that cannot | ||||
508 | be deduced from the message itself, or is hard to do so reliably, or would | ||||
509 | represent unnecessary work for SpamAssassin to obtain it. The argument will | ||||
510 | be stored to a Mail::SpamAssassin::Message object as 'suppl_attrib', thus | ||||
511 | made available to the rest of the code as well as to plugins. The exact list | ||||
512 | of attributes will evolve through time, any unknown attribute should be | ||||
513 | ignored. Possible examples are: SMTP envelope information, a flag indicating | ||||
514 | that a message as supplied by a caller was truncated due to size limit, an | ||||
515 | already verified list of DKIM signature objects, or perhaps a list of rule | ||||
516 | hits predetermined by a caller, which makes another possible way for a | ||||
517 | caller to provide meta information (instead of having to insert made-up | ||||
518 | header fields in order to pass information), or maybe just plain rule hits. | ||||
519 | |||||
520 | For more information, please see the C<Mail::SpamAssassin::Message> | ||||
521 | and C<Mail::SpamAssassin::Message::Node> POD. | ||||
522 | |||||
523 | =cut | ||||
524 | |||||
525 | # spent 4.85s (29.0ms+4.82) within Mail::SpamAssassin::parse which was called 234 times, avg 20.7ms/call:
# 234 times (29.0ms+4.82s) by main::wanted at line 566 of /usr/local/bin/sa-learn, avg 20.7ms/call | ||||
526 | 234 | 676µs | my($self, $message, $parsenow, $suppl_attrib) = @_; | ||
527 | |||||
528 | 234 | 4.26ms | 234 | 2.11ms | my $start_time = time; # spent 2.11ms making 234 calls to Time::HiRes::time, avg 9µs/call |
529 | 234 | 2.56ms | 234 | 4.50ms | $self->init(1); # spent 4.50ms making 234 calls to Mail::SpamAssassin::init, avg 19µs/call |
530 | 234 | 1.95ms | 234 | 2.14ms | my $timer = $self->time_method("parse"); # spent 2.14ms making 234 calls to Mail::SpamAssassin::time_method, avg 9µs/call |
531 | |||||
532 | 234 | 462µs | my $master_deadline; | ||
533 | # passed in at a function call | ||||
534 | 234 | 521µ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 | ||||
538 | 234 | 1.96ms | if ($self->{conf}->{time_limit}) { # defined and nonzero | ||
539 | 234 | 780µs | my $time_limit_deadline = $start_time + $self->{conf}->{time_limit}; | ||
540 | 234 | 966µs | if (!defined $master_deadline || $time_limit_deadline < $master_deadline) { | ||
541 | 234 | 483µs | $master_deadline = $time_limit_deadline; | ||
542 | } | ||||
543 | } | ||||
544 | 234 | 1.05ms | if (defined $master_deadline) { | ||
545 | 234 | 2.08ms | 234 | 2.28ms | dbg("config: time limit %.1f s", $master_deadline - $start_time); # spent 2.28ms making 234 calls to Mail::SpamAssassin::Logger::dbg, avg 10µs/call |
546 | } | ||||
547 | |||||
548 | my $msg = Mail::SpamAssassin::Message->new({ | ||||
549 | message=>$message, parsenow=>$parsenow, | ||||
550 | normalize=>$self->{conf}->{normalize_charset}, | ||||
551 | 234 | 5.81ms | 234 | 4.79s | master_deadline=>$master_deadline, suppl_attrib=>$suppl_attrib }); # spent 4.79s making 234 calls to Mail::SpamAssassin::Message::new, avg 20.5ms/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. | ||||
556 | 234 | 2.65ms | 234 | 16.7ms | $self->call_plugins("post_message_parse", { message => $msg }); # spent 16.7ms making 234 calls to Mail::SpamAssassin::call_plugins, avg 71µs/call |
557 | |||||
558 | 234 | 2.26ms | return $msg; | ||
559 | } | ||||
560 | |||||
561 | |||||
562 | ########################################################################### | ||||
563 | |||||
564 | =item $status = $f->check ($mail) | ||||
565 | |||||
566 | Check a mail, encapsulated in a C<Mail::SpamAssassin::Message> object, | ||||
567 | to determine if it is spam or not. | ||||
568 | |||||
569 | Returns a C<Mail::SpamAssassin::PerMsgStatus> object which can be | ||||
570 | used to test or manipulate the mail message. | ||||
571 | |||||
572 | Note that the C<Mail::SpamAssassin> object can be re-used for further messages | ||||
573 | without affecting this check; in OO terminology, the C<Mail::SpamAssassin> | ||||
574 | object is a "factory". However, if you do this, be sure to call the | ||||
575 | C<finish()> method on the status objects when you're done with them. | ||||
576 | |||||
577 | =cut | ||||
578 | |||||
579 | sub 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 | |||||
591 | Check a mail, encapsulated in a plain string C<$mailtext>, to determine if it | ||||
592 | is spam or not. | ||||
593 | |||||
594 | Otherwise identical to C<check()> above. | ||||
595 | |||||
596 | =cut | ||||
597 | |||||
598 | sub 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 | |||||
617 | Learn from a mail, encapsulated in a C<Mail::SpamAssassin::Message> object. | ||||
618 | |||||
619 | If C<$isspam> is set, the mail is assumed to be spam, otherwise it will | ||||
620 | be learnt as non-spam. | ||||
621 | |||||
622 | If C<$forget> is set, the attributes of the mail will be removed from | ||||
623 | both the non-spam and spam learning databases. | ||||
624 | |||||
625 | C<$id> is an optional message-identification string, used internally | ||||
626 | to tag the message. If it is C<undef>, the Message-Id of the message | ||||
627 | will be used. It should be unique to that message. | ||||
628 | |||||
629 | Returns a C<Mail::SpamAssassin::PerMsgLearner> object which can be used to | ||||
630 | manipulate the learning process for each mail. | ||||
631 | |||||
632 | Note that the C<Mail::SpamAssassin> object can be re-used for further messages | ||||
633 | without affecting this check; in OO terminology, the C<Mail::SpamAssassin> | ||||
634 | object is a "factory". However, if you do this, be sure to call the | ||||
635 | C<finish()> method on the learner objects when you're done with them. | ||||
636 | |||||
637 | C<learn()> and C<check()> can be run using the same factory. C<init_learner()> | ||||
638 | must be called before using this method. | ||||
639 | |||||
640 | =cut | ||||
641 | |||||
642 | # spent 96432s (24.5ms+96432) within Mail::SpamAssassin::learn which was called 234 times, avg 412s/call:
# 234 times (24.5ms+96432s) by main::wanted at line 574 of /usr/local/bin/sa-learn, avg 412s/call | ||||
643 | 234 | 631µs | my ($self, $mail_obj, $id, $isspam, $forget) = @_; | ||
644 | 234 | 468µs | local ($_); | ||
645 | |||||
646 | 234 | 1.37ms | require Mail::SpamAssassin::PerMsgLearner; | ||
647 | 234 | 1.82ms | 234 | 4.12ms | $self->init(1); # spent 4.12ms making 234 calls to Mail::SpamAssassin::init, avg 18µs/call |
648 | 234 | 2.70ms | 234 | 8.04ms | my $msg = Mail::SpamAssassin::PerMsgLearner->new($self, $mail_obj); # spent 8.04ms making 234 calls to Mail::SpamAssassin::PerMsgLearner::new, avg 34µs/call |
649 | |||||
650 | 234 | 1.63ms | if ($forget) { | ||
651 | dbg("learn: forgetting message"); | ||||
652 | $msg->forget($id); | ||||
653 | } elsif ($isspam) { | ||||
654 | 234 | 1.60ms | 234 | 1.57ms | dbg("learn: learning spam"); # spent 1.57ms making 234 calls to Mail::SpamAssassin::Logger::dbg, avg 7µs/call |
655 | 234 | 2.09ms | 234 | 96432s | $msg->learn_spam($id); # spent 96432s making 234 calls to Mail::SpamAssassin::PerMsgLearner::learn_spam, avg 412s/call |
656 | } else { | ||||
657 | dbg("learn: learning ham"); | ||||
658 | $msg->learn_ham($id); | ||||
659 | } | ||||
660 | |||||
661 | 234 | 2.61ms | $msg; | ||
662 | } | ||||
663 | |||||
664 | ########################################################################### | ||||
665 | |||||
666 | =item $f->init_learner ( [ { opt => val, ... } ] ) | ||||
667 | |||||
668 | Initialise learning. You may pass the following attribute-value pairs to this | ||||
669 | method. | ||||
670 | |||||
671 | =over 4 | ||||
672 | |||||
673 | =item caller_will_untie | ||||
674 | |||||
675 | Whether or not the code calling this method will take care of untie'ing | ||||
676 | from the Bayes databases (by calling C<finish_learner()>) (optional, default 0). | ||||
677 | |||||
678 | =item force_expire | ||||
679 | |||||
680 | Should an expiration run be forced to occur immediately? (optional, default 0). | ||||
681 | |||||
682 | =item learn_to_journal | ||||
683 | |||||
684 | Should learning data be written to the journal, instead of directly to the | ||||
685 | databases? (optional, default 0). | ||||
686 | |||||
687 | =item wait_for_lock | ||||
688 | |||||
689 | Whether or not to wait a long time for locks to complete (optional, default 0). | ||||
690 | |||||
691 | =item opportunistic_expire_check_only | ||||
692 | |||||
693 | During the opportunistic journal sync and expire check, don't actually do the | ||||
694 | expire but report back whether or not it should occur (optional, default 0). | ||||
695 | |||||
696 | =item no_relearn | ||||
697 | |||||
698 | If doing a learn operation, and the message has already been learned as | ||||
699 | the opposite type, don't re-learn the message. | ||||
700 | |||||
701 | =back | ||||
702 | |||||
703 | =cut | ||||
704 | |||||
705 | # spent 228µs (205+24) within Mail::SpamAssassin::init_learner which was called:
# once (205µs+24µs) by main::RUNTIME at line 349 of /usr/local/bin/sa-learn | ||||
706 | 1 | 2µs | my $self = shift; | ||
707 | 1 | 2µs | my $opts = shift; | ||
708 | 1 | 8µs | 1 | 7µs | dbg("learn: initializing learner"); # spent 7µs making 1 call to Mail::SpamAssassin::Logger::dbg |
709 | |||||
710 | # Make sure we're already initialized ... | ||||
711 | 1 | 9µs | 1 | 17µs | $self->init(1); # spent 17µs making 1 call to Mail::SpamAssassin::init |
712 | |||||
713 | 1 | 12µ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 | |||||
722 | 1 | 2µs | my %ret; | ||
723 | |||||
724 | # Set any other options that need setting ... | ||||
725 | 1 | 48µs | while( my($k,$v) = each %kv ) { | ||
726 | 6 | 50µs | $ret{$k} = $self->{$v}; | ||
727 | 10 | 41µs | if (exists $opts->{$k}) { $self->{$v} = $opts->{$k}; } | ||
728 | } | ||||
729 | |||||
730 | 1 | 13µs | return \%ret; | ||
731 | } | ||||
732 | |||||
733 | ########################################################################### | ||||
734 | |||||
735 | =item $f->rebuild_learner_caches ({ opt => val }) | ||||
736 | |||||
737 | Rebuild any cache databases; should be called after the learning process. | ||||
738 | Options include: C<verbose>, which will output diagnostics to C<stdout> | ||||
739 | if set to 1. | ||||
740 | |||||
741 | =cut | ||||
742 | |||||
743 | sub 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 | |||||
752 | Finish learning. | ||||
753 | |||||
754 | =cut | ||||
755 | |||||
756 | # spent 3.09ms (28µs+3.06) within Mail::SpamAssassin::finish_learner which was called:
# once (28µs+3.06ms) by main::RUNTIME at line 501 of /usr/local/bin/sa-learn | ||||
757 | 1 | 2µs | my $self = shift; | ||
758 | 1 | 14µs | 1 | 3.06ms | $self->{bayes_scanner}->force_close(1) if $self->{bayes_scanner}; # spent 3.06ms making 1 call to Mail::SpamAssassin::Bayes::force_close |
759 | 1 | 10µs | 1; | ||
760 | } | ||||
761 | |||||
762 | =item $f->dump_bayes_db() | ||||
763 | |||||
764 | Dump the contents of the Bayes DB | ||||
765 | |||||
766 | =cut | ||||
767 | |||||
768 | sub 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 | |||||
775 | Signals that the current user has changed (possibly using C<setuid>), meaning | ||||
776 | that SpamAssassin should close any per-user databases it has open, and re-open | ||||
777 | using ones appropriate for the new user. | ||||
778 | |||||
779 | Note that this should be called I<after> reading any per-user configuration, as | ||||
780 | that data may override some paths opened in this method. You may pass the | ||||
781 | following attribute-value pairs: | ||||
782 | |||||
783 | =over 4 | ||||
784 | |||||
785 | =item username | ||||
786 | |||||
787 | The username of the user. This will be used for the C<username> attribute. | ||||
788 | |||||
789 | =item user_dir | ||||
790 | |||||
791 | A directory to use as a 'home directory' for the current user's data, | ||||
792 | overriding the system default. This directory must be readable and writable by | ||||
793 | the process. Note that the resulting C<userstate_dir> will be the | ||||
794 | C<.spamassassin> subdirectory of this dir. | ||||
795 | |||||
796 | =item userstate_dir | ||||
797 | |||||
798 | A directory to use as a directory for the current user's data, overriding the | ||||
799 | system default. This directory must be readable and writable by the process. | ||||
800 | The default is C<user_dir/.spamassassin>. | ||||
801 | |||||
802 | =back | ||||
803 | |||||
804 | =cut | ||||
805 | |||||
806 | sub 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 | |||||
860 | Report a mail, encapsulated in a C<Mail::SpamAssassin::Message> object, as | ||||
861 | human-verified spam. This will submit the mail message to live, | ||||
862 | collaborative, spam-blocker databases, allowing other users to block this | ||||
863 | message. | ||||
864 | |||||
865 | It will also submit the mail to SpamAssassin's Bayesian learner. | ||||
866 | |||||
867 | Options is an optional reference to a hash of options. Currently these | ||||
868 | can be: | ||||
869 | |||||
870 | =over 4 | ||||
871 | |||||
872 | =item dont_report_to_dcc | ||||
873 | |||||
874 | Inhibits reporting of the spam to DCC. | ||||
875 | |||||
876 | =item dont_report_to_pyzor | ||||
877 | |||||
878 | Inhibits reporting of the spam to Pyzor. | ||||
879 | |||||
880 | =item dont_report_to_razor | ||||
881 | |||||
882 | Inhibits reporting of the spam to Razor. | ||||
883 | |||||
884 | =item dont_report_to_spamcop | ||||
885 | |||||
886 | Inhibits reporting of the spam to SpamCop. | ||||
887 | |||||
888 | =back | ||||
889 | |||||
890 | =cut | ||||
891 | |||||
892 | sub 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 | |||||
913 | Revoke a mail, encapsulated in a C<Mail::SpamAssassin::Message> object, as | ||||
914 | human-verified ham (non-spam). This will revoke the mail message from live, | ||||
915 | collaborative, spam-blocker databases, allowing other users to block this | ||||
916 | message. | ||||
917 | |||||
918 | It will also submit the mail to SpamAssassin's Bayesian learner as nonspam. | ||||
919 | |||||
920 | Options is an optional reference to a hash of options. Currently these | ||||
921 | can be: | ||||
922 | |||||
923 | =over 4 | ||||
924 | |||||
925 | =item dont_report_to_razor | ||||
926 | |||||
927 | Inhibits revoking of the spam to Razor. | ||||
928 | |||||
929 | |||||
930 | =back | ||||
931 | |||||
932 | =cut | ||||
933 | |||||
934 | sub 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 | |||||
953 | Given a string containing an email address, add it to the automatic | ||||
954 | whitelist database. | ||||
955 | |||||
956 | If $cli_p is set then underlying plugin may give visual feedback on additions/failures. | ||||
957 | |||||
958 | =cut | ||||
959 | |||||
960 | sub 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 | |||||
971 | Given a mail message, find as many addresses in the usual headers (To, Cc, From | ||||
972 | etc.), and the message body, and add them to the automatic whitelist database. | ||||
973 | |||||
974 | If $cli_p is set then underlying plugin may give visual feedback on additions/failures. | ||||
975 | |||||
976 | =cut | ||||
977 | |||||
978 | sub 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 | |||||
991 | Given a string containing an email address, remove it from the automatic | ||||
992 | whitelist database. | ||||
993 | |||||
994 | If $cli_p is set then underlying plugin may give visual feedback on additions/failures. | ||||
995 | |||||
996 | =cut | ||||
997 | |||||
998 | sub 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 | |||||
1009 | Given a mail message, find as many addresses in the usual headers (To, Cc, From | ||||
1010 | etc.), and the message body, and remove them from the automatic whitelist | ||||
1011 | database. | ||||
1012 | |||||
1013 | If $cli_p is set then underlying plugin may give visual feedback on additions/failures. | ||||
1014 | |||||
1015 | =cut | ||||
1016 | |||||
1017 | sub 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 | |||||
1030 | Given a string containing an email address, add it to the automatic | ||||
1031 | whitelist database with a high score, effectively blacklisting them. | ||||
1032 | |||||
1033 | If $cli_p is set then underlying plugin may give visual feedback on additions/failures. | ||||
1034 | |||||
1035 | =cut | ||||
1036 | |||||
1037 | sub 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 | |||||
1047 | Given a mail message, find addresses in the From headers and add them to the | ||||
1048 | automatic whitelist database with a high score, effectively blacklisting them. | ||||
1049 | |||||
1050 | Note that To and Cc addresses are not used. | ||||
1051 | |||||
1052 | If $cli_p is set then underlying plugin may give visual feedback on additions/failures. | ||||
1053 | |||||
1054 | =cut | ||||
1055 | |||||
1056 | sub 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 | |||||
1078 | Returns the text of the message, with any SpamAssassin-added text (such | ||||
1079 | as the report, or X-Spam-Status headers) stripped. | ||||
1080 | |||||
1081 | Note that the B<$mail> object is not modified. | ||||
1082 | |||||
1083 | Warning: 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" | ||||
1085 | to use one or the other consistently throughout. | ||||
1086 | |||||
1087 | =cut | ||||
1088 | |||||
1089 | sub 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 | |||||
1234 | Read a configuration file and parse user preferences from it. | ||||
1235 | |||||
1236 | User preferences are as defined in the C<Mail::SpamAssassin::Conf> manual page. | ||||
1237 | In other words, they include scoring options, scores, whitelists and | ||||
1238 | blacklists, and so on, but do not include rule definitions, privileged | ||||
1239 | settings, etc. unless C<allow_user_rules> is enabled; and they never include | ||||
1240 | the administrator settings. | ||||
1241 | |||||
1242 | =cut | ||||
1243 | |||||
1244 | sub 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 | |||||
1276 | Read configuration paramaters from SQL database and parse scores from it. This | ||||
1277 | will only take effect if the perl C<DBI> module is installed, and the | ||||
1278 | configuration parameters C<user_scores_dsn>, C<user_scores_sql_username>, and | ||||
1279 | C<user_scores_sql_password> are set correctly. | ||||
1280 | |||||
1281 | The username in C<$username> will also be used for the C<username> attribute of | ||||
1282 | the Mail::SpamAssassin object. | ||||
1283 | |||||
1284 | =cut | ||||
1285 | |||||
1286 | sub 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 | |||||
1302 | Read configuration paramaters from an LDAP server and parse scores from it. | ||||
1303 | This will only take effect if the perl C<Net::LDAP> and C<URI> modules are | ||||
1304 | installed, and the configuration parameters C<user_scores_dsn>, | ||||
1305 | C<user_scores_ldap_username>, and C<user_scores_ldap_password> are set | ||||
1306 | correctly. | ||||
1307 | |||||
1308 | The username in C<$username> will also be used for the C<username> attribute of | ||||
1309 | the Mail::SpamAssassin object. | ||||
1310 | |||||
1311 | =cut | ||||
1312 | |||||
1313 | sub 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 | |||||
1327 | Set the persistent address list factory, used to create objects for the | ||||
1328 | automatic whitelist algorithm's persistent-storage back-end. See | ||||
1329 | C<Mail::SpamAssassin::PersistentAddrList> for the API these factory objects | ||||
1330 | must implement, and the API the objects they produce must implement. | ||||
1331 | |||||
1332 | =cut | ||||
1333 | |||||
1334 | # spent 11µs within Mail::SpamAssassin::set_persistent_address_list_factory which was called:
# once (11µs+0s) by Mail::SpamAssassin::Plugin::TxRep::open_storages at line 1650 of Mail/SpamAssassin/Plugin/TxRep.pm | ||||
1335 | 1 | 2µs | my ($self, $fac) = @_; | ||
1336 | 1 | 12µs | $self->{pers_addr_list_factory} = $fac; | ||
1337 | } | ||||
1338 | |||||
1339 | ########################################################################### | ||||
1340 | |||||
1341 | =item $f->compile_now ($use_user_prefs, $keep_userstate) | ||||
1342 | |||||
1343 | Compile all patterns, load all configuration files, and load all | ||||
1344 | possibly-required Perl modules. | ||||
1345 | |||||
1346 | Normally, Mail::SpamAssassin uses lazy evaluation where possible, but if you | ||||
1347 | plan to fork() or start a new perl interpreter thread to process a message, | ||||
1348 | this is suboptimal, as each process/thread will have to perform these actions. | ||||
1349 | |||||
1350 | Call this function in the master thread or process to perform the actions | ||||
1351 | straightaway, so that the sub-processes will not have to. | ||||
1352 | |||||
1353 | If C<$use_user_prefs> is 0, this will initialise the SpamAssassin | ||||
1354 | configuration without reading the per-user configuration file and it will | ||||
1355 | assume that you will call C<read_scoreonly_config> at a later point. | ||||
1356 | |||||
1357 | If C<$keep_userstate> is true, compile_now() will revert any configuration | ||||
1358 | options which have a default with I<__userstate__> in it post-init(), | ||||
1359 | and then re-change the option before returning. This lets you change | ||||
1360 | I<$ENV{'HOME'}> to a temp directory, have compile_now() and create any | ||||
1361 | files there as necessary without disturbing the actual files as changed | ||||
1362 | by a configuration option. By default, this is disabled. | ||||
1363 | |||||
1364 | =cut | ||||
1365 | |||||
1366 | sub 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 | |||||
1455 | Output some diagnostic information, useful for debugging SpamAssassin | ||||
1456 | problems. | ||||
1457 | |||||
1458 | =cut | ||||
1459 | |||||
1460 | sub 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 | |||||
1475 | Syntax-check the current set of rules. Returns the number of | ||||
1476 | syntax errors discovered, or 0 if the configuration is valid. | ||||
1477 | |||||
1478 | =cut | ||||
1479 | |||||
1480 | sub 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 | |||||
1520 | Destroy this object, so that it will be garbage-collected once it | ||||
1521 | goes out of scope. The object will no longer be usable after this | ||||
1522 | method is called. | ||||
1523 | |||||
1524 | =cut | ||||
1525 | |||||
1526 | sub 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 | |||||
1550 | sub timer_enable { | ||||
1551 | my ($self) = @_; | ||||
1552 | dbg("config: timing enabled") if !$self->{timer_enabled}; | ||||
1553 | $self->{timer_enabled} = 1; | ||||
1554 | } | ||||
1555 | |||||
1556 | sub 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 | ||||
1563 | sub timer_reset { | ||||
1564 | my ($self) = @_; | ||||
1565 | delete $self->{timers}; | ||||
1566 | delete $self->{timers_order}; | ||||
1567 | } | ||||
1568 | |||||
1569 | sub 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 | |||||
1584 | sub 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 91.2ms within Mail::SpamAssassin::time_method which was called 8305 times, avg 11µs/call:
# 3216 times (45.1ms+0s) by Mail::SpamAssassin::Plugin::TxRep::check_reputation at line 1444 of Mail/SpamAssassin/Plugin/TxRep.pm, avg 14µs/call
# 3216 times (27.5ms+0s) by Mail::SpamAssassin::Plugin::TxRep::check_reputation at line 1495 of Mail/SpamAssassin/Plugin/TxRep.pm, avg 9µs/call
# 468 times (4.65ms+0s) by Mail::SpamAssassin::PerMsgStatus::get_uri_detail_list at line 2236 of Mail/SpamAssassin/PerMsgStatus.pm, avg 10µs/call
# 468 times (4.43ms+0s) by Mail::SpamAssassin::Plugin::TxRep::check_senders_reputation at line 1247 of Mail/SpamAssassin/Plugin/TxRep.pm, avg 9µs/call
# 234 times (2.94ms+0s) by Mail::SpamAssassin::Plugin::Bayes::_learn_trapped at line 475 of Mail/SpamAssassin/Plugin/Bayes.pm, avg 13µs/call
# 234 times (2.26ms+0s) by Mail::SpamAssassin::Plugin::Bayes::learn_message at line 382 of Mail/SpamAssassin/Plugin/Bayes.pm, avg 10µs/call
# 234 times (2.19ms+0s) by Mail::SpamAssassin::PerMsgStatus::extract_message_metadata at line 1702 of Mail/SpamAssassin/PerMsgStatus.pm, avg 9µs/call
# 234 times (2.14ms+0s) by Mail::SpamAssassin::parse at line 530, avg 9µs/call
# once (10µs+0s) by Mail::SpamAssassin::init at line 1660 | ||||
1606 | 8305 | 22.9ms | my ($self, $name) = @_; | ||
1607 | 8305 | 106ms | return unless $self->{timer_enabled}; | ||
1608 | return Mail::SpamAssassin::Util::ScopedTimer->new($self, $name); | ||||
1609 | } | ||||
1610 | |||||
1611 | sub 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 12.8s (9.23ms+12.8) within Mail::SpamAssassin::init which was called 470 times, avg 27.2ms/call:
# 234 times (4.50ms+0s) by Mail::SpamAssassin::parse at line 529, avg 19µs/call
# 234 times (4.12ms+0s) by Mail::SpamAssassin::learn at line 647, avg 18µs/call
# once (590µs+12.8s) by main::RUNTIME at line 236 of /usr/local/bin/sa-learn
# once (17µs+0s) by Mail::SpamAssassin::init_learner at line 711 | ||||
1647 | 470 | 1.08ms | my ($self, $use_user_pref) = @_; | ||
1648 | |||||
1649 | # Allow init() to be called multiple times, but only run once. | ||||
1650 | 470 | 2.00ms | if (defined $self->{_initted}) { | ||
1651 | # If the PID changes, reseed the PRNG (if permitted) and the DNS ID counter | ||||
1652 | 469 | 2.51ms | if ($self->{_initted} != $$) { | ||
1653 | $self->{_initted} = $$; | ||||
1654 | srand if !$self->{skip_prng_reseeding}; | ||||
1655 | $self->{resolver}->reinit_post_fork(); | ||||
1656 | } | ||||
1657 | 469 | 4.02ms | return; | ||
1658 | } | ||||
1659 | |||||
1660 | 1 | 9µs | 1 | 10µ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() | ||||
1662 | 1 | 54µs | $self->{_initted} = $$; | ||
1663 | |||||
1664 | #fix spamd reading root prefs file | ||||
1665 | 1 | 2µs | if (!defined $use_user_pref) { | ||
1666 | $use_user_pref = 1; | ||||
1667 | } | ||||
1668 | |||||
1669 | 1 | 5µs | if (!defined $self->{config_text}) { | ||
1670 | 1 | 7µs | $self->{config_text} = ''; | ||
1671 | |||||
1672 | # read a file called "init.pre" in site rules dir *before* all others; | ||||
1673 | # even the system config. | ||||
1674 | 1 | 3µs | my $siterules = $self->{site_rules_filename}; | ||
1675 | 1 | 12µs | 1 | 491µs | $siterules ||= $self->first_existing_path (@site_rules_path); # spent 491µs making 1 call to Mail::SpamAssassin::first_existing_path |
1676 | |||||
1677 | 1 | 3µs | my $sysrules = $self->{rules_filename}; | ||
1678 | 1 | 9µs | 1 | 227µs | $sysrules ||= $self->first_existing_path (@default_rules_path); # spent 227µs making 1 call to Mail::SpamAssassin::first_existing_path |
1679 | |||||
1680 | 1 | 5µs | if ($siterules) { | ||
1681 | 1 | 20µs | 1 | 2.27ms | $self->{config_text} .= $self->read_pre($siterules, 'site rules pre files'); # spent 2.27ms making 1 call to Mail::SpamAssassin::read_pre |
1682 | } | ||||
1683 | else { | ||||
1684 | warn "config: could not find site rules directory\n"; | ||||
1685 | } | ||||
1686 | |||||
1687 | 1 | 4µs | if ($sysrules) { | ||
1688 | 1 | 9µs | 1 | 278µs | $self->{config_text} .= $self->read_pre($sysrules, 'sys rules pre files'); # spent 278µs making 1 call to Mail::SpamAssassin::read_pre |
1689 | } | ||||
1690 | else { | ||||
1691 | warn "config: could not find sys rules directory\n"; | ||||
1692 | } | ||||
1693 | |||||
1694 | 1 | 4µs | if ($sysrules) { | ||
1695 | 1 | 11µs | 1 | 473µs | my $cftext = $self->read_cf($sysrules, 'default rules dir'); # spent 473µs making 1 call to Mail::SpamAssassin::read_cf |
1696 | 1 | 3µs | if ($self->{require_rules} && $cftext !~ /\S/) { | ||
1697 | die "config: no rules were found! Do you need to run 'sa-update'?\n"; | ||||
1698 | } | ||||
1699 | 1 | 6µs | $self->{config_text} .= $cftext; | ||
1700 | } | ||||
1701 | |||||
1702 | 1 | 5µs | if (!$self->{languages_filename}) { | ||
1703 | 1 | 10µs | 1 | 709µs | $self->{languages_filename} = $self->find_rule_support_file("languages"); # spent 709µs making 1 call to Mail::SpamAssassin::find_rule_support_file |
1704 | } | ||||
1705 | |||||
1706 | 1 | 6µs | if ($siterules && !$self->{ignore_site_cf_files}) { | ||
1707 | 1 | 12µs | 1 | 749µs | $self->{config_text} .= $self->read_cf($siterules, 'site rules dir'); # spent 749µs making 1 call to Mail::SpamAssassin::read_cf |
1708 | } | ||||
1709 | |||||
1710 | 1 | 5µs | if ( $use_user_pref != 0 ) { | ||
1711 | 1 | 9µs | 1 | 512µs | $self->get_and_create_userstate_dir(); # spent 512µs making 1 call to Mail::SpamAssassin::get_and_create_userstate_dir |
1712 | |||||
1713 | # user prefs file | ||||
1714 | 1 | 3µs | my $fname = $self->{userprefs_filename}; | ||
1715 | 1 | 9µs | 1 | 291µs | $fname ||= $self->first_existing_path (@default_userprefs_path); # spent 291µs making 1 call to Mail::SpamAssassin::first_existing_path |
1716 | |||||
1717 | 1 | 3µ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 | |||||
1734 | 1 | 10µs | 1 | 268µs | $self->{config_text} .= $self->read_cf($fname, 'user prefs file'); # spent 268µs making 1 call to Mail::SpamAssassin::read_cf |
1735 | } | ||||
1736 | } | ||||
1737 | |||||
1738 | 1 | 3µs | if ($self->{pre_config_text}) { | ||
1739 | $self->{config_text} = $self->{pre_config_text} . $self->{config_text}; | ||||
1740 | } | ||||
1741 | 1 | 4µs | if ($self->{post_config_text}) { | ||
1742 | 1 | 3µs | $self->{config_text} .= $self->{post_config_text}; | ||
1743 | } | ||||
1744 | |||||
1745 | 1 | 18µs | 1 | 7µ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! | ||||
1755 | 1 | 3µs | $self->{conf}->{main} = $self; | ||
1756 | 1 | 9µs | 1 | 18µs | if (would_log('dbg', 'config_text') > 1) { # spent 18µs making 1 call to Mail::SpamAssassin::Logger::would_log |
1757 | dbg('config_text: '.$self->{config_text}); | ||||
1758 | } | ||||
1759 | 1 | 12µs | 1 | 5.03s | $self->{conf}->parse_rules ($self->{config_text}); # spent 5.03s making 1 call to Mail::SpamAssassin::Conf::parse_rules |
1760 | 1 | 13µs | 1 | 7.67s | $self->{conf}->finish_parsing(0); # spent 7.67s making 1 call to Mail::SpamAssassin::Conf::finish_parsing |
1761 | 1 | 6µs | delete $self->{conf}->{main}; # to allow future GC'ing | ||
1762 | |||||
1763 | 1 | 5µs | undef $self->{config_text}; # ensure it's actually freed | ||
1764 | 1 | 4µs | delete $self->{config_text}; | ||
1765 | |||||
1766 | 1 | 3µ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 | ||||
1771 | 1 | 6µs | if ($self->{conf}->{use_bayes}) { | ||
1772 | 1 | 7µs | require Mail::SpamAssassin::Bayes; | ||
1773 | 1 | 27µs | 1 | 35.0ms | $self->{bayes_scanner} = new Mail::SpamAssassin::Bayes ($self); # spent 35.0ms making 1 call to Mail::SpamAssassin::Bayes::new |
1774 | } | ||||
1775 | 1 | 8µs | $self->{'learn_to_journal'} = $self->{conf}->{bayes_learn_to_journal}; | ||
1776 | |||||
1777 | # Figure out/set our initial scoreset | ||||
1778 | 1 | 2µs | my $set = 0; | ||
1779 | 1 | 4µs | $set |= 1 unless $self->{local_tests_only}; | ||
1780 | 1 | 13µs | 1 | 1.42ms | $set |= 2 if $self->{bayes_scanner} && $self->{bayes_scanner}->is_scan_available(); # spent 1.42ms making 1 call to Mail::SpamAssassin::Bayes::is_scan_available |
1781 | 1 | 13µs | 1 | 46µs | $self->{conf}->set_score_set ($set); # spent 46µs making 1 call to Mail::SpamAssassin::Conf::set_score_set |
1782 | |||||
1783 | 1 | 3µs | if ($self->{only_these_rules}) { | ||
1784 | $self->{conf}->trim_rules($self->{only_these_rules}); | ||||
1785 | } | ||||
1786 | |||||
1787 | 1 | 5µs | if (!$self->{timer_enabled}) { | ||
1788 | # enable timing implicitly if _TIMING_ is used in add_header templates | ||||
1789 | 2 | 21µs | foreach my $hf_ref (@{$self->{conf}->{'headers_ham'}}, | ||
1790 | 1 | 4µs | @{$self->{conf}->{'headers_spam'}}) { | ||
1791 | 9 | 107µs | 9 | 32µs | if ($hf_ref->[1] =~ /_TIMING_/) { $self->timer_enable(); last } # spent 32µs making 9 calls to Mail::SpamAssassin::CORE:match, avg 4µs/call |
1792 | } | ||||
1793 | } | ||||
1794 | |||||
1795 | # should be called only after configuration has been parsed | ||||
1796 | 1 | 29µs | 1 | 6.74ms | $self->{resolver} = Mail::SpamAssassin::DnsResolver->new($self); # spent 6.74ms making 1 call to Mail::SpamAssassin::DnsResolver::new |
1797 | |||||
1798 | # TODO -- open DNS cache etc. if necessary | ||||
1799 | } | ||||
1800 | |||||
1801 | # spent 31.6ms (1.37+30.3) within Mail::SpamAssassin::read_cf which was called 61 times, avg 519µs/call:
# 58 times (1.32ms+28.8ms) by Mail::SpamAssassin::Conf::Parser::parse at line 343 of Mail/SpamAssassin/Conf/Parser.pm, avg 520µs/call
# once (14µs+735µs) by Mail::SpamAssassin::init at line 1707
# once (20µs+454µs) by Mail::SpamAssassin::init at line 1695
# once (14µs+254µs) by Mail::SpamAssassin::init at line 1734 | ||||
1802 | 61 | 210µs | my ($self, $allpaths, $desc) = @_; | ||
1803 | 61 | 1.14ms | 61 | 30.3ms | return $self->_read_cf_pre($allpaths,$desc,\&get_cf_files_in_dir); # spent 30.3ms making 61 calls to Mail::SpamAssassin::_read_cf_pre, avg 496µs/call |
1804 | } | ||||
1805 | |||||
1806 | sub read_pre { | ||||
1807 | 2 | 6µs | my ($self, $allpaths, $desc) = @_; | ||
1808 | 2 | 31µs | 2 | 2.51ms | return $self->_read_cf_pre($allpaths,$desc,\&get_pre_files_in_dir); # spent 2.51ms making 2 calls to Mail::SpamAssassin::_read_cf_pre, avg 1.25ms/call |
1809 | } | ||||
1810 | |||||
1811 | sub _read_cf_pre { | ||||
1812 | 63 | 163µs | my ($self, $allpaths, $desc, $filelistmethod) = @_; | ||
1813 | |||||
1814 | 63 | 135µs | return '' unless defined ($allpaths); | ||
1815 | |||||
1816 | 63 | 166µs | my $txt = ''; | ||
1817 | 63 | 451µs | foreach my $path (split("\000", $allpaths)) | ||
1818 | { | ||||
1819 | 63 | 518µs | 63 | 418µs | dbg("config: using \"$path\" for $desc"); # spent 418µs making 63 calls to Mail::SpamAssassin::Logger::dbg, avg 7µs/call |
1820 | |||||
1821 | 63 | 5.24ms | 63 | 4.71ms | my $stat_errn = stat($path) ? 0 : 0+$!; # spent 4.71ms making 63 calls to Mail::SpamAssassin::CORE:stat, avg 75µs/call |
1822 | 63 | 2.44ms | 240 | 694µs | if ($stat_errn == ENOENT) { # spent 208µs making 59 calls to Mail::SpamAssassin::CORE:fteread, avg 4µs/call
# spent 189µs making 63 calls to Mail::SpamAssassin::CORE:ftdir, avg 3µs/call
# spent 174µs making 59 calls to Mail::SpamAssassin::CORE:ftsize, avg 3µs/call
# spent 123µ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 _) { | ||||
1827 | 4 | 36µs | 4 | 1.88ms | foreach my $file ($self->$filelistmethod($path)) { # spent 1.16ms making 2 calls to Mail::SpamAssassin::get_pre_files_in_dir, avg 579µs/call
# spent 718µs making 2 calls to Mail::SpamAssassin::get_cf_files_in_dir, avg 359µs/call |
1828 | 9 | 92µs | 9 | 1.39ms | $txt .= read_cf_file($file); # spent 1.39ms making 9 calls to Mail::SpamAssassin::read_cf_file, avg 154µs/call |
1829 | } | ||||
1830 | } elsif (-f _ && -s _ && -r _) { | ||||
1831 | 59 | 1.72ms | 59 | 17.2ms | $txt .= read_cf_file($path); # spent 17.2ms making 59 calls to Mail::SpamAssassin::read_cf_file, avg 292µs/call |
1832 | } | ||||
1833 | } | ||||
1834 | |||||
1835 | 63 | 694µs | return $txt; | ||
1836 | } | ||||
1837 | |||||
1838 | |||||
1839 | sub read_cf_file { | ||||
1840 | 68 | 188µs | my($path) = @_; | ||
1841 | 68 | 156µs | my $txt = ''; | ||
1842 | |||||
1843 | 68 | 337µs | local *IN; | ||
1844 | 68 | 4.63ms | 68 | 3.72ms | if (open (IN, "<".$path)) { # spent 3.72ms making 68 calls to Mail::SpamAssassin::CORE:open, avg 55µs/call |
1845 | |||||
1846 | 136 | 281µs | my($inbuf,$nread); $txt = ''; | ||
1847 | 164 | 6.99ms | 164 | 4.22ms | while ( $nread=read(IN,$inbuf,16384) ) { $txt .= $inbuf } # spent 4.22ms making 164 calls to Mail::SpamAssassin::CORE:read, avg 26µs/call |
1848 | 68 | 145µs | defined $nread or die "error reading $path: $!"; | ||
1849 | 68 | 1.31ms | 68 | 858µs | close IN or die "error closing $path: $!"; # spent 858µs making 68 calls to Mail::SpamAssassin::CORE:close, avg 13µs/call |
1850 | 68 | 193µs | undef $inbuf; | ||
1851 | |||||
1852 | 68 | 1.55ms | $txt = "file start $path\n" . $txt; | ||
1853 | # add an extra \n in case file did not end in one. | ||||
1854 | 68 | 280µs | $txt .= "\nfile end $path\n"; | ||
1855 | |||||
1856 | 68 | 643µs | 68 | 579µs | dbg("config: read file $path"); # spent 579µs making 68 calls to Mail::SpamAssassin::Logger::dbg, avg 9µs/call |
1857 | } | ||||
1858 | else { | ||||
1859 | warn "config: cannot open \"$path\": $!\n"; | ||||
1860 | } | ||||
1861 | |||||
1862 | 68 | 1.09ms | return $txt; | ||
1863 | } | ||||
1864 | |||||
1865 | sub get_and_create_userstate_dir { | ||||
1866 | 2 | 4µs | my ($self, $dir) = @_; | ||
1867 | |||||
1868 | 2 | 4µ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 | ||||
1872 | 2 | 9µ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 | |||||
1882 | 2 | 21µs | 2 | 397µs | $fname ||= $self->first_existing_path (@default_userstate_dir); # spent 397µs making 2 calls to Mail::SpamAssassin::first_existing_path, avg 199µs/call |
1883 | |||||
1884 | # bug 4932: use the last default_userstate_dir entry if none of the others | ||||
1885 | # already exist | ||||
1886 | 2 | 4µs | $fname ||= $self->sed_path($default_userstate_dir[-1]); | ||
1887 | |||||
1888 | 2 | 6µ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 | ||||
1895 | 2 | 40µs | 2 | 15µs | my $stat_errn = stat($fname) ? 0 : 0+$!; # spent 15µs making 2 calls to Mail::SpamAssassin::CORE:stat, avg 8µs/call |
1896 | 2 | 26µs | 2 | 5µs | if ($stat_errn == 0 && !-d _) { # spent 5µs making 2 calls to Mail::SpamAssassin::CORE:ftdir, avg 2µ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 { | ||||
1902 | 4 | 25µs | 2 | 250µs | mkpath($fname, 0, 0700); 1; # spent 250µs making 2 calls to File::Path::mkpath, avg 125µs/call |
1903 | 2 | 9µs | } or do { | ||
1904 | my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat; | ||||
1905 | dbg("config: mkdir $fname failed: $eval_stat"); | ||||
1906 | }; | ||||
1907 | } | ||||
1908 | |||||
1909 | 2 | 18µs | $fname; | ||
1910 | } | ||||
1911 | |||||
1912 | =item $fullpath = $f->find_rule_support_file ($filename) | ||||
1913 | |||||
1914 | Find a rule-support file, such as C<languages> or C<triplets.txt>, | ||||
1915 | in the system-wide rules directory, and return its full path if | ||||
1916 | it 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 709µs (123+586) within Mail::SpamAssassin::find_rule_support_file which was called:
# once (123µs+586µs) by Mail::SpamAssassin::init at line 1703 | ||||
1923 | 1 | 3µs | my ($self, $filename) = @_; | ||
1924 | |||||
1925 | return $self->first_existing_path( | ||||
1926 | 16 | 164µs | 16 | 586µs | map { my $p = $_; $p =~ s{$}{/$filename}; $p } @default_rules_path ); # spent 543µ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 18µ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 | |||||
1931 | Copy default preferences file into home directory for later use and | ||||
1932 | modification, if it does not already exist and C<dont_copy_prefs> is | ||||
1933 | not set. | ||||
1934 | |||||
1935 | =cut | ||||
1936 | |||||
1937 | sub 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 107µs (65+42) within Mail::SpamAssassin::expand_name which was called 2 times, avg 53µs/call:
# 2 times (65µs+42µs) by Mail::SpamAssassin::sed_path at line 2039, avg 53µs/call | ||||
2002 | 2 | 8µs | my ($self, $name) = @_; | ||
2003 | 2 | 10µs | my $home = $self->{user_dir} || $ENV{HOME} || ''; | ||
2004 | |||||
2005 | 2 | 15µs | 2 | 36µs | if (am_running_on_windows()) { # spent 36µs making 2 calls to Mail::SpamAssassin::Util::am_running_on_windows, avg 18µ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 { | ||||
2016 | 2 | 38µs | 2 | 6µs | return $home if ($home && $home =~ /\//o); # spent 6µs making 2 calls to Mail::SpamAssassin::CORE:match, avg 3µs/call |
2017 | return (getpwnam($name))[7] if ($name ne ''); | ||||
2018 | return (getpwuid($>))[7]; | ||||
2019 | } | ||||
2020 | } | ||||
2021 | |||||
2022 | # spent 96.8ms (77.7+19.1) within Mail::SpamAssassin::sed_path which was called 3751 times, avg 26µs/call:
# 3216 times (61.6ms+458µs) by Mail::SpamAssassin::DBBasedAddrList::new_checker at line 68 of Mail/SpamAssassin/DBBasedAddrList.pm, avg 19µs/call
# 235 times (5.38ms+149µs) by Mail::SpamAssassin::BayesStore::DBM::tie_db_readonly at line 158 of Mail/SpamAssassin/BayesStore/DBM.pm, avg 24µs/call
# 234 times (5.03ms+141µ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.13ms+17.4ms) by Mail::SpamAssassin::Conf::Parser::fix_path_relative_to_current_file at line 1499 of Mail/SpamAssassin/Conf/Parser.pm, avg 388µs/call
# 7 times (562µs+919µs) by Mail::SpamAssassin::first_existing_path at line 2056, avg 212µs/call
# once (16µs+0s) by Mail::SpamAssassin::BayesStore::DBM::tie_db_writable at line 256 of Mail/SpamAssassin/BayesStore/DBM.pm | ||||
2023 | 3751 | 11.6ms | my ($self, $path) = @_; | ||
2024 | 3751 | 8.94ms | return if !defined $path; | ||
2025 | |||||
2026 | 3751 | 19.7ms | if (exists($self->{conf}->{sed_path_cache}->{$path})) { | ||
2027 | 3684 | 44.1ms | return $self->{conf}->{sed_path_cache}->{$path}; | ||
2028 | } | ||||
2029 | |||||
2030 | 67 | 167µs | my $orig_path = $path; | ||
2031 | |||||
2032 | 68 | 767µs | 69 | 287µs | $path =~ s/__local_rules_dir__/$self->{LOCAL_RULES_DIR} || ''/ges; # spent 277µ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 5µs/call |
2033 | 69 | 633µs | 71 | 197µs | $path =~ s/__local_state_dir__/$self->{LOCAL_STATE_DIR} || ''/ges; # spent 186µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call
# spent 10µs making 4 calls to Mail::SpamAssassin::CORE:substcont, avg 3µs/call |
2034 | 68 | 635µs | 69 | 206µs | $path =~ s/__def_rules_dir__/$self->{DEF_RULES_DIR} || ''/ges; # spent 200µ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 |
2035 | 67 | 594µs | 67 | 187µs | $path =~ s{__prefix__}{$self->{PREFIX} || $Config{prefix} || '/usr'}ges; # spent 187µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call |
2036 | 68 | 631µs | 70 | 531µs | $path =~ s{__userstate__}{$self->get_and_create_userstate_dir() || ''}ges; # spent 299µs making 1 call to Mail::SpamAssassin::get_and_create_userstate_dir
# spent 220µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call
# spent 12µs making 2 calls to Mail::SpamAssassin::CORE:substcont, avg 6µs/call |
2037 | 67 | 624µs | 67 | 209µs | $path =~ s{__perl_major_ver__}{$self->get_perl_major_version()}ges; # spent 209µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call |
2038 | 67 | 613µs | 67 | 202µs | $path =~ s/__version__/${VERSION}/gs; # spent 202µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call |
2039 | 69 | 634µs | 73 | 298µs | $path =~ s/^\~([^\/]*)/$self->expand_name($1)/es; # spent 179µs making 67 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call
# spent 107µs making 2 calls to Mail::SpamAssassin::expand_name, avg 53µs/call
# spent 12µs making 4 calls to Mail::SpamAssassin::CORE:substcont, avg 3µs/call |
2040 | |||||
2041 | 67 | 609µs | 67 | 17.0ms | $path = Mail::SpamAssassin::Util::untaint_file_path ($path); # spent 17.0ms making 67 calls to Mail::SpamAssassin::Util::untaint_file_path, avg 253µs/call |
2042 | 67 | 420µs | $self->{conf}->{sed_path_cache}->{$orig_path} = $path; | ||
2043 | 67 | 600µs | return $path; | ||
2044 | } | ||||
2045 | |||||
2046 | sub 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 1.95ms (245µs+1.70) within Mail::SpamAssassin::first_existing_path which was called 6 times, avg 325µs/call:
# 2 times (71µs+326µs) by Mail::SpamAssassin::get_and_create_userstate_dir at line 1882, avg 199µs/call
# once (70µs+473µs) by Mail::SpamAssassin::find_rule_support_file at line 1926
# once (47µs+444µs) by Mail::SpamAssassin::init at line 1675
# once (28µs+263µs) by Mail::SpamAssassin::init at line 1715
# once (29µs+198µs) by Mail::SpamAssassin::init at line 1678 | ||||
2053 | 6 | 12µs | my $self = shift; | ||
2054 | 6 | 11µs | my $path; | ||
2055 | 6 | 14µs | foreach my $p (@_) { | ||
2056 | 7 | 53µs | 7 | 1.48ms | $path = $self->sed_path ($p); # spent 1.51ms making 7 calls to Mail::SpamAssassin::sed_path, avg 216µs/call, recursion: max depth 1, sum of overlapping time 30µs |
2057 | 7 | 18µs | if (defined $path) { | ||
2058 | 7 | 259µs | 7 | 194µs | my($errn) = stat($path) ? 0 : 0+$!; # spent 194µs making 7 calls to Mail::SpamAssassin::CORE:stat, avg 28µs/call |
2059 | 7 | 18µs | if ($errn == ENOENT) { } # does not exist | ||
2060 | elsif ($errn) { warn "config: path \"$path\" is inaccessible: $!\n" } | ||||
2061 | 6 | 59µs | else { return $path } | ||
2062 | } | ||||
2063 | } | ||||
2064 | return; | ||||
2065 | } | ||||
2066 | |||||
2067 | ########################################################################### | ||||
2068 | |||||
2069 | # spent 718µs (33+686) within Mail::SpamAssassin::get_cf_files_in_dir which was called 2 times, avg 359µs/call:
# 2 times (33µs+686µs) by Mail::SpamAssassin::_read_cf_pre at line 1827, avg 359µs/call | ||||
2070 | 2 | 4µs | my ($self, $dir) = @_; | ||
2071 | 2 | 26µs | 2 | 686µs | return $self->_get_cf_pre_files_in_dir($dir, 'cf'); # spent 686µs making 2 calls to Mail::SpamAssassin::_get_cf_pre_files_in_dir, avg 343µs/call |
2072 | } | ||||
2073 | |||||
2074 | # spent 1.16ms (36µs+1.12) within Mail::SpamAssassin::get_pre_files_in_dir which was called 2 times, avg 579µs/call:
# 2 times (36µs+1.12ms) by Mail::SpamAssassin::_read_cf_pre at line 1827, avg 579µs/call | ||||
2075 | 2 | 5µs | my ($self, $dir) = @_; | ||
2076 | 2 | 31µs | 2 | 1.12ms | return $self->_get_cf_pre_files_in_dir($dir, 'pre'); # spent 1.12ms making 2 calls to Mail::SpamAssassin::_get_cf_pre_files_in_dir, avg 561µs/call |
2077 | } | ||||
2078 | |||||
2079 | # spent 1.81ms (855µs+952µs) within Mail::SpamAssassin::_get_cf_pre_files_in_dir which was called 4 times, avg 452µs/call:
# 2 times (491µs+631µs) by Mail::SpamAssassin::get_pre_files_in_dir at line 2076, avg 561µs/call
# 2 times (364µs+322µs) by Mail::SpamAssassin::get_cf_files_in_dir at line 2071, avg 343µs/call | ||||
2080 | 4 | 9µs | my ($self, $dir, $type) = @_; | ||
2081 | |||||
2082 | 4 | 9µ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 { | ||||
2101 | 4 | 122µs | 4 | 94µs | opendir(SA_CF_DIR, $dir) or warn "config: cannot opendir $dir: $!\n"; # spent 94µs making 4 calls to Mail::SpamAssassin::CORE:open_dir, avg 23µs/call |
2102 | 52 | 1.44ms | 93 | 779µs | my @cfs = grep { $_ ne '.' && $_ ne '..' && # spent 301µs making 9 calls to Mail::SpamAssassin::CORE:ftfile, avg 33µs/call
# spent 220µs making 4 calls to Mail::SpamAssassin::CORE:readdir, avg 55µs/call
# spent 137µs making 40 calls to Mail::SpamAssassin::CORE:regcomp, avg 3µs/call
# spent 121µs making 40 calls to Mail::SpamAssassin::CORE:match, avg 3µs/call |
2103 | /\.${type}$/i && -f "$dir/$_" } readdir(SA_CF_DIR); | ||||
2104 | 4 | 58µs | 4 | 30µs | closedir SA_CF_DIR; # spent 30µs making 4 calls to Mail::SpamAssassin::CORE:closedir, avg 7µs/call |
2105 | |||||
2106 | 26 | 183µs | 4 | 50µs | return map { "$dir/$_" } sort { $a cmp $b } @cfs; # spent 50µs making 4 calls to Mail::SpamAssassin::CORE:sort, avg 13µs/call |
2107 | } | ||||
2108 | } | ||||
2109 | |||||
2110 | ########################################################################### | ||||
2111 | |||||
2112 | sub 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 96432s (75.1ms+96432) within Mail::SpamAssassin::call_plugins which was called 1910 times, avg 50.5s/call:
# 501 times (12.9ms+140ms) by Mail::SpamAssassin::Conf::Parser::parse at line 467 of Mail/SpamAssassin/Conf/Parser.pm, avg 305µs/call
# 234 times (7.16ms+96432s) by Mail::SpamAssassin::Bayes::learn at line 120 of Mail/SpamAssassin/Bayes.pm, avg 412s/call
# 234 times (8.58ms+8.12ms) by Mail::SpamAssassin::parse at line 556, avg 71µs/call
# 234 times (10.5ms+-10.5ms) by Mail::SpamAssassin::Plugin::Bayes::_learn_trapped at line 488 of Mail/SpamAssassin/Plugin/Bayes.pm, avg 0s/call
# 234 times (15.6ms+-15.6ms) by Mail::SpamAssassin::Message::Metadata::extract at line 106 of Mail/SpamAssassin/Message/Metadata.pm, avg 0s/call
# 234 times (9.72ms+-9.72ms) by Mail::SpamAssassin::PerMsgStatus::extract_message_metadata at line 1750 of Mail/SpamAssassin/PerMsgStatus.pm, avg 0s/call
# 234 times (10.4ms+-10.4ms) by Mail::SpamAssassin::PerMsgStatus::finish at line 1659 of Mail/SpamAssassin/PerMsgStatus.pm, avg 0s/call
# once (47µs+155ms) by Mail::SpamAssassin::Conf::Parser::finish_parsing at line 942 of Mail/SpamAssassin/Conf/Parser.pm
# once (48µs+34.9ms) by Mail::SpamAssassin::Bayes::new at line 61 of Mail/SpamAssassin/Bayes.pm
# once (32µs+3.01ms) by Mail::SpamAssassin::Bayes::force_close at line 83 of Mail/SpamAssassin/Bayes.pm
# once (33µs+1.36ms) by Mail::SpamAssassin::Bayes::is_scan_available at line 153 of Mail/SpamAssassin/Bayes.pm
# once (42µs+643µs) by Mail::SpamAssassin::Conf::Parser::finish_parsing at line 853 of Mail/SpamAssassin/Conf/Parser.pm | ||||
2122 | 1910 | 3.99ms | my $self = shift; | ||
2123 | |||||
2124 | # We could potentially get called after a finish(), so just return. | ||||
2125 | 1910 | 6.44ms | return unless $self->{plugins}; | ||
2126 | |||||
2127 | # safety net in case some plugin changes global settings, Bug 6218 | ||||
2128 | 1910 | 13.6ms | local $/ = $/; # prevent underlying modules from changing the global $/ | ||
2129 | |||||
2130 | 1910 | 4.12ms | my $subname = shift; | ||
2131 | 1910 | 42.5ms | 1910 | 96432s | return $self->{plugins}->callback($subname, @_); # spent 96448s making 1910 calls to Mail::SpamAssassin::PluginHandler::callback, avg 50.5s/call, recursion: max depth 1, sum of overlapping time 15.1s |
2132 | } | ||||
2133 | |||||
2134 | ########################################################################### | ||||
2135 | |||||
2136 | sub 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 99.3ms (78.1+21.2) within Mail::SpamAssassin::find_all_addrs_in_line which was called 759 times, avg 131µs/call:
# 758 times (77.9ms+21.0ms) by Mail::SpamAssassin::Plugin::Bayes::_pre_chew_addr_header at line 1468 of Mail/SpamAssassin/Plugin/Bayes.pm, avg 131µs/call
# once (191µs+145µs) by Mail::SpamAssassin::PerMsgStatus::all_to_addrs at line 3124 of Mail/SpamAssassin/PerMsgStatus.pm | ||||
2168 | 759 | 5.33ms | my ($self, $line) = @_; | ||
2169 | |||||
2170 | # a more permissive pattern based on "dot-atom" as per RFC2822 | ||||
2171 | 759 | 1.66ms | my $ID_PATTERN = '[-a-z0-9_\+\:\=\!\#\$\%\&\*\^\?\{\}\|\~\/\.]+'; | ||
2172 | 759 | 1.64ms | my $HOST_PATTERN = '[-a-z0-9_\+\:\/]+'; | ||
2173 | |||||
2174 | 759 | 1.32ms | my @addrs; | ||
2175 | my %seen; | ||||
2176 | 759 | 36.3ms | 1542 | 14.9ms | while ($line =~ s/(?:mailto:)?\s* # spent 12.6ms making 771 calls to Mail::SpamAssassin::CORE:subst, avg 16µs/call
# spent 2.23ms making 771 calls to Mail::SpamAssassin::CORE:regcomp, avg 3µs/call |
2177 | ($ID_PATTERN \@ | ||||
2178 | $HOST_PATTERN(?:\.$HOST_PATTERN)+)//oix) | ||||
2179 | { | ||||
2180 | 755 | 2.55ms | my $addr = $1; | ||
2181 | 755 | 8.15ms | 755 | 2.37ms | $addr =~ s/^mailto://; # spent 2.37ms making 755 calls to Mail::SpamAssassin::CORE:subst, avg 3µs/call |
2182 | 1498 | 5.80ms | next if (defined ($seen{$addr})); $seen{$addr} = 1; | ||
2183 | 743 | 27.8ms | 1486 | 3.93ms | push (@addrs, $addr); # spent 2.66ms making 743 calls to Mail::SpamAssassin::CORE:subst, avg 4µs/call
# spent 1.27ms making 743 calls to Mail::SpamAssassin::CORE:regcomp, avg 2µs/call |
2184 | } | ||||
2185 | |||||
2186 | 759 | 9.85ms | return @addrs; | ||
2187 | } | ||||
2188 | |||||
2189 | ########################################################################### | ||||
2190 | |||||
2191 | # sa_die -- used to die with a useful exit code. | ||||
2192 | |||||
2193 | sub sa_die { | ||||
2194 | my $exitcode = shift; | ||||
2195 | warn @_; | ||||
2196 | exit $exitcode; | ||||
2197 | } | ||||
2198 | |||||
2199 | ########################################################################### | ||||
2200 | |||||
2201 | =item $f->copy_config ( [ $source ], [ $dest ] ) | ||||
2202 | |||||
2203 | Used for daemons to keep a persistent Mail::SpamAssassin object's | ||||
2204 | configuration correct if switching between users. Pass an associative | ||||
2205 | array reference as either $source or $dest, and set the other to 'undef' | ||||
2206 | so 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 | |||||
2222 | Note that the contents of the associative arrays should be considered | ||||
2223 | opaque by calling code. | ||||
2224 | |||||
2225 | =cut | ||||
2226 | |||||
2227 | sub 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 | |||||
2254 | Return the list of plugins currently loaded by this SpamAssassin object's | ||||
2255 | configuration; each entry in the list is an object of type | ||||
2256 | C<Mail::SpamAssassin::Plugin>. | ||||
2257 | |||||
2258 | (This API was added in SpamAssassin 3.2.0.) | ||||
2259 | |||||
2260 | =cut | ||||
2261 | |||||
2262 | sub get_loaded_plugins_list { | ||||
2263 | my ($self) = @_; | ||||
2264 | return $self->{plugins}->get_loaded_plugins_list(); | ||||
2265 | } | ||||
2266 | |||||
2267 | 1 | 39µs | 1; | ||
2268 | __END__ | ||||
# spent 858µs within Mail::SpamAssassin::CORE:close which was called 68 times, avg 13µs/call:
# 68 times (858µs+0s) by Mail::SpamAssassin::read_cf_file at line 1849, avg 13µs/call | |||||
# spent 30µs within Mail::SpamAssassin::CORE:closedir which was called 4 times, avg 7µs/call:
# 4 times (30µs+0s) by Mail::SpamAssassin::_get_cf_pre_files_in_dir at line 2104, avg 7µs/call | |||||
sub Mail::SpamAssassin::CORE:ftdir; # opcode | |||||
# spent 208µs within Mail::SpamAssassin::CORE:fteread which was called 59 times, avg 4µs/call:
# 59 times (208µs+0s) by Mail::SpamAssassin::_read_cf_pre at line 1822, avg 4µs/call | |||||
sub Mail::SpamAssassin::CORE:ftfile; # opcode | |||||
# spent 174µs within Mail::SpamAssassin::CORE:ftsize which was called 59 times, avg 3µs/call:
# 59 times (174µs+0s) by Mail::SpamAssassin::_read_cf_pre at line 1822, avg 3µs/call | |||||
# spent 187µs within Mail::SpamAssassin::CORE:match which was called 54 times, avg 3µs/call:
# 40 times (121µs+0s) by Mail::SpamAssassin::_get_cf_pre_files_in_dir at line 2102, avg 3µs/call
# 9 times (32µs+0s) by Mail::SpamAssassin::init at line 1791, avg 4µs/call
# 2 times (6µs+0s) by Mail::SpamAssassin::expand_name at line 2016, avg 3µs/call
# once (12µs+0s) by main::BEGIN@65 at line 109
# once (8µs+0s) by Mail::SpamAssassin::Version at line 127
# once (7µs+0s) by Mail::SpamAssassin::init at line 1745 | |||||
# spent 3.72ms within Mail::SpamAssassin::CORE:open which was called 68 times, avg 55µs/call:
# 68 times (3.72ms+0s) by Mail::SpamAssassin::read_cf_file at line 1844, avg 55µs/call | |||||
# spent 94µs within Mail::SpamAssassin::CORE:open_dir which was called 4 times, avg 23µs/call:
# 4 times (94µs+0s) by Mail::SpamAssassin::_get_cf_pre_files_in_dir at line 2101, avg 23µs/call | |||||
# spent 4.22ms within Mail::SpamAssassin::CORE:read which was called 164 times, avg 26µs/call:
# 164 times (4.22ms+0s) by Mail::SpamAssassin::read_cf_file at line 1847, avg 26µs/call | |||||
# spent 220µs within Mail::SpamAssassin::CORE:readdir which was called 4 times, avg 55µs/call:
# 4 times (220µs+0s) by Mail::SpamAssassin::_get_cf_pre_files_in_dir at line 2102, avg 55µs/call | |||||
# spent 3.64ms within Mail::SpamAssassin::CORE:regcomp which was called 1554 times, avg 2µs/call:
# 771 times (2.23ms+0s) by Mail::SpamAssassin::find_all_addrs_in_line at line 2176, avg 3µs/call
# 743 times (1.27ms+0s) by Mail::SpamAssassin::find_all_addrs_in_line at line 2183, avg 2µs/call
# 40 times (137µs+0s) by Mail::SpamAssassin::_get_cf_pre_files_in_dir at line 2102, avg 3µs/call | |||||
# spent 50µs within Mail::SpamAssassin::CORE:sort which was called 4 times, avg 13µs/call:
# 4 times (50µs+0s) by Mail::SpamAssassin::_get_cf_pre_files_in_dir at line 2106, avg 13µs/call | |||||
# spent 4.92ms within Mail::SpamAssassin::CORE:stat which was called 72 times, avg 68µs/call:
# 63 times (4.71ms+0s) by Mail::SpamAssassin::_read_cf_pre at line 1821, avg 75µs/call
# 7 times (194µs+0s) by Mail::SpamAssassin::first_existing_path at line 2058, avg 28µs/call
# 2 times (15µs+0s) by Mail::SpamAssassin::get_and_create_userstate_dir at line 1895, avg 8µs/call | |||||
# spent 19.3ms within Mail::SpamAssassin::CORE:subst which was called 2810 times, avg 7µs/call:
# 771 times (12.6ms+0s) by Mail::SpamAssassin::find_all_addrs_in_line at line 2176, avg 16µs/call
# 755 times (2.37ms+0s) by Mail::SpamAssassin::find_all_addrs_in_line at line 2181, avg 3µs/call
# 743 times (2.66ms+0s) by Mail::SpamAssassin::find_all_addrs_in_line at line 2183, avg 4µs/call
# 67 times (277µs+0s) by Mail::SpamAssassin::sed_path at line 2032, avg 4µs/call
# 67 times (220µs+0s) by Mail::SpamAssassin::sed_path at line 2036, avg 3µs/call
# 67 times (209µs+0s) by Mail::SpamAssassin::sed_path at line 2037, avg 3µs/call
# 67 times (202µs+0s) by Mail::SpamAssassin::sed_path at line 2038, avg 3µs/call
# 67 times (200µs+0s) by Mail::SpamAssassin::sed_path at line 2034, avg 3µs/call
# 67 times (187µs+0s) by Mail::SpamAssassin::sed_path at line 2035, avg 3µs/call
# 67 times (186µs+0s) by Mail::SpamAssassin::sed_path at line 2033, avg 3µs/call
# 67 times (179µs+0s) by Mail::SpamAssassin::sed_path at line 2039, avg 3µs/call
# 5 times (18µs+0s) by Mail::SpamAssassin::find_rule_support_file at line 1926, avg 4µs/call | |||||
# spent 75µs within Mail::SpamAssassin::CORE:substcont which was called 24 times, avg 3µs/call:
# 10 times (25µs+0s) by Mail::SpamAssassin::find_rule_support_file at line 1926, avg 2µs/call
# 4 times (12µs+0s) by Mail::SpamAssassin::sed_path at line 2039, avg 3µs/call
# 4 times (10µs+0s) by Mail::SpamAssassin::sed_path at line 2033, avg 3µs/call
# 2 times (12µ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 5µs/call
# 2 times (6µs+0s) by Mail::SpamAssassin::sed_path at line 2034, avg 3µs/call |