| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Sisimai::Message; |
|
2
|
87
|
|
|
87
|
|
98267
|
use v5.26; |
|
|
87
|
|
|
|
|
310
|
|
|
3
|
87
|
|
|
87
|
|
457
|
use strict; |
|
|
87
|
|
|
|
|
143
|
|
|
|
87
|
|
|
|
|
2262
|
|
|
4
|
87
|
|
|
87
|
|
386
|
use warnings; |
|
|
87
|
|
|
|
|
160
|
|
|
|
87
|
|
|
|
|
4945
|
|
|
5
|
87
|
|
|
87
|
|
56983
|
use Sisimai::RFC1894; |
|
|
87
|
|
|
|
|
282
|
|
|
|
87
|
|
|
|
|
3894
|
|
|
6
|
87
|
|
|
87
|
|
48155
|
use Sisimai::RFC2045; |
|
|
87
|
|
|
|
|
282
|
|
|
|
87
|
|
|
|
|
4315
|
|
|
7
|
87
|
|
|
87
|
|
40643
|
use Sisimai::RFC5322; |
|
|
87
|
|
|
|
|
404
|
|
|
|
87
|
|
|
|
|
3911
|
|
|
8
|
87
|
|
|
87
|
|
39947
|
use Sisimai::RFC5965; |
|
|
87
|
|
|
|
|
264
|
|
|
|
87
|
|
|
|
|
3026
|
|
|
9
|
87
|
|
|
87
|
|
544
|
use Sisimai::Address; |
|
|
87
|
|
|
|
|
162
|
|
|
|
87
|
|
|
|
|
1894
|
|
|
10
|
87
|
|
|
87
|
|
401
|
use Sisimai::String; |
|
|
87
|
|
|
|
|
125
|
|
|
|
87
|
|
|
|
|
1746
|
|
|
11
|
87
|
|
|
87
|
|
38127
|
use Sisimai::Order; |
|
|
87
|
|
|
|
|
231
|
|
|
|
87
|
|
|
|
|
3525
|
|
|
12
|
87
|
|
|
87
|
|
546
|
use Sisimai::Lhost; |
|
|
87
|
|
|
|
|
149
|
|
|
|
87
|
|
|
|
|
319820
|
|
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
state $Fields1894 = Sisimai::RFC1894->FIELDINDEX; |
|
15
|
|
|
|
|
|
|
state $Fields5322 = Sisimai::RFC5322->FIELDINDEX; |
|
16
|
|
|
|
|
|
|
state $Fields5965 = Sisimai::RFC5965->FIELDINDEX; |
|
17
|
|
|
|
|
|
|
state $FieldTable = { map { lc $_ => $_ } ($Fields1894->@*, $Fields5322->@*, $Fields5965->@*) }; |
|
18
|
|
|
|
|
|
|
state $Boundaries = ["Content-Type: message/rfc822", "Content-Type: text/rfc822-headers"]; |
|
19
|
|
|
|
|
|
|
state $MediaTypes = [ |
|
20
|
|
|
|
|
|
|
["message/xdelivery-status", "message/delivery-status"], |
|
21
|
|
|
|
|
|
|
["message/disposition-notification", "message/delivery-status"], |
|
22
|
|
|
|
|
|
|
["message/global-delivery-status", "message/delivery-status"], |
|
23
|
|
|
|
|
|
|
["message/global-disposition-notification", "message/delivery-status"], |
|
24
|
|
|
|
|
|
|
["message/global-delivery-status", "message/delivery-status"], |
|
25
|
|
|
|
|
|
|
["message/global-headers", "text/rfc822-headers"], |
|
26
|
|
|
|
|
|
|
["message/global", "message/rfc822"], |
|
27
|
|
|
|
|
|
|
]; |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
my $TryOnFirst = []; |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
sub rise { |
|
32
|
|
|
|
|
|
|
# Constructor of Sisimai::Message |
|
33
|
|
|
|
|
|
|
# @param [Hash] argvs Email text data |
|
34
|
|
|
|
|
|
|
# @options argvs [String] data Entire email message |
|
35
|
|
|
|
|
|
|
# @options argvs [Code] hook Reference to callback method |
|
36
|
|
|
|
|
|
|
# @return [Hash] Structured email data |
|
37
|
|
|
|
|
|
|
# [Undef] If each value of the arguments are missing |
|
38
|
3509
|
|
|
3509
|
1
|
417455
|
my $class = shift; |
|
39
|
3509
|
|
100
|
|
|
10647
|
my $argvs = shift || return undef; |
|
40
|
3508
|
|
100
|
|
|
11064
|
my $email = $argvs->{'data'} || return undef; |
|
41
|
3507
|
|
|
|
|
25280
|
my $thing = {'from' => '', 'header' => {}, 'rfc822' => '', 'ds' => [], 'catch' => undef}; |
|
42
|
3507
|
|
|
|
|
8292
|
my $param = {}; |
|
43
|
|
|
|
|
|
|
|
|
44
|
3507
|
|
|
|
|
6524
|
my $aftersplit = undef; |
|
45
|
3507
|
|
|
|
|
6358
|
my $beforefact = undef; |
|
46
|
3507
|
|
|
|
|
9181
|
my $parseagain = 0; |
|
47
|
|
|
|
|
|
|
|
|
48
|
3507
|
|
|
|
|
14111
|
while($parseagain < 2) { |
|
49
|
|
|
|
|
|
|
# 1. Split email data to headers and a body part. |
|
50
|
3524
|
100
|
|
|
|
15300
|
last unless $aftersplit = __PACKAGE__->part(\$email); |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
# 2. Convert email headers from text to hash reference |
|
53
|
3523
|
|
|
|
|
10160
|
$thing->{'from'} = $aftersplit->[0]; |
|
54
|
3523
|
|
|
|
|
14682
|
$thing->{'header'} = __PACKAGE__->makemap(\$aftersplit->[1]); |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
# 3. Decode and rewrite the "Subject:" header |
|
57
|
3523
|
100
|
|
|
|
12663
|
if( $thing->{'header'}->{'subject'} ) { |
|
58
|
|
|
|
|
|
|
# Decode MIME-Encoded "Subject:" header |
|
59
|
3506
|
|
|
|
|
8892
|
my $cv = $thing->{'header'}->{'subject'}; |
|
60
|
3506
|
100
|
|
|
|
39151
|
my $cq = Sisimai::RFC2045->is_encoded(\$cv) ? Sisimai::RFC2045->decodeH([split(/[ ]/, $cv)]) : $cv; |
|
61
|
3506
|
|
|
|
|
9329
|
my $cl = lc $cq; |
|
62
|
3506
|
100
|
|
|
|
9024
|
my $p1 = index($cl, 'fwd:'); $p1 = index($cl, 'fw:') if $p1 < 0; |
|
|
3506
|
|
|
|
|
25277
|
|
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# Remove "Fwd:" string from the "Subject:" header |
|
65
|
3506
|
100
|
|
|
|
10695
|
if( $p1 > -1 ) { |
|
66
|
|
|
|
|
|
|
# Delete quoted strings, quote symbols(>) |
|
67
|
30
|
|
|
|
|
314
|
$cq = Sisimai::String->sweep(substr($cq, index($cq, ':') + 1,)); |
|
68
|
30
|
|
|
|
|
470
|
s/^[>][ ]//gm, s/^[>]$//gm for $aftersplit->[2]; |
|
69
|
|
|
|
|
|
|
} |
|
70
|
3506
|
|
|
|
|
10896
|
$thing->{'header'}->{'subject'} = $cq; |
|
71
|
|
|
|
|
|
|
} |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# 4. Rewrite message body for detecting the bounce reason |
|
74
|
3523
|
|
|
|
|
30394
|
$TryOnFirst = Sisimai::Order->make($thing->{'header'}->{'subject'}); |
|
75
|
3523
|
|
100
|
|
|
33989
|
$param = {'hook' => $argvs->{'hook'} || undef, 'mail' => $thing, 'body' => \$aftersplit->[2]}; |
|
76
|
3523
|
100
|
|
|
|
19392
|
last if $beforefact = __PACKAGE__->sift(%$param); |
|
77
|
60
|
100
|
|
|
|
253
|
last unless grep { index($aftersplit->[2], $_) > -1 } @$Boundaries; |
|
|
120
|
|
|
|
|
466
|
|
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
# 5. Try to sift again |
|
80
|
|
|
|
|
|
|
# There is a bounce message inside of mutipart/*, try to sift the first message/rfc822 |
|
81
|
|
|
|
|
|
|
# part as a entire message body again. rfc3464/1086-a847b090.eml is the email but the |
|
82
|
|
|
|
|
|
|
# results decodd by sisimai are unstable. |
|
83
|
17
|
|
|
|
|
42
|
$parseagain++; |
|
84
|
17
|
|
|
|
|
109
|
$email = Sisimai::RFC5322->part(\$aftersplit->[2], $Boundaries, 1)->[1]; |
|
85
|
17
|
|
|
|
|
64
|
$email =~ s/\A[\r\n\s]+//m; |
|
86
|
17
|
50
|
|
|
|
93
|
last unless length $email > 128; |
|
87
|
|
|
|
|
|
|
} |
|
88
|
3507
|
100
|
|
|
|
12075
|
return undef unless $beforefact; |
|
89
|
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
# 6. Rewrite headers of the original message in the body part |
|
91
|
3463
|
|
|
|
|
22036
|
$thing->{ $_ } = $beforefact->{ $_ } for ('ds', 'catch', 'rfc822'); |
|
92
|
3463
|
|
66
|
|
|
13261
|
my $r = $beforefact->{'rfc822'} || $aftersplit->[2]; |
|
93
|
3463
|
50
|
|
|
|
20817
|
$thing->{'rfc822'} = ref $r ? $r : __PACKAGE__->makemap(\$r, 1); |
|
94
|
|
|
|
|
|
|
|
|
95
|
3463
|
|
|
|
|
31673
|
return $thing; |
|
96
|
|
|
|
|
|
|
} |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
sub part { |
|
99
|
|
|
|
|
|
|
# Divide email data up headers and a body part. |
|
100
|
|
|
|
|
|
|
# @param [String] email Email data |
|
101
|
|
|
|
|
|
|
# @return [Array] Email data after split |
|
102
|
|
|
|
|
|
|
# @since v4.14.0 |
|
103
|
3526
|
|
|
3526
|
0
|
8050
|
my $class = shift; |
|
104
|
3526
|
|
100
|
|
|
11315
|
my $email = shift // return undef; |
|
105
|
3524
|
|
|
|
|
12294
|
my $parts = ['', '', '']; # 0:From, 1:Header, 2:Body |
|
106
|
|
|
|
|
|
|
|
|
107
|
3524
|
|
|
|
|
20725
|
$$email =~ s/\A\s+//m; |
|
108
|
3524
|
100
|
|
|
|
53378
|
$$email =~ s/\r\n/\n/gm if rindex($$email, "\r\n") > -1; |
|
109
|
|
|
|
|
|
|
|
|
110
|
3524
|
|
100
|
|
|
32241
|
($parts->[1], $parts->[2]) = split(/\n\n/, $$email, 2); $parts->[2] ||= ""; |
|
|
3524
|
|
|
|
|
13638
|
|
|
111
|
3524
|
100
|
66
|
|
|
21701
|
return undef if $parts->[1] eq "" || $parts->[2] eq ""; |
|
112
|
|
|
|
|
|
|
|
|
113
|
3523
|
100
|
|
|
|
15602
|
if( substr($parts->[1], 0, 5) eq 'From ' ) { |
|
114
|
|
|
|
|
|
|
# From MAILER-DAEMON Tue Feb 11 00:00:00 2014 |
|
115
|
461
|
|
|
|
|
2646
|
$parts->[0] = [split(/\n/, $parts->[1], 2)]->[0]; |
|
116
|
461
|
|
|
|
|
2050
|
$parts->[0] =~ y/\r\n//d; |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
} else { |
|
119
|
|
|
|
|
|
|
# Set pseudo UNIX From line |
|
120
|
3062
|
|
|
|
|
7850
|
$parts->[0] = 'MAILER-DAEMON Tue Feb 11 00:00:00 2014'; |
|
121
|
|
|
|
|
|
|
} |
|
122
|
3523
|
50
|
|
|
|
15939
|
$parts->[1] .= "\n" unless substr($parts->[1], -1, 1) eq "\n"; |
|
123
|
|
|
|
|
|
|
|
|
124
|
3523
|
|
|
|
|
10231
|
for my $e ('image/', 'application/', 'text/html') { |
|
125
|
|
|
|
|
|
|
# https://github.com/sisimai/p5-sisimai/issues/492, Reduce email size |
|
126
|
10569
|
100
|
|
|
|
15844
|
my $p0 = 0; my $p1 = 0; my $ep = $e eq 'text/html' ? ' |