File Coverage

lib/Sisimai/Lhost/MessagingServer.pm
Criterion Covered Total %
statement 64 66 96.9
branch 34 40 85.0
condition 16 26 61.5
subroutine 6 6 100.0
pod 2 2 100.0
total 122 140 87.1


line stmt bran cond sub pod time code
1             package Sisimai::Lhost::MessagingServer;
2 37     37   3365 use parent 'Sisimai::Lhost';
  37         65  
  37         196  
3 37     37   2871 use v5.26;
  37         108  
4 37     37   285 use strict;
  37         67  
  37         822  
5 37     37   134 use warnings;
  37         58  
  37         28595  
6              
7 1     1 1 3 sub description { 'Oracle Communications Messaging Server: https://docs.oracle.com/en/industries/communications/messaging-server/index.html' }
8             sub inquire {
9             # Detect an error from Oracle Communications Messaging Server
10             # @param [Hash] mhead Message headers of a bounce email
11             # @param [String] mbody Message body of a bounce email
12             # @return [Hash] Bounce data list and message/rfc822 part
13             # @return [undef] failed to decode or the arguments are missing
14             # @since v4.1.3
15 931     931 1 3346 my $class = shift;
16 931   100     2062 my $mhead = shift // return undef;
17 930   100     2234 my $mbody = shift // return undef;
18 929 100 50     1181 my $match = 0; $match ||= 1 if rindex($mhead->{'content-type'}, 'Boundary_(ID_') > -1;
  929         2564  
19 929 100 50     2260 $match ||= 1 if index($mhead->{'subject'}, 'Delivery Notification: ') == 0;
20 929 100       2296 return undef unless $match;
21              
22 61         131 state $indicators = __PACKAGE__->INDICATORS;
23 61         102 state $boundaries = ['Content-Type: message/rfc822', 'Return-path: '];
24 61         89 state $startingof = {'message' => ['This report relates to a message you sent with the following header fields:']};
25              
26 61         185 my $dscontents = [__PACKAGE__->DELIVERYSTATUS]; my $v = undef;
  61         101  
27 61         251 my $emailparts = Sisimai::RFC5322->part($mbody, $boundaries);
28 61         85 my $readcursor = 0; # (Integer) Points the current cursor position
29 61         62 my $recipients = 0; # (Integer) The number of 'Final-Recipient' header
30              
31 61         541 for my $e ( split("\n", $emailparts->[0]) ) {
32             # Read error messages and delivery status lines from the head of the email to the previous
33             # line of the beginning of the original message.
34 2028 100       2207 unless( $readcursor ) {
35             # Beginning of the bounce message or message/delivery-status part
36 61 50       256 $readcursor |= $indicators->{'deliverystatus'} if index($e, $startingof->{'message'}->[0]) == 0;
37 61         75 next;
38             }
39 1967 100 66     3756 next if ($readcursor & $indicators->{'deliverystatus'}) == 0 || $e eq "";
40              
41             # --Boundary_(ID_0000000000000000000000)
42             # Content-type: text/plain; charset=us-ascii
43             # Content-language: en-US
44             #
45             # This report relates to a message you sent with the following header fields:
46             #
47             # Message-id:
48             # Date: Fri, 21 Nov 2014 23:34:45 +0900
49             # From: Shironeko
50             # To: kijitora@example.jp
51             # Subject: Nyaaaaaaaaaaaaaaaaaaaaaan
52             #
53             # Your message cannot be delivered to the following recipients:
54             #
55             # Recipient address: kijitora@example.jp
56             # Reason: Remote SMTP server has rejected address
57             # Diagnostic code: smtp;550 5.1.1 ... User Unknown
58             # Remote system: dns;mx.example.jp (TCP|17.111.174.67|47323|192.0.2.225|25) (6jo.example.jp ESMTP SENDMAIL-VM)
59 1526         1292 $v = $dscontents->[-1];
60              
61 1526 100 100     2796 if( Sisimai::String->aligned(\$e, [' Recipient address: ', '@', '.']) ||
    100          
    100          
    100          
    100          
62             Sisimai::String->aligned(\$e, [' Original address: ', '@', '.']) ) {
63             # Recipient address: @smtp.example.net:kijitora@server
64             # Original address: kijitora@example.jp
65 71         404 my $cv = Sisimai::Address->s3s4(substr($e, rindex($e, ' ') + 1),);
66 71 100       206 next unless Sisimai::Address->is_emailaddress($cv);
67              
68 66 100 66     253 if( $v->{'recipient'} && $cv ne $v->{'recipient'}) {
69             # There are multiple recipient addresses in the message body.
70 5         23 push @$dscontents, __PACKAGE__->DELIVERYSTATUS;
71 5         10 $v = $dscontents->[-1];
72             }
73 66         89 $v->{'recipient'} = $cv;
74 66         143 $recipients++;
75              
76             } elsif( index($e, ' Date: ') == 0 ) {
77             # Date: Fri, 21 Nov 2014 23:34:45 +0900
78 61         186 $v->{'date'} = substr($e, index($e, ':') + 2,);
79              
80             } elsif( index($e, ' Reason: ') == 0 ) {
81             # Reason: Remote SMTP server has rejected address
82 66         177 $v->{'diagnosis'} = substr($e, index($e, ':') + 2,);
83              
84             } elsif( index($e, ' Diagnostic code: ') == 0 ) {
85             # Diagnostic code: smtp;550 5.1.1 ... User Unknown
86 46         70 my $p1 = index($e, ':');
87 46         62 my $p2 = index($e, ';');
88 46         126 $v->{'spec'} = uc substr($e, $p1 + 2, $p2 - $p1 - 2);
89 46         108 $v->{'diagnosis'} = substr($e, $p2 + 1,);
90              
91             } elsif( index($e, ' Remote system: ') == 0 ) {
92             # Remote system: dns;mx.example.jp (TCP|17.111.174.67|47323|192.0.2.225|25)
93             # (6jo.example.jp ESMTP SENDMAIL-VM)
94 46         50 my $p1 = index($e, ';');
95 46         76 my $p2 = index($e, '(');
96              
97 46         81 my $remotehost = substr($e, $p1 + 1, $p2 - $p1 - 2);
98 46         2103 my $sessionlog = [split('|', substr($e, $p2,))];
99 46         214 $v->{'rhost'} = $remotehost;
100              
101             # The value does not include ".", use IP address instead.
102             # (TCP|17.111.174.67|47323|192.0.2.225|25)
103 46 50       278 next unless $sessionlog->[0] eq '(TCP';
104 0         0 $v->{'lhost'} = $sessionlog->[1];
105 0 0       0 $v->{'rhost'} = $sessionlog->[3] unless index($remotehost, '.') > 1;
106              
107             } else {
108             # Original-envelope-id: 0NFC009FLKOUVMA0@mr21p30im-asmtp004.me.com
109             # Reporting-MTA: dns;mr21p30im-asmtp004.me.com (tcp-daemon)
110             # Arrival-date: Thu, 29 Apr 2014 23:34:45 +0000 (GMT)
111             #
112             # Original-recipient: rfc822;kijitora@example.jp
113             # Final-recipient: rfc822;kijitora@example.jp
114             # Action: failed
115             # Status: 5.1.1 (Remote SMTP server has rejected address)
116             # Remote-MTA: dns;mx.example.jp (TCP|17.111.174.67|47323|192.0.2.225|25)
117             # (6jo.example.jp ESMTP SENDMAIL-VM)
118             # Diagnostic-code: smtp;550 5.1.1 ... User Unknown
119             #
120 1236 100       2862 if( index($e, 'Status: ') == 0 ) {
    100          
    100          
121             # Status: 5.1.1 (Remote SMTP server has rejected address)
122 66         74 my $p1 = index($e, ':');
123 66         106 my $p2 = index($e, '(');
124 66         131 $v->{'status'} = substr($e, $p1 + 2, $p2 - $p1 - 3);
125 66   33     161 $v->{'diagnosis'} ||= substr($e, $p2 + 1, index($e, ')') - $p2 - 1);
126              
127             } elsif( index($e, 'Arrival-Date: ') == 0 ) {
128             # Arrival-date: Thu, 29 Apr 2014 23:34:45 +0000 (GMT)
129 51   33     149 $v->{'date'} ||= substr($e, index($e, ':') + 2,);
130              
131             } elsif( index($e, 'Reporting-MTA: ') == 0 ) {
132             # Reporting-MTA: dns;mr21p30im-asmtp004.me.com (tcp-daemon)
133 61         109 my $localhost = substr($e, index($e, ';') + 1,);
134 61   33     187 $v->{'lhost'} ||= $localhost;
135 61 50       166 $v->{'lhost'} = $localhost unless index($v->{'lhost'}, '.') > 0;
136             }
137             } # End of error message part
138             }
139 61 50       263 return undef unless $recipients;
140 61         278 return {"ds" => $dscontents, "rfc822" => $emailparts->[1]};
141             }
142              
143             1;
144             __END__