File Coverage

lib/Sisimai/Lhost/ApacheJames.pm
Criterion Covered Total %
statement 56 58 96.5
branch 32 44 72.7
condition 9 16 56.2
subroutine 6 6 100.0
pod 2 2 100.0
total 105 126 83.3


line stmt bran cond sub pod time code
1             package Sisimai::Lhost::ApacheJames;
2 38     38   3395 use parent 'Sisimai::Lhost';
  38         65  
  38         194  
3 38     38   2504 use v5.26;
  38         104  
4 38     38   158 use strict;
  38         56  
  38         661  
5 38     38   153 use warnings;
  38         55  
  38         24479  
6              
7 1     1 1 2 sub description { 'James: https://james.apache.org/' }
8             sub inquire {
9             # Detect an error from Apache James
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.26
15 886     886 1 2768 my $class = shift;
16 886   100     1720 my $mhead = shift // return undef;
17 885   100     1844 my $mbody = shift // return undef;
18 884 50 0     1068 my $match = 0; $match ||= 1 if $mhead->{'subject'} eq '[BOUNCE]';
  884         2136  
19 884 100 50     9648 $match ||= 1 if defined $mhead->{'message-id'} && rindex($mhead->{'message-id'}, '.JavaMail.') > -1;
      100        
20 884 50 0     1956 $match ||= 1 if grep { rindex($_, 'JAMES SMTP Server') > -1 } $mhead->{'received'}->@*;
  1701         3148  
21 884 100       2376 return undef unless $match;
22              
23 6         29 state $indicators = __PACKAGE__->INDICATORS;
24 6         12 state $boundaries = ["Content-Type: message/rfc822"];
25 6         16 state $startingof = {
26             # apache-james-2.3.2/src/java/org/apache/james/transport/mailets/
27             # AbstractNotify.java|124: out.println("Error message below:");
28             # AbstractNotify.java|128: out.println("Message details:");
29             "message" => ["Message details:"],
30             };
31              
32 6         32 my $dscontents = [__PACKAGE__->DELIVERYSTATUS]; my $v = $dscontents->[-1];
  6         13  
33 6         27 my $emailparts = Sisimai::RFC5322->part($mbody, $boundaries);
34 6         10 my $readcursor = 0; # Points the current cursor position
35 6         8 my $recipients = 0; # The number of 'Final-Recipient' header
36 6         16 my $alternates = ["", "", "", ""]; # [Envelope-From, Header-From, Date, Subject]
37              
38 6         39 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 84 100       101 unless( $readcursor ) {
42             # Beginning of the bounce message or message/delivery-status part
43 36 100       65 if( index($e, $startingof->{"message"}->[0]) == 0 ) {
44             # Message details:
45             # Subject: Nyaaan
46 6         14 $readcursor |= $indicators->{"deliverystatus"}; next;
  6         10  
47             }
48 30 100       57 $v->{"diagnosis"} .= $e." " if $e ne "";
49 30         28 next;
50             }
51 48 50 33     126 next if ($readcursor & $indicators->{'deliverystatus'}) == 0 || $e eq "";
52              
53             # Message details:
54             # Subject: Nyaaan
55             # Sent date: Thu Apr 29 01:20:50 JST 2015
56             # MAIL FROM: shironeko@example.jp
57             # RCPT TO: kijitora@example.org
58             # From: Neko
59             # To: kijitora@example.org
60             # Size (in bytes): 1024
61             # Number of lines: 64
62 48 100       143 if( index($e, " RCPT TO: ") == 0 ) {
    100          
    100          
    100          
    100          
63             # RCPT TO: kijitora@example.org
64 6 50       17 if( $v->{"recipient"} ) {
65             # There are multiple recipient addresses in the message body.
66 0         0 push @$dscontents, __PACKAGE__->DELIVERYSTATUS;
67 0         0 $v = $dscontents->[-1];
68             }
69 6         13 $v->{"recipient"} = substr($e, 12,);
70 6         9 $recipients++;
71              
72             } elsif( index($e, " Sent date: ") == 0 ) {
73             # Sent date: Thu Apr 29 01:20:50 JST 2015
74 6         10 $v->{"date"} = substr($e, 13,);
75 6         11 $alternates->[2] = $v->{"date"};
76              
77             } elsif( index($e, " Subject: ") == 0 ) {
78             # Subject: Nyaaan
79 6         22 $alternates->[3] = substr($e, 11,);
80              
81             } elsif( index($e, " MAIL FROM: ") == 0 ) {
82             # MAIL FROM: shironeko@example.jp
83 6         13 $alternates->[0] = substr($e, 13,);
84              
85             } elsif( index($e, " From: ") == 0 ) {
86             # From: Neko
87 6         30 $alternates->[1] = substr($e, 8,);
88             }
89             }
90 6 50       18 return undef unless $recipients;
91              
92 6 50       29 if( $emailparts->[1] eq "" ) {
93             # The original message is empty
94 6 50       21 $emailparts->[1] .= sprintf("From: %s\n", $alternates->[1]) if $alternates->[1] ne "";
95 6 50       20 $emailparts->[1] .= sprintf("Date: %s\n", $alternates->[2]) if $alternates->[2] ne "";
96             }
97 6 50       16 if( index($emailparts->[1], "Return-Path: ") < 0 ) {
98             # Set the envelope from address as a Return-Path: header
99 6 50       17 $emailparts->[1] .= sprintf("Return-Path: <%s>\n", $alternates->[0]) if $alternates->[0] ne "";
100             }
101 6 50       20 if( index($emailparts->[1], "\nSubject: ") < 0 ) {
102             # Set the envelope from address as a Return-Path: header
103 6 50       20 $emailparts->[1] .= sprintf("Subject: %s\n", $alternates->[3]) if $alternates->[3] ne "";
104             }
105 6         26 return {"ds" => $dscontents, "rfc822" => $emailparts->[1]};
106             }
107              
108             1;
109             __END__