File Coverage

lib/Sisimai/Lhost/EZweb.pm
Criterion Covered Total %
statement 69 71 97.1
branch 41 50 82.0
condition 22 24 91.6
subroutine 6 6 100.0
pod 2 2 100.0
total 140 153 91.5


line stmt bran cond sub pod time code
1             package Sisimai::Lhost::EZweb;
2 36     36   3427 use parent 'Sisimai::Lhost';
  36         54  
  36         189  
3 36     36   2388 use v5.26;
  36         177  
4 36     36   146 use strict;
  36         56  
  36         808  
5 36     36   140 use warnings;
  36         61  
  36         28828  
6              
7 1     1 1 3 sub description { 'au EZweb: https://www.au.com/mobile/' }
8             sub inquire {
9             # Detect an error from EZweb
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.0.0
15 906     906 1 2560 my $class = shift;
16 906   100     1814 my $mhead = shift // return undef;
17 905   100     1874 my $mbody = shift // return undef;
18              
19             # Pre-process email headers of NON-STANDARD bounce message au by EZweb, as known as ezweb.ne.jp.
20             # Subject: Mail System Error - Returned Mail
21             # From:
22             # Received: from ezweb.ne.jp (wmflb12na02.ezweb.ne.jp [222.15.69.197])
23             # Received: from nmomta.auone-net.jp ([aaa.bbb.ccc.ddd]) by ...
24 904 100       1222 my $match = 0; $match++ if rindex($mhead->{'from'}, 'Postmaster@ezweb.ne.jp') > -1;
  904         2003  
25 904 50       2185 $match++ if rindex($mhead->{'from'}, 'Postmaster@au.com') > -1;
26 904 100       1805 $match++ if $mhead->{'subject'} eq 'Mail System Error - Returned Mail';
27 904 100       1895 $match++ if grep { rindex($_, 'ezweb.ne.jp (EZweb Mail) with') > -1 } $mhead->{'received'}->@*;
  1710         3393  
28 904 50       1295 $match++ if grep { rindex($_, '.au.com (') > -1 } $mhead->{'received'}->@*;
  1710         2895  
29 904 100       2019 if( defined $mhead->{'message-id'} ) {
30 842 100       2174 $match++ if substr($mhead->{'message-id'}, -13, 13) eq '.ezweb.ne.jp>';
31 842 50       1842 $match++ if substr($mhead->{'message-id'}, -8, 8) eq '.au.com>';
32             }
33 904 100       2177 return undef if $match < 2;
34              
35 41         155 require Sisimai::SMTP::Command;
36 41         88 state $indicators = __PACKAGE__->INDICATORS;
37 41         63 state $boundaries = ["--------------------------------------------------", "Content-Type: message/rfc822"];
38 41         82 state $startingof = {"message" => ['The user(s) ', 'Your message ', 'Each of the following', '<']};
39 41         57 state $unpaiduser = [
40             # http://www.naruhodo-au.kddi.com/qa3429203.html
41             # The recipient may be unpaid user...?
42             "The user(s) account is disabled.",
43             "The user(s) account is temporarily limited.",
44             ];
45              
46 41         230 my $fieldtable = Sisimai::RFC1894->FIELDTABLE;
47 41         168 my $dscontents = [__PACKAGE__->DELIVERYSTATUS]; my $v = undef;
  41         78  
48 41         177 my $emailparts = Sisimai::RFC5322->part($mbody, $boundaries);
49 41         59 my $readcursor = 0; # Points the current cursor position
50 41         70 my $recipients = 0; # The number of 'Final-Recipient' header
51              
52 41         306 for my $e ( split("\n", $emailparts->[0]) ) {
53             # Read error messages and delivery status lines from the head of the email to the previous
54             # line of the beginning of the original message.
55 533 100       683 unless( $readcursor ) {
56             # Beginning of the bounce message or message/delivery-status part
57 301 100       341 $readcursor |= $indicators->{'deliverystatus'} if grep { index($e, $_) > -1 } $startingof->{'message'}->@*;
  1204         1573  
58             }
59 533 100 100     1165 next if ($readcursor & $indicators->{'deliverystatus'}) == 0 || $e eq "";
60              
61             # The user(s) account is disabled.
62             #
63             # <***@ezweb.ne.jp>: 550 user unknown (in reply to RCPT TO command)
64             #
65             # -- OR --
66             # Each of the following recipients was rejected by a remote
67             # mail server.
68             #
69             # Recipient: <******@ezweb.ne.jp>
70             # >>> RCPT TO:<******@ezweb.ne.jp>
71             # <<< 550 <******@ezweb.ne.jp>: User unknown
72 197         247 $v = $dscontents->[-1];
73              
74 197 100 100     594 if( Sisimai::String->aligned(\$e, ['<', '@', '>']) && (index($e, 'Recipient: <') > 1 || index($e, '<') == 0) ) {
    100 100        
75             # Recipient: <******@ezweb.ne.jp> OR <***@ezweb.ne.jp>: 550 user unknown ...
76 41         73 my $p1 = index($e, '<');
77 41         63 my $p2 = index($e, '>');
78              
79 41 50       103 if( $v->{'recipient'} ) {
80             # There are multiple recipient addresses in the message body.
81 0         0 push @$dscontents, __PACKAGE__->DELIVERYSTATUS;
82 0         0 $v = $dscontents->[-1];
83             }
84 41         320 $v->{'recipient'} = Sisimai::Address->s3s4(substr($e, $p1, $p2 - $p1));
85 41         124 $v->{"diagnosis"} .= " ".$e;
86 41         89 $recipients++;
87              
88             } elsif( my $f = Sisimai::RFC1894->match($e) ) {
89             # $e matched with any field defined in RFC3464
90 50 50       92 next unless my $o = Sisimai::RFC1894->field($e);
91 50 50       93 next unless exists $fieldtable->{ $o->[0] };
92 50         135 $v->{ $fieldtable->{ $o->[0] } } = $o->[2];
93              
94             } else {
95             # The line does not begin with a DSN field defined in RFC3464
96             # >>> RCPT TO:<******@ezweb.ne.jp>
97             # <<< 550 ...
98 106 50       290 next if Sisimai::String->is_8bit(\$e);
99 106 100       311 $v->{"command"} = Sisimai::SMTP::Command->find($e) if index($e, " >>> ") > -1;
100 106         355 $v->{"diagnosis"} .= " ".$e;
101             } # End of error message part
102             }
103 41 50       160 return undef unless $recipients;
104              
105 41         81 for my $e ( @$dscontents ) {
106             # Check each value of DeliveryMatter{}, try to detect the bounce reason.
107 41         126 $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'});
108 41   100     305 $e->{"command"} ||= Sisimai::SMTP::Command->find($e->{"diagnosis"}) || "";
      100        
109              
110 41 100 66     188 if( defined $mhead->{'x-spasign'} && $mhead->{'x-spasign'} eq 'NG' ) {
111             # Content-Type: text/plain; ..., X-SPASIGN: NG (spamghetti, au by EZweb)
112             # Filtered recipient returns message that include 'X-SPASIGN' header
113 6         13 $e->{'reason'} = 'filtered';
114              
115             } else {
116             # There is no X-SPASIGN header or the value of the header is not "NG"
117 35 100       86 $e->{'reason'} = 'suspend' if grep { index($e->{'diagnosis'}, $_) > -1 } $unpaiduser->@*;
  70         233  
118             }
119 41 100       130 next if $e->{'reason'};
120 20 100 66     106 next if index($e->{'recipient'}, '@ezweb.ne.jp') > 1 || index($e->{'recipient'}, '@au.com') > 1;
121 5 50       20 $e->{"reason"} = "userunknown" if index($e->{"diagnosis"}, "<") == 0;
122             }
123 41         217 return {"ds" => $dscontents, "rfc822" => $emailparts->[1]};
124             }
125              
126             1;
127             __END__