1package Sisimai::Lhost::Zoho; 2use parent 'Sisimai::Lhost'; 3use feature ':5.10'; 4use strict; 5use warnings; 6 7sub description { 'Zoho Mail: https://www.zoho.com' } 8sub make { 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 parse or the arguments are missing 14 # @since v4.1.7 15 my $class = shift; 16 my $mhead = shift // return undef; 17 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 return undef unless $mhead->{'x-zohomail'}; 23 24 state $indicators = __PACKAGE__->INDICATORS; 25 state $rebackbone = qr|^Received:[ ]*from[ ]mail[.]zoho[.]com[ ]by[ ]mx[.]zohomail[.]com|m; 26 state $startingof = { 'message' => ['This message was created automatically by mail delivery'] }; 27 state $messagesof = { 'expired' => ['Host not reachable'] }; 28 29 my $dscontents = [__PACKAGE__->DELIVERYSTATUS]; 30 my $emailsteak = Sisimai::RFC5322->fillet($mbody, $rebackbone); 31 my $readcursor = 0; # (Integer) Points the current cursor position 32 my $recipients = 0; # (Integer) The number of 'Final-Recipient' header 33 my $qprintable = 0; 34 my $v = undef; 35 36 for my $e ( split("\n", $emailsteak->[0]) ) { 37 # Read error messages and delivery status lines from the head of the email 38 # to the previous line of the beginning of the original message. 39 unless( $readcursor ) { 40 # Beginning of the bounce message or message/delivery-status part 41 $readcursor |= $indicators->{'deliverystatus'} if index($e, $startingof->{'message'}->[0]) == 0; 42 next; 43 } 44 next unless $readcursor & $indicators->{'deliverystatus'}; 45 next unless length $e; 46 47 # This message was created automatically by mail delivery software. 48 # A message that you sent could not be delivered to one or more of its recip= 49 # ients. This is a permanent error.=20 50 # 51 # kijitora@example.co.jp Invalid Address, ERROR_CODE :550, ERROR_CODE :5.1.= 52 # 1 <kijitora@example.co.jp>... User Unknown 53 54 # This message was created automatically by mail delivery software. 55 # A message that you sent could not be delivered to one or more of its recipients. This is a permanent error. 56 # 57 # shironeko@example.org Invalid Address, ERROR_CODE :550, ERROR_CODE :Requested action not taken: mailbox unavailable 58 $v = $dscontents->[-1]; 59 60 if( $e =~ /\A([^ ]+[@][^ ]+)[ \t]+(.+)\z/ ) { 61 # kijitora@example.co.jp Invalid Address, ERROR_CODE :550, ERROR_CODE :5.1.= 62 if( $v->{'recipient'} ) { 63 # There are multiple recipient addresses in the message body. 64 push @$dscontents, __PACKAGE__->DELIVERYSTATUS; 65 $v = $dscontents->[-1]; 66 } 67 $v->{'recipient'} = $1; 68 $v->{'diagnosis'} = $2; 69 70 if( substr($v->{'diagnosis'}, -1, 1) eq '=' ) { 71 # Quoted printable 72 substr($v->{'diagnosis'}, -1, 1, ''); 73 $qprintable = 1; 74 } 75 $recipients++; 76 77 } elsif( $e =~ /\A\[Status: .+[<]([^ ]+[@][^ ]+)[>],/ ) { 78 # Expired 79 # [Status: Error, Address: <kijitora@6kaku.example.co.jp>, ResponseCode 421, , Host not reachable.] 80 if( $v->{'recipient'} ) { 81 # There are multiple recipient addresses in the message body. 82 push @$dscontents, __PACKAGE__->DELIVERYSTATUS; 83 $v = $dscontents->[-1]; 84 } 85 $v->{'recipient'} = $1; 86 $v->{'diagnosis'} = $e; 87 $recipients++; 88 89 } else { 90 # Continued line 91 next unless $qprintable; 92 $v->{'diagnosis'} .= $e; 93 } 94 } 95 return undef unless $recipients; 96 97 for my $e ( @$dscontents ) { 98 $e->{'diagnosis'} =~ y/\n/ /; 99 $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'}); 100 101 SESSION: for my $r ( keys %$messagesof ) { 102 # Verify each regular expression of session errors 103 next unless grep { index($e->{'diagnosis'}, $_) > -1 } @{ $messagesof->{ $r } }; 104 $e->{'reason'} = $r; 105 last; 106 } 107 } 108 return { 'ds' => $dscontents, 'rfc822' => $emailsteak->[1] }; 109} 110 1111; 112__END__ 113 114=encoding utf-8 115 116=head1 NAME 117 118Sisimai::Lhost::Zoho - bounce mail parser class for C<Zoho Mail>. 119 120=head1 SYNOPSIS 121 122 use Sisimai::Lhost::Zoho; 123 124=head1 DESCRIPTION 125 126Sisimai::Lhost::Zoho parses a bounce email which created by C<Zoho! MAIL>. 127Methods in the module are called from only Sisimai::Message. 128 129=head1 CLASS METHODS 130 131=head2 C<B<description()>> 132 133C<description()> returns description string of this module. 134 135 print Sisimai::Lhost::Zoho->description; 136 137=head2 C<B<make(I<header data>, I<reference to body string>)>> 138 139C<make()> method parses a bounced email and return results as a array reference. 140See Sisimai::Message for more details. 141 142=head1 AUTHOR 143 144azumakuniyuki 145 146=head1 COPYRIGHT 147 148Copyright (C) 2014-2020 azumakuniyuki, All rights reserved. 149 150=head1 LICENSE 151 152This software is distributed under The BSD 2-Clause License. 153 154=cut 155 156