1package Sisimai::Lhost::InterScanMSS; 2use parent 'Sisimai::Lhost'; 3use feature ':5.10'; 4use strict; 5use warnings; 6 7sub description { 'Trend Micro InterScan Messaging Security Suite' } 8sub make { 9 # Detect an error from InterScanMSS 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.2 15 my $class = shift; 16 my $mhead = shift // return undef; 17 my $mbody = shift // return undef; 18 my $match = 0; 19 my $tryto = [ 20 'Mail could not be delivered', 21 'メッセージを配信できません。', 22 'メール配信に失敗しました', 23 ]; 24 25 # 'received' => qr/[ ][(]InterScanMSS[)][ ]with[ ]/, 26 $match ||= 1 if index($mhead->{'from'}, '"InterScan MSS"') == 0; 27 $match ||= 1 if index($mhead->{'from'}, '"InterScan Notification"') == 0; 28 $match ||= 1 if grep { $mhead->{'subject'} eq $_ } @$tryto; 29 return undef unless $match; 30 31 state $rebackbone = qr|^Content-type:[ ]message/rfc822|m; 32 my $dscontents = [__PACKAGE__->DELIVERYSTATUS]; 33 my $emailsteak = Sisimai::RFC5322->fillet($mbody, $rebackbone); 34 my $recipients = 0; # (Integer) The number of 'Final-Recipient' header 35 my $v = undef; 36 37 for my $e ( split("\n", $emailsteak->[0]) ) { 38 # Read error messages and delivery status lines from the head of the email 39 # to the previous line of the beginning of the original message. 40 next unless length $e; 41 42 $v = $dscontents->[-1]; 43 if( $e =~ /\A.+[<>]{3}[ \t]+.+[<]([^ ]+[@][^ ]+)[>]\z/ || 44 $e =~ /\A.+[<>]{3}[ \t]+.+[<]([^ ]+[@][^ ]+)[>]/ || 45 $e =~ /\A(?:Reason:[ ]+)?Unable[ ]to[ ]deliver[ ]message[ ]to[ ][<](.+)[>]/ ) { 46 # Sent <<< RCPT TO:<kijitora@example.co.jp> 47 # Received >>> 550 5.1.1 <kijitora@example.co.jp>... user unknown 48 # Unable to deliver message to <kijitora@neko.example.jp> 49 my $cr = $1; 50 if( $v->{'recipient'} && $cr ne $v->{'recipient'} ) { 51 # There are multiple recipient addresses in the message body. 52 push @$dscontents, __PACKAGE__->DELIVERYSTATUS; 53 $v = $dscontents->[-1]; 54 } 55 $v->{'recipient'} = $cr; 56 $v->{'diagnosis'} = $e if $e =~ /Unable[ ]to[ ]deliver[ ]/; 57 $recipients = scalar @$dscontents; 58 } 59 60 if( $e =~ /\ASent[ \t]+[<]{3}[ \t]+([A-Z]{4})[ \t]/ ) { 61 # Sent <<< RCPT TO:<kijitora@example.co.jp> 62 $v->{'command'} = $1 63 64 } elsif( $e =~ /\AReceived[ \t]+[>]{3}[ \t]+(\d{3}[ \t]+.+)\z/ ) { 65 # Received >>> 550 5.1.1 <kijitora@example.co.jp>... user unknown 66 $v->{'diagnosis'} = $1; 67 68 } else { 69 # Error message in non-English 70 next unless $e =~ /[ ][<>]{3}[ ]/; 71 $v->{'command'} = $1 if $e =~ /[ ][>]{3}[ ]([A-Z]{4})/; # >>> RCPT TO ... 72 $v->{'diagnosis'} = $1 if $e =~ /[ ][<]{3}[ ](.+)/; # <<< 550 5.1.1 User unknown 73 } 74 } 75 return undef unless $recipients; 76 77 for my $e ( @$dscontents ) { 78 # Set default values if each value is empty. 79 $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'}); 80 $e->{'reason'} = 'userunknown' if $e->{'diagnosis'} =~ /Unable[ ]to[ ]deliver/; 81 } 82 return { 'ds' => $dscontents, 'rfc822' => $emailsteak->[1] }; 83} 84 851; 86__END__ 87 88=encoding utf-8 89 90=head1 NAME 91 92Sisimai::Lhost::InterScanMSS - bounce mail parser class for 93C<Trend Micro InterScan Messaging Security Suite>. 94 95=head1 SYNOPSIS 96 97 use Sisimai::Lhost::InterScanMSS; 98 99=head1 DESCRIPTION 100 101Sisimai::Lhost::InterScanMSS parses a bounce email which created by C<Trend Micro 102InterScan Messaging Security Suite>. Methods in the module are called from only 103Sisimai::Message. 104 105=head1 CLASS METHODS 106 107=head2 C<B<description()>> 108 109C<description()> returns description string of this module. 110 111 print Sisimai::Lhost::InterScanMSS->description; 112 113=head2 C<B<make(I<header data>, I<reference to body string>)>> 114 115C<make()> method parses a bounced email and return results as a array reference. 116See Sisimai::Message for more details. 117 118=head1 AUTHOR 119 120azumakuniyuki 121 122=head1 COPYRIGHT 123 124Copyright (C) 2014-2020 azumakuniyuki, All rights reserved. 125 126=head1 LICENSE 127 128This software is distributed under The BSD 2-Clause License. 129 130=cut 131 132