line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Sisimai::Data; |
2
|
76
|
|
|
76
|
|
206822
|
use feature ':5.10'; |
|
76
|
|
|
|
|
247
|
|
|
76
|
|
|
|
|
6122
|
|
3
|
76
|
|
|
76
|
|
425
|
use strict; |
|
76
|
|
|
|
|
138
|
|
|
76
|
|
|
|
|
1319
|
|
4
|
76
|
|
|
76
|
|
296
|
use warnings; |
|
76
|
|
|
|
|
107
|
|
|
76
|
|
|
|
|
1762
|
|
5
|
76
|
|
|
76
|
|
6306
|
use Sisimai::Address; |
|
76
|
|
|
|
|
134
|
|
|
76
|
|
|
|
|
1472
|
|
6
|
76
|
|
|
76
|
|
6096
|
use Sisimai::String; |
|
76
|
|
|
|
|
151
|
|
|
76
|
|
|
|
|
1997
|
|
7
|
76
|
|
|
76
|
|
28475
|
use Sisimai::Reason; |
|
76
|
|
|
|
|
157
|
|
|
76
|
|
|
|
|
2042
|
|
8
|
76
|
|
|
76
|
|
20989
|
use Sisimai::Rhost; |
|
76
|
|
|
|
|
155
|
|
|
76
|
|
|
|
|
1900
|
|
9
|
76
|
|
|
76
|
|
22914
|
use Sisimai::Time; |
|
76
|
|
|
|
|
210
|
|
|
76
|
|
|
|
|
658
|
|
10
|
76
|
|
|
76
|
|
35890
|
use Sisimai::DateTime; |
|
76
|
|
|
|
|
227
|
|
|
76
|
|
|
|
|
2323
|
|
11
|
76
|
|
|
76
|
|
27211
|
use Sisimai::SMTP::Error; |
|
76
|
|
|
|
|
175
|
|
|
76
|
|
|
|
|
4099
|
|
12
|
|
|
|
|
|
|
use Class::Accessor::Lite ( |
13
|
76
|
|
|
|
|
903
|
'new' => 0, |
14
|
|
|
|
|
|
|
'rw' => [ |
15
|
|
|
|
|
|
|
'catch', # [?] Results generated by hook method |
16
|
|
|
|
|
|
|
'token', # [String] Message token/MD5 Hex digest value |
17
|
|
|
|
|
|
|
'lhost', # [String] local host name/Local MTA |
18
|
|
|
|
|
|
|
'rhost', # [String] Remote host name/Remote MTA |
19
|
|
|
|
|
|
|
'alias', # [String] Alias of the recipient address |
20
|
|
|
|
|
|
|
'listid', # [String] List-Id header of each ML |
21
|
|
|
|
|
|
|
'reason', # [String] Bounce reason |
22
|
|
|
|
|
|
|
'action', # [String] The value of Action: header |
23
|
|
|
|
|
|
|
'origin', # [String] Email path as a data source |
24
|
|
|
|
|
|
|
'subject', # [String] UTF-8 Subject text |
25
|
|
|
|
|
|
|
'timestamp', # [Sisimai::Time] Date: header in the original message |
26
|
|
|
|
|
|
|
'addresser', # [Sisimai::Address] From address |
27
|
|
|
|
|
|
|
'recipient', # [Sisimai::Address] Recipient address which bounced |
28
|
|
|
|
|
|
|
'messageid', # [String] Message-Id: header |
29
|
|
|
|
|
|
|
'replycode', # [String] SMTP Reply Code |
30
|
|
|
|
|
|
|
'smtpagent', # [String] Module(Engine) name |
31
|
|
|
|
|
|
|
'softbounce', # [Integer] 1 = Soft bounce, 0 = Hard bounce, -1 = ? |
32
|
|
|
|
|
|
|
'smtpcommand', # [String] The last SMTP command |
33
|
|
|
|
|
|
|
'destination', # [String] The domain part of the "recipinet" |
34
|
|
|
|
|
|
|
'senderdomain', # [String] The domain part of the "addresser" |
35
|
|
|
|
|
|
|
'feedbacktype', # [String] Feedback Type |
36
|
|
|
|
|
|
|
'diagnosticcode', # [String] Diagnostic-Code: Header |
37
|
|
|
|
|
|
|
'diagnostictype', # [String] The 1st part of Diagnostic-Code: Header |
38
|
|
|
|
|
|
|
'deliverystatus', # [String] Delivery Status(DSN) |
39
|
|
|
|
|
|
|
'timezoneoffset', # [Integer] Time zone offset(seconds) |
40
|
|
|
|
|
|
|
] |
41
|
76
|
|
|
76
|
|
418
|
); |
|
76
|
|
|
|
|
133
|
|
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
sub new { |
44
|
|
|
|
|
|
|
# Constructor of Sisimai::Data |
45
|
|
|
|
|
|
|
# @param [Hash] argvs Data |
46
|
|
|
|
|
|
|
# @return [Sisimai::Data] Structured email data |
47
|
2933
|
|
|
2933
|
0
|
4257
|
my $class = shift; |
48
|
2933
|
|
|
|
|
27251
|
my $argvs = { @_ }; |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
# Create email address object |
51
|
2933
|
|
|
|
|
10520
|
my $as = Sisimai::Address->make($argvs->{'addresser'}); |
52
|
2933
|
|
|
|
|
9242
|
my $ar = Sisimai::Address->make({ 'address' => $argvs->{'recipient'} }); |
53
|
2933
|
100
|
|
|
|
8826
|
return undef unless ref $as eq 'Sisimai::Address'; |
54
|
2932
|
50
|
|
|
|
5309
|
return undef unless ref $ar eq 'Sisimai::Address'; |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
my $thing = { |
57
|
|
|
|
|
|
|
'addresser' => $as, |
58
|
|
|
|
|
|
|
'recipient' => $ar, |
59
|
|
|
|
|
|
|
'senderdomain' => $as->host, |
60
|
|
|
|
|
|
|
'destination' => $ar->host, |
61
|
|
|
|
|
|
|
'alias' => $argvs->{'alias'} || $ar->alias, |
62
|
2932
|
|
66
|
|
|
7858
|
'token' => Sisimai::String->token($as->address, $ar->address, $argvs->{'timestamp'}), |
63
|
|
|
|
|
|
|
}; |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# Create Sisimai::Time object |
66
|
2932
|
|
|
|
|
10862
|
$thing->{'timestamp'} = Sisimai::Time->new($argvs->{'timestamp'}); |
67
|
2932
|
|
50
|
|
|
234723
|
$thing->{'timezoneoffset'} = $argvs->{'timezoneoffset'} // '+0000'; |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# Callback method |
70
|
2932
|
|
100
|
|
|
11267
|
$thing->{'catch'} = $argvs->{'catch'} // undef; |
71
|
|
|
|
|
|
|
|
72
|
2932
|
|
|
|
|
12421
|
my @v1 = (qw| |
73
|
|
|
|
|
|
|
listid subject messageid smtpagent diagnosticcode diagnostictype deliverystatus |
74
|
|
|
|
|
|
|
reason lhost rhost smtpcommand feedbacktype action softbounce replycode origin |
75
|
|
|
|
|
|
|
|); |
76
|
2932
|
|
100
|
|
|
56155
|
$thing->{ $_ } = $argvs->{ $_ } // '' for @v1; |
77
|
2932
|
|
50
|
|
|
9458
|
$thing->{'replycode'} ||= Sisimai::SMTP::Reply->find($argvs->{'diagnosticcode'}) || ''; |
|
|
|
66
|
|
|
|
|
78
|
|
|
|
|
|
|
|
79
|
2932
|
|
|
|
|
16771
|
return bless($thing, __PACKAGE__); |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub make { |
83
|
|
|
|
|
|
|
# Another constructor of Sisimai::Data |
84
|
|
|
|
|
|
|
# @param [Hash] argvs |
85
|
|
|
|
|
|
|
# @option argvs [Sisimai::Message] data Data Object |
86
|
|
|
|
|
|
|
# @option argvs [Integeer] delivered Include "delivered" status or not |
87
|
|
|
|
|
|
|
# @return [Array, Undef] List of Sisimai::Data or Undef if the |
88
|
|
|
|
|
|
|
# argument is not Sisimai::Message object |
89
|
2779
|
|
|
2779
|
1
|
4178794
|
my $class = shift; |
90
|
2779
|
|
|
|
|
6709
|
my $argvs = { @_ }; |
91
|
|
|
|
|
|
|
|
92
|
2779
|
100
|
|
|
|
6824
|
return undef unless exists $argvs->{'data'}; |
93
|
2778
|
50
|
|
|
|
7307
|
return undef unless ref $argvs->{'data'} eq 'Sisimai::Message'; |
94
|
2778
|
50
|
|
|
|
7523
|
return undef unless $argvs->{'data'}->ds; |
95
|
2778
|
50
|
|
|
|
13311
|
return undef unless $argvs->{'data'}->rfc822; |
96
|
|
|
|
|
|
|
|
97
|
2778
|
|
|
|
|
11094
|
state $retryindex = Sisimai::Reason->retry; |
98
|
2778
|
|
|
|
|
3665
|
state $rfc822head = Sisimai::RFC5322->HEADERFIELDS('all'); |
99
|
|
|
|
|
|
|
|
100
|
2778
|
|
100
|
|
|
7858
|
my $delivered1 = $argvs->{'delivered'} // 0; |
101
|
2778
|
|
|
|
|
4355
|
my $messageobj = $argvs->{'data'}; |
102
|
2778
|
|
|
|
|
4780
|
my $rfc822data = $messageobj->rfc822; |
103
|
2778
|
|
|
|
|
9042
|
my $objectlist = []; |
104
|
|
|
|
|
|
|
|
105
|
2778
|
|
|
|
|
3564
|
LOOP_DELIVERY_STATUS: for my $e ( @{ $messageobj->ds } ) { |
|
2778
|
|
|
|
|
4508
|
|
106
|
|
|
|
|
|
|
# Create parameters for new() constructor. |
107
|
|
|
|
|
|
|
my $p = { |
108
|
|
|
|
|
|
|
'catch' => $messageobj->catch // undef, |
109
|
|
|
|
|
|
|
'lhost' => $e->{'lhost'} // '', |
110
|
|
|
|
|
|
|
'rhost' => $e->{'rhost'} // '', |
111
|
|
|
|
|
|
|
'alias' => $e->{'alias'} // '', |
112
|
|
|
|
|
|
|
'action' => $e->{'action'} // '', |
113
|
|
|
|
|
|
|
'reason' => $e->{'reason'} // '', |
114
|
|
|
|
|
|
|
'replycode' => $e->{'replycode'} // '', |
115
|
|
|
|
|
|
|
'smtpagent' => $e->{'agent'} // '', |
116
|
|
|
|
|
|
|
'recipient' => $e->{'recipient'} // '', |
117
|
|
|
|
|
|
|
'softbounce' => $e->{'softbounce'} // '', |
118
|
|
|
|
|
|
|
'smtpcommand' => $e->{'command'} // '', |
119
|
|
|
|
|
|
|
'feedbacktype' => $e->{'feedbacktype'} // '', |
120
|
|
|
|
|
|
|
'diagnosticcode' => $e->{'diagnosis'} // '', |
121
|
|
|
|
|
|
|
'diagnostictype' => $e->{'spec'} // '', |
122
|
2944
|
|
100
|
|
|
11985
|
'deliverystatus' => $e->{'status'} // '', |
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
123
|
|
|
|
|
|
|
}; |
124
|
2944
|
100
|
|
|
|
65895
|
unless( $delivered1 ) { |
125
|
|
|
|
|
|
|
# Skip if the value of "deliverystatus" begins with "2." such as 2.1.5 |
126
|
2431
|
100
|
|
|
|
6305
|
next if index($p->{'deliverystatus'}, '2.') == 0; |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
EMAIL_ADDRESS: { |
130
|
|
|
|
|
|
|
# Detect email address from message/rfc822 part |
131
|
2932
|
|
|
|
|
3304
|
for my $f ( @{ $rfc822head->{'addresser'} } ) { |
|
2932
|
|
|
|
|
3126
|
|
|
2932
|
|
|
|
|
6356
|
|
132
|
|
|
|
|
|
|
# Check each header in message/rfc822 part |
133
|
4392
|
|
|
|
|
6375
|
my $h = lc $f; |
134
|
4392
|
100
|
|
|
|
7559
|
next unless exists $rfc822data->{ $h }; |
135
|
2737
|
50
|
|
|
|
5314
|
next unless $rfc822data->{ $h }; |
136
|
|
|
|
|
|
|
|
137
|
2737
|
|
50
|
|
|
12954
|
my $j = Sisimai::Address->find($rfc822data->{ $h }) || []; |
138
|
2737
|
50
|
|
|
|
5701
|
next unless scalar @$j; |
139
|
2737
|
|
|
|
|
8275
|
$p->{'addresser'} = $j->[0]; |
140
|
2737
|
|
|
|
|
4772
|
last; |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
2932
|
100
|
|
|
|
6539
|
unless( $p->{'addresser'} ) { |
144
|
|
|
|
|
|
|
# Fallback: Get the sender address from the header of the bounced |
145
|
|
|
|
|
|
|
# email if the address is not set at loop above. |
146
|
195
|
|
50
|
|
|
977
|
my $j = Sisimai::Address->find($messageobj->{'header'}->{'to'}) || []; |
147
|
195
|
50
|
|
|
|
1129
|
$p->{'addresser'} = $j->[0] if scalar @$j; |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
} |
150
|
2932
|
50
|
|
|
|
5447
|
next unless $p->{'addresser'}; |
151
|
2932
|
50
|
|
|
|
5468
|
next unless $p->{'recipient'}; |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
TIMESTAMP: { |
154
|
|
|
|
|
|
|
# Convert from a time stamp or a date string to a machine time. |
155
|
2932
|
|
|
|
|
3626
|
my $datestring = undef; |
|
2932
|
|
|
|
|
4292
|
|
156
|
2932
|
|
|
|
|
3564
|
my $zoneoffset = 0; |
157
|
2932
|
100
|
|
|
|
2948
|
my @datevalues; push @datevalues, $e->{'date'} if $e->{'date'}; |
|
2932
|
|
|
|
|
6056
|
|
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# Date information did not exist in message/delivery-status part,... |
160
|
2932
|
|
|
|
|
3505
|
for my $f ( @{ $rfc822head->{'date'} } ) { |
|
2932
|
|
|
|
|
5355
|
|
161
|
|
|
|
|
|
|
# Get the value of Date header or other date related header. |
162
|
11728
|
100
|
|
|
|
18244
|
next unless $rfc822data->{ $f }; |
163
|
2552
|
|
|
|
|
3886
|
push @datevalues, $rfc822data->{ $f }; |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
# Set "date" getting from the value of "Date" in the bounce message |
167
|
2932
|
100
|
|
|
|
6853
|
push @datevalues, $messageobj->{'header'}->{'date'} if scalar(@datevalues) < 2; |
168
|
|
|
|
|
|
|
|
169
|
2932
|
|
|
|
|
6295
|
while( my $v = shift @datevalues ) { |
170
|
|
|
|
|
|
|
# Parse each date value in the array |
171
|
2932
|
|
|
|
|
17170
|
$datestring = Sisimai::DateTime->parse($v); |
172
|
2932
|
50
|
|
|
|
7191
|
last if $datestring; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
2932
|
50
|
33
|
|
|
17586
|
if( defined $datestring && $datestring =~ /\A(.+)[ ]+([-+]\d{4})\z/ ) { |
176
|
|
|
|
|
|
|
# Get the value of timezone offset from $datestring |
177
|
|
|
|
|
|
|
# Wed, 26 Feb 2014 06:05:48 -0500 |
178
|
2932
|
|
|
|
|
7067
|
$datestring = $1; |
179
|
2932
|
|
|
|
|
11172
|
$zoneoffset = Sisimai::DateTime->tz2second($2); |
180
|
2932
|
|
|
|
|
6733
|
$p->{'timezoneoffset'} = $2; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
|
183
|
2932
|
|
|
|
|
3989
|
eval { |
184
|
|
|
|
|
|
|
# Convert from the date string to an object then calculate time |
185
|
|
|
|
|
|
|
# zone offset. |
186
|
2932
|
|
|
|
|
15856
|
my $t = Sisimai::Time->strptime($datestring, '%a, %d %b %Y %T'); |
187
|
2932
|
|
50
|
|
|
200987
|
$p->{'timestamp'} = ($t->epoch - $zoneoffset) // undef; |
188
|
|
|
|
|
|
|
}; |
189
|
|
|
|
|
|
|
} |
190
|
2932
|
50
|
|
|
|
32944
|
next unless defined $p->{'timestamp'}; |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
OTHER_TEXT_HEADERS: { |
193
|
|
|
|
|
|
|
# Scan "Received:" header of the original message |
194
|
2932
|
|
50
|
|
|
3354
|
my $recvheader = $argvs->{'data'}->{'header'}->{'received'} || []; |
|
2932
|
|
|
|
|
8810
|
|
195
|
2932
|
100
|
|
|
|
6230
|
if( scalar @$recvheader ) { |
196
|
|
|
|
|
|
|
# Get localhost and remote host name from Received header. |
197
|
2784
|
|
100
|
|
|
6336
|
$e->{'lhost'} ||= shift @{ Sisimai::RFC5322->received($recvheader->[0]) }; |
|
1034
|
|
|
|
|
3860
|
|
198
|
2784
|
|
100
|
|
|
7101
|
$e->{'rhost'} ||= pop @{ Sisimai::RFC5322->received($recvheader->[-1]) }; |
|
780
|
|
|
|
|
1926
|
|
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
|
201
|
2932
|
|
|
|
|
5135
|
for my $v ('rhost', 'lhost') { |
202
|
5864
|
|
|
|
|
10203
|
$p->{ $v } =~ y/[]()//d; # Remove square brackets and curly brackets from the host variable |
203
|
5864
|
|
|
|
|
10012
|
$p->{ $v } =~ s/\A.+=//; # Remove string before "=" |
204
|
5864
|
50
|
|
|
|
11658
|
chop $p->{ $v } if substr($p->{ $v }, -1, 1) eq "\r"; # Remove CR at the end of the value |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
# Check space character in each value and get the first element |
207
|
5864
|
100
|
|
|
|
10782
|
$p->{ $v } = (split(' ', $p->{ $v }, 2))[0] if rindex($p->{ $v }, ' ') > -1; |
208
|
5864
|
100
|
|
|
|
10531
|
chop $p->{ $v } if substr($p->{ $v }, -1, 1) eq '.'; # Remove "." at the end of the value |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
# Subject: header of the original message |
212
|
2932
|
|
50
|
|
|
7639
|
$p->{'subject'} = $rfc822data->{'subject'} // ''; |
213
|
2932
|
50
|
|
|
|
5804
|
chop $p->{'subject'} if substr($p->{'subject'}, -1, 1) eq "\r"; |
214
|
|
|
|
|
|
|
|
215
|
2932
|
100
|
100
|
|
|
10846
|
if( $p->{'listid'} = $rfc822data->{'list-id'} // '' ) { |
216
|
|
|
|
|
|
|
# Get the value of List-Id header: "List name " |
217
|
32
|
100
|
|
|
|
243
|
$p->{'listid'} = $1 if $p->{'listid'} =~ /\A.*([<].+[>]).*\z/; |
218
|
32
|
|
|
|
|
77
|
$p->{'listid'} =~ y/<>//d; |
219
|
32
|
50
|
|
|
|
129
|
chop $p->{'listid'} if substr($p->{'listid'}, -1, 1) eq "\r"; |
220
|
32
|
50
|
|
|
|
93
|
$p->{'listid'} = '' if rindex($p->{'listid'}, ' ') > -1; |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
2932
|
100
|
100
|
|
|
8886
|
if( $p->{'messageid'} = $rfc822data->{'message-id'} // '' ) { |
224
|
|
|
|
|
|
|
# Leave only string inside of angle brackets(<>) |
225
|
2512
|
100
|
|
|
|
5834
|
$p->{'messageid'} = $1 if $p->{'messageid'} =~ /\A([^ ]+)[ ].*/; |
226
|
2512
|
100
|
|
|
|
16091
|
$p->{'messageid'} = $1 if $p->{'messageid'} =~ /[<]([^ ]+?)[>]/; |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
CHECK_DELIVERY_STATUS_VALUE: { |
230
|
|
|
|
|
|
|
# Cleanup the value of "Diagnostic-Code:" header |
231
|
2932
|
50
|
|
|
|
4398
|
chop $p->{'diagnosticcode'} if substr($p->{'diagnosticcode'}, -1, 1) eq "\r"; |
|
2932
|
|
|
|
|
6556
|
|
232
|
|
|
|
|
|
|
|
233
|
2932
|
100
|
|
|
|
5021
|
if( $p->{'diagnosticcode'} ) { |
234
|
|
|
|
|
|
|
# Count the number of D.S.N. and SMTP Reply Code |
235
|
2880
|
|
|
|
|
15280
|
my $vs = Sisimai::SMTP::Status->find($p->{'diagnosticcode'}); |
236
|
2880
|
|
|
|
|
12201
|
my $vr = Sisimai::SMTP::Reply->find($p->{'diagnosticcode'}); |
237
|
2880
|
|
|
|
|
3999
|
my $vm = 0; |
238
|
|
|
|
|
|
|
|
239
|
2880
|
100
|
|
|
|
4694
|
if( $vs ) { |
240
|
|
|
|
|
|
|
# How many times does the D.S.N. appeared |
241
|
1388
|
|
|
|
|
19364
|
$vm += 1 while $p->{'diagnosticcode'} =~ /\b\Q$vs\E\b/g; |
242
|
1388
|
100
|
|
|
|
5454
|
$p->{'deliverystatus'} = $vs if $vs =~ /\A[45][.][1-9][.][1-9]\z/; |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
|
245
|
2880
|
100
|
|
|
|
5277
|
if( $vr ) { |
246
|
|
|
|
|
|
|
# How many times does the SMTP reply code appeared |
247
|
2040
|
|
|
|
|
20910
|
$vm += 1 while $p->{'diagnosticcode'} =~ /\b$vr\b/g; |
248
|
2040
|
|
66
|
|
|
7110
|
$p->{'replycode'} ||= $vr; |
249
|
|
|
|
|
|
|
} |
250
|
|
|
|
|
|
|
|
251
|
2880
|
100
|
|
|
|
5874
|
if( $vm > 2 ) { |
252
|
|
|
|
|
|
|
# Build regular expression for removing string like '550-5.1.1' |
253
|
|
|
|
|
|
|
# from the value of "diagnosticcode" |
254
|
181
|
|
|
|
|
2709
|
my $re = qr/[ ]$vr[- ](?:\Q$vs\E)?/; |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
# 550-5.7.1 [192.0.2.222] Our system has detected that this message is |
257
|
|
|
|
|
|
|
# 550-5.7.1 likely unsolicited mail. To reduce the amount of spam sent to Gmail, |
258
|
|
|
|
|
|
|
# 550-5.7.1 this message has been blocked. Please visit |
259
|
|
|
|
|
|
|
# 550 5.7.1 https://support.google.com/mail/answer/188131 for more information. |
260
|
181
|
|
|
|
|
1820
|
$p->{'diagnosticcode'} =~ s/$re/ /g; |
261
|
181
|
|
|
|
|
673
|
$p->{'diagnosticcode'} =~ s|.+||i; |
262
|
181
|
|
|
|
|
668
|
$p->{'diagnosticcode'} = Sisimai::String->sweep($p->{'diagnosticcode'}); |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
} |
265
|
2932
|
50
|
0
|
|
|
6385
|
$p->{'diagnostictype'} ||= 'X-UNIX' if $p->{'reason'} eq 'mailererror'; |
266
|
2932
|
100
|
100
|
|
|
10415
|
$p->{'diagnostictype'} ||= 'SMTP' unless $p->{'reason'} =~ /\A(?:feedback|vacation)\z/; |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
# Check the value of SMTP command |
270
|
2932
|
100
|
|
|
|
8712
|
$p->{'smtpcommand'} = '' unless $p->{'smtpcommand'} =~ /\A(?:EHLO|HELO|MAIL|RCPT|DATA|QUIT)\z/; |
271
|
2932
|
|
|
|
|
5066
|
$p->{'origin'} = $argvs->{'origin'}; # Set the path to the original email |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
# Check "Action:" field |
274
|
2932
|
100
|
|
|
|
6579
|
next if length $p->{'action'}; |
275
|
1332
|
100
|
100
|
|
|
7472
|
if( $p->{'reason'} eq 'expired' ) { |
|
|
100
|
|
|
|
|
|
276
|
|
|
|
|
|
|
# Action: delayed |
277
|
80
|
|
|
|
|
202
|
$p->{'action'} = 'delayed'; |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
} elsif( index($p->{'deliverystatus'}, '5') == 0 || index($p->{'deliverystatus'}, '4') == 0 ) { |
280
|
|
|
|
|
|
|
# Action: failed |
281
|
590
|
|
|
|
|
1131
|
$p->{'action'} = 'failed'; |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
} |
284
|
2932
|
50
|
|
|
|
18560
|
next unless my $o = __PACKAGE__->new(%$p); |
285
|
|
|
|
|
|
|
|
286
|
2932
|
100
|
100
|
|
|
11532
|
if( $o->reason eq '' || exists $retryindex->{ $o->reason } ) { |
287
|
|
|
|
|
|
|
# Decide the reason of email bounce |
288
|
2425
|
100
|
|
|
|
14572
|
my $r; $r = Sisimai::Rhost->get($o) if Sisimai::Rhost->match($o->rhost); |
|
2425
|
|
|
|
|
5582
|
|
289
|
2425
|
100
|
100
|
|
|
7299
|
$r ||= Sisimai::Rhost->get($o, $o->destination) if Sisimai::Rhost->match($o->destination); |
290
|
2425
|
|
66
|
|
|
15380
|
$r ||= Sisimai::Reason->get($o); |
291
|
2425
|
|
50
|
|
|
5285
|
$r ||= 'undefined'; |
292
|
2425
|
|
|
|
|
4868
|
$o->reason($r); |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
|
295
|
2932
|
100
|
100
|
|
|
18542
|
if( $o->reason eq 'delivered' || $o->reason eq 'feedback' || $o->reason eq 'vacation' ) { |
|
|
|
100
|
|
|
|
|
296
|
|
|
|
|
|
|
# The value of reason is "delivered", "vacation" or "feedback". |
297
|
146
|
|
|
|
|
1428
|
$o->softbounce(-1); |
298
|
146
|
100
|
|
|
|
772
|
$o->replycode('') unless $o->reason eq 'delivered'; |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
} else { |
301
|
|
|
|
|
|
|
# Bounce message which reason is "feedback" or "vacation" does |
302
|
|
|
|
|
|
|
# not have the value of "deliverystatus". |
303
|
2786
|
50
|
|
|
|
32496
|
unless( length $o->softbounce ) { |
304
|
|
|
|
|
|
|
# Set the value of softbounce |
305
|
2786
|
|
|
|
|
15211
|
my $textasargv = $p->{'deliverystatus'}.' '.$p->{'diagnosticcode'}; |
306
|
2786
|
100
|
|
|
|
8016
|
substr($textasargv, 0, 1, '') if substr($textasargv, 0, 1) eq ' '; |
307
|
2786
|
|
|
|
|
4979
|
my $softorhard = Sisimai::SMTP::Error->soft_or_hard($o->reason, $textasargv); |
308
|
|
|
|
|
|
|
|
309
|
2786
|
50
|
|
|
|
4718
|
if( $softorhard ) { |
310
|
|
|
|
|
|
|
# Returned value is "soft" or "hard" |
311
|
2786
|
100
|
|
|
|
8236
|
$o->softbounce($softorhard eq 'soft' ? 1 : 0); |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
} else { |
314
|
|
|
|
|
|
|
# Returned value is an empty string or undef |
315
|
0
|
|
|
|
|
0
|
$o->softbounce(-1); |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
|
319
|
2786
|
100
|
|
|
|
14515
|
unless( $o->deliverystatus ) { |
320
|
|
|
|
|
|
|
# Set pseudo status code |
321
|
611
|
|
|
|
|
2892
|
my $textasargv = $o->replycode.' '.$p->{'diagnosticcode'}; |
322
|
611
|
100
|
|
|
|
3982
|
substr($textasargv, 0, 1, '') if substr($textasargv, 0, 1) eq ' '; |
323
|
|
|
|
|
|
|
|
324
|
611
|
|
|
|
|
1573
|
my $getchecked = Sisimai::SMTP::Error->is_permanent($textasargv); |
325
|
611
|
100
|
|
|
|
1422
|
my $tmpfailure = defined $getchecked ? ( $getchecked == 1 ? 0 : 1 ) : 0; |
|
|
100
|
|
|
|
|
|
326
|
|
|
|
|
|
|
|
327
|
611
|
50
|
|
|
|
1348
|
if( my $pseudocode = Sisimai::SMTP::Status->code($o->reason, $tmpfailure) ) { |
328
|
|
|
|
|
|
|
# Set the value of "deliverystatus" and "softbounce". |
329
|
611
|
|
|
|
|
1323
|
$o->deliverystatus($pseudocode); |
330
|
611
|
50
|
|
|
|
2919
|
if( $o->softbounce == -1 ) { |
331
|
|
|
|
|
|
|
# Set the value of "softbounce" again when the value is -1 |
332
|
0
|
0
|
|
|
|
0
|
if( my $softorhard = Sisimai::SMTP::Error->soft_or_hard($o->reason, $pseudocode) ) { |
333
|
|
|
|
|
|
|
# Returned value is "soft" or "hard" |
334
|
0
|
0
|
|
|
|
0
|
$o->softbounce($softorhard eq 'soft' ? 1 : 0); |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
} else { |
337
|
|
|
|
|
|
|
# Returned value is an empty string or undef |
338
|
0
|
|
|
|
|
0
|
$o->softbounce(-1); |
339
|
|
|
|
|
|
|
} |
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
|
344
|
2786
|
100
|
|
|
|
13037
|
if( $o->replycode ) { |
345
|
|
|
|
|
|
|
# Check both of the first digit of "deliverystatus" and "replycode" |
346
|
2036
|
|
|
|
|
8285
|
my $d1 = substr($o->deliverystatus, 0, 1); |
347
|
2036
|
|
|
|
|
7528
|
my $r1 = substr($o->replycode, 0, 1); |
348
|
2036
|
100
|
|
|
|
7818
|
$o->replycode('') unless $d1 eq $r1; |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
} |
351
|
2932
|
|
|
|
|
24335
|
push @$objectlist, $o; |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
} # End of for(LOOP_DELIVERY_STATUS) |
354
|
|
|
|
|
|
|
|
355
|
2778
|
|
|
|
|
10554
|
return $objectlist; |
356
|
|
|
|
|
|
|
} |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
sub damn { |
359
|
|
|
|
|
|
|
# Convert from object to hash reference |
360
|
|
|
|
|
|
|
# @return [Hash] Data in Hash reference |
361
|
1276
|
|
|
1276
|
1
|
1533218
|
my $self = shift; |
362
|
1276
|
|
|
|
|
1727
|
my $data = undef; |
363
|
|
|
|
|
|
|
|
364
|
1276
|
|
|
|
|
2271
|
eval { |
365
|
1276
|
|
|
|
|
1904
|
my $v = {}; |
366
|
1276
|
|
|
|
|
2072
|
state $stringdata = [qw| |
367
|
|
|
|
|
|
|
token lhost rhost listid alias reason subject messageid smtpagent |
368
|
|
|
|
|
|
|
smtpcommand destination diagnosticcode senderdomain deliverystatus |
369
|
|
|
|
|
|
|
timezoneoffset feedbacktype diagnostictype action replycode catch |
370
|
|
|
|
|
|
|
softbounce origin |
371
|
|
|
|
|
|
|
|]; |
372
|
|
|
|
|
|
|
|
373
|
1276
|
|
|
|
|
2454
|
for my $e ( @$stringdata ) { |
374
|
|
|
|
|
|
|
# Copy string data |
375
|
28072
|
|
100
|
|
|
122881
|
$v->{ $e } = $self->$e // ''; |
376
|
|
|
|
|
|
|
} |
377
|
1276
|
|
|
|
|
6691
|
$v->{'addresser'} = $self->addresser->address; |
378
|
1276
|
|
|
|
|
11206
|
$v->{'recipient'} = $self->recipient->address; |
379
|
1276
|
|
|
|
|
8499
|
$v->{'timestamp'} = $self->timestamp->epoch; |
380
|
1276
|
|
|
|
|
14797
|
$data = $v; |
381
|
|
|
|
|
|
|
}; |
382
|
1276
|
|
|
|
|
19006
|
return $data; |
383
|
|
|
|
|
|
|
} |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
sub dump { |
386
|
|
|
|
|
|
|
# Data dumper |
387
|
|
|
|
|
|
|
# @param [String] type Data format: json, yaml |
388
|
|
|
|
|
|
|
# @return [String, Undef] Dumped data or Undef if the value of first |
389
|
|
|
|
|
|
|
# argument is neither "json" nor "yaml" |
390
|
639
|
|
|
639
|
0
|
6879076
|
my $self = shift; |
391
|
639
|
|
50
|
|
|
2436
|
my $type = shift || 'json'; |
392
|
639
|
50
|
|
|
|
3969
|
return undef unless $type =~ /\A(?:json|yaml)\z/; |
393
|
|
|
|
|
|
|
|
394
|
639
|
|
|
|
|
2151
|
my $referclass = 'Sisimai::Data::'.uc($type); |
395
|
639
|
|
|
|
|
1416
|
my $modulepath = 'Sisimai/Data/'.uc($type).'.pm'; |
396
|
|
|
|
|
|
|
|
397
|
639
|
|
|
|
|
4448
|
require $modulepath; |
398
|
639
|
|
|
|
|
2451
|
return $referclass->dump($self); |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
1; |
402
|
|
|
|
|
|
|
__END__ |