← Index
NYTProf Performance Profile   « line view »
For /usr/local/bin/sa-learn
  Run on Sun Nov 5 03:09:29 2017
Reported on Mon Nov 6 13:20:46 2017

Filename/usr/local/lib/perl5/site_perl/Mail/SpamAssassin/Message.pm
StatementsExecuted 896594 statements in 5.64s
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
234112.26s4.79sMail::SpamAssassin::Message::::newMail::SpamAssassin::Message::new
197112.08s2.45sMail::SpamAssassin::Message::::_parse_multipartMail::SpamAssassin::Message::_parse_multipart
114442201553ms553msMail::SpamAssassin::Message::::CORE:matchMail::SpamAssassin::Message::CORE:match (opcode)
117031156ms27.2sMail::SpamAssassin::Message::::get_body_text_array_commonMail::SpamAssassin::Message::get_body_text_array_common
70211143ms143msMail::SpamAssassin::Message::::split_into_array_of_short_linesMail::SpamAssassin::Message::split_into_array_of_short_lines
23411106ms128msMail::SpamAssassin::Message::::finishMail::SpamAssassin::Message::finish
2341163.5ms2.72sMail::SpamAssassin::Message::::parse_bodyMail::SpamAssassin::Message::parse_body
81122157.4ms57.4msMail::SpamAssassin::Message::::CORE:substMail::SpamAssassin::Message::CORE:subst (opcode)
4251151.6ms202msMail::SpamAssassin::Message::::_parse_normalMail::SpamAssassin::Message::_parse_normal
12464133.1ms33.1msMail::SpamAssassin::Message::::CORE:regcompMail::SpamAssassin::Message::CORE:regcomp (opcode)
7022232.7ms4.42sMail::SpamAssassin::Message::::receive_dateMail::SpamAssassin::Message::receive_date
7021132.3ms26.9sMail::SpamAssassin::Message::::get_rendered_body_text_arrayMail::SpamAssassin::Message::get_rendered_body_text_array
18762126.2ms26.2msMail::SpamAssassin::Message::::get_metadataMail::SpamAssassin::Message::get_metadata
2341125.7ms26.6msMail::SpamAssassin::Message::::get_all_metadataMail::SpamAssassin::Message::get_all_metadata
9364116.7ms16.7msMail::SpamAssassin::Message::::put_metadataMail::SpamAssassin::Message::put_metadata
7021116.4ms2.89sMail::SpamAssassin::Message::::find_partsMail::SpamAssassin::Message::find_parts
468228.74ms3.76sMail::SpamAssassin::Message::::extract_message_metadataMail::SpamAssassin::Message::extract_message_metadata
1118.50ms51.8msMail::SpamAssassin::Message::::BEGIN@55Mail::SpamAssassin::Message::BEGIN@55
702117.85ms7.85msMail::SpamAssassin::Message::::get_pristine_bodyMail::SpamAssassin::Message::get_pristine_body
234116.59ms18.5msMail::SpamAssassin::Message::::finish_metadataMail::SpamAssassin::Message::finish_metadata
702115.02ms5.02msMail::SpamAssassin::Message::::CORE:qrMail::SpamAssassin::Message::CORE:qr (opcode)
234114.73ms252msMail::SpamAssassin::Message::::get_visible_rendered_body_text_arrayMail::SpamAssassin::Message::get_visible_rendered_body_text_array
1114.39ms7.45msMail::SpamAssassin::Message::::BEGIN@49Mail::SpamAssassin::Message::BEGIN@49
234113.87ms133msMail::SpamAssassin::Message::::get_invisible_rendered_body_text_arrayMail::SpamAssassin::Message::get_invisible_rendered_body_text_array
234113.58ms3.58msMail::SpamAssassin::Message::::get_pristine_headerMail::SpamAssassin::Message::get_pristine_header
16113.14ms3.14msMail::SpamAssassin::Message::::CORE:unlinkMail::SpamAssassin::Message::CORE:unlink (opcode)
79111.54ms1.54msMail::SpamAssassin::Message::::DESTROYMail::SpamAssassin::Message::DESTROY
1111.29ms177msMail::SpamAssassin::Message::::BEGIN@56Mail::SpamAssassin::Message::BEGIN@56
23411994µs994µsMail::SpamAssassin::Message::::CORE:sortMail::SpamAssassin::Message::CORE:sort (opcode)
1611172µs172µsMail::SpamAssassin::Message::::CORE:closeMail::SpamAssassin::Message::CORE:close (opcode)
11144µs58µsMail::SpamAssassin::Message::::BEGIN@45Mail::SpamAssassin::Message::BEGIN@45
11127µs105µsMail::SpamAssassin::Message::::BEGIN@47Mail::SpamAssassin::Message::BEGIN@47
11123µs66µsMail::SpamAssassin::Message::::BEGIN@46Mail::SpamAssassin::Message::BEGIN@46
11123µs93µsMail::SpamAssassin::Message::::BEGIN@60Mail::SpamAssassin::Message::BEGIN@60
11122µs592µsMail::SpamAssassin::Message::::BEGIN@57Mail::SpamAssassin::Message::BEGIN@57
11121µs147µsMail::SpamAssassin::Message::::BEGIN@58Mail::SpamAssassin::Message::BEGIN@58
11118µs18µsMail::SpamAssassin::Message::::BEGIN@54Mail::SpamAssassin::Message::BEGIN@54
0000s0sMail::SpamAssassin::Message::::delete_metadataMail::SpamAssassin::Message::delete_metadata
0000s0sMail::SpamAssassin::Message::::get_bodyMail::SpamAssassin::Message::get_body
0000s0sMail::SpamAssassin::Message::::get_decoded_body_text_arrayMail::SpamAssassin::Message::get_decoded_body_text_array
0000s0sMail::SpamAssassin::Message::::get_mbox_separatorMail::SpamAssassin::Message::get_mbox_separator
0000s0sMail::SpamAssassin::Message::::get_mimepart_digestsMail::SpamAssassin::Message::get_mimepart_digests
0000s0sMail::SpamAssassin::Message::::get_pristineMail::SpamAssassin::Message::get_pristine
0000s0sMail::SpamAssassin::Message::::split_into_array_of_short_paragraphsMail::SpamAssassin::Message::split_into_array_of_short_paragraphs
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# <@LICENSE>
2# Licensed to the Apache Software Foundation (ASF) under one or more
3# contributor license agreements. See the NOTICE file distributed with
4# this work for additional information regarding copyright ownership.
5# The ASF licenses this file to you under the Apache License, Version 2.0
6# (the "License"); you may not use this file except in compliance with
7# the License. You may obtain a copy of the License at:
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16# </@LICENSE>
17
18=head1 NAME
19
20Mail::SpamAssassin::Message - decode, render, and hold an RFC-2822 message
21
22=head1 DESCRIPTION
23
24This module encapsulates an email message and allows access to the various MIME
25message parts and message metadata.
26
27The message structure, after initiating a parse() cycle, looks like this:
28
29 Message object, also top-level node in Message::Node tree
30 |
31 +---> Message::Node for other parts in MIME structure
32 | |---> [ more Message::Node parts ... ]
33 | [ others ... ]
34 |
35 +---> Message::Metadata object to hold metadata
36
37=head1 PUBLIC METHODS
38
39=over 4
40
41=cut
42
43package Mail::SpamAssassin::Message;
44
45261µs273µs
# spent 58µs (44+15) within Mail::SpamAssassin::Message::BEGIN@45 which was called: # once (44µs+15µs) by Mail::SpamAssassin::BEGIN@75 at line 45
use strict;
# spent 58µs making 1 call to Mail::SpamAssassin::Message::BEGIN@45 # spent 15µs making 1 call to strict::import
46261µs2109µs
# spent 66µs (23+43) within Mail::SpamAssassin::Message::BEGIN@46 which was called: # once (23µs+43µs) by Mail::SpamAssassin::BEGIN@75 at line 46
use warnings;
# spent 66µs making 1 call to Mail::SpamAssassin::Message::BEGIN@46 # spent 43µs making 1 call to warnings::import
472144µs2182µs
# spent 105µs (27+77) within Mail::SpamAssassin::Message::BEGIN@47 which was called: # once (27µs+77µs) by Mail::SpamAssassin::BEGIN@75 at line 47
use re 'taint';
# spent 105µs making 1 call to Mail::SpamAssassin::Message::BEGIN@47 # spent 78µs making 1 call to re::import
48
49
# spent 7.45ms (4.39+3.06) within Mail::SpamAssassin::Message::BEGIN@49 which was called: # once (4.39ms+3.06ms) by Mail::SpamAssassin::BEGIN@75 at line 52
BEGIN {
503324µs1429µs eval { require Digest::SHA; import Digest::SHA qw(sha1 sha1_hex); 1 }
# spent 429µs making 1 call to Exporter::import
51112µs or do { require Digest::SHA1; import Digest::SHA1 qw(sha1 sha1_hex) }
52178µs17.45ms}
# spent 7.45ms making 1 call to Mail::SpamAssassin::Message::BEGIN@49
53
54263µs118µs
# spent 18µs within Mail::SpamAssassin::Message::BEGIN@54 which was called: # once (18µs+0s) by Mail::SpamAssassin::BEGIN@75 at line 54
use Mail::SpamAssassin;
# spent 18µs making 1 call to Mail::SpamAssassin::Message::BEGIN@54
552426µs151.8ms
# spent 51.8ms (8.50+43.3) within Mail::SpamAssassin::Message::BEGIN@55 which was called: # once (8.50ms+43.3ms) by Mail::SpamAssassin::BEGIN@75 at line 55
use Mail::SpamAssassin::Message::Node;
# spent 51.8ms making 1 call to Mail::SpamAssassin::Message::BEGIN@55
562470µs1177ms
# spent 177ms (1.29+176) within Mail::SpamAssassin::Message::BEGIN@56 which was called: # once (1.29ms+176ms) by Mail::SpamAssassin::BEGIN@75 at line 56
use Mail::SpamAssassin::Message::Metadata;
# spent 177ms making 1 call to Mail::SpamAssassin::Message::BEGIN@56
57266µs21.16ms
# spent 592µs (22+570) within Mail::SpamAssassin::Message::BEGIN@57 which was called: # once (22µs+570µs) by Mail::SpamAssassin::BEGIN@75 at line 57
use Mail::SpamAssassin::Constants qw(:sa);
# spent 592µs making 1 call to Mail::SpamAssassin::Message::BEGIN@57 # spent 570µs making 1 call to Exporter::import
58264µs2273µs
# spent 147µs (21+126) within Mail::SpamAssassin::Message::BEGIN@58 which was called: # once (21µs+126µs) by Mail::SpamAssassin::BEGIN@75 at line 58
use Mail::SpamAssassin::Logger;
# spent 147µs making 1 call to Mail::SpamAssassin::Message::BEGIN@58 # spent 126µs making 1 call to Exporter::import
59
6029.13ms2164µs
# spent 93µs (23+71) within Mail::SpamAssassin::Message::BEGIN@60 which was called: # once (23µs+71µs) by Mail::SpamAssassin::BEGIN@75 at line 60
use vars qw(@ISA);
# spent 93µs making 1 call to Mail::SpamAssassin::Message::BEGIN@60 # spent 71µs making 1 call to vars::import
61
62122µs@ISA = qw(Mail::SpamAssassin::Message::Node);
63
64# ---------------------------------------------------------------------------
65
66=item new()
67
68Creates a Mail::SpamAssassin::Message object. Takes a hash reference
69as a parameter. The used hash key/value pairs are as follows:
70
71C<message> is either undef (which will use STDIN), a scalar - a string
72containing an entire message, a reference to such string, an array reference
73of the message with one line per array element, or either a file glob
74or an IO::File object which holds the entire contents of the message.
75
76Note: The message is expected to generally be in RFC 2822 format, optionally
77including an mbox message separator line (the "From " line) as the first line.
78
79C<parse_now> specifies whether or not to create the MIME tree
80at object-creation time or later as necessary.
81
82The I<parse_now> option, by default, is set to false (0).
83This allows SpamAssassin to not have to generate the tree of
84Mail::SpamAssassin::Message::Node objects and their related data if the
85tree is not going to be used. This is handy, for instance, when running
86C<spamassassin -d>, which only needs the pristine header and body which
87is always handled when the object is created.
88
89C<subparse> specifies how many MIME recursion levels should be parsed.
90Defaults to 20.
91
92=cut
93
94# month mappings (ripped from Util.pm)
95114µsmy %MONTH = (jan => 1, feb => 2, mar => 3, apr => 4, may => 5, jun => 6,
96 jul => 7, aug => 8, sep => 9, oct => 10, nov => 11, dec => 12);
97
98# day of week mapping (starting from zero)
9914µsmy @DAY_OF_WEEK = qw/Sun Mon Tue Wed Thu Fri Sat/ ;
100
101
# spent 4.79s (2.26+2.53) within Mail::SpamAssassin::Message::new which was called 234 times, avg 20.5ms/call: # 234 times (2.26s+2.53s) by Mail::SpamAssassin::parse at line 551 of Mail/SpamAssassin.pm, avg 20.5ms/call
sub new {
102234674µs my $class = shift;
103234646µs $class = ref($class) || $class;
104
105234563µs my($opts) = @_;
1062341.03ms my $message = defined $opts->{'message'} ? $opts->{'message'} : \*STDIN;
107234874µs my $parsenow = $opts->{'parsenow'} || 0;
108234644µs my $normalize = $opts->{'normalize'} || 0;
109
110 # Specifies whether or not to parse message/rfc822 parts into its own tree.
111 # If the # > 0, it'll subparse, otherwise it won't. By default, do twenty
112 # levels deep.
113234777µs my $subparse = defined $opts->{'subparse'} ? $opts->{'subparse'} : 20;
114
1152343.34ms2347.50ms my $self = $class->SUPER::new({normalize=>$normalize});
# spent 7.50ms making 234 calls to Mail::SpamAssassin::Message::Node::new, avg 32µs/call
116
117234776µs $self->{tmpfiles} = [];
118234838µs $self->{pristine_headers} = '';
119234994µs $self->{pristine_body} = '';
1202341.04ms $self->{mime_boundary_state} = {};
121234794µs $self->{line_ending} = "\012";
122234711µs $self->{master_deadline} = $opts->{'master_deadline'};
123234646µs $self->{suppl_attrib} = $opts->{'suppl_attrib'};
124
125234520µs if ($self->{suppl_attrib}) { # caller-provided additional information
126 # pristine_body_length is currently used by an eval test check_body_length
127 # Possible To-Do: Base the length on the @message array later down?
128 if (defined $self->{suppl_attrib}{body_size}) {
129 # Optional info provided by a caller; should reflect the original
130 # message body size if provided, and as such it may differ from the
131 # $self->{pristine_body} size, e.g. when the caller passed a truncated
132 # message to SpamAssassin, or when counting line-endings differently.
133 $self->{pristine_body_length} = $self->{suppl_attrib}{body_size};
134 }
135 if (ref $self->{suppl_attrib}{mimepart_digests}) {
136 # Optional info provided by a caller: an array of digest codes (e.g. SHA1)
137 # of each MIME part. Should reflect the original message if provided.
138 # As such it may differ from digests calculated by get_mimepart_digests(),
139 # e.g. when the caller passed a truncated message to SpamAssassin.
140 $self->{mimepart_digests} = $self->{suppl_attrib}{mimepart_digests};
141 }
142 }
143
144234582µs bless($self,$class);
145
146 # create the metadata holder class
1472343.13ms2344.87ms $self->{metadata} = Mail::SpamAssassin::Message::Metadata->new($self);
# spent 4.87ms making 234 calls to Mail::SpamAssassin::Message::Metadata::new, avg 21µs/call
148
149 # Ok, go ahead and do the message "parsing"
150
151 # protect it from abuse ...
152234460µs local $_;
153
154 # Figure out how the message was passed to us, and deal with it.
155234487µs my @message;
1562341.33ms if (ref $message eq 'ARRAY') {
15746852.9ms @message = @{$message};
158 }
159 elsif (ref($message) eq 'GLOB' || ref($message) =~ /^IO::/) {
160 if (defined fileno $message) {
161
162 # sysread+split avoids a Perl I/O bug (Bug 5985)
163 # and is faster than (<$message>) by 10..25 %
164 # (a drawback is a short-term double storage of a text in $raw_str)
165 #
166 my($nread,$raw_str); $raw_str = '';
167 while ( $nread=sysread($message, $raw_str, 16384, length $raw_str) ) { }
168 defined $nread or die "error reading: $!";
169 @message = split(/^/m, $raw_str, -1);
170
171 if ($raw_str eq '') {
172 dbg("message: empty message read");
173 } elsif (length($raw_str) > 128*1024) {
174 # ditch rarely used large chunks of allocated memory, Bug 6514
175 # http://www.perlmonks.org/?node_id=803515
176 # about 97% of mail messages are below 128 kB,
177 # about 98% of mail messages are below 256 kB (2010 statistics)
178 # dbg("message: deallocating %.2f MB", length($raw_str)/1024/1024);
179 undef $raw_str;
180 }
181 }
182 }
183 elsif (ref $message eq 'SCALAR') {
184 @message = split(/^/m, $$message, -1);
185 }
186 elsif (ref $message) {
187 dbg("message: Input is a reference of unknown type!");
188 }
189 elsif (defined $message) {
190 @message = split(/^/m, $message, -1);
191 }
192
193 # Pull off mbox and mbx separators
194 # also deal with null messages
1952344.92ms4681.62ms if (!@message) {
# spent 1.62ms making 468 calls to Mail::SpamAssassin::Message::CORE:match, avg 3µs/call
196 # bug 4884:
197 # if we get here, it means that the input was null, so fake the message
198 # content as a single newline...
199 @message = ("\n");
200 } elsif ($message[0] =~ /^From\s+(?!:)/) {
201 # careful not to confuse with obsolete syntax which allowed WSP before ':'
202 # mbox formated mailbox
203 $self->{'mbox_sep'} = shift @message;
204 } elsif ($message[0] =~ MBX_SEPARATOR) {
205 $_ = shift @message;
206
207 # Munge the mbx message separator into mbox format as a sort of
208 # de facto portability standard in SA's internals. We need to
209 # to this so that Mail::SpamAssassin::Util::parse_rfc822_date
210 # can parse the date string...
211 if (/([\s\d]\d)-([a-zA-Z]{3})-(\d{4})\s(\d{2}):(\d{2}):(\d{2})/) {
212 # $1 = day of month
213 # $2 = month (text)
214 # $3 = year
215 # $4 = hour
216 # $5 = min
217 # $6 = sec
218 my @arr = localtime(timelocal($6,$5,$4,$1,$MONTH{lc($2)}-1,$3));
219 my $address;
220 foreach (@message) {
221 if (/^From:[^<]*<([^>]+)>/) {
222 $address = $1;
223 last;
224 } elsif (/^From:\s*([^<> ]+)/) {
225 $address = $1;
226 last;
227 }
228 }
229 $self->{'mbox_sep'} = "From $address $DAY_OF_WEEK[$arr[6]] $2 $1 $4:$5:$6 $3\n";
230 }
231 }
232
233 # bug 4363
234 # Check to see if we should do CRLF instead of just LF
235 # For now, just check the first and last line and do whatever it does
2362344.06ms4681.42ms if (@message && ($message[0] =~ /\015\012/ || $message[-1] =~ /\015\012/)) {
# spent 1.42ms making 468 calls to Mail::SpamAssassin::Message::CORE:match, avg 3µs/call
237 $self->{line_ending} = "\015\012";
238 dbg("message: line ending changed to CRLF");
239 }
240
241 # Is a CRLF -> LF line endings conversion necessary?
242234972µs my $squash_crlf = $self->{line_ending} eq "\015\012";
243
244 # Go through all the header fields of the message
245234510µs my $hdr_errors = 0;
246234413µs my $header;
247234429µs for (;;) {
248 # make sure not to lose the last header field when there is no body
2491348737.8ms my $eof = !@message;
2501348739.7ms my $current = $eof ? "\n" : shift @message;
251
25213487250ms1348763.2ms if ( $current =~ /^[ \t]/ ) {
# spent 63.2ms making 13487 calls to Mail::SpamAssassin::Message::CORE:match, avg 5µs/call
253 # This wasn't useful in terms of a rule, but we may want to treat it
254 # specially at some point. Perhaps ignore it?
255 #unless ($current =~ /\S/) {
256 # $self->{'obsolete_folding_whitespace'} = 1;
257 #}
258
259584310.00ms $header = '' if !defined $header; # header starts with a continuation!?
260584316.0ms $header .= $current; # append continuations, no matter what
261584336.7ms $self->{'pristine_headers'} .= $current;
262 }
263 else { # not a continuation
264 # Ok, there's a header here, let's go ahead and add it in.
265764426.0ms if (defined $header) { # deal with a previous header field
266741083.2ms my ($key, $value) = split (/:/s, $header, 2);
267
268 # If it's not a valid header (aka: not in the form "foo:bar"), skip it.
269741029.0ms if (defined $value) {
270 # CRLF -> LF line-endings conversion if necessary
271741011.5ms $value =~ s/\015\012/\012/sg if $squash_crlf;
2727410108ms741027.1ms $key =~ s/[ \t]+\z//; # strip WSP before colon, obsolete rfc822 syn
# spent 27.1ms making 7410 calls to Mail::SpamAssassin::Message::CORE:subst, avg 4µs/call
273 # limit the length of the pairs we store
274741021.9ms if (length($key) > MAX_HEADER_KEY_LENGTH) {
275 $key = substr($key, 0, MAX_HEADER_KEY_LENGTH);
276 $self->{'truncated_header'} = 1;
277 }
278741014.3ms if (length($value) > MAX_HEADER_VALUE_LENGTH) {
279 $value = substr($value, 0, MAX_HEADER_VALUE_LENGTH);
280 $self->{'truncated_header'} = 1;
281 }
282741052.1ms74101.91s $self->header($key, $value);
# spent 1.91s making 7410 calls to Mail::SpamAssassin::Message::Node::header, avg 258µs/call
283 }
284 }
285
2867644196ms1505459.8ms if ($current =~ /^\r?$/) { # a regular end of a header section
# spent 59.8ms making 15054 calls to Mail::SpamAssassin::Message::CORE:match, avg 4µs/call
287234867µs if ($eof) {
288 $self->{'missing_head_body_separator'} = 1;
289 } else {
290234760µs $self->{'pristine_headers'} .= $current;
291 }
292234722µs last;
293 }
294 elsif ($current =~ /^--/) { # mime boundary encountered, bail out
295 $self->{'missing_head_body_separator'} = 1;
296 unshift(@message, $current);
297 last;
298 }
299 # should we assume entering a body on encountering invalid header field?
300 else {
301 # no re "strict"; # since perl 5.21.8: Ranges of ASCII printables...
3027410132ms741062.5ms if ($current !~ /^[\041-\071\073-\176]+[ \t]*:/) {
# spent 62.5ms making 7410 calls to Mail::SpamAssassin::Message::CORE:match, avg 8µs/call
303 # A field name MUST be composed of printable US-ASCII characters
304 # (i.e., characters that have values between 33 (041) and 126 (176),
305 # inclusive), except colon (072). Obsolete header field syntax
306 # allowed WSP before a colon.
307 if (++$hdr_errors <= 3) {
308 # just consume but ignore a few invalid header fields
309 } else { # enough is enough...
310 $self->{'missing_head_body_separator'} = 1;
311 unshift(@message, $current);
312 last;
313 }
314 }
315 }
316
317 # start collecting a new header field
318741016.0ms $header = $current;
319741047.5ms $self->{'pristine_headers'} .= $current;
320 }
321 }
322234608µs undef $header;
323
324 # Store the pristine body for later -- store as a copy since @message
325 # will get modified below
32623425.9ms $self->{'pristine_body'} = join('', @message);
327
3282341.04ms if (!defined $self->{pristine_body_length}) {
3292341.26ms $self->{'pristine_body_length'} = length $self->{'pristine_body'};
330 }
331
332 # iterate over lines in reverse order
333 # merge multiple blank lines into a single one
334234506µs my $start;
335234245ms for (my $cnt=$#message; $cnt>=0; $cnt--) {
336 # CRLF -> LF line-endings conversion if necessary
33771012110ms $message[$cnt] =~ s/\015\012\z/\012/ if $squash_crlf;
338
339 # line is blank
34071012930ms71012333ms if ($message[$cnt] =~ /^\s*$/) {
# spent 333ms making 71012 calls to Mail::SpamAssassin::Message::CORE:match, avg 5µs/call
341 # /^\s*$/ is about 5% faster then !/\S/, but still expensive here
342456212.7ms if (!defined $start) {
34333295.64ms $start=$cnt;
344 }
34545629.53ms next unless $cnt == 0;
346 }
347
348 # line is not blank, or we've reached the beginning
349
350 # if we've got a series of blank lines, get rid of them
35166480121ms if (defined $start) {
35233295.73ms my $max_blank_lines = 20;
35333295.86ms my $num = $start-$cnt;
35433295.56ms if ($num > $max_blank_lines) {
355638µs splice @message, $cnt+2, $num-$max_blank_lines;
356 }
357332926.0ms undef $start;
358 }
359 }
360
361 # Figure out the boundary
362234455µs my ($boundary);
3632345.52ms46848.4ms ($self->{'type'}, $boundary) = Mail::SpamAssassin::Util::parse_content_type($self->header('content-type'));
# spent 34.3ms making 234 calls to Mail::SpamAssassin::Util::parse_content_type, avg 147µs/call # spent 14.1ms making 234 calls to Mail::SpamAssassin::Message::Node::header, avg 60µs/call
3642342.77ms2342.57ms dbg("message: main message type: ".$self->{'type'});
# spent 2.57ms making 234 calls to Mail::SpamAssassin::Logger::dbg, avg 11µs/call
365
366# dbg("message: \$message[0]: \"" . $message[0] . "\"");
367
368 # bug 6845: if main message type is multipart and the message body does not begin with
369 # either a blank line or the boundary (if defined), insert a blank line
370 # to ensure proper parsing - do not consider MIME headers at the beginning of the body
371 # to be part of the message headers.
37223420.6ms4271.85ms if ($self->{'type'} =~ /^multipart\//i && $#message > 0 && $message[0] =~ /\S/)
# spent 1.85ms making 427 calls to Mail::SpamAssassin::Message::CORE:match, avg 4µs/call
373 {
37417013.1ms3409.80ms if (!defined $boundary || $message[0] !~ /^--\Q$boundary\E/)
# spent 9.20ms making 170 calls to Mail::SpamAssassin::Message::CORE:regcomp, avg 54µs/call # spent 596µs making 170 calls to Mail::SpamAssassin::Message::CORE:match, avg 4µs/call
375 {
37639260µs39362µs dbg("message: Inserting blank line at top of body to ensure correct multipart MIME parsing");
# spent 362µs making 39 calls to Mail::SpamAssassin::Logger::dbg, avg 9µs/call
37739193µs unshift(@message, "\012");
378 }
379 }
380
381# dbg("message: \$message[0]: \"" . $message[0] . "\"");
382# dbg("message: \$message[1]: \"" . $message[1] . "\"");
383
384 # parse queue, simple array of parts to parse:
385 # 0: part object, already in the tree
386 # 1: boundary used to focus body parsing
387 # 2: message content
388 # 3: how many MIME subparts to parse down
389 #
3902341.92ms $self->{'parse_queue'} = [ [ $self, $boundary, \@message, $subparse ] ];
391
392 # If the message does need to get parsed, save off a copy of the body
393 # in a format we can easily parse later so we don't have to rip from
394 # pristine_body ... If we do want to parse now, go ahead and do so ...
395 #
396234461µs if ($parsenow) {
397 $self->parse_body();
398 }
399
4002344.92ms $self;
401}
402
403# ---------------------------------------------------------------------------
404
405=item find_parts()
406
407Used to search the tree for specific MIME parts. See
408I<Mail::SpamAssassin::Message::Node> for more details.
409
410=cut
411
412# Used to find any MIME parts whose simple content-type matches a given regexp
413# Searches it's own and any children parts. Returns an array of MIME
414# objects which match.
415#
416
# spent 2.89s (16.4ms+2.87) within Mail::SpamAssassin::Message::find_parts which was called 702 times, avg 4.12ms/call: # 702 times (16.4ms+2.87s) by Mail::SpamAssassin::Message::get_body_text_array_common at line 1111, avg 4.12ms/call
sub find_parts {
4177021.55ms my $self = shift;
418
419 # ok, we need to do the parsing now...
4207023.56ms2342.72s $self->parse_body() if (exists $self->{'parse_queue'});
# spent 2.72s making 234 calls to Mail::SpamAssassin::Message::parse_body, avg 11.6ms/call
421
422 # and pass through to the Message::Node version of the method
42370211.5ms702153ms return $self->SUPER::find_parts(@_);
# spent 153ms making 702 calls to Mail::SpamAssassin::Message::Node::find_parts, avg 217µs/call
424}
425
426# ---------------------------------------------------------------------------
427
428=item get_pristine_header()
429
430Returns pristine headers of the message. If no specific header name
431is given as a parameter (case-insensitive), then all headers will be
432returned as a scalar, including the blank line at the end of the headers.
433
434If called in an array context, an array will be returned with each
435specific header in a different element. In a scalar context, the last
436specific header is returned.
437
438ie: If 'Subject' is specified as the header, and there are 2 Subject
439headers in a message, the last/bottom one in the message is returned in
440scalar context or both are returned in array context.
441
442Btw, returning the last header field (not the first) happens to be consistent
443with DKIM signatures, which search for and cover multiple header fields
444bottom-up according to the 'h' tag. Let's keep it this way.
445
446Note: the returned header will include the ending newline and any embedded
447whitespace folding.
448
449=cut
450
451
# spent 3.58ms within Mail::SpamAssassin::Message::get_pristine_header which was called 234 times, avg 15µs/call: # 234 times (3.58ms+0s) by Mail::SpamAssassin::PerMsgStatus::_get at line 1913 of Mail/SpamAssassin/PerMsgStatus.pm, avg 15µs/call
sub get_pristine_header {
452234548µs my ($self, $hdr) = @_;
453
45423410.3ms return $self->{pristine_headers} if !defined $hdr || $hdr eq '';
455 my(@ret) =
456 $self->{pristine_headers} =~ /^\Q$hdr\E[ \t]*:[ \t]*(.*?\n(?![ \t]))/smgi;
457 # taintedness is retained by "use re 'taint'" (fix in bug 5283 now redundant)
458 if (!@ret) {
459 return $self->get_header($hdr);
460 } elsif (wantarray) {
461 return @ret;
462 } else {
463 return $ret[-1];
464 }
465}
466
467=item get_mbox_separator()
468
469Returns the mbox separator found in the message, or undef if there
470wasn't one.
471
472=cut
473
474sub get_mbox_separator {
475 return $_[0]->{mbox_sep};
476}
477
478=item get_body()
479
480Returns an array of the pristine message body, one line per array element.
481
482=cut
483
484sub get_body {
485 my ($self) = @_;
486 my @ret = split(/^/m, $self->{pristine_body});
487 return \@ret;
488}
489
490# ---------------------------------------------------------------------------
491
492=item get_pristine()
493
494Returns a scalar of the entire pristine message.
495
496=cut
497
498sub get_pristine {
499 my ($self) = @_;
500 return $self->{pristine_headers} . $self->{pristine_body};
501}
502
503=item get_pristine_body()
504
505Returns a scalar of the pristine message body.
506
507=cut
508
509
# spent 7.85ms within Mail::SpamAssassin::Message::get_pristine_body which was called 702 times, avg 11µs/call: # 702 times (7.85ms+0s) by Mail::SpamAssassin::Plugin::Bayes::get_msgid at line 998 of Mail/SpamAssassin/Plugin/Bayes.pm, avg 11µs/call
sub get_pristine_body {
5107021.71ms my ($self) = @_;
5117027.49ms return $self->{pristine_body};
512}
513
514# ---------------------------------------------------------------------------
515
516=item extract_message_metadata($permsgstatus)
517
518=cut
519
520
# spent 3.76s (8.74ms+3.75) within Mail::SpamAssassin::Message::extract_message_metadata which was called 468 times, avg 8.02ms/call: # 234 times (6.12ms+3.75s) by Mail::SpamAssassin::PerMsgStatus::extract_message_metadata at line 1703 of Mail/SpamAssassin/PerMsgStatus.pm, avg 16.0ms/call # 234 times (2.62ms+0s) by Mail::SpamAssassin::Plugin::Bayes::get_body_from_msg at line 1025 of Mail/SpamAssassin/Plugin/Bayes.pm, avg 11µs/call
sub extract_message_metadata {
5214681.03ms my ($self, $permsgstatus) = @_;
522
523 # do this only once per message, it can be expensive
5244683.01ms return if $self->{already_extracted_metadata};
5252341.17ms $self->{already_extracted_metadata} = 1;
526
5272343.79ms2343.75s $self->{metadata}->extract ($self, $permsgstatus);
# spent 3.75s making 234 calls to Mail::SpamAssassin::Message::Metadata::extract, avg 16.0ms/call
528}
529
530# ---------------------------------------------------------------------------
531
532=item $str = get_metadata($hdr)
533
534=cut
535
536
# spent 26.2ms within Mail::SpamAssassin::Message::get_metadata which was called 1876 times, avg 14µs/call: # 1642 times (23.5ms+0s) by Mail::SpamAssassin::PerMsgStatus::_get at line 1987 of Mail/SpamAssassin/PerMsgStatus.pm, avg 14µs/call # 234 times (2.69ms+0s) by Mail::SpamAssassin::PerMsgStatus::extract_message_metadata at line 1741 of Mail/SpamAssassin/PerMsgStatus.pm, avg 11µs/call
sub get_metadata {
53718764.51ms my ($self, $hdr) = @_;
53818764.08ms if (!$self->{metadata}) {
539 warn "metadata: oops! get_metadata() called after finish_metadata()"; return;
540 }
541# dbg("message: get_metadata - %s: %s", $hdr, defined $_ ? $_ : '<undef>')
542# for $self->{metadata}->{strings}->{lc $hdr};
543
544187622.5ms $self->{metadata}->{strings}->{lc $hdr};
545}
546
547=item put_metadata($hdr, $text)
548
549=cut
550
551
# spent 16.7ms within Mail::SpamAssassin::Message::put_metadata which was called 936 times, avg 18µs/call: # 234 times (4.33ms+0s) by Mail::SpamAssassin::Message::Metadata::parse_received_headers at line 280 of Mail/SpamAssassin/Message/Metadata/Received.pm, avg 19µs/call # 234 times (4.21ms+0s) by Mail::SpamAssassin::Message::Metadata::parse_received_headers at line 282 of Mail/SpamAssassin/Message/Metadata/Received.pm, avg 18µs/call # 234 times (4.15ms+0s) by Mail::SpamAssassin::Message::Metadata::parse_received_headers at line 284 of Mail/SpamAssassin/Message/Metadata/Received.pm, avg 18µs/call # 234 times (4.01ms+0s) by Mail::SpamAssassin::Message::Metadata::parse_received_headers at line 286 of Mail/SpamAssassin/Message/Metadata/Received.pm, avg 17µs/call
sub put_metadata {
5529364.32ms my ($self, $hdr, $text) = @_;
5539362.04ms if (!$self->{metadata}) {
554 warn "metadata: oops! put_metadata() called after finish_metadata()"; return;
555 }
556# dbg("message: put_metadata - %s: %s", $hdr, $text);
55793613.0ms $self->{metadata}->{strings}->{lc $hdr} = $text;
558}
559
560=item delete_metadata($hdr)
561
562=cut
563
564sub delete_metadata {
565 my ($self, $hdr) = @_;
566 if (!$self->{metadata}) {
567 warn "metadata: oops! delete_metadata() called after finish_metadata()"; return;
568 }
569 delete $self->{metadata}->{strings}->{lc $hdr};
570}
571
572=item $str = get_all_metadata()
573
574=cut
575
576
# spent 26.6ms (25.7+994µs) within Mail::SpamAssassin::Message::get_all_metadata which was called 234 times, avg 114µs/call: # 234 times (25.7ms+994µs) by Mail::SpamAssassin::Plugin::Bayes::_tokenize_headers at line 1304 of Mail/SpamAssassin/Plugin/Bayes.pm, avg 114µs/call
sub get_all_metadata {
577234635µs my ($self) = @_;
578
579234877µs if (!$self->{metadata}) {
580 warn "metadata: oops! get_all_metadata() called after finish_metadata()"; return;
581 }
582234452µs my @ret;
583234931µs my $keys_ref = $self->{metadata}->{strings};
5842344.70ms234994µs foreach my $key (sort keys %$keys_ref) {
# spent 994µs making 234 calls to Mail::SpamAssassin::Message::CORE:sort, avg 4µs/call
5859365.22ms my $val = $keys_ref->{$key};
5869361.65ms $val = '' if !defined $val;
5879369.16ms push (@ret, "$key: $val\n");
588 }
5892343.66ms return (wantarray ? @ret : join('', @ret));
590}
591
592# ---------------------------------------------------------------------------
593
594=item finish_metadata()
595
596Destroys the metadata for this message. Once a message has been
597scanned fully, the metadata is no longer required. Destroying
598this will free up some memory.
599
600=cut
601
602
# spent 18.5ms (6.59+11.9) within Mail::SpamAssassin::Message::finish_metadata which was called 234 times, avg 79µs/call: # 234 times (6.59ms+11.9ms) by Mail::SpamAssassin::Message::finish at line 620, avg 79µs/call
sub finish_metadata {
603234513µs my ($self) = @_;
6042342.60ms if (defined ($self->{metadata})) {
6052342.84ms23411.9ms $self->{metadata}->finish();
# spent 11.9ms making 234 calls to Mail::SpamAssassin::Message::Metadata::finish, avg 51µs/call
606234818µs delete $self->{metadata};
607 }
608}
609
610=item finish()
611
612Clean up an object so that it can be destroyed.
613
614=cut
615
616
# spent 128ms (106+21.8) within Mail::SpamAssassin::Message::finish which was called 234 times, avg 547µs/call: # 234 times (106ms+21.8ms) by main::wanted at line 588 of /usr/local/bin/sa-learn, avg 547µs/call
sub finish {
617234547µs my ($self) = @_;
618
619 # Clean ourself up
6202342.06ms23418.5ms $self->finish_metadata();
# spent 18.5ms making 234 calls to Mail::SpamAssassin::Message::finish_metadata, avg 79µs/call
621
622 # These will only be in the root Message node
6232341.17ms delete $self->{'mime_boundary_state'};
624234556µs delete $self->{'mbox_sep'};
625234569µs delete $self->{'normalize'};
6262341.04ms delete $self->{'pristine_body'};
627234944µs delete $self->{'pristine_headers'};
628234740µs delete $self->{'line_ending'};
629234728µs delete $self->{'missing_head_body_separator'};
630
6312341.12ms my @toclean = ( $self );
632
633 # Go ahead and clean up all of the Message::Node parts
6342347.57ms while (my $part = shift @toclean) {
635 # bug 5557: windows requires tmp file be closed before it can be rm'd
6366222.45ms if (ref $part->{'raw'} eq 'GLOB') {
63716358µs16172µs close($part->{'raw'}) or die "error closing input file: $!";
# spent 172µs making 16 calls to Mail::SpamAssassin::Message::CORE:close, avg 11µs/call
638 }
639
640 # bug 5858: avoid memory leak with deep MIME structure
6416221.51ms if (defined ($part->{metadata})) {
642 $part->{metadata}->finish();
643 delete $part->{metadata};
644 }
645
64662213.3ms delete $part->{'headers'};
64762214.2ms delete $part->{'raw_headers'};
6486226.08ms delete $part->{'header_order'};
64962232.3ms delete $part->{'raw'};
6506221.86ms delete $part->{'decoded'};
6516221.62ms delete $part->{'rendered'};
6526221.52ms delete $part->{'visible_rendered'};
6536221.43ms delete $part->{'invisible_rendered'};
6546221.73ms delete $part->{'type'};
6556221.35ms delete $part->{'rendered_type'};
656
657 # if there are children nodes, add them to the queue of nodes to clean up
6586221.66ms if (exists $part->{'body_parts'}) {
6593941.55ms push(@toclean, @{$part->{'body_parts'}});
660197542µs delete $part->{'body_parts'};
661 }
662 }
663
664 # delete temporary files
6652343.07ms if ($self->{'tmpfiles'}) {
6664682.12ms for my $fn (@{$self->{'tmpfiles'}}) {
667163.36ms163.14ms unlink($fn) or warn "cannot unlink $fn: $!";
# spent 3.14ms making 16 calls to Mail::SpamAssassin::Message::CORE:unlink, avg 196µs/call
668 }
669234674µs delete $self->{'tmpfiles'};
670 }
671}
672
673# also use a DESTROY method, just to ensure (as much as possible) that
674# temporary files are deleted even if the finish() method is omitted
675
# spent 1.54ms within Mail::SpamAssassin::Message::DESTROY which was called 79 times, avg 19µs/call: # 79 times (1.54ms+0s) by main::wanted at line 589 of /usr/local/bin/sa-learn, avg 19µs/call
sub DESTROY {
67679195µs my $self = shift;
677 # best practices: prevent potential calls to eval and to system routines
678 # in code of a DESTROY method from clobbering global variables $@ and $!
67979575µs local($@,$!); # keep outer error handling unaffected by DESTROY
680791.06ms if ($self->{'tmpfiles'}) {
681 for my $fn (@{$self->{'tmpfiles'}}) {
682 unlink($fn) or dbg("message: cannot unlink $fn: $!");
683 }
684 }
685}
686
687# ---------------------------------------------------------------------------
688
689=item receive_date()
690
691Return a time_t value with the received date of the current message,
692or current time if received time couldn't be determined.
693
694=cut
695
696
# spent 4.42s (32.7ms+4.39) within Mail::SpamAssassin::Message::receive_date which was called 702 times, avg 6.29ms/call: # 468 times (13.9ms+2.92s) by Mail::SpamAssassin::Plugin::TxRep::check_senders_reputation at line 1249 of Mail/SpamAssassin/Plugin/TxRep.pm, avg 6.27ms/call # 234 times (18.8ms+1.46s) by Mail::SpamAssassin::Plugin::Bayes::_learn_trapped at line 465 of Mail/SpamAssassin/Plugin/Bayes.pm, avg 6.34ms/call
sub receive_date {
6977021.76ms my($self) = @_;
698
69970218.5ms14044.39s return Mail::SpamAssassin::Util::receive_date(scalar $self->get_all_headers(0,1));
# spent 3.92s making 702 calls to Mail::SpamAssassin::Message::Node::get_all_headers, avg 5.59ms/call # spent 462ms making 702 calls to Mail::SpamAssassin::Util::receive_date, avg 659µs/call
700}
701
702# ---------------------------------------------------------------------------
703
704=back
705
706=head1 PARSING METHODS, NON-PUBLIC
707
708These methods take a RFC2822-esque formatted message and create a tree
709with all of the MIME body parts included. Those parts will be decoded
710as necessary, and text/html parts will be rendered into a standard text
711format, suitable for use in SpamAssassin.
712
713=over 4
714
715=item parse_body()
716
717parse_body() passes the body part that was passed in onto the
718correct part parser, either _parse_multipart() for multipart/* parts,
719or _parse_normal() for everything else. Multipart sections become the
720root of sub-trees, while everything else becomes a leaf in the tree.
721
722For multipart messages, the first call to parse_body() doesn't create a
723new sub-tree and just uses the parent node to contain children. All other
724calls to parse_body() will cause a new sub-tree root to be created and
725children will exist underneath that root. (this is just so the tree
726doesn't have a root node which points at the actual root node ...)
727
728=cut
729
730
# spent 2.72s (63.5ms+2.66) within Mail::SpamAssassin::Message::parse_body which was called 234 times, avg 11.6ms/call: # 234 times (63.5ms+2.66s) by Mail::SpamAssassin::Message::find_parts at line 420, avg 11.6ms/call
sub parse_body {
731234544µs my($self) = @_;
732
733 # This shouldn't happen, but just in case, abort.
734234666µs return unless (exists $self->{'parse_queue'});
735
7362341.59ms2341.50ms dbg("message: ---- MIME PARSER START ----");
# spent 1.50ms making 234 calls to Mail::SpamAssassin::Logger::dbg, avg 6µs/call
737
738109032.5ms while (my $toparse = shift @{$self->{'parse_queue'}}) {
739 # multipart sections are required to have a boundary set ... If this
740 # one doesn't, assume it's malformed and send it to be parsed as a
741 # non-multipart section
742 #
7436222.48ms my ($msg, $boundary, $body, $subparse) = @$toparse;
744
74562210.5ms6222.62ms if ($msg->{'type'} =~ m{^multipart/}i && defined $boundary && $subparse > 0) {
# spent 2.62ms making 622 calls to Mail::SpamAssassin::Message::CORE:match, avg 4µs/call
7461971.89ms1972.45s $self->_parse_multipart($toparse);
# spent 2.45s making 197 calls to Mail::SpamAssassin::Message::_parse_multipart, avg 12.4ms/call
747 }
748 else {
749 # If it's not multipart, go ahead and just deal with it.
7504253.32ms425202ms $self->_parse_normal($toparse);
# spent 202ms making 425 calls to Mail::SpamAssassin::Message::_parse_normal, avg 474µs/call
751
752 # bug 5041: process message/*, but exclude message/partial content types
7534254.79ms4251.53ms if ($msg->{'type'} =~ m{^message/(?!partial\z)}i && $subparse > 0)
# spent 1.53ms making 425 calls to Mail::SpamAssassin::Message::CORE:match, avg 4µs/call
754 {
755 # Just decode the part, but we don't need the resulting string here.
756 $msg->decode(0);
757
758 # bug 7125: decode and parse only message/rfc822 or message/global,
759 # but do not treat other message/* content types (like the ones listed
760 # here) as a message consisting of a header and a body, as they are not:
761 # message/delivery-status, message/global-delivery-status,
762 # message/feedback-report, message/global-headers,
763 # message/global-disposition-notification,
764 # message/disposition-notification, (and message/partial)
765
766 # bug 5051, bug 3748: check $msg->{decoded}: sometimes message/* parts
767 # have no content, and we get stuck waiting for STDIN, which is bad. :(
768
769 if ($msg->{'type'} =~ m{^message/(?:rfc822|global)\z}i &&
770 defined $msg->{'decoded'} && $msg->{'decoded'} ne '')
771 {
772 # Ok, so this part is still semi-recursive, since M::SA::Message
773 # calls M::SA::Message, but we don't subparse the new message,
774 # and pull a sneaky "steal our child's queue" maneuver to deal
775 # with it on our own time. Reference the decoded array directly
776 # since it's faster.
777 #
778 my $msg_obj = Mail::SpamAssassin::Message->new({
779 message => $msg->{'decoded'},
780 parsenow => 0,
781 normalize => $self->{normalize},
782 subparse => $subparse - 1,
783 });
784
785 # Add the new message to the current node
786 $msg->add_body_part($msg_obj);
787
788 # now this is the sneaky bit ... steal the sub-message's parse_queue
789 # and add it to ours. then we'll handle the sub-message in our
790 # normal loop and get all the glory. muhaha. :)
791 push(@{$self->{'parse_queue'}}, @{$msg_obj->{'parse_queue'}});
792 delete $msg_obj->{'parse_queue'};
793
794 # Ok, we've subparsed, so go ahead and remove the raw and decoded
795 # data because we won't need them anymore (the tree under this part
796 # will have that data)
797 if (ref $msg->{'raw'} eq 'GLOB') {
798 # Make sure we close it if it's a temp file -- Bug 5166
799 close($msg->{'raw'})
800 or die "error closing input file: $!";
801 }
802
803 delete $msg->{'raw'};
804
805 delete $msg->{'decoded'};
806 }
807 }
808 }
809 }
810
8112341.64ms2341.43ms dbg("message: ---- MIME PARSER END ----");
# spent 1.43ms making 234 calls to Mail::SpamAssassin::Logger::dbg, avg 6µs/call
812
813 # we're done parsing, so remove the queue variable
8142342.48ms delete $self->{'parse_queue'};
815}
816
817=item _parse_multipart()
818
819Generate a root node, and for each child part call parse_body()
820to generate the tree.
821
822=cut
823
824
# spent 2.45s (2.08+365ms) within Mail::SpamAssassin::Message::_parse_multipart which was called 197 times, avg 12.4ms/call: # 197 times (2.08s+365ms) by Mail::SpamAssassin::Message::parse_body at line 746, avg 12.4ms/call
sub _parse_multipart {
825197469µs my($self, $toparse) = @_;
826
8273941.52ms my ($msg, $boundary, $body, $subparse) = @{$toparse};
828
829 # we're not supposed to be a leaf, so prep ourselves
830197810µs $msg->{'body_parts'} = [];
831
832 # the next set of objects will be one level deeper
833197448µs $subparse--;
834
8351972.01ms1971.42ms dbg("message: parsing multipart, got boundary: ".(defined $boundary ? $boundary : ''));
# spent 1.42ms making 197 calls to Mail::SpamAssassin::Logger::dbg, avg 7µs/call
836
837 # NOTE: The MIME boundary REs here are very specific to be mostly RFC 1521
838 # compliant, but also allow possible malformations to still work. Please
839 # see Bugzilla bug 3749 for more information before making any changes!
840
841 # ignore preamble per RFC 1521, unless there's no boundary ...
842197908µs if ( defined $boundary ) {
843197356µs my $line;
8443941.22ms my $tmp_line = @{$body};
8451971.52ms for ($line=0; $line < $tmp_line; $line++) {
846# dbg("message: multipart line $line: \"" . $body->[$line] . "\"");
847 # specifically look for an opening boundary
84833614.1ms3948.85ms if (substr($body->[$line],0,2) eq '--' # triage
# spent 7.97ms making 197 calls to Mail::SpamAssassin::Message::CORE:regcomp, avg 40µs/call # spent 882µs making 197 calls to Mail::SpamAssassin::Message::CORE:match, avg 4µs/call
849 && $body->[$line] =~ /^--\Q$boundary\E\s*$/) {
850 # Make note that we found the opening boundary
8511971.04ms $self->{mime_boundary_state}->{$boundary} = 1;
852
853 # if the line after the opening boundary isn't a header, flag it.
854 # we need to make sure that there's actually another line though.
855 # no re "strict"; # since perl 5.21.8: Ranges of ASCII printables...
8561973.19ms1971.14ms if ($line+1 < $tmp_line && $body->[$line+1] !~ /^[\041-\071\073-\176]+:/) {
# spent 1.14ms making 197 calls to Mail::SpamAssassin::Message::CORE:match, avg 6µs/call
857 $self->{'missing_mime_headers'} = 1;
858 }
859
860197742µs last;
861 }
862 }
863
864 # Found a boundary, ignore the preamble
865197796µs if ( $line < $tmp_line ) {
8663941.72ms splice @{$body}, 0, $line+1;
867 }
868
869 # Else, there's no boundary, so leave the whole part...
870 }
871
872 # prepare a new tree node
8731972.94ms1975.36ms my $part_msg = Mail::SpamAssassin::Message::Node->new({ normalize=>$self->{normalize} });
# spent 5.36ms making 197 calls to Mail::SpamAssassin::Message::Node::new, avg 27µs/call
874197436µs my $in_body = 0;
875197379µs my $header;
876 my $part_array;
877 my $found_end_boundary;
878
8793941.24ms my $line_count = @{$body};
8803945.91ms foreach ( @{$body} ) {
881 # if we're on the last body line, or we find any boundary marker,
882 # deal with the mime part;
883 # a triage before an unlikely-to-match regexp avoids a CPU hotspot
88467383353ms98211.9ms $found_end_boundary = defined $boundary && substr($_,0,2) eq '--'
# spent 9.16ms making 491 calls to Mail::SpamAssassin::Message::CORE:regcomp, avg 19µs/call # spent 2.72ms making 491 calls to Mail::SpamAssassin::Message::CORE:match, avg 6µs/call
885 && /^--\Q$boundary\E(?:--)?\s*$/;
88667383111ms if ( --$line_count == 0 || $found_end_boundary ) {
8873881.62ms my $line = $_; # remember the last line
888
889 # If at last line and no end boundary found, the line belongs to body
890 # TODO:
891 # Is $self->{mime_boundary_state}->{$boundary}-- needed here?
892 # Could "missing end boundary" be a useful rule? Mark it somewhere?
893 # If SA processed truncated message from amavis etc, this could also
894 # be hit legimately..
8953882.27ms if (!$found_end_boundary) {
896 # TODO: This is duplicate code from few pages down below..
897 while (length ($_) > MAX_BODY_LINE_LENGTH) {
898 push (@{$part_array}, substr($_, 0, MAX_BODY_LINE_LENGTH)."\n");
899 substr($_, 0, MAX_BODY_LINE_LENGTH) = '';
900 }
901 push ( @{$part_array}, $_ );
902 }
903 # per rfc 1521, the CRLF before the boundary is part of the boundary:
904 # NOTE: The CRLF preceding the encapsulation line is conceptually
905 # attached to the boundary so that it is possible to have a part
906 # that does not end with a CRLF (line break). Body parts that must
907 # be considered to end with line breaks, therefore, must have two
908 # CRLFs preceding the encapsulation line, the first of which is part
909 # of the preceding body part, and the second of which is part of the
910 # encapsulation boundary.
911 elsif ($part_array) {
9123881.77ms chomp( $part_array->[-1] ); # trim the CRLF that's part of the boundary
9136812.70ms splice @{$part_array}, -1 if ( $part_array->[-1] eq '' ); # blank line for the boundary only ...
914 }
915 else {
916 # Invalid parts can have no body, so fake in a blank body
917 # in that case.
918 $part_array = [];
919 }
920
921388684µs my($p_boundary);
9223887.74ms776113ms ($part_msg->{'type'}, $p_boundary) = Mail::SpamAssassin::Util::parse_content_type($part_msg->header('content-type'));
# spent 90.3ms making 388 calls to Mail::SpamAssassin::Util::parse_content_type, avg 233µs/call # spent 23.1ms making 388 calls to Mail::SpamAssassin::Message::Node::header, avg 60µs/call
923388993µs $p_boundary ||= $boundary;
9243884.78ms3884.66ms dbg("message: found part of type ".$part_msg->{'type'}.", boundary: ".(defined $p_boundary ? $p_boundary : ''));
# spent 4.66ms making 388 calls to Mail::SpamAssassin::Logger::dbg, avg 12µs/call
925
926 # we've created a new node object, so add it to the queue along with the
927 # text that belongs to that part, then add the new part to the current
928 # node to create the tree.
9297765.03ms push(@{$self->{'parse_queue'}}, [ $part_msg, $p_boundary, $part_array, $subparse ]);
9303883.21ms38815.7ms $msg->add_body_part($part_msg);
# spent 15.7ms making 388 calls to Mail::SpamAssassin::Message::Node::add_body_part, avg 41µs/call
931
932 # rfc 1521 says /^--boundary--$/, some MUAs may just require /^--boundary--/
933 # but this causes problems with horizontal lines when the boundary is
934 # made up of dashes as well, etc.
9353881.25ms if (defined $boundary) {
936 # no re "strict"; # since perl 5.21.8: Ranges of ASCII printables...
93738817.1ms9679.46ms if ($line =~ /^--\Q${boundary}\E--\s*$/) {
# spent 6.80ms making 388 calls to Mail::SpamAssassin::Message::CORE:regcomp, avg 18µs/call # spent 2.66ms making 579 calls to Mail::SpamAssassin::Message::CORE:match, avg 5µs/call
938 # Make a note that we've seen the end boundary
939197643µs $self->{mime_boundary_state}->{$boundary}--;
9401971.04ms last;
941 }
942 elsif ($line_count && $body->[-$line_count] !~ /^[\041-\071\073-\176]+:/) {
943 # if we aren't on an end boundary and there are still lines left, it
944 # means we hit a new start boundary. therefore, the next line ought
945 # to be a mime header. if it's not, mark it.
946 $self->{'missing_mime_headers'} = 1;
947 }
948 }
949
950 # make sure we start with a new clean node
951191431µs $in_body = 0;
9521912.16ms1915.56ms $part_msg = Mail::SpamAssassin::Message::Node->new({ normalize=>$self->{normalize} });
# spent 5.56ms making 191 calls to Mail::SpamAssassin::Message::Node::new, avg 29µs/call
953191435µs undef $part_array;
954191467µs undef $header;
955
956191742µs next;
957 }
958
95966995104ms if (!$in_body) {
960 # s/\s+$//; # bug 5127: don't clean this up (yet)
961 # no re "strict"; # since perl 5.21.8: Ranges of ASCII printables...
962123327.6ms16859.98ms if (/^[\041-\071\073-\176]+[ \t]*:/) {
# spent 9.98ms making 1685 calls to Mail::SpamAssassin::Message::CORE:match, avg 6µs/call
9637812.88ms if ($header) {
9643935.18ms my ( $key, $value ) = split ( /:\s*/, $header, 2 );
9653933.08ms39396.4ms $part_msg->header( $key, $value );
# spent 96.4ms making 393 calls to Mail::SpamAssassin::Message::Node::header, avg 245µs/call
966 }
9677811.99ms $header = $_;
9687811.73ms next;
969 }
970 elsif (/^[ \t]/ && $header) {
971 # $_ =~ s/^\s*//; # bug 5127, again
97264260µs $header .= $_;
97364181µs next;
974 }
975 else {
9763881.45ms if ($header) {
9773884.77ms my ( $key, $value ) = split ( /:\s*/, $header, 2 );
9783883.50ms38878.7ms $part_msg->header( $key, $value );
# spent 78.7ms making 388 calls to Mail::SpamAssassin::Message::Node::header, avg 203µs/call
979 }
9803881.29ms $in_body = 1;
981
982 # if there's a blank line separator, that's good. if there isn't,
983 # it's a body line, so drop through.
9843884.93ms3882.21ms if (/^\r?$/) {
# spent 2.21ms making 388 calls to Mail::SpamAssassin::Message::CORE:match, avg 6µs/call
985388879µs next;
986 }
987 else {
988 $self->{'missing_mime_head_body_separator'} = 1;
989 }
990 }
991 }
992
993 # we run into a perl bug if the lines are astronomically long (probably
994 # due to lots of regexp backtracking); so split any individual line
995 # over MAX_BODY_LINE_LENGTH bytes in length. This can wreck HTML
996 # totally -- but IMHO the only reason a luser would use
997 # MAX_BODY_LINE_LENGTH-byte lines is to crash filters, anyway.
99865762394ms while (length ($_) > MAX_BODY_LINE_LENGTH) {
999 push (@{$part_array}, substr($_, 0, MAX_BODY_LINE_LENGTH)."\n");
1000 substr($_, 0, MAX_BODY_LINE_LENGTH) = '';
1001 }
1002131524990ms push ( @{$part_array}, $_ );
1003 }
1004
1005 # Look for a message epilogue
1006 # originally ignored whitespace: 0.185 0.2037 0.0654 0.757 0.00 0.00 TVD_TAB
1007 # ham FPs were all "." on a line by itself.
1008 # spams seem to only have NULL chars afterwards ?
10091972.01ms if ($line_count) {
101075727µs for(; $line_count > 0; $line_count--) {
1011871.05ms87360µs if ($body->[-$line_count] =~ /[^\s.]/) {
# spent 360µs making 87 calls to Mail::SpamAssassin::Message::CORE:match, avg 4µs/call
1012 $self->{mime_epilogue_exists} = 1;
1013 last;
1014 }
1015 }
1016 }
1017
1018}
1019
1020=item _parse_normal()
1021
1022Generate a leaf node and add it to the parent.
1023
1024=cut
1025
1026
# spent 202ms (51.6+150) within Mail::SpamAssassin::Message::_parse_normal which was called 425 times, avg 474µs/call: # 425 times (51.6ms+150ms) by Mail::SpamAssassin::Message::parse_body at line 750, avg 474µs/call
sub _parse_normal {
1027425920µs my($self, $toparse) = @_;
1028
10298503.14ms my ($msg, $boundary, $body) = @{$toparse};
1030
10314252.77ms4252.65ms dbg("message: parsing normal part");
# spent 2.65ms making 425 calls to Mail::SpamAssassin::Logger::dbg, avg 6µs/call
1032
1033 # 0: content-type, 1: boundary, 2: charset, 3: filename
10344257.23ms85095.8ms my @ct = Mail::SpamAssassin::Util::parse_content_type($msg->header('content-type'));
# spent 71.6ms making 425 calls to Mail::SpamAssassin::Util::parse_content_type, avg 168µs/call # spent 24.1ms making 425 calls to Mail::SpamAssassin::Message::Node::header, avg 57µs/call
1035
1036 # multipart sections are required to have a boundary set ... If this
1037 # one doesn't, assume it's malformed and revert to text/plain
10384254.91ms4251.26ms $msg->{'type'} = ($ct[0] !~ m@^multipart/@i || defined $boundary ) ? $ct[0] : 'text/plain';
# spent 1.26ms making 425 calls to Mail::SpamAssassin::Message::CORE:match, avg 3µs/call
10394252.07ms $msg->{'charset'} = $ct[2];
1040
1041 # attempt to figure out a name for this attachment if there is one ...
10424253.30ms42522.6ms my $disp = $msg->header('content-disposition') || '';
# spent 22.6ms making 425 calls to Mail::SpamAssassin::Message::Node::header, avg 53µs/call
10434254.34ms425884µs if ($disp =~ /name="?([^\";]+)"?/i) {
# spent 884µs making 425 calls to Mail::SpamAssassin::Message::CORE:match, avg 2µs/call
1044949µs $msg->{'name'} = $1;
1045 }
1046 elsif ($ct[3]) {
1047627µs $msg->{'name'} = $ct[3];
1048 }
1049
10504252.37ms $msg->{'boundary'} = $boundary;
1051
1052 # If the part type is not one that we're likely to want to use, go
1053 # ahead and write the part data out to a temp file -- why keep sucking
1054 # up RAM with something we're not going to use?
1055 #
10564256.24ms4253.23ms if ($msg->{'type'} !~ m@^(?:text/(?:plain|html)$|message\b)@) {
# spent 3.23ms making 425 calls to Mail::SpamAssassin::Message::CORE:match, avg 8µs/call
10571631µs my($filepath, $fh);
1058 eval {
105932198µs1611.4ms ($filepath, $fh) = Mail::SpamAssassin::Util::secure_tmpfile(); 1;
# spent 11.4ms making 16 calls to Mail::SpamAssassin::Util::secure_tmpfile, avg 712µs/call
10601674µs } or do {
1061 my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat;
1062 info("message: failed to create a temp file: %s", $eval_stat);
1063 };
10641665µs if ($fh) {
1065 # The temp file was created, add it to the list of pending deletions
1066 # we cannot just delete immediately in the POSIX idiom, as this is
1067 # unportable (to win32 at least)
106832139µs push @{$self->{tmpfiles}}, $filepath;
106916118µs1696µs dbg("message: storing a message part to file %s", $filepath);
# spent 96µs making 16 calls to Mail::SpamAssassin::Logger::dbg, avg 6µs/call
107032467µs168.29ms $fh->print(@{$body}) or die "error writing to $filepath: $!";
# spent 8.29ms making 16 calls to IO::Handle::print, avg 518µs/call
1071161.12ms16942µs $fh->flush or die "error writing (flush) to $filepath: $!";
# spent 942µs making 16 calls to IO::Handle::flush, avg 59µs/call
10721676µs $msg->{'raw'} = $fh;
1073 }
1074 }
1075
1076 # if the part didn't get a temp file, go ahead and store the data in memory
10774255.69ms if (!defined $msg->{'raw'}) {
10784092.65ms4092.92ms dbg("message: storing a body to memory");
# spent 2.92ms making 409 calls to Mail::SpamAssassin::Logger::dbg, avg 7µs/call
10794092.78ms $msg->{'raw'} = $body;
1080 }
1081}
1082
1083# ---------------------------------------------------------------------------
1084
1085sub get_mimepart_digests {
1086 my ($self) = @_;
1087
1088 if (!exists $self->{mimepart_digests}) {
1089 # traverse all parts which are leaves, recursively
1090 $self->{mimepart_digests} =
1091 [ map(sha1_hex($_->decode) . ':' . lc($_->{type}||''),
1092 $self->find_parts(qr/^/,1,1)) ];
1093 }
1094 return $self->{mimepart_digests};
1095}
1096
1097# ---------------------------------------------------------------------------
1098
1099# common code for get_rendered_body_text_array,
1100# get_visible_rendered_body_text_array, get_invisible_rendered_body_text_array
1101#
1102
# spent 27.2s (156ms+27.1) within Mail::SpamAssassin::Message::get_body_text_array_common which was called 1170 times, avg 23.3ms/call: # 702 times (68.6ms+26.8s) by Mail::SpamAssassin::Message::get_rendered_body_text_array at line 1159, avg 38.2ms/call # 234 times (51.9ms+196ms) by Mail::SpamAssassin::Message::get_visible_rendered_body_text_array at line 1164, avg 1.06ms/call # 234 times (35.5ms+93.2ms) by Mail::SpamAssassin::Message::get_invisible_rendered_body_text_array at line 1169, avg 550µs/call
sub get_body_text_array_common {
110311703.17ms my ($self, $method_name) = @_;
1104
110511703.47ms my $key = 'text_' . $method_name;
1106163829.2ms if (exists $self->{$key}) { return $self->{$key} }
1107
11087023.52ms $self->{$key} = [];
1109
1110 # Find all parts which are leaves
111170217.7ms14042.90s my @parts = $self->find_parts(qr/./,1);
# spent 2.89s making 702 calls to Mail::SpamAssassin::Message::find_parts, avg 4.12ms/call # spent 5.02ms making 702 calls to Mail::SpamAssassin::Message::CORE:qr, avg 7µs/call
11127021.62ms return $self->{$key} unless @parts;
1113
1114 # the html metadata may have already been set, so let's not bother if it's
1115 # already been done.
11167022.97ms my $html_needs_setting = !exists $self->{metadata}->{html};
1117
11187027.72ms46861.1ms my $text = $method_name eq 'invisible_rendered' ? ''
# spent 61.1ms making 468 calls to Mail::SpamAssassin::Message::Node::get_header, avg 131µs/call
1119 : ($self->get_header('subject') || "\n");
1120
1121 # Go through each part
112270211.7ms for (my $pt = 0 ; $pt <= $#parts ; $pt++ ) {
112312752.96ms my $p = $parts[$pt];
1124
1125 # put a blank line between parts ...
112612754.12ms $text .= "\n" if $text ne '';
1127
1128127514.5ms127523.9s my($type, $rnd) = $p->$method_name(); # decode this part
# spent 23.9s making 425 calls to Mail::SpamAssassin::Message::Node::rendered, avg 56.2ms/call # spent 25.7ms making 425 calls to Mail::SpamAssassin::Message::Node::invisible_rendered, avg 61µs/call # spent 15.4ms making 425 calls to Mail::SpamAssassin::Message::Node::visible_rendered, avg 36µs/call
112912757.63ms if ( defined $rnd ) {
1130 # Only text/* types are rendered ...
113112276.24ms $text .= $rnd;
1132
1133 # TVD - if there are multiple parts, what should we do?
1134 # right now, just use the last one. we may need to give some priority
1135 # at some point, ie: use text/html rendered if it exists, or
1136 # text/plain rendered as html otherwise.
113712273.05ms if ($html_needs_setting && $type eq 'text/html') {
1138189879µs $self->{metadata}->{html} = $p->{html_results};
1139 }
1140 }
1141 }
1142
1143 # whitespace handling (warning: small changes have large effects!)
114470237.4ms70230.4ms $text =~ s/\n+\s*\n+/\f/gs; # double newlines => form feed
# spent 30.4ms making 702 calls to Mail::SpamAssassin::Message::CORE:subst, avg 43µs/call
1145# $text =~ tr/ \t\n\r\x0b\xa0/ /s; # whitespace (incl. VT, NBSP) => space
114670211.7ms $text =~ tr/ \t\n\r\x0b/ /s; # whitespace (incl. VT) => space
11477025.16ms $text =~ tr/\f/\n/; # form feeds => newline
1148
114970213.6ms702143ms my @textary = split_into_array_of_short_lines($text);
# spent 143ms making 702 calls to Mail::SpamAssassin::Message::split_into_array_of_short_lines, avg 203µs/call
11507023.79ms $self->{$key} = \@textary;
1151
11527027.24ms return $self->{$key};
1153}
1154
1155# ---------------------------------------------------------------------------
1156
1157
# spent 26.9s (32.3ms+26.8) within Mail::SpamAssassin::Message::get_rendered_body_text_array which was called 702 times, avg 38.3ms/call: # 702 times (32.3ms+26.8s) by Mail::SpamAssassin::PerMsgStatus::get_decoded_stripped_body_text_array at line 1785 of Mail/SpamAssassin/PerMsgStatus.pm, avg 38.3ms/call
sub get_rendered_body_text_array {
11587021.50ms my ($self) = @_;
115970210.1ms70226.8s return $self->get_body_text_array_common('rendered');
# spent 26.8s making 702 calls to Mail::SpamAssassin::Message::get_body_text_array_common, avg 38.2ms/call
1160}
1161
1162
# spent 252ms (4.73+247) within Mail::SpamAssassin::Message::get_visible_rendered_body_text_array which was called 234 times, avg 1.08ms/call: # 234 times (4.73ms+247ms) by Mail::SpamAssassin::Plugin::Bayes::_get_msgdata_from_permsgstatus at line 1044 of Mail/SpamAssassin/Plugin/Bayes.pm, avg 1.08ms/call
sub get_visible_rendered_body_text_array {
1163234603µs my ($self) = @_;
11642343.99ms234247ms return $self->get_body_text_array_common('visible_rendered');
# spent 247ms making 234 calls to Mail::SpamAssassin::Message::get_body_text_array_common, avg 1.06ms/call
1165}
1166
1167
# spent 133ms (3.87+129) within Mail::SpamAssassin::Message::get_invisible_rendered_body_text_array which was called 234 times, avg 567µs/call: # 234 times (3.87ms+129ms) by Mail::SpamAssassin::Plugin::Bayes::_get_msgdata_from_permsgstatus at line 1046 of Mail/SpamAssassin/Plugin/Bayes.pm, avg 567µs/call
sub get_invisible_rendered_body_text_array {
1168234580µs my ($self) = @_;
11692342.96ms234129ms return $self->get_body_text_array_common('invisible_rendered');
# spent 129ms making 234 calls to Mail::SpamAssassin::Message::get_body_text_array_common, avg 550µs/call
1170}
1171
1172# ---------------------------------------------------------------------------
1173
1174sub get_decoded_body_text_array {
1175 my ($self) = @_;
1176
1177 if (defined $self->{text_decoded}) { return $self->{text_decoded}; }
1178 $self->{text_decoded} = [ ];
1179
1180 # Find all parts which are leaves
1181 my @parts = $self->find_parts(qr/^(?:text|message)\b/i,1);
1182 return $self->{text_decoded} unless @parts;
1183
1184 # Go through each part
1185 for(my $pt = 0 ; $pt <= $#parts ; $pt++ ) {
1186 # bug 4843: skip text/calendar parts since they're usually an attachment
1187 # and not displayed
1188 next if ($parts[$pt]->{'type'} eq 'text/calendar');
1189
1190 push(@{$self->{text_decoded}}, "\n") if ( @{$self->{text_decoded}} );
1191 push(@{$self->{text_decoded}},
1192 split_into_array_of_short_paragraphs($parts[$pt]->decode()));
1193 }
1194
1195 return $self->{text_decoded};
1196}
1197
1198# ---------------------------------------------------------------------------
1199
1200
# spent 143ms within Mail::SpamAssassin::Message::split_into_array_of_short_lines which was called 702 times, avg 203µs/call: # 702 times (143ms+0s) by Mail::SpamAssassin::Message::get_body_text_array_common at line 1149, avg 203µs/call
sub split_into_array_of_short_lines {
12017021.34ms my @result;
120270217.5ms foreach my $line (split (/^/m, $_[0])) {
1203889534.8ms while (length ($line) > MAX_BODY_LINE_LENGTH) {
1204 # try splitting "nicely" so that we don't chop a url in half or
1205 # something. if there's no space, then just split at max length.
1206921.06ms my $length = rindex($line, ' ', MAX_BODY_LINE_LENGTH) + 1;
120792178µs $length ||= MAX_BODY_LINE_LENGTH;
1208921.11ms push (@result, substr($line, 0, $length, ''));
1209 }
1210889573.6ms push (@result, $line);
1211 }
121270223.5ms @result;
1213}
1214
1215# ---------------------------------------------------------------------------
1216
1217# split a text into array of paragraphs of sizes between
1218# $chunk_size and 2 * $chunk_size, returning the resulting array
1219
1220sub split_into_array_of_short_paragraphs {
1221 my @result;
1222 my $chunk_size = 1024;
1223 my $text_l = length($_[0]);
1224 my($j,$ofs);
1225 for ($ofs = 0; $text_l - $ofs > 2 * $chunk_size; $ofs = $j+1) {
1226 $j = index($_[0], "\n", $ofs+$chunk_size);
1227 if ($j < 0) {
1228 $j = index($_[0], " ", $ofs+$chunk_size);
1229 if ($j < 0) { $j = $ofs+$chunk_size }
1230 }
1231 push(@result, substr($_[0], $ofs, $j-$ofs+1));
1232 }
1233 push(@result, substr($_[0], $ofs)) if $ofs < $text_l;
1234 @result;
1235}
1236
1237# ---------------------------------------------------------------------------
1238
1239119µs1;
1240
1241=back
1242
1243=cut
 
# spent 172µs within Mail::SpamAssassin::Message::CORE:close which was called 16 times, avg 11µs/call: # 16 times (172µs+0s) by Mail::SpamAssassin::Message::finish at line 637, avg 11µs/call
sub Mail::SpamAssassin::Message::CORE:close; # opcode
# spent 553ms within Mail::SpamAssassin::Message::CORE:match which was called 114442 times, avg 5µs/call: # 71012 times (333ms+0s) by Mail::SpamAssassin::Message::new at line 340, avg 5µs/call # 15054 times (59.8ms+0s) by Mail::SpamAssassin::Message::new at line 286, avg 4µs/call # 13487 times (63.2ms+0s) by Mail::SpamAssassin::Message::new at line 252, avg 5µs/call # 7410 times (62.5ms+0s) by Mail::SpamAssassin::Message::new at line 302, avg 8µs/call # 1685 times (9.98ms+0s) by Mail::SpamAssassin::Message::_parse_multipart at line 962, avg 6µs/call # 622 times (2.62ms+0s) by Mail::SpamAssassin::Message::parse_body at line 745, avg 4µs/call # 579 times (2.66ms+0s) by Mail::SpamAssassin::Message::_parse_multipart at line 937, avg 5µs/call # 491 times (2.72ms+0s) by Mail::SpamAssassin::Message::_parse_multipart at line 884, avg 6µs/call # 468 times (1.62ms+0s) by Mail::SpamAssassin::Message::new at line 195, avg 3µs/call # 468 times (1.42ms+0s) by Mail::SpamAssassin::Message::new at line 236, avg 3µs/call # 427 times (1.85ms+0s) by Mail::SpamAssassin::Message::new at line 372, avg 4µs/call # 425 times (3.23ms+0s) by Mail::SpamAssassin::Message::_parse_normal at line 1056, avg 8µs/call # 425 times (1.53ms+0s) by Mail::SpamAssassin::Message::parse_body at line 753, avg 4µs/call # 425 times (1.26ms+0s) by Mail::SpamAssassin::Message::_parse_normal at line 1038, avg 3µs/call # 425 times (884µs+0s) by Mail::SpamAssassin::Message::_parse_normal at line 1043, avg 2µs/call # 388 times (2.21ms+0s) by Mail::SpamAssassin::Message::_parse_multipart at line 984, avg 6µs/call # 197 times (1.14ms+0s) by Mail::SpamAssassin::Message::_parse_multipart at line 856, avg 6µs/call # 197 times (882µs+0s) by Mail::SpamAssassin::Message::_parse_multipart at line 848, avg 4µs/call # 170 times (596µs+0s) by Mail::SpamAssassin::Message::new at line 374, avg 4µs/call # 87 times (360µs+0s) by Mail::SpamAssassin::Message::_parse_multipart at line 1011, avg 4µs/call
sub Mail::SpamAssassin::Message::CORE:match; # opcode
# spent 5.02ms within Mail::SpamAssassin::Message::CORE:qr which was called 702 times, avg 7µs/call: # 702 times (5.02ms+0s) by Mail::SpamAssassin::Message::get_body_text_array_common at line 1111, avg 7µs/call
sub Mail::SpamAssassin::Message::CORE:qr; # opcode
# spent 33.1ms within Mail::SpamAssassin::Message::CORE:regcomp which was called 1246 times, avg 27µs/call: # 491 times (9.16ms+0s) by Mail::SpamAssassin::Message::_parse_multipart at line 884, avg 19µs/call # 388 times (6.80ms+0s) by Mail::SpamAssassin::Message::_parse_multipart at line 937, avg 18µs/call # 197 times (7.97ms+0s) by Mail::SpamAssassin::Message::_parse_multipart at line 848, avg 40µs/call # 170 times (9.20ms+0s) by Mail::SpamAssassin::Message::new at line 374, avg 54µs/call
sub Mail::SpamAssassin::Message::CORE:regcomp; # opcode
# spent 994µs within Mail::SpamAssassin::Message::CORE:sort which was called 234 times, avg 4µs/call: # 234 times (994µs+0s) by Mail::SpamAssassin::Message::get_all_metadata at line 584, avg 4µs/call
sub Mail::SpamAssassin::Message::CORE:sort; # opcode
# spent 57.4ms within Mail::SpamAssassin::Message::CORE:subst which was called 8112 times, avg 7µs/call: # 7410 times (27.1ms+0s) by Mail::SpamAssassin::Message::new at line 272, avg 4µs/call # 702 times (30.4ms+0s) by Mail::SpamAssassin::Message::get_body_text_array_common at line 1144, avg 43µs/call
sub Mail::SpamAssassin::Message::CORE:subst; # opcode
# spent 3.14ms within Mail::SpamAssassin::Message::CORE:unlink which was called 16 times, avg 196µs/call: # 16 times (3.14ms+0s) by Mail::SpamAssassin::Message::finish at line 667, avg 196µs/call
sub Mail::SpamAssassin::Message::CORE:unlink; # opcode