File Coverage

lib/Sisimai/RFC3464/ThirdParty.pm
Criterion Covered Total %
statement 42 42 100.0
branch 10 14 71.4
condition 5 10 50.0
subroutine 10 10 100.0
pod 1 3 33.3
total 68 79 86.0


line stmt bran cond sub pod time code
1             package Sisimai::RFC3464::ThirdParty;
2 32     32   406 use v5.26;
  32         126  
3 32     32   169 use strict;
  32         92  
  32         954  
4 32     32   167 use warnings;
  32         55  
  32         10850  
5              
6             state $ThirdParty = {
7             #"Aol" => ["X-Outbound-Mail-Relay-"], # X-Outbound-Mail-Relay-(Queue-ID|Sender)
8             "PowerMTA" => ["X-PowerMTA-"], # X-PowerMTA-(VirtualMTA|BounceCategory)
9             #"Yandex" => ["X-Yandex-"], # X-Yandex-(Queue-ID|Sender)
10             };
11              
12             sub is3rdparty {
13             # is3rdparty() returns true if the argument is a line generated by a MTA which have fields defined
14             # in RFC3464 inside of a bounce mail the MTA returns
15             # @param string argv1 A line of a bounce mail
16             # @return bool The line indicates that a bounce mail generated by the 3rd party MTA
17 238     238 1 492 my $class = shift;
18 238 100 50     805 my $argv1 = shift || return 0; return __PACKAGE__->returnedby($argv1) ? 1 : 0;
  238         759  
19             }
20              
21             sub returnedby {
22             # returnedby() returns an MTA name of the 3rd party
23             # @param string argv1 A line of a bounce mail
24             # @return string An MTA name of the 3rd party
25 270     270 0 464 my $class = shift;
26 270 50 50     682 my $argv1 = shift || return 0; return 0 unless index($argv1, "X-") == 0;
  270         773  
27              
28 270         742 for my $e ( keys %$ThirdParty ) {
29             # Does the argument include the 3rd party specific field?
30 270 100       1090 return $e if index($argv1, $ThirdParty->{ $e }->[0]) == 0;
31             }
32 206         1064 return ""
33             }
34              
35             sub xfield {
36             # xfield() returns rfc1894.Field() compatible slice for the specific field of the 3rd party MTA
37             # @param string argv1 A line of the error message
38             # @return [] RFC1894->field() compatible array
39             # @see Sisimai::RFC1894
40 32     32 0 37 my $class = shift;
41 32   50     60 my $argv1 = shift || return [];
42 32 50       73 my $party = __PACKAGE__->returnedby($argv1); return [] unless $party;
  32         65  
43 32         133 return sprintf("Sisimai::RFC3464::ThirdParty::%s", $party)->xfield($argv1);
44             }
45             1;
46              
47             # -------------------------------------------------------------------------------------------------
48             package Sisimai::RFC3464::ThirdParty::PowerMTA;
49 32     32   541 use v5.26;
  32         166  
50 32     32   196 use strict;
  32         62  
  32         970  
51 32     32   145 use warnings;
  32         65  
  32         13109  
52              
53             state $FieldGroup = {
54             "x-powermta-virtualmta" => "host", # X-PowerMTA-VirtualMTA: mx22.neko.example.jp
55             "x-powermta-bouncecategory" => "text", # X-PowerMTA-BounceCategory: bad-mailbox
56             };
57             state $MessagesOf = {
58             "bad-domain" => "hostunknown",
59             "bad-mailbox" => "userunknown",
60             "inactive-mailbox" => "suspend",
61             "message-expired" => "expired",
62             "no-answer-from-host" => "networkerror",
63             "policy-related" => "policyviolation",
64             "quota-issues" => "mailboxfull",
65             "routing-errors" => "systemerror",
66             "spam-related" => "spamdetected",
67             };
68              
69             sub xfield {
70             # Returns an array which is compatible with the value returned from Sisimai::RFC1894->field()
71             # @param string argv1 A line of the error message
72             # @return Array ["field-name", "value-type", "value", "field-group", "comment"]
73             # @see https://bird.com/email/power-mta
74 32     32   45 my $class = shift;
75 32   50     50 my $argv1 = shift || return [];
76              
77 32         94 my $fieldparts = [split(":", $argv1, 2)]; # ["Final-Recipient", " rfc822; "]
78 32         64 my $xfieldname = lc $fieldparts->[0]; # "final-recipient"
79 32 50       53 my $xgroupname = $FieldGroup->{ $xfieldname }; return [] unless $xgroupname;
  32         62  
80 32         85 my $xfieldlist = ["", "", Sisimai::String->sweep($fieldparts->[1]), $xgroupname, "", "PowerMTA"];
81              
82             # - 0: Field-Name
83             # - 1: Sub Type: RFC822, DNS, X-Unix, and so on)
84             # - 2: Value
85             # - 3: Field Group(addr, code, date, host, stat, text)
86             # - 4: Comment
87             # - 5: 3rd Party MTA-Name
88 32 100       86 if( $xfieldname eq "x-powermta-bouncecategory" ) {
    50          
89             # X-PowerMTA-BounceCategory: bad-mailbox
90             # Set the bounce reason picked from the value of the field
91 16         30 $xfieldlist->[0] = $xfieldname;
92 16   50     92 $xfieldlist->[4] = sprintf("reason:%s", $MessagesOf->{ $xfieldlist->[2] } || "");
93              
94             } elsif( $xfieldname eq "x-powermta-virtualmta" ) {
95             # X-PowerMTA-VirtualMTA: mx22.neko.example.jp
96 16         34 $xfieldlist->[0] = "Reporting-MTA";
97             }
98              
99 32         79 return $xfieldlist;
100             }
101              
102             1;
103              
104             __END__