File Coverage

lib/Sisimai/Lhost/MailMarshal.pm
Criterion Covered Total %
statement 57 59 96.6
branch 31 36 86.1
condition 12 13 92.3
subroutine 6 6 100.0
pod 2 2 100.0
total 108 116 93.1


line stmt bran cond sub pod time code
1             package Sisimai::Lhost::MailMarshal;
2 36     36   4743 use parent 'Sisimai::Lhost';
  36         86  
  36         278  
3 36     36   3045 use v5.26;
  36         131  
4 36     36   4211 use strict;
  36         111  
  36         1089  
5 36     36   164 use warnings;
  36         82  
  36         36312  
6              
7 1     1 1 8 sub description { 'Trustwave Secure Email Gateway: https://www.trustwave.com/en-us/services/email-security/' }
8             sub inquire {
9             # Detect an error from Trustwave Secure Email Gateway (Formerly MailMarshal)
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.9
15 899     899 1 3101 my $class = shift;
16 899   100     2911 my $mhead = shift // return undef;
17 898   100     2447 my $mbody = shift // return undef;
18              
19 897 100       4437 return undef unless $mhead->{'subject'};
20 880 100       4652 return undef unless index($mhead->{'subject'}, 'Undeliverable Mail: "') == 0;
21              
22 6         39 state $indicators = __PACKAGE__->INDICATORS;
23 6         30 state $startingof = {
24             'message' => ['Your message:'],
25             'error' => ['Could not be delivered because of'],
26             'rcpts' => ['The following recipients were affected:'],
27             };
28              
29 6         40 my $dscontents = [__PACKAGE__->DELIVERYSTATUS]; my $v = undef;
  6         14  
30 6         10 my $readcursor = 0; # (Integer) Points the current cursor position
31 6         9 my $recipients = 0; # (Integer) The number of 'Final-Recipient' header
32 6         10 my $endoferror = 0; # (Integer) Flag for the end of error message
33              
34 6         13 my $boundaries = ['+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++'];
35 6 50       26 my $q = Sisimai::RFC2045->boundary($mhead->{'content-type'}, 1); push @$boundaries, $q if $q;
  6         24  
36 6         33 my $emailparts = Sisimai::RFC5322->part($mbody, $boundaries);
37              
38 6         68 for my $e ( split("\n", $emailparts->[0]) ) {
39             # Read error messages and delivery status lines from the head of the email to the previous
40             # line of the beginning of the original message.
41 132 100       210 unless( $readcursor ) {
42             # Beginning of the bounce message or message/delivery-status part
43 6 50       53 $readcursor |= $indicators->{'deliverystatus'} if index($e, $startingof->{'message'}->[0]) == 0;
44             }
45 132 50       223 next if ($readcursor & $indicators->{'deliverystatus'}) == 0;
46              
47             # Your message:
48             # From: originalsender@example.com
49             # Subject: ...
50             #
51             # Could not be delivered because of
52             #
53             # 550 5.1.1 User unknown
54             #
55             # The following recipients were affected:
56             # dummyuser@blabla.xxxxxxxxxxxx.com
57 132         158 $v = $dscontents->[-1];
58              
59 132 100 66     332 if( index($e, ' ') == 0 && index($e, '@') > 1 ) {
60             # The following recipients were affected:
61             # dummyuser@blabla.xxxxxxxxxxxx.com
62 6 50       20 if( $v->{'recipient'} ) {
63             # There are multiple recipient addresses in the message body.
64 0         0 push @$dscontents, __PACKAGE__->DELIVERYSTATUS;
65 0         0 $v = $dscontents->[-1];
66             }
67 6         20 $v->{'recipient'} = substr($e, 4,);
68 6         36 $recipients++;
69              
70             } else {
71             # Get error message lines
72 126 100 100     379 if( $e eq $startingof->{'error'}->[0] ) {
    100          
73             # Could not be delivered because of
74             #
75             # 550 5.1.1 User unknown
76 6         15 $v->{'diagnosis'} = $e;
77              
78             } elsif( $v->{'diagnosis'} && ! $endoferror ) {
79             # Append error messages
80 24 100       60 $endoferror = 1 if index($e, $startingof->{'rcpts'}->[0]) == 0;
81 24 100       50 next if $endoferror;
82              
83 18         38 $v->{'diagnosis'} .= ' '.$e;
84              
85             } else {
86             # Additional Information
87             # ======================
88             # Original Sender:
89             # Sender-MTA: <10.11.12.13>
90             # Remote-MTA: <10.0.0.1>
91             # Reporting-MTA:
92             # MessageName:
93             # Last-Attempt-Date: <16:21:07 seg, 22 Dezembro 2014>
94 96         122 my $p1 = index($e, '<');
95 96         111 my $p2 = index($e, '>');
96 96 100 100     423 if( index($e, 'Original Sender: ') == 0 ) {
    100          
    100          
    100          
97             # Original Sender:
98             # Use this line instead of "From" header of the original message.
99 6         50 $emailparts->[1] .= sprintf("From: %s\n", substr($e, $p1 + 1, $p2 - $p1 - 1));
100              
101             } elsif( index($e, 'Sender-MTA: ') == 0 ) {;
102             # Sender-MTA: <10.11.12.13>
103 6         35 $v->{'lhost'} = substr($e, $p1 + 1, $p2 - $p1 - 1);
104              
105             } elsif( index($e , 'Reporting-MTA: ') == 0 ) {
106             # Reporting-MTA:
107 6         19 $v->{'rhost'} = substr($e, $p1 + 1, $p2 - $p1 - 1);
108              
109             } elsif( index($e, ' From:') > 0 || index($e, ' Subject:') > 0 ) {
110             # From: originalsender@example.com
111             # Subject: ...
112 12 100       27 $p1 = index($e, ' From:'); $p1 = index($e, ' Subject:') if $p1 < 0;
  12         24  
113 12         19 $p2 = index($e, ':');
114              
115 12         30 my $cf = substr($e, $p1 + 1, $p2 - $p1 - 1);
116 12         82 my $cv = Sisimai::String->sweep(substr($e, $p2 + 1,));
117 12         42 $emailparts->[1] .= sprintf("%s: %s\n", $cf, $cv);
118             }
119             }
120             }
121             }
122 6 50       31 return undef unless $recipients;
123              
124 6         35 $_->{'diagnosis'} = Sisimai::String->sweep($_->{'diagnosis'}) for @$dscontents;
125 6         42 return {"ds" => $dscontents, "rfc822" => $emailparts->[1]};
126             }
127              
128             1;
129             __END__