File Coverage

lib/Sisimai/Lhost/ApacheJames.pm
Criterion Covered Total %
statement 58 60 96.6
branch 29 38 76.3
condition 8 16 50.0
subroutine 6 6 100.0
pod 2 2 100.0
total 103 122 84.4


line stmt bran cond sub pod time code
1             package Sisimai::Lhost::ApacheJames;
2 18     18   4970 use parent 'Sisimai::Lhost';
  18         31  
  18         84  
3 18     18   946 use feature ':5.10';
  18         46  
  18         1066  
4 18     18   82 use strict;
  18         42  
  18         335  
5 18     18   85 use warnings;
  18         24  
  18         12161  
6              
7 2     2 1 1569 sub description { 'Java Apache Mail Enterprise Server' }
8             sub make {
9             # Detect an error from ApacheJames
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 parse or the arguments are missing
14             # @since v4.1.26
15 208     208 1 741 my $class = shift;
16 208   100     663 my $mhead = shift // return undef;
17 207   50     428 my $mbody = shift // return undef;
18 207         336 my $match = 0;
19              
20             # 'subject' => qr/\A\[BOUNCE\]\z/,
21             # 'received' => qr/JAMES SMTP Server/,
22             # 'message-id' => qr/\d+[.]JavaMail[.].+[@]/,
23 207 50 0     443 $match ||= 1 if $mhead->{'subject'} eq '[BOUNCE]';
24 207 100 50     903 $match ||= 1 if defined $mhead->{'message-id'} && rindex($mhead->{'message-id'}, '.JavaMail.') > -1;
      100        
25 207 50 0     327 $match ||= 1 if grep { rindex($_, 'JAMES SMTP Server') > -1 } @{ $mhead->{'received'} };
  386         826  
  207         416  
26 207 100       599 return undef unless $match;
27              
28 6         31 state $indicators = __PACKAGE__->INDICATORS;
29 6         25 state $rebackbone = qr|^Content-Type:[ ]message/rfc822|m;
30 6         18 state $startingof = {
31             # apache-james-2.3.2/src/java/org/apache/james/transport/mailets/
32             # AbstractNotify.java|124: out.println("Error message below:");
33             # AbstractNotify.java|128: out.println("Message details:");
34             'message' => [''],
35             'error' => ['Error message below:'],
36             };
37              
38 6         27 my $dscontents = [__PACKAGE__->DELIVERYSTATUS];
39 6         32 my $emailsteak = Sisimai::RFC5322->fillet($mbody, $rebackbone);
40 6         13 my $readcursor = 0; # (Integer) Points the current cursor position
41 6         9 my $recipients = 0; # (Integer) The number of 'Final-Recipient' header
42 6         8 my $diagnostic = ''; # (String) Alternative diagnostic message
43 6         10 my $subjecttxt = undef; # (String) Alternative Subject text
44 6         80 my $gotmessage = 0; # (Integer) Flag for error message
45 6         67 my $v = undef;
46              
47 6         47 for my $e ( split("\n", $emailsteak->[0]) ) {
48             # Read error messages and delivery status lines from the head of the email
49             # to the previous line of the beginning of the original message.
50 114 100       136 unless( $readcursor ) {
51             # Beginning of the bounce message or message/delivery-status part
52 6 50       37 $readcursor |= $indicators->{'deliverystatus'} if index($e, $startingof->{'message'}->[0]) == 0;
53 6         16 next;
54             }
55 108 50       133 next unless $readcursor & $indicators->{'deliverystatus'};
56 108 100       127 next unless length $e;
57              
58             # Message details:
59             # Subject: Nyaaan
60             # Sent date: Thu Apr 29 01:20:50 JST 2015
61             # MAIL FROM: shironeko@example.jp
62             # RCPT TO: kijitora@example.org
63             # From: Neko
64             # To: kijitora@example.org
65             # Size (in bytes): 1024
66             # Number of lines: 64
67 84         72 $v = $dscontents->[-1];
68              
69 84 100       205 if( $e =~ /\A[ ][ ]RCPT[ ]TO:[ ]([^ ]+[@][^ ]+)\z/ ) {
    100          
    100          
70             # RCPT TO: kijitora@example.org
71 6 50       23 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 6         13 $v->{'recipient'} = $1;
77 6         10 $recipients++;
78              
79             } elsif( $e =~ /\A[ ][ ]Sent[ ]date:[ ](.+)\z/ ) {
80             # Sent date: Thu Apr 29 01:20:50 JST 2015
81 6         15 $v->{'date'} = $1;
82              
83             } elsif( $e =~ /\A[ ][ ]Subject:[ ](.+)\z/ ) {
84             # Subject: Nyaaan
85 6         26 $subjecttxt = $1;
86              
87             } else {
88 66 100       83 next if $gotmessage == 1;
89              
90 24 100       36 if( $v->{'diagnosis'} ) {
91             # Get an error message text
92 18 100       42 if( $e eq 'Message details:' ) {
93             # Message details:
94             # Subject: nyaan
95             # ...
96 6         10 $gotmessage = 1;
97              
98             } else {
99             # Append error message text like the followng:
100             # Error message below:
101             # 550 - Requested action not taken: no such user here
102 12         24 $v->{'diagnosis'} .= ' '.$e;
103             }
104             } else {
105             # Error message below:
106             # 550 - Requested action not taken: no such user here
107 6 50       15 $v->{'diagnosis'} = $e if $e eq $startingof->{'error'}->[0];
108 6 50       28 $v->{'diagnosis'} .= ' '.$e unless $gotmessage;
109             }
110             }
111             }
112 6 50       37 return undef unless $recipients;
113              
114             # Set the value of $subjecttxt as a Subject if there is no original message
115             # in the bounce mail.
116 6 50       39 $emailsteak->[1] .= sprintf("Subject: %s\n", $subjecttxt) unless $emailsteak->[1] =~ /^Subject:/m;
117 6   33     50 $_->{'diagnosis'} = Sisimai::String->sweep($_->{'diagnosis'} || $diagnostic) for @$dscontents;
118 6         30 return { 'ds' => $dscontents, 'rfc822' => $emailsteak->[1] };
119             }
120              
121             1;
122             __END__