File Coverage

lib/Sisimai/Lhost/Zoho.pm
Criterion Covered Total %
statement 48 53 90.5
branch 17 22 77.2
condition 5 7 71.4
subroutine 6 6 100.0
pod 2 2 100.0
total 78 90 86.6


line stmt bran cond sub pod time code
1             package Sisimai::Lhost::Zoho;
2 40     40   3323 use parent 'Sisimai::Lhost';
  40         60  
  40         210  
3 40     40   3902 use v5.26;
  40         128  
4 40     40   152 use strict;
  40         52  
  40         876  
5 40     40   148 use warnings;
  40         69  
  40         20918  
6              
7 1     1 1 3 sub description { 'Zoho Mail: https://www.zoho.com/mail/' }
8             sub inquire {
9             # Detect an error from Zoho Mail
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.7
15 923     923 1 2967 my $class = shift;
16 923 100 100     1848 my $mhead = shift // return undef; return undef unless $mhead->{'x-zohomail'};
  922         2570  
17 26   50     73 my $mbody = shift // return undef;
18              
19             # X-ZohoMail: Si CHF_MF_NL SS_10 UW48 UB48 FMWL UW48 UB48 SGR3_1_09124_42
20             # X-Zoho-Virus-Status: 2
21             # X-Mailer: Zoho Mail
22 26         44 state $indicators = __PACKAGE__->INDICATORS;
23 26         32 state $boundaries = ['Received: from mail.zoho.com by mx.zohomail.com'];
24 26         26 state $startingof = {'message' => ['This message was created automatically by mail delivery']};
25              
26 26         77 my $dscontents = [__PACKAGE__->DELIVERYSTATUS]; my $v = undef;
  26         36  
27 26         80 my $emailparts = Sisimai::RFC5322->part($mbody, $boundaries);
28 26         28 my $readcursor = 0; # (Integer) Points the current cursor position
29 26         32 my $recipients = 0; # (Integer) The number of 'Final-Recipient' header
30 26         31 my $qprintable = 0;
31              
32 26         118 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 219 100       262 unless( $readcursor ) {
36             # Beginning of the bounce message or message/delivery-status part
37 26 50       108 $readcursor |= $indicators->{'deliverystatus'} if index($e, $startingof->{'message'}->[0]) == 0;
38 26         30 next;
39             }
40 193 100 66     380 next if ($readcursor & $indicators->{'deliverystatus'}) == 0 || $e eq "";
41              
42             # This message was created automatically by mail delivery software.
43             # A message that you sent could not be delivered to one or more of its recip=
44             # ients. This is a permanent error.=20
45             #
46             # kijitora@example.co.jp Invalid Address, ERROR_CODE :550, ERROR_CODE :5.1.=
47             # 1 ... User Unknown
48              
49             # This message was created automatically by mail delivery software.
50             # A message that you sent could not be delivered to one or more of its recipients. This is a permanent error.
51             #
52             # shironeko@example.org Invalid Address, ERROR_CODE :550, ERROR_CODE :Requested action not taken: mailbox unavailable
53 132         115 $v = $dscontents->[-1];
54              
55 132 100       245 if( Sisimai::String->aligned(\$e, ['@', ' ', 'ERROR_CODE :']) ) {
    100          
56             # kijitora@example.co.jp Invalid Address, ERROR_CODE :550, ERROR_CODE :5.1.=
57 26 100       48 if( $v->{'recipient'} ) {
58             # There are multiple recipient addresses in the message body.
59 5         15 push @$dscontents, __PACKAGE__->DELIVERYSTATUS;
60 5         11 $v = $dscontents->[-1];
61             }
62 26         43 $v->{'recipient'} = substr($e, 0, index($e, ' '));
63 26         55 $v->{'diagnosis'} = substr($e, index($e, ' ') + 1,);
64              
65 26 50       53 if( substr($v->{'diagnosis'}, -1, 1) eq '=' ) {
66             # Quoted printable
67 0         0 substr($v->{'diagnosis'}, -1, 1, '');
68 0         0 $qprintable = 1;
69             }
70 26         36 $recipients++;
71              
72             } elsif( index($e, '[Status: ') == 0 ) {
73             # Expired
74             # [Status: Error, Address: , ResponseCode 421, , Host not reachable.]
75 5 50       19 if( $v->{'recipient'} ) {
76             # There are multiple recipient addresses in the message body.
77 0         0 push @$dscontents, __PACKAGE__->DELIVERYSTATUS;
78 0         0 $v = $dscontents->[-1];
79             }
80 5         7 my $p1 = index($e, '<');
81 5         10 my $p2 = index($e, '>', $p1 + 2);
82 5         35 $v->{'recipient'} = Sisimai::Address->s3s4(substr($e, $p1, $p2 - $p1));
83 5         13 $v->{'diagnosis'} = $e;
84 5         11 $recipients++;
85              
86             } else {
87             # Continued line
88 101 50       148 next unless $qprintable;
89 0         0 $v->{'diagnosis'} .= $e;
90             }
91             }
92 26 50       60 return undef unless $recipients;
93 26         90 return {"ds" => $dscontents, "rfc822" => $emailparts->[1]};
94             }
95              
96             1;
97             __END__