1package Sisimai::Lhost::Activehunter; 2use parent 'Sisimai::Lhost'; 3use feature ':5.10'; 4use strict; 5use warnings; 6 7sub description { 'TransWARE Active!hunter' }; 8sub make { 9 # Detect an error from TransWARE Active!hunter 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.1 15 my $class = shift; 16 my $mhead = shift // return undef; 17 my $mbody = shift // return undef; 18 19 # 'from' => qr/\A"MAILER-DAEMON"/, 20 # 'subject' => qr/FAILURE NOTICE :/, 21 return undef unless defined $mhead->{'x-ahmailid'}; 22 23 state $indicators = __PACKAGE__->INDICATORS; 24 state $rebackbone = qr|^Content-Type:[ ]message/rfc822|m; 25 state $startingof = { 'message' => [' ----- The following addresses had permanent fatal errors -----'] }; 26 27 my $dscontents = [__PACKAGE__->DELIVERYSTATUS]; 28 my $emailsteak = Sisimai::RFC5322->fillet($mbody, $rebackbone); 29 my $readcursor = 0; # (Integer) Points the current cursor position 30 my $recipients = 0; # (Integer) The number of 'Final-Recipient' header 31 my $v = undef; 32 33 for my $e ( split("\n", $emailsteak->[0]) ) { 34 # Read error messages and delivery status lines from the head of the email 35 # to the previous line of the beginning of the original message. 36 unless( $readcursor ) { 37 # Beginning of the bounce message or delivery status part 38 $readcursor |= $indicators->{'deliverystatus'} if index($e, $startingof->{'message'}->[0]) == 0; 39 next; 40 } 41 next unless $readcursor & $indicators->{'deliverystatus'}; 42 next unless length $e; 43 44 # ----- The following addresses had permanent fatal errors ----- 45 # 46 # >>> kijitora@example.org <kijitora@example.org> 47 # 48 # ----- Transcript of session follows ----- 49 # 550 sorry, no mailbox here by that name (#5.1.1 - chkusr) 50 $v = $dscontents->[-1]; 51 52 if( $e =~ /\A[>]{3}[ \t]+.+[<]([^ ]+?[@][^ ]+?)[>]\z/ ) { 53 # >>> kijitora@example.org <kijitora@example.org> 54 if( $v->{'recipient'} ) { 55 # There are multiple recipient addresses in the message body. 56 push @$dscontents, __PACKAGE__->DELIVERYSTATUS; 57 $v = $dscontents->[-1]; 58 } 59 $v->{'recipient'} = $1; 60 $recipients++; 61 62 } else { 63 # ----- Transcript of session follows ----- 64 # 550 sorry, no mailbox here by that name (#5.1.1 - chkusr) 65 next unless $e =~ /\A[0-9A-Za-z]+/; 66 next if length $v->{'diagnosis'}; 67 $v->{'diagnosis'} ||= $e; 68 } 69 } 70 return undef unless $recipients; 71 72 require Sisimai::String; 73 $_->{'diagnosis'} = Sisimai::String->sweep($_->{'diagnosis'}) for @$dscontents; 74 return { 'ds' => $dscontents, 'rfc822' => $emailsteak->[1] }; 75} 76 771; 78__END__ 79 80=encoding utf-8 81 82=head1 NAME 83 84Sisimai::Lhost::Activehunter - bounce mail parser class for Active!hunter. 85 86=head1 SYNOPSIS 87 88 use Sisimai::Lhost::Activehunter; 89 90=head1 DESCRIPTION 91 92Sisimai::Lhost::Activehunter parses a bounce email which created by C<TransWARE 93Active!hunter>. Methods in the module are called from only Sisimai::Message. 94 95=head1 CLASS METHODS 96 97=head2 C<B<description()>> 98 99C<description()> returns description string of this module. 100 101 print Sisimai::Lhost::Activehunter->description; 102 103=head2 C<B<make(I<header data>, I<reference to body string>)>> 104 105C<make()> method parses a bounced email and return results as a array reference. 106See Sisimai::Message for more details. 107 108=head1 AUTHOR 109 110azumakuniyuki 111 112=head1 COPYRIGHT 113 114Copyright (C) 2014-2020 azumakuniyuki, All rights reserved. 115 116=head1 LICENSE 117 118This software is distributed under The BSD 2-Clause License. 119 120=cut 121 122