File Coverage

lib/Sisimai/Lhost/EinsUndEins.pm
Criterion Covered Total %
statement 53 55 96.3
branch 21 26 80.7
condition 9 12 75.0
subroutine 6 6 100.0
pod 2 2 100.0
total 91 101 90.1


line stmt bran cond sub pod time code
1             package Sisimai::Lhost::EinsUndEins;
2 38     38   3553 use parent 'Sisimai::Lhost';
  38         57  
  38         228  
3 38     38   2428 use v5.26;
  38         118  
4 38     38   137 use strict;
  38         76  
  38         920  
5 38     38   125 use warnings;
  38         60  
  38         24263  
6              
7             # X-UI-Out-Filterresults: unknown:0;
8 1     1 1 3 sub description { '1&1: https://www.1und1.de/' }
9             sub inquire {
10             # Detect an error from 1&1
11             # @param [Hash] mhead Message headers of a bounce email
12             # @param [String] mbody Message body of a bounce email
13             # @return [Hash] Bounce data list and message/rfc822 part
14             # @return [undef] failed to decodes or the arguments are missing
15             # @since v4.1.9
16 892     892 1 2516 my $class = shift;
17 892   100     1968 my $mhead = shift // return undef;
18 891   100     1820 my $mbody = shift // return undef;
19              
20 890 100       2488 return undef unless index($mhead->{'from'}, '"Mail Delivery System"') == 0;
21 32 100       115 return undef unless $mhead->{'subject'} eq 'Mail delivery failed: returning message to sender';
22              
23 17         47 state $indicators = __PACKAGE__->INDICATORS;
24 17         29 state $boundaries = ['--- The header of the original message is following. ---'];
25 17         29 state $startingof = {
26             'message' => ['This message was created automatically by mail delivery software'],
27             'error' => ['For the following reason:'],
28             };
29              
30 17         60 my $dscontents = [__PACKAGE__->DELIVERYSTATUS]; my $v = undef;
  17         24  
31 17         58 my $emailparts = Sisimai::RFC5322->part($mbody, $boundaries);
32 17         23 my $readcursor = 0; # (Integer) Points the current cursor position
33 17         20 my $recipients = 0; # (Integer) The number of 'Final-Recipient' header
34              
35 17         91 for my $e ( split("\n", $emailparts->[0]) ) {
36             # Read error messages and delivery status lines from the head of the email to the previous
37             # line of the beginning of the original message.
38 166 100       181 unless( $readcursor ) {
39             # Beginning of the bounce message or message/delivery-status part
40 17 50       54 $readcursor |= $indicators->{'deliverystatus'} if index($e, $startingof->{'message'}->[0]) == 0;
41 17         29 next;
42             }
43 149 100 66     332 next if ($readcursor & $indicators->{'deliverystatus'}) == 0 || $e eq "";
44              
45             # The following address failed:
46             #
47             # general@example.eu
48             #
49             # For the following reason:
50             #
51             # Mail size limit exceeded. For explanation visit
52             # http://postmaster.1and1.com/en/error-messages?ip=%1s
53 97         93 $v = $dscontents->[-1];
54              
55 97 100       281 if( $e =~ /\A\s*([^ ]+[@][^ ]+?)[:]?\z/ ) {
    100          
56             # general@example.eu OR
57             # the line begin with 4 space characters, end with ":" like " neko@example.eu:"
58 17 50       43 if( $v->{'recipient'} ) {
59             # There are multiple recipient addresses in the message body.
60 0         0 push @$dscontents, __PACKAGE__->DELIVERYSTATUS;
61 0         0 $v = $dscontents->[-1];
62             }
63 17         92 $v->{'recipient'} = Sisimai::Address->s3s4($e);
64 17         29 $recipients++;
65              
66             } elsif( index($e, $startingof->{'error'}->[0]) == 0 ) {
67             # For the following reason:
68 6         13 $v->{'diagnosis'} = $e;
69              
70             } else {
71 74 100       95 if( length $v->{'diagnosis'} ) {
72             # Get error message and append the error message strings
73 12         26 $v->{'diagnosis'} .= ' '.$e;
74              
75             } else {
76             # OR the following format:
77             # neko@example.fr:
78             # SMTP error from remote server for TEXT command, host: ...
79 62         129 $v->{'alterrors'} .= ' '.$e;
80             }
81             }
82             }
83 17 50       53 return undef unless $recipients;
84              
85 17         56 require Sisimai::SMTP::Command;
86 17         31 for my $e ( @$dscontents ) {
87 17   50     89 $e->{'diagnosis'} ||= $e->{'alterrors'} || '';
      66        
88 17         107 $e->{'command'} = Sisimai::SMTP::Command->find($e->{'diagnosis'});
89              
90 17 100       57 if( Sisimai::String->aligned(\$e->{'diagnosis'}, ['host: ', ' reason:']) ) {
91             # SMTP error from remote server for TEXT command,
92             # host: smtp-in.orange.fr (193.252.22.65)
93             # reason: 550 5.2.0 Mail rejete. Mail rejected. ofr_506 [506]
94 11         26 my $p1 = index($e->{'diagnosis'}, 'host: ');
95 11         23 my $p2 = index($e->{'diagnosis'}, ' reason:');
96              
97 11         38 $e->{'rhost'} = substr($e->{'diagnosis'}, $p1 + 6, $p2 - $p1 - 6);
98 11 50       35 $e->{'command'} = 'DATA' if index($e->{'diagnosis'}, 'for TEXT command') > -1;
99 11 50       38 $e->{'spec'} = 'SMTP' if index($e->{'diagnosis'}, 'SMTP error') > -1;
100 11         54 $e->{'status'} = Sisimai::SMTP::Status->find($e->{'diagnosis'});
101             } else {
102             # For the following reason:
103 6         22 substr($e->{'diagnosis'}, 0, length $startingof->{'error'}->[0], '');
104             }
105             }
106 17         96 return {"ds" => $dscontents, "rfc822" => $emailparts->[1]};
107             }
108              
109             1;
110             __END__