File Coverage

lib/Sisimai/Lhost/Domino.pm
Criterion Covered Total %
statement 88 93 94.6
branch 40 48 83.3
condition 26 45 57.7
subroutine 9 9 100.0
pod 2 2 100.0
total 165 197 83.7


line stmt bran cond sub pod time code
1             package Sisimai::Lhost::Domino;
2 38     38   3749 use parent 'Sisimai::Lhost';
  38         62  
  38         229  
3 38     38   2872 use v5.26;
  38         115  
4 38     38   150 use strict;
  38         234  
  38         816  
5 38     38   130 use warnings;
  38         278  
  38         1955  
6 38     38   161 use Sisimai::String;
  38         6379  
  38         837  
7 38     38   158 use Encode;
  38         87  
  38         3934  
8 38     38   194 use Encode::Guess; Encode::Guess->add_suspects(Sisimai::String->encodenames->@*);
  38         50  
  38         314  
9              
10 1     1 1 4 sub description { 'HCL Domino' }
11             sub inquire {
12             # Detect an error from HCL Domino (Formerly IBM Domino (Formerly Lotus Domino))
13             # @param [Hash] mhead Message headers of a bounce email
14             # @param [String] mbody Message body of a bounce email
15             # @return [Hash] Bounce data list and message/rfc822 part
16             # @return [undef] failed to decode or the arguments are missing
17             # @since v4.0.2
18 903     903 1 2546 my $class = shift;
19 903   100     2090 my $mhead = shift // return undef;
20 902   100     1853 my $mbody = shift // return undef;
21              
22 901 100 50     1197 my $match = 0; $match ||= 1 if index($mhead->{'subject'}, 'DELIVERY FAILURE:') == 0;
  901         2365  
23 901 50 0     2341 $match ||= 1 if index($mhead->{'subject'}, 'DELIVERY_FAILURE:') == 0;
24 901 100       2343 return undef unless $match > 0;
25              
26 17         84 state $indicators = __PACKAGE__->INDICATORS;
27 17         24 state $boundaries = ['Content-Type: message/rfc822'];
28 17         49 state $startingof = {'message' => ['Your message']};
29 17         21 state $messagesof = {
30             'userunknown' => [
31             'not listed in Domino Directory',
32             'not listed in public Name & Address Book',
33             'no se encuentra en el Directorio de Domino',
34             "non répertorié dans l'annuaire Domino",
35             'Domino ディレクトリには見つかりません',
36             ],
37             };
38              
39 17         62 require Sisimai::RFC1123;
40 17         72 my $fieldtable = Sisimai::RFC1894->FIELDTABLE;
41 17         20 my $permessage = {}; # (Hash) Store values of each Per-Message field
42 17         61 my $dscontents = [__PACKAGE__->DELIVERYSTATUS]; my $v = undef;
  17         28  
43 17         64 my $emailparts = Sisimai::RFC5322->part($mbody, $boundaries);
44 17         21 my $readcursor = 0; # (Integer) Points the current cursor position
45 17         22 my $recipients = 0; # (Integer) The number of 'Final-Recipient' header
46 17         19 my $subjecttxt = ''; # (String) The value of Subject:
47 17         20 my $p = '';
48              
49 17         99 for my $e ( split("\n", $emailparts->[0]) ) {
50             # Read error messages and delivery status lines from the head of the email to the previous
51             # line of the beginning of the original message.
52 297 100       337 unless( $readcursor ) {
53             # Beginning of the bounce message or message/delivery-status part
54 17 50       59 $readcursor |= $indicators->{'deliverystatus'} if index($e, $startingof->{'message'}->[0]) == 0;
55 17         24 next;
56             }
57 280 100 66     584 next if ($readcursor & $indicators->{'deliverystatus'}) == 0 || $e eq "";
58              
59             # Your message
60             #
61             # Subject: Test Bounce
62             #
63             # was not delivered to:
64             #
65             # kijitora@example.net
66             #
67             # because:
68             #
69             # User some.name (kijitora@example.net) not listed in Domino Directory
70             #
71 168         166 $v = $dscontents->[-1];
72 168 100 100     459 if( $e eq 'was not delivered to:' ) {
    100 100        
    100          
73             # was not delivered to:
74             # kijitora@example.net
75 17 50       32 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 17   33     54 $v->{'recipient'} ||= $e;
81 17         22 $recipients++;
82              
83             } elsif( index($e, ' ') == 0 && index($e, '@') > -1 && index($e, ' ', 3) < 0 ) {
84             # Continued from the line "was not delivered to:"
85             # kijitora@example.net
86 17         91 $v->{'recipient'} = Sisimai::Address->s3s4(substr($e, 2,));
87              
88             } elsif( $e eq 'because:' ) {
89             # because:
90             # User some.name (kijitora@example.net) not listed in Domino Directory
91 17         30 $v->{'diagnosis'} = $e;
92              
93             } else {
94 117 100 66     295 if( exists $v->{'diagnosis'} && $v->{'diagnosis'} eq 'because:' ) {
    100          
95             # Error message, continued from the line "because:"
96 17         27 $v->{'diagnosis'} = $e;
97              
98             } elsif( index($e, ' Subject: ') == 0 ) {
99             # Subject: Nyaa
100 17         48 $subjecttxt = substr($e, 11,);
101              
102             } else {
103             # There are some fields defined in RFC3464, try to match
104 83 100       146 my $f = Sisimai::RFC1894->match($e); next if $f < 1;
  83         104  
105 61 50       86 my $o = Sisimai::RFC1894->field($e); next unless $o;
  61         83  
106 61 100       215 next if $o->[3] eq 'addr';
107              
108 50 100       63 if( $o->[3] eq 'code' ) {
109             # Diagnostic-Code: SMTP; 550 5.1.1 ... User Unknown
110 11   33     56 $v->{'spec'} ||= $o->[1];
111 11   33     23 $v->{'diagnosis'} ||= $o->[2];
112              
113             } else {
114             # Other DSN fields defined in RFC3464
115 39 50       72 next unless exists $fieldtable->{ $o->[0] };
116 39 50 66     123 next if $o->[3] eq "host" && Sisimai::RFC1123->is_internethost($o->[2]) == 0;
117 39         71 $v->{ $fieldtable->{ $o->[0] } } = $o->[2];
118              
119 39 100       69 next unless $f == 1;
120 11         40 $permessage->{ $fieldtable->{ $o->[0] } } = $o->[2];
121             }
122             }
123             }
124             }
125 17 50       53 return undef unless $recipients;
126              
127 17         25 for my $e ( @$dscontents ) {
128             # Check the utf8 flag and fix
129 17         17 UTF8FLAG: while(1) {
130             # Delete the utf8 flag because there are a string including some characters which have
131             # utf8 flag but utf8::is_utf8 returns false
132 17 100 66     78 last if $e->{'diagnosis'} eq "" || Sisimai::String->is_8bit(\$e->{'diagnosis'}) == 0;
133              
134 5         8 my $cv = $e->{'diagnosis'};
135 5         21 my $ce = Encode::Guess->guess($cv);
136 5 50       911 last unless ref $ce;
137              
138 0         0 $cv = Encode::encode_utf8($cv);
139 0         0 $e->{'diagnosis'} = $cv;
140 0         0 last;
141             }
142              
143 17         51 $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'});
144 17         57 $e->{'recipient'} = Sisimai::Address->s3s4($e->{'recipient'});
145 17   0     55 $e->{ $_ } ||= $permessage->{ $_ } || '' for keys %$permessage;
      33        
146              
147 17         35 for my $r ( keys %$messagesof ) {
148             # Check each regular expression of Domino error messages
149 17 100       58 next unless grep { index($e->{'diagnosis'}, $_) > -1 } $messagesof->{ $r }->@*;
  85         172  
150 11         24 $e->{'reason'} = $r;
151 11   50     57 $e->{'status'} ||= Sisimai::SMTP::Status->code($r, 0) || '';
      66        
152 11         18 last;
153             }
154             }
155              
156             # Set the value of $subjecttxt as a Subject if there is no original message in the bounce mail.
157 17 100       46 $emailparts->[1] .= sprintf("Subject: %s\n", $subjecttxt) if index($emailparts->[1], "\nSubject:") < 0;
158              
159 17         101 return {"ds" => $dscontents, "rfc822" => $emailparts->[1]};
160             }
161              
162             1;
163             __END__