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 37     37   5460 use parent 'Sisimai::Lhost';
  37         97  
  37         252  
3 37     37   3001 use v5.26;
  37         151  
4 37     37   220 use strict;
  37         81  
  37         1084  
5 37     37   162 use warnings;
  37         77  
  37         3480  
6 37     37   229 use Sisimai::String;
  37         96  
  37         3754  
7 37     37   211 use Encode;
  37         91  
  37         5281  
8 37     37   259 use Encode::Guess; Encode::Guess->add_suspects(Sisimai::String->encodenames->@*);
  37         79  
  37         368  
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 905     905 1 4581 my $class = shift;
19 905   100     3434 my $mhead = shift // return undef;
20 904   100     3385 my $mbody = shift // return undef;
21              
22 903 100 50     1520 my $match = 0; $match ||= 1 if index($mhead->{'subject'}, 'DELIVERY FAILURE:') == 0;
  903         17298  
23 903 50 0     3739 $match ||= 1 if index($mhead->{'subject'}, 'DELIVERY_FAILURE:') == 0;
24 903 100       3452 return undef unless $match > 0;
25              
26 17         59 state $indicators = __PACKAGE__->INDICATORS;
27 17         32 state $boundaries = ['Content-Type: message/rfc822'];
28 17         61 state $startingof = {'message' => ['Your message']};
29 17         46 state $messagesof = {
30             'filtered' => ['Cannot route mail to user'],
31             'systemerror' => ['Several matches found in Domino Directory'],
32             'userunknown' => [
33             'not listed in Domino Directory',
34             'not listed in public Name & Address Book',
35             'no se encuentra en el Directorio de Domino',
36             "non répertorié dans l'annuaire Domino",
37             'Domino ディレクトリには見つかりません',
38             ],
39             };
40              
41 17         79 require Sisimai::RFC1123;
42 17         132 my $fieldtable = Sisimai::RFC1894->FIELDTABLE;
43 17         30 my $permessage = {}; # (Hash) Store values of each Per-Message field
44 17         85 my $dscontents = [__PACKAGE__->DELIVERYSTATUS]; my $v = undef;
  17         35  
45 17         86 my $emailparts = Sisimai::RFC5322->part($mbody, $boundaries);
46 17         33 my $readcursor = 0; # (Integer) Points the current cursor position
47 17         26 my $recipients = 0; # (Integer) The number of 'Final-Recipient' header
48 17         3073 my $subjecttxt = ''; # (String) The value of Subject:
49 17         49 my $p = '';
50              
51 17         157 for my $e ( split("\n", $emailparts->[0]) ) {
52             # Read error messages and delivery status lines from the head of the email to the previous
53             # line of the beginning of the original message.
54 297 100       566 unless( $readcursor ) {
55             # Beginning of the bounce message or message/delivery-status part
56 17 50       102 $readcursor |= $indicators->{'deliverystatus'} if index($e, $startingof->{'message'}->[0]) == 0;
57 17         36 next;
58             }
59 280 100 66     993 next if ($readcursor & $indicators->{'deliverystatus'}) == 0 || $e eq "";
60              
61             # Your message
62             #
63             # Subject: Test Bounce
64             #
65             # was not delivered to:
66             #
67             # kijitora@example.net
68             #
69             # because:
70             #
71             # User some.name (kijitora@example.net) not listed in Domino Directory
72             #
73 168         252 $v = $dscontents->[-1];
74 168 100 100     722 if( $e eq 'was not delivered to:' ) {
    100 100        
    100          
75             # was not delivered to:
76             # kijitora@example.net
77 17 50       58 if( $v->{'recipient'} ) {
78             # There are multiple recipient addresses in the message body.
79 0         0 push @$dscontents, __PACKAGE__->DELIVERYSTATUS;
80 0         0 $v = $dscontents->[-1];
81             }
82 17   33     89 $v->{'recipient'} ||= $e;
83 17         1513 $recipients++;
84              
85             } elsif( index($e, ' ') == 0 && index($e, '@') > -1 && index($e, ' ', 3) < 0 ) {
86             # Continued from the line "was not delivered to:"
87             # kijitora@example.net
88 17         163 $v->{'recipient'} = Sisimai::Address->s3s4(substr($e, 2,));
89              
90             } elsif( $e eq 'because:' ) {
91             # because:
92             # User some.name (kijitora@example.net) not listed in Domino Directory
93 17         41 $v->{'diagnosis'} = $e;
94              
95             } else {
96 117 100 66     472 if( exists $v->{'diagnosis'} && $v->{'diagnosis'} eq 'because:' ) {
    100          
97             # Error message, continued from the line "because:"
98 17         51 $v->{'diagnosis'} = $e;
99              
100             } elsif( index($e, ' Subject: ') == 0 ) {
101             # Subject: Nyaa
102 17         55 $subjecttxt = substr($e, 11,);
103              
104             } else {
105             # There are some fields defined in RFC3464, try to match
106 83 100       250 my $f = Sisimai::RFC1894->match($e); next if $f < 1;
  83         210  
107 61 50       164 my $o = Sisimai::RFC1894->field($e); next unless $o;
  61         154  
108 61 100       156 next if $o->[3] eq 'addr';
109              
110 50 100       98 if( $o->[3] eq 'code' ) {
111             # Diagnostic-Code: SMTP; 550 5.1.1 ... User Unknown
112 11   33     67 $v->{'spec'} ||= $o->[1];
113 11   33     42 $v->{'diagnosis'} ||= $o->[2];
114              
115             } else {
116             # Other DSN fields defined in RFC3464
117 39 50       121 next unless exists $fieldtable->{ $o->[0] };
118 39 50 66     193 next if $o->[3] eq "host" && Sisimai::RFC1123->is_internethost($o->[2]) == 0;
119 39         133 $v->{ $fieldtable->{ $o->[0] } } = $o->[2];
120              
121 39 100       114 next unless $f == 1;
122 11         49 $permessage->{ $fieldtable->{ $o->[0] } } = $o->[2];
123             }
124             }
125             }
126             }
127 17 50       81 return undef unless $recipients;
128              
129 17         52 for my $e ( @$dscontents ) {
130             # Check the utf8 flag and fix
131 17         25 UTF8FLAG: while(1) {
132             # Delete the utf8 flag because there are a string including some characters which have
133             # utf8 flag but utf8::is_utf8 returns false
134 17 100 66     113 last if $e->{'diagnosis'} eq "" || Sisimai::String->is_8bit(\$e->{'diagnosis'}) == 0;
135              
136 5         11 my $cv = $e->{'diagnosis'};
137 5         29 my $ce = Encode::Guess->guess($cv);
138 5 50       1369 last unless ref $ce;
139              
140 0         0 $cv = Encode::encode_utf8($cv);
141 0         0 $e->{'diagnosis'} = $cv;
142 0         0 last;
143             }
144              
145 17         68 $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'});
146 17         71 $e->{'recipient'} = Sisimai::Address->s3s4($e->{'recipient'});
147 17   0     82 $e->{ $_ } ||= $permessage->{ $_ } || '' for keys %$permessage;
      33        
148              
149 17         65 for my $r ( keys %$messagesof ) {
150             # Check each regular expression of Domino error messages
151 33 100       72 next unless grep { index($e->{'diagnosis'}, $_) > -1 } $messagesof->{ $r }->@*;
  101         243  
152 11         31 $e->{'reason'} = $r;
153 11   50     94 $e->{'status'} ||= Sisimai::SMTP::Status->code($r, 0) || '';
      66        
154 11         24 last;
155             }
156             }
157              
158             # Set the value of $subjecttxt as a Subject if there is no original message in the bounce mail.
159 17 100       73 $emailparts->[1] .= sprintf("Subject: %s\n", $subjecttxt) if index($emailparts->[1], "\nSubject:") < 0;
160              
161 17         145 return {"ds" => $dscontents, "rfc822" => $emailparts->[1]};
162             }
163              
164             1;
165             __END__