File Coverage

lib/Sisimai/Lhost/MessagingServer.pm
Criterion Covered Total %
statement 72 74 97.3
branch 36 42 85.7
condition 16 26 61.5
subroutine 6 6 100.0
pod 2 2 100.0
total 132 150 88.0


line stmt bran cond sub pod time code
1             package Sisimai::Lhost::MessagingServer;
2 36     36   4498 use parent 'Sisimai::Lhost';
  36         78  
  36         252  
3 36     36   2814 use v5.26;
  36         130  
4 36     36   189 use strict;
  36         86  
  36         1001  
5 36     36   184 use warnings;
  36         114  
  36         39121  
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 933     933 1 4906 my $class = shift;
16 933   100     3339 my $mhead = shift // return undef;
17 932   100     2799 my $mbody = shift // return undef;
18 931 100 50     1590 my $match = 0; $match ||= 1 if rindex($mhead->{'content-type'}, 'Boundary_(ID_') > -1;
  931         5466  
19 931 100 50     4408 $match ||= 1 if index($mhead->{'subject'}, 'Delivery Notification: ') == 0;
20 931 100       3574 return undef unless $match;
21              
22 61         358 state $indicators = __PACKAGE__->INDICATORS;
23 61         111 state $boundaries = ['Content-Type: message/rfc822', 'Return-path: '];
24 61         114 state $startingof = {'message' => ['This report relates to a message you sent with the following header fields:']};
25 61         108 state $messagesof = {'hostunknown' => ['Illegal host/domain name found']};
26              
27 61         340 my $dscontents = [__PACKAGE__->DELIVERYSTATUS]; my $v = undef;
  61         131  
28 61         347 my $emailparts = Sisimai::RFC5322->part($mbody, $boundaries);
29 61         135 my $readcursor = 0; # (Integer) Points the current cursor position
30 61         118 my $recipients = 0; # (Integer) The number of 'Final-Recipient' header
31              
32 61         766 for my $e ( split("\n", $emailparts->[0]) ) {
33             # Read error messages and delivery status lines from the head of the email to the previous
34             # line of the beginning of the original message.
35 2028 100       3351 unless( $readcursor ) {
36             # Beginning of the bounce message or message/delivery-status part
37 61 50       355 $readcursor |= $indicators->{'deliverystatus'} if index($e, $startingof->{'message'}->[0]) == 0;
38 61         112 next;
39             }
40 1967 100 66     5878 next if ($readcursor & $indicators->{'deliverystatus'}) == 0 || $e eq "";
41              
42             # --Boundary_(ID_0000000000000000000000)
43             # Content-type: text/plain; charset=us-ascii
44             # Content-language: en-US
45             #
46             # This report relates to a message you sent with the following header fields:
47             #
48             # Message-id:
49             # Date: Fri, 21 Nov 2014 23:34:45 +0900
50             # From: Shironeko
51             # To: kijitora@example.jp
52             # Subject: Nyaaaaaaaaaaaaaaaaaaaaaan
53             #
54             # Your message cannot be delivered to the following recipients:
55             #
56             # Recipient address: kijitora@example.jp
57             # Reason: Remote SMTP server has rejected address
58             # Diagnostic code: smtp;550 5.1.1 ... User Unknown
59             # Remote system: dns;mx.example.jp (TCP|17.111.174.67|47323|192.0.2.225|25) (6jo.example.jp ESMTP SENDMAIL-VM)
60 1526         2031 $v = $dscontents->[-1];
61              
62 1526 100 100     4127 if( Sisimai::String->aligned(\$e, [' Recipient address: ', '@', '.']) ||
    100          
    100          
    100          
    100          
63             Sisimai::String->aligned(\$e, [' Original address: ', '@', '.']) ) {
64             # Recipient address: @smtp.example.net:kijitora@server
65             # Original address: kijitora@example.jp
66 71         728 my $cv = Sisimai::Address->s3s4(substr($e, rindex($e, ' ') + 1),);
67 71 100       344 next unless Sisimai::Address->is_emailaddress($cv);
68              
69 66 100 66     395 if( $v->{'recipient'} && $cv ne $v->{'recipient'}) {
70             # There are multiple recipient addresses in the message body.
71 5         28 push @$dscontents, __PACKAGE__->DELIVERYSTATUS;
72 5         13 $v = $dscontents->[-1];
73             }
74 66         133 $v->{'recipient'} = $cv;
75 66         179 $recipients++;
76              
77             } elsif( index($e, ' Date: ') == 0 ) {
78             # Date: Fri, 21 Nov 2014 23:34:45 +0900
79 61         297 $v->{'date'} = substr($e, index($e, ':') + 2,);
80              
81             } elsif( index($e, ' Reason: ') == 0 ) {
82             # Reason: Remote SMTP server has rejected address
83 66         321 $v->{'diagnosis'} = substr($e, index($e, ':') + 2,);
84              
85             } elsif( index($e, ' Diagnostic code: ') == 0 ) {
86             # Diagnostic code: smtp;550 5.1.1 ... User Unknown
87 46         98 my $p1 = index($e, ':');
88 46         80 my $p2 = index($e, ';');
89 46         178 $v->{'spec'} = uc substr($e, $p1 + 2, $p2 - $p1 - 2);
90 46         184 $v->{'diagnosis'} = substr($e, $p2 + 1,);
91              
92             } elsif( index($e, ' Remote system: ') == 0 ) {
93             # Remote system: dns;mx.example.jp (TCP|17.111.174.67|47323|192.0.2.225|25)
94             # (6jo.example.jp ESMTP SENDMAIL-VM)
95 46         76 my $p1 = index($e, ';');
96 46         85 my $p2 = index($e, '(');
97              
98 46         116 my $remotehost = substr($e, $p1 + 1, $p2 - $p1 - 2);
99 46         3151 my $sessionlog = [split('|', substr($e, $p2,))];
100 46         366 $v->{'rhost'} = $remotehost;
101              
102             # The value does not include ".", use IP address instead.
103             # (TCP|17.111.174.67|47323|192.0.2.225|25)
104 46 50       400 next unless $sessionlog->[0] eq '(TCP';
105 0         0 $v->{'lhost'} = $sessionlog->[1];
106 0 0       0 $v->{'rhost'} = $sessionlog->[3] unless index($remotehost, '.') > 1;
107              
108             } else {
109             # Original-envelope-id: 0NFC009FLKOUVMA0@mr21p30im-asmtp004.me.com
110             # Reporting-MTA: dns;mr21p30im-asmtp004.me.com (tcp-daemon)
111             # Arrival-date: Thu, 29 Apr 2014 23:34:45 +0000 (GMT)
112             #
113             # Original-recipient: rfc822;kijitora@example.jp
114             # Final-recipient: rfc822;kijitora@example.jp
115             # Action: failed
116             # Status: 5.1.1 (Remote SMTP server has rejected address)
117             # Remote-MTA: dns;mx.example.jp (TCP|17.111.174.67|47323|192.0.2.225|25)
118             # (6jo.example.jp ESMTP SENDMAIL-VM)
119             # Diagnostic-code: smtp;550 5.1.1 ... User Unknown
120             #
121 1236 100       4455 if( index($e, 'Status: ') == 0 ) {
    100          
    100          
122             # Status: 5.1.1 (Remote SMTP server has rejected address)
123 66         109 my $p1 = index($e, ':');
124 66         112 my $p2 = index($e, '(');
125 66         246 $v->{'status'} = substr($e, $p1 + 2, $p2 - $p1 - 3);
126 66   33     323 $v->{'diagnosis'} ||= substr($e, $p2 + 1, index($e, ')') - $p2 - 1);
127              
128             } elsif( index($e, 'Arrival-Date: ') == 0 ) {
129             # Arrival-date: Thu, 29 Apr 2014 23:34:45 +0000 (GMT)
130 51   33     196 $v->{'date'} ||= substr($e, index($e, ':') + 2,);
131              
132             } elsif( index($e, 'Reporting-MTA: ') == 0 ) {
133             # Reporting-MTA: dns;mr21p30im-asmtp004.me.com (tcp-daemon)
134 61         157 my $localhost = substr($e, index($e, ';') + 1,);
135 61   33     350 $v->{'lhost'} ||= $localhost;
136 61 50       238 $v->{'lhost'} = $localhost unless index($v->{'lhost'}, '.') > 0;
137             }
138             } # End of error message part
139             }
140 61 50       432 return undef unless $recipients;
141              
142 61         144 for my $e ( @$dscontents ) {
143 66         268 $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'});
144              
145 66         228 SESSION: for my $r ( keys %$messagesof ) {
146             # Verify each regular expression of session errors
147 66 100       204 next unless grep { index($e->{'diagnosis'}, $_) > -1 } $messagesof->{ $r }->@*;
  66         354  
148 5         13 $e->{'reason'} = $r;
149 5         12 last;
150             }
151             }
152 61         426 return {"ds" => $dscontents, "rfc822" => $emailparts->[1]};
153             }
154              
155             1;
156             __END__