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 33     33   358 use v5.26;
  33         91  
3 33     33   157 use strict;
  33         43  
  33         664  
4 33     33   122 use warnings;
  33         66  
  33         9757  
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 313 my $class = shift;
18 238 100 50     407 my $argv1 = shift || return 0; return __PACKAGE__->returnedby($argv1) ? 1 : 0;
  238         448  
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 270 my $class = shift;
26 270 50 50     396 my $argv1 = shift || return 0; return 0 unless index($argv1, "X-") == 0;
  270         502  
27              
28 270         446 for my $e ( keys %$ThirdParty ) {
29             # Does the argument include the 3rd party specific field?
30 270 100       672 return $e if index($argv1, $ThirdParty->{ $e }->[0]) == 0;
31             }
32 206         629 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 33 my $class = shift;
41 32   50     50 my $argv1 = shift || return [];
42 32 50       45 my $party = __PACKAGE__->returnedby($argv1); return [] unless $party;
  32         58  
43 32         127 return sprintf("Sisimai::RFC3464::ThirdParty::%s", $party)->xfield($argv1);
44             }
45             1;
46              
47             # -------------------------------------------------------------------------------------------------
48             package Sisimai::RFC3464::ThirdParty::PowerMTA;
49 33     33   345 use v5.26;
  33         118  
50 33     33   133 use strict;
  33         60  
  33         733  
51 33     33   124 use warnings;
  33         50  
  33         8855  
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   30 my $class = shift;
75 32   50     58 my $argv1 = shift || return [];
76              
77 32         66 my $fieldparts = [split(":", $argv1, 2)]; # ["Final-Recipient", " rfc822; "]
78 32         56 my $xfieldname = lc $fieldparts->[0]; # "final-recipient"
79 32 50       42 my $xgroupname = $FieldGroup->{ $xfieldname }; return [] unless $xgroupname;
  32         50  
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       71 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         23 $xfieldlist->[0] = $xfieldname;
92 16   50     75 $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         21 $xfieldlist->[0] = "Reporting-MTA";
97             }
98              
99 32         82 return $xfieldlist;
100             }
101              
102             1;
103              
104             __END__