File Coverage

lib/Sisimai/Lhost/mFILTER.pm
Criterion Covered Total %
statement 62 64 96.8
branch 33 40 82.5
condition 25 31 80.6
subroutine 6 6 100.0
pod 2 2 100.0
total 128 143 89.5


line stmt bran cond sub pod time code
1             package Sisimai::Lhost::mFILTER;
2 37     37   4588 use parent 'Sisimai::Lhost';
  37         80  
  37         309  
3 37     37   3283 use v5.26;
  37         457  
4 37     37   252 use strict;
  37         66  
  37         1362  
5 37     37   193 use warnings;
  37         168  
  37         44176  
6              
7 1     1 1 5 sub description { 'Digital Arts m-FILTER: https://www.daj.jp/bs/mf/' }
8             sub inquire {
9             # Detect an error from Digital Arts m-FILTER
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.1
15 905     905 1 4295 my $class = shift;
16 905   100     2629 my $mhead = shift // return undef;
17 904   100     2314 my $mbody = shift // return undef;
18              
19 903         2409 state $indicators = __PACKAGE__->INDICATORS;
20 903         1548 state $boundaries = ['-------original message', '-------original mail info'];
21 903         2157 state $startingof = {
22             'command' => ['-------SMTP command'],
23             'error' => ['-------server message'],
24             };
25              
26             # X-Mailer: m-FILTER
27 903         1445 my $proceedsto = 0;
28 903 100 100     4053 $proceedsto = 1 if defined $mhead->{'x-mailer'} && $mhead->{'x-mailer'} eq 'm-FILTER';
29 903 100 100     2740 $proceedsto ||= 1 if grep { index($$mbody, $_) > 1 } @$boundaries;
  1806         7031  
30 903 100 50     2954 $proceedsto ||= 1 if grep { index($$mbody, $_) > 1 } $startingof->{'command'}->@*;
  903         4150  
31 903 100 50     2216 $proceedsto ||= 1 if grep { index($$mbody, $_) > 1 } $startingof->{'error'}->@*;
  903         3864  
32 903 100       3792 return undef unless $proceedsto;
33              
34 27         133 my $dscontents = [__PACKAGE__->DELIVERYSTATUS]; my $v = undef;
  27         54  
35 27         116 my $emailparts = Sisimai::RFC5322->part($mbody, $boundaries);
36 27         61 my $readcursor = 0; # (Integer) Points the current cursor position
37 27         50 my $recipients = 0; # (Integer) The number of 'Final-Recipient' header
38 27         133 my $markingset = {'diagnosis' => 0, 'command' => 0};
39              
40 27         231 for my $e ( split("\n", $emailparts->[0]) ) {
41             # Read error messages and delivery status lines from the head of the email to the previous
42             # line of the beginning of the original message.
43 351 100       550 unless( $readcursor ) {
44             # Beginning of the bounce message or message/delivery-status part
45 162 100 66     663 if( index($e, '@') > 1 && index($e, ' ') < 0 && Sisimai::Address->is_emailaddress($e) ) {
      100        
46 27         95 $readcursor |= $indicators->{'deliverystatus'};
47             }
48             }
49 351 100 100     960 next if ($readcursor & $indicators->{'deliverystatus'}) == 0 || $e eq "";
50              
51             # このメールは「m-FILTER」が自動的に生成して送信しています。
52             # メールサーバーとの通信中、下記の理由により
53             # このメールは送信できませんでした。
54             #
55             # 以下のメールアドレスへの送信に失敗しました。
56             # kijitora@example.jp
57             #
58             #
59             # -------server message
60             # 550 5.1.1 unknown user
61             #
62             # -------SMTP command
63             # DATA
64             #
65             # -------original message
66 135         181 $v = $dscontents->[-1];
67              
68 135 100 100     500 if( index($e, '@') > 0 && index($e, ' ') < 0 ) {
    100 66        
69             # 以下のメールアドレスへの送信に失敗しました。
70             # kijitora@example.jp
71 27 50       118 if( $v->{'recipient'} ) {
72             # There are multiple recipient addresses in the message body.
73 0         0 push @$dscontents, __PACKAGE__->DELIVERYSTATUS;
74 0         0 $v = $dscontents->[-1];
75             }
76 27         68 $v->{'recipient'} = $e;
77 27         57 $recipients++;
78              
79             } elsif( length $e == 4 && index($e, ' ') < 0 ) {
80             # -------SMTP command
81             # DATA
82 27 50       71 next if $v->{'command'};
83 27 50       93 $v->{'command'} = $e if $markingset->{'command'};
84              
85             } else {
86             # Get error message and SMTP command
87 81 100       213 if( $e eq $startingof->{'error'}->[0] ) {
    100          
88             # -------server message
89 27         64 $markingset->{'diagnosis'} = 1;
90              
91             } elsif( $e eq $startingof->{'command'}->[0] ) {
92             # -------SMTP command
93 27         44 $markingset->{'command'} = 1;
94              
95             } else {
96             # 550 5.1.1 unknown user
97 27 50       69 next if index($e, '-') == 0;
98 27 50       99 next if $v->{'diagnosis'};
99 27         52 $v->{'diagnosis'} = $e;
100             }
101             } # End of error message part
102             }
103 27 50       112 return undef unless $recipients;
104              
105 27         68 for my $e ( @$dscontents ) {
106 27         154 $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'});
107              
108             # Get localhost and remote host name from Received header.
109 27 50       95 next unless scalar $mhead->{'received'}->@*;
110 27         46 my $rheads = $mhead->{'received'};
111 27         143 my $rhosts = Sisimai::RFC5322->received($rheads->[-1]);
112              
113 27   33     217 $e->{'lhost'} ||= shift Sisimai::RFC5322->received($rheads->[0])->@*;
114 27         102 for my $ee ( $rhosts->[0], $rhosts->[1] ) {
115             # Avoid "... by m-FILTER"
116 54 100       187 next unless rindex($ee, '.') > -1;
117 27         70 $e->{'rhost'} = $ee;
118             }
119             }
120 27         235 return {"ds" => $dscontents, "rfc822" => $emailparts->[1]};
121             }
122              
123             1;
124             __END__