diff --git a/ChangeLog.md b/ChangeLog.md index c6e1d176..1c70d7cf 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -4,6 +4,28 @@ RELEASE NOTES for Perl version of Sisimai - download: "https://metacpan.org/pod/Sisimai" - document: "https://libsisimai.org/" +v5.0.1p1 +--------------------------------------------------------------------------------------------------- +- release: "" +- version: "" +- changes: + - #507 Migrate the CI from TravisCI to GitHub Actions + - Fixed an issue where constant initialization would fail on Perl 5.26 + - `5.7.23` returned from Office365 is an error related to SPF vilation (authfailure) + - #508 Fixed an issue that Sisimai could not get the value of `alias` address correctly when an + email forwarded and bounced + - `Sisimai::RFC5322->received` now returns a list including all the elements except date time and + (comments) found in the `Received` header + - Update the error message patterns in `Sisimai::Rhost::Mimecast` + - Update the error message patterns in the followings: + - `AuthFailure` + - `Blocked` + - `Expired` + - `MailboxFull` + - `SecurityError` + - `SpamDetected` + - `Suspend` + v5.0.1 --------------------------------------------------------------------------------------------------- - release: "Sun, 3 Mar 2024 17:17:17 +0900 (JST)" diff --git a/lib/Sisimai/Fact.pm b/lib/Sisimai/Fact.pm index 5c576d4f..d5fd913c 100644 --- a/lib/Sisimai/Fact.pm +++ b/lib/Sisimai/Fact.pm @@ -165,20 +165,21 @@ sub rise { OTHER_TEXT_HEADERS: { # Scan "Received:" header of the original message - my $recvheader = $mesg1->{'header'}->{'received'} || []; - if( scalar @$recvheader ) { - # Get localhost and remote host name from Received header. - $e->{'lhost'} ||= shift Sisimai::RFC5322->received($recvheader->[0])->@*; - $e->{'rhost'} ||= pop Sisimai::RFC5322->received($recvheader->[-1])->@*; + my $rr = $mesg1->{'header'}->{'received'} || []; + if( scalar @$rr ) { + # Get a local host name and a remote host name from the Received header. + $p->{'rhost'} ||= Sisimai::RFC5322->received($rr->[-1])->[1] || ''; + $p->{'lhost'} = '' if $p->{'lhost'} eq $p->{'rhost'}; + $p->{'lhost'} ||= Sisimai::RFC5322->received($rr->[ 0])->[0]; } for my $v ('rhost', 'lhost') { # Check and rewrite each host name $p->{ $v } = [split('@', $p->{ $v })]->[-1] if index($p->{ $v }, '@') > -1; - y/[]()//d, s/\A.+=// for $p->{ $v }; # Remove [] and (), and strings before "=" + y/[]()//d, s/\A.+=// for $p->{ $v }; # Remove [], (), and strings before "=" chop $p->{ $v } if substr($p->{ $v }, -1, 1) eq "\r"; # Remove CR at the end of the value - # Check space character in each value and get the first element + # Check a space character in each value and get the first element $p->{ $v } = (split(' ', $p->{ $v }, 2))[0] if rindex($p->{ $v }, ' ') > -1; chop $p->{ $v } if substr($p->{ $v }, -1, 1) eq '.'; # Remove "." at the end of the value } @@ -307,6 +308,31 @@ sub rise { $o->{'timezoneoffset'} = $p->{'timezoneoffset'} // '+0000'; } + ALIAS: while(1) { + # Look up the Envelope-To address from the Received: header in the original message + # when the recipient address is same with the value of $o->{'alias'}. + last if length $o->{'alias'} == 0; + last if $o->{'recipient'}->address ne $o->{'alias'}; + last unless exists $rfc822data->{'received'}; + last unless scalar $rfc822data->{'received'}->@*; + + for my $er ( reverse $rfc822data->{'received'}->@* ) { + # Search for the string " for " from the Received: header + next unless index($er, ' for ') > 1; + my $or = Sisimai::RFC5322->received($er); + + next unless scalar @$or; + next unless length $or->[5]; + next unless Sisimai::Address->is_emailaddress($or->[5]); + next if $o->{'recipient'}->address eq $or->[5]; + + $o->{'alias'} = $or->[5]; + last; + } + last; + } + $o->{'alias'} = '' if $o->{'alias'} eq $o->{'recipient'}->{'address'}; + REASON: { # Decide the reason of email bounce if( $o->{'reason'} eq '' || exists $retryindex->{ $o->{'reason'} } ) { @@ -729,7 +755,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2014-2022 azumakuniyuki, All rights reserved. +Copyright (C) 2014-2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/AmazonSES.pm b/lib/Sisimai/Lhost/AmazonSES.pm index 7c74fef1..1f124e14 100644 --- a/lib/Sisimai/Lhost/AmazonSES.pm +++ b/lib/Sisimai/Lhost/AmazonSES.pm @@ -288,8 +288,7 @@ sub inquire { for my $e ( @$dscontents ) { # Set default values if each value is empty. - $e->{'lhost'} ||= $permessage->{'rhost'}; - $e->{ $_ } ||= $permessage->{ $_ } || '' for keys %$permessage; + $e->{ $_ } ||= $permessage->{ $_ } || '' for keys %$permessage; $e->{'diagnosis'} =~ y/\n/ /; $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'}); @@ -358,7 +357,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2014-2023 azumakuniyuki, All rights reserved. +Copyright (C) 2014-2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/AmazonWorkMail.pm b/lib/Sisimai/Lhost/AmazonWorkMail.pm index 1fb63d4a..cc9e9e50 100644 --- a/lib/Sisimai/Lhost/AmazonWorkMail.pm +++ b/lib/Sisimai/Lhost/AmazonWorkMail.pm @@ -100,7 +100,6 @@ sub inquire { for my $e ( @$dscontents ) { # Set default values if each value is empty. - $e->{'lhost'} ||= $permessage->{'rhost'}; $e->{ $_ } ||= $permessage->{ $_ } || '' for keys %$permessage; $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'}); @@ -155,7 +154,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2016-2021,2023 azumakuniyuki, All rights reserved. +Copyright (C) 2016-2021,2023,2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/Aol.pm b/lib/Sisimai/Lhost/Aol.pm index af9608f3..acec5ae7 100644 --- a/lib/Sisimai/Lhost/Aol.pm +++ b/lib/Sisimai/Lhost/Aol.pm @@ -106,7 +106,6 @@ sub inquire { for my $e ( @$dscontents ) { # Set default values if each value is empty. - $e->{'lhost'} ||= $permessage->{'rhost'}; $e->{ $_ } ||= $permessage->{ $_ } || '' for keys %$permessage; $e->{'diagnosis'} =~ y/\n/ /; @@ -158,7 +157,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2014-2023 azumakuniyuki, All rights reserved. +Copyright (C) 2014-2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/Bigfoot.pm b/lib/Sisimai/Lhost/Bigfoot.pm index 91ff3a0d..26f04711 100644 --- a/lib/Sisimai/Lhost/Bigfoot.pm +++ b/lib/Sisimai/Lhost/Bigfoot.pm @@ -113,7 +113,6 @@ sub inquire { for my $e ( @$dscontents ) { # Set default values if each value is empty. - $e->{'lhost'} ||= $permessage->{'rhost'}; $e->{ $_ } ||= $permessage->{ $_ } || '' for keys %$permessage; $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'}); @@ -160,7 +159,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2014-2023 azumakuniyuki, All rights reserved. +Copyright (C) 2014-2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/Domino.pm b/lib/Sisimai/Lhost/Domino.pm index 8203903f..b5922f66 100644 --- a/lib/Sisimai/Lhost/Domino.pm +++ b/lib/Sisimai/Lhost/Domino.pm @@ -146,7 +146,6 @@ sub inquire { $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'}); $e->{'recipient'} = Sisimai::Address->s3s4($e->{'recipient'}); - $e->{'lhost'} ||= $permessage->{'rhost'}; $e->{ $_ } ||= $permessage->{ $_ } || '' for keys %$permessage; for my $r ( keys %$messagesof ) { @@ -201,7 +200,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2014-2023 azumakuniyuki, All rights reserved. +Copyright (C) 2014-2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/Exim.pm b/lib/Sisimai/Lhost/Exim.pm index 20404ede..cc070493 100644 --- a/lib/Sisimai/Lhost/Exim.pm +++ b/lib/Sisimai/Lhost/Exim.pm @@ -151,7 +151,6 @@ sub inquire { my $readcursor = 0; # (Integer) Points the current cursor position my $nextcursor = 0; my $recipients = 0; # (Integer) The number of 'Final-Recipient' header - my $localhost0 = ''; # (String) Local MTA my $boundary00 = ''; # (String) Boundary string my $v = undef; @@ -338,18 +337,13 @@ sub inquire { } return undef unless $recipients; - if( scalar $mhead->{'received'}->@* ) { - # Get the name of local MTA - # Received: from marutamachi.example.org (c192128.example.net [192.0.2.128]) - $p1 = index($mhead->{'received'}->[-1], 'from '); - $p2 = index($mhead->{'received'}->[-1], ' ', $p1 + 5); - $localhost0 = substr($mhead->{'received'}->[-1], $p1 + 5, $p2 - $p1 - 5) if $p1 > -1 && $p2 > $p1; - } + # Get the name of the local MTA + # Received: from marutamachi.example.org (c192128.example.net [192.0.2.128]) + my $receivedby = $mhead->{'received'} || []; + my $recvdtoken = Sisimai::RFC5322->received($receivedby->[-1]); for my $e ( @$dscontents ) { - # Set default values if each value is empty. - $e->{'lhost'} ||= $localhost0; - + # Check the error message, the rhost, the lhost, and the smtp command. if( ! $e->{'diagnosis'} && length($boundary00) > 0 ) { # Empty Diagnostic-Code: or error message # --NNNNNNNNNN-eximdsn-MMMMMMMMMM @@ -396,14 +390,10 @@ sub inquire { # host neko.example.jp [192.0.2.222]: 550 5.1.1 ... User Unknown $p1 = index($e->{'diagnosis'}, 'host '); $p2 = index($e->{'diagnosis'}, ' ', $p1 + 5); - $e->{'rhost'} = substr($e->{'diagnosis'}, $p1 + 5, $p2 - $p1 - 5) if $p1 > -1; - - if( ! $e->{'rhost'} && scalar $mhead->{'received'}->@* ) { - # Get localhost and remote host name from Received header. - my $r0 = $mhead->{'received'}; - $e->{'rhost'} = pop Sisimai::RFC5322->received($r0->[-1])->@*; - } + $e->{'rhost'} = substr($e->{'diagnosis'}, $p1 + 5, $p2 - $p1 - 5) if $p1 > -1; + $e->{'rhost'} ||= $recvdtoken->[1]; } + $e->{'lhost'} ||= $recvdtoken->[0]; unless( $e->{'command'} ) { # Get the SMTP command name for the session @@ -539,7 +529,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2014-2023 azumakuniyuki, All rights reserved. +Copyright (C) 2014-2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/Facebook.pm b/lib/Sisimai/Lhost/Facebook.pm index 54a46ca7..47de5691 100644 --- a/lib/Sisimai/Lhost/Facebook.pm +++ b/lib/Sisimai/Lhost/Facebook.pm @@ -155,7 +155,6 @@ sub inquire { return undef unless $recipients; for my $e ( @$dscontents ) { - $e->{'lhost'} ||= $permessage->{'lhost'}; $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'}); my $p0 = index($e->{'diagnosis'}, '-'); @@ -226,7 +225,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2014-2023 azumakuniyuki, All rights reserved. +Copyright (C) 2014-2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/GSuite.pm b/lib/Sisimai/Lhost/GSuite.pm index 8660a130..739701fc 100644 --- a/lib/Sisimai/Lhost/GSuite.pm +++ b/lib/Sisimai/Lhost/GSuite.pm @@ -140,7 +140,6 @@ sub inquire { for my $e ( @$dscontents ) { # Set default values if each value is empty. - $e->{'lhost'} ||= $permessage->{'rhost'}; $e->{ $_ } ||= $permessage->{ $_ } || '' for keys %$permessage; if( exists $anotherset->{'diagnosis'} && $anotherset->{'diagnosis'} ) { @@ -228,7 +227,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2017-2023 azumakuniyuki, All rights reserved. +Copyright (C) 2017-2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/GoogleGroups.pm b/lib/Sisimai/Lhost/GoogleGroups.pm index 12dd5929..73f0c12d 100644 --- a/lib/Sisimai/Lhost/GoogleGroups.pm +++ b/lib/Sisimai/Lhost/GoogleGroups.pm @@ -56,10 +56,9 @@ sub inquire { my @entiremesg = split(/\n\n/, $emailparts->[0], 5); pop @entiremesg; my $issuedcode = join(' ', @entiremesg); $issuedcode =~ y/\n/ /; + my $receivedby = $mhead->{'received'} || []; $recordwide->{'diagnosis'} = Sisimai::String->sweep($issuedcode); - - my $serverlist = Sisimai::RFC5322->received($mhead->{'received'}->[0]); - $recordwide->{'rhost'} = shift @$serverlist; + $recordwide->{'rhost'} = Sisimai::RFC5322->received($receivedby->[0])->[1]; for my $e ( split(',', $mhead->{'x-failed-recipients'}) ) { # X-Failed-Recipients: neko@example.jp, nyaan@example.org, ... @@ -115,7 +114,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2020-2023 azumakuniyuki, All rights reserved. +Copyright (C) 2020-2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/MXLogic.pm b/lib/Sisimai/Lhost/MXLogic.pm index 96952f00..3584ca6d 100644 --- a/lib/Sisimai/Lhost/MXLogic.pm +++ b/lib/Sisimai/Lhost/MXLogic.pm @@ -80,7 +80,6 @@ sub inquire { my $emailparts = Sisimai::RFC5322->part($mbody, $boundaries); my $readcursor = 0; # (Integer) Points the current cursor position my $recipients = 0; # (Integer) The number of 'Final-Recipient' header - my $localhost0 = ''; # (String) Local MTA my $v = undef; for my $e ( split("\n", $emailparts->[0]) ) { @@ -126,20 +125,13 @@ sub inquire { } return undef unless $recipients; - if( scalar $mhead->{'received'}->@* ) { - # Get the name of local MTA - my $p1 = index(lc $mhead->{'received'}->[-1], 'from '); - my $p2 = index( $mhead->{'received'}->[-1], ' ', $p1 + 5); - - if( ($p1 + 1) * ($p2 + 1) > 0 ) { - # Received: from marutamachi.example.org (c192128.example.net [192.0.2.128]) - $localhost0 = substr($mhead->{'received'}->[-1], $p1 + 5, $p2 - $p1 - 5); - } - } + # Get the name of the local MTA + # Received: from marutamachi.example.org (c192128.example.net [192.0.2.128]) + my $receivedby = $mhead->{'received'} || []; + my $recvdtoken = Sisimai::RFC5322->received($receivedby->[-1]); for my $e ( @$dscontents ) { - # Set default values if each value is empty. - $e->{'lhost'} ||= $localhost0; + # Check the error message, the rhost, the lhost, and the smtp command. $e->{'diagnosis'} =~ s/[-]{2}.*\z//g; $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'}); @@ -149,13 +141,11 @@ sub inquire { my $p2 = index($e->{'diagnosis'}, ' ', $p1 + 5); # host neko.example.jp [192.0.2.222]: 550 5.1.1 ... User Unknown - $e->{'rhost'} = substr($e->{'diagnosis'}, $p1 + 5, $p2 - $p1 - 5) if $p1 > -1; - - unless( $e->{'rhost'} ) { - # Get localhost and remote host name from Received header. - $e->{'rhost'} = pop Sisimai::RFC5322->received($mhead->{'received'}->[-1])->@* if scalar $mhead->{'received'}->@*; - } + # Get the remote host name from the error message or the Received header. + $e->{'rhost'} = substr($e->{'diagnosis'}, $p1 + 5, $p2 - $p1 - 5) if $p1 > -1; + $e->{'rhost'} ||= $recvdtoken->[1]; } + $e->{'lhost'} ||= $recvdtoken->[0]; unless( $e->{'command'} ) { # Get the SMTP command name for the session @@ -232,7 +222,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2014-2023 azumakuniyuki, All rights reserved. +Copyright (C) 2014-2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/MailRu.pm b/lib/Sisimai/Lhost/MailRu.pm index d60b3fd9..97623670 100644 --- a/lib/Sisimai/Lhost/MailRu.pm +++ b/lib/Sisimai/Lhost/MailRu.pm @@ -69,7 +69,6 @@ sub inquire { my $emailparts = Sisimai::RFC5322->part($mbody, $boundaries); my $readcursor = 0; # (Integer) Points the current cursor position my $recipients = 0; # (Integer) The number of 'Final-Recipient' header - my $localhost0 = ''; # (String) Local MTA my $v = undef; for my $e ( split("\n", $emailparts->[0]) ) { @@ -142,16 +141,14 @@ sub inquire { } return undef unless $recipients; + # Get the name of the local MTA + # Received: from marutamachi.example.org (c192128.example.net [192.0.2.128]) + my $receivedby = $mhead->{'received'} || []; + my $recvdtoken = Sisimai::RFC5322->received($receivedby->[-1]); my $p1 = -1; my $p2 = -1; - if( scalar $mhead->{'received'}->@* ) { - # Get the name of local MTA - # Received: from marutamachi.example.org (c192128.example.net [192.0.2.128]) - $p1 = index($mhead->{'received'}->[-1], 'from '); - $p2 = index($mhead->{'received'}->[-1], ' ', $p1 + 5); - $localhost0 = substr($mhead->{'received'}->[-1], $p1 + 5, $p2 - $p1 - 5) if $p1 > -1; - } for my $e ( @$dscontents ) { + # Check the error message, the rhost, the lhost, and the smtp command. if( exists $e->{'alterrors'} && $e->{'alterrors'} ) { # Copy alternative error message $e->{'diagnosis'} ||= $e->{'alterrors'}; @@ -170,17 +167,10 @@ sub inquire { # host neko.example.jp [192.0.2.222]: 550 5.1.1 ... User Unknown $p1 = index($e->{'diagnosis'}, 'host '); $p2 = index($e->{'diagnosis'}, ' ', $p1 + 5); - $e->{'rhost'} = substr($e->{'diagnosis'}, $p1 + 5, $p2 - $p1 - 5) if $p1 > -1; - - unless( $e->{'rhost'} ) { - if( scalar $mhead->{'received'}->@* ) { - # Get localhost and remote host name from Received header. - my $r0 = $mhead->{'received'}; - $e->{'rhost'} = pop Sisimai::RFC5322->received($r0->[-1])->@*; - } - } + $e->{'rhost'} = substr($e->{'diagnosis'}, $p1 + 5, $p2 - $p1 - 5) if $p1 > -1; + $e->{'rhost'} ||= $recvdtoken->[1]; } - $e->{'lhost'} ||= $localhost0; + $e->{'lhost'} ||= $recvdtoken->[0]; unless( $e->{'command'} ) { # Get the SMTP command name for the session @@ -254,7 +244,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2014-2023 azumakuniyuki, All rights reserved. +Copyright (C) 2014-2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/MessageLabs.pm b/lib/Sisimai/Lhost/MessageLabs.pm index 06038349..6d1b1d7e 100644 --- a/lib/Sisimai/Lhost/MessageLabs.pm +++ b/lib/Sisimai/Lhost/MessageLabs.pm @@ -102,7 +102,6 @@ sub inquire { for my $e ( @$dscontents ) { # Set default values if each value is empty. - $e->{'lhost'} ||= $permessage->{'rhost'}; $e->{ $_ } ||= $permessage->{ $_ } || '' for keys %$permessage; $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'}); @@ -153,7 +152,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2014-2021,2023 azumakuniyuki, All rights reserved. +Copyright (C) 2014-2021,2023,2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/Outlook.pm b/lib/Sisimai/Lhost/Outlook.pm index a520a668..96943006 100644 --- a/lib/Sisimai/Lhost/Outlook.pm +++ b/lib/Sisimai/Lhost/Outlook.pm @@ -102,8 +102,7 @@ sub inquire { for my $e ( @$dscontents ) { # Set default values if each value is empty. - $e->{'lhost'} ||= $permessage->{'rhost'}; - $e->{ $_ } ||= $permessage->{ $_ } || '' for keys %$permessage; + $e->{ $_ } ||= $permessage->{ $_ } || '' for keys %$permessage; $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'}); unless( $e->{'diagnosis'} ) { @@ -166,7 +165,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2014-2023 azumakuniyuki, All rights reserved. +Copyright (C) 2014-2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/Postfix.pm b/lib/Sisimai/Lhost/Postfix.pm index bbc08e55..a07211ce 100644 --- a/lib/Sisimai/Lhost/Postfix.pm +++ b/lib/Sisimai/Lhost/Postfix.pm @@ -232,7 +232,6 @@ sub inquire { for my $e ( @$dscontents ) { # Set default values if each value is empty. - $e->{'lhost'} ||= $permessage->{'rhost'}; $e->{ $_ } ||= $permessage->{ $_ } || '' for keys %$permessage; if( $anotherset->{'diagnosis'} ) { @@ -324,7 +323,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2014-2023 azumakuniyuki, All rights reserved. +Copyright (C) 2014-2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/ReceivingSES.pm b/lib/Sisimai/Lhost/ReceivingSES.pm index a9c80487..f5b762d7 100644 --- a/lib/Sisimai/Lhost/ReceivingSES.pm +++ b/lib/Sisimai/Lhost/ReceivingSES.pm @@ -101,7 +101,6 @@ sub inquire { for my $e ( @$dscontents ) { # Set default values if each value is empty. - $e->{'lhost'} ||= $permessage->{'rhost'}; $e->{ $_ } ||= $permessage->{ $_ } || '' for keys %$permessage; $e->{'diagnosis'} =~ y/\n/ /; $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'}); @@ -164,7 +163,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2015-2023 azumakuniyuki, All rights reserved. +Copyright (C) 2015-2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/SendGrid.pm b/lib/Sisimai/Lhost/SendGrid.pm index 85370773..5f9f191d 100644 --- a/lib/Sisimai/Lhost/SendGrid.pm +++ b/lib/Sisimai/Lhost/SendGrid.pm @@ -148,6 +148,7 @@ sub inquire { $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'}); $e->{'replycode'} = Sisimai::SMTP::Reply->find($e->{'diagnosis'}) || ''; $e->{'status'} = substr($e->{'replycode'}, 0, 1).'.0.0' if length $e->{'replycode'} == 3; + $e->{'command'} ||= $thecommand; if( $e->{'status'} eq '5.0.0' || $e->{'status'} eq '4.0.0' ) { # Get the value of D.S.N. from the error message or the value of Diagnostic-Code header. @@ -162,8 +163,6 @@ sub inquire { $e->{'status'} = Sisimai::SMTP::Status->code('expired') || $e->{'status'}; } } - $e->{'lhost'} ||= $permessage->{'rhost'}; - $e->{'command'} ||= $thecommand; } return { 'ds' => $dscontents, 'rfc822' => $emailparts->[1] }; } @@ -205,7 +204,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2014-2023 azumakuniyuki, All rights reserved. +Copyright (C) 2014-2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/Sendmail.pm b/lib/Sisimai/Lhost/Sendmail.pm index ffd9908d..93fb9f30 100644 --- a/lib/Sisimai/Lhost/Sendmail.pm +++ b/lib/Sisimai/Lhost/Sendmail.pm @@ -168,8 +168,7 @@ sub inquire { for my $e ( @$dscontents ) { # Set default values if each value is empty. - $e->{'lhost'} ||= $permessage->{'rhost'}; - $e->{ $_ } ||= $permessage->{ $_ } || '' for keys %$permessage; + $e->{ $_ } ||= $permessage->{ $_ } || '' for keys %$permessage; if( exists $anotherset->{'diagnosis'} && $anotherset->{'diagnosis'} ) { # Copy alternative error message @@ -247,7 +246,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2014-2023 azumakuniyuki, All rights reserved. +Copyright (C) 2014-2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/Yandex.pm b/lib/Sisimai/Lhost/Yandex.pm index e489c5d9..810ba4ad 100644 --- a/lib/Sisimai/Lhost/Yandex.pm +++ b/lib/Sisimai/Lhost/Yandex.pm @@ -112,7 +112,6 @@ sub inquire { for my $e ( @$dscontents ) { # Set default values if each value is empty. - $e->{'lhost'} ||= $permessage->{'rhost'}; $e->{ $_ } ||= $permessage->{ $_ } || '' for keys %$permessage; $e->{'diagnosis'} =~ y/\n/ /; $e->{'diagnosis'} = Sisimai::String->sweep($e->{'diagnosis'}); @@ -158,7 +157,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2014-2021,2023 azumakuniyuki, All rights reserved. +Copyright (C) 2014-2021,2023,2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Lhost/mFILTER.pm b/lib/Sisimai/Lhost/mFILTER.pm index 6b8b8ad9..4f9ddb5a 100644 --- a/lib/Sisimai/Lhost/mFILTER.pm +++ b/lib/Sisimai/Lhost/mFILTER.pm @@ -110,7 +110,7 @@ sub inquire { my $rhosts = Sisimai::RFC5322->received($rheads->[-1]); $e->{'lhost'} ||= shift Sisimai::RFC5322->received($rheads->[0])->@*; - for my $ee ( @$rhosts ) { + for my $ee ( $rhosts->[0], $rhosts->[1] ) { # Avoid "... by m-FILTER" next unless rindex($ee, '.') > -1; $e->{'rhost'} = $ee; @@ -156,7 +156,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2014-2023 azumakuniyuki, All rights reserved. +Copyright (C) 2014-2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/lib/Sisimai/Message.pm b/lib/Sisimai/Message.pm index 9d0837c7..f5df9a8b 100644 --- a/lib/Sisimai/Message.pm +++ b/lib/Sisimai/Message.pm @@ -195,17 +195,26 @@ sub makemap { # https://gist.github.com/xtetsuji/b080e1f5551d17242f6415aba8a00239 my $firstpairs = { $$argv0 =~ /^([\w-]+):[ ]*(.*?)\n(?![\s\t])/gms }; my $headermaps = { 'subject' => '' }; - my $recvheader = []; + $headermaps->{ lc $_ } = $firstpairs->{ $_ } for keys %$firstpairs; + my $receivedby = []; - $headermaps->{ lc $_ } = $firstpairs->{ $_ } for keys %$firstpairs; for my $e ( values %$headermaps ) { s/\n\s+/ /, y/\t / /s for $e } if( index($$argv0, "\nReceived:") > 0 || index($$argv0, "Received:") == 0 ) { # Capture values of each Received: header - $recvheader = [$$argv0 =~ /^Received:[ ]*(.*?)\n(?![\s\t])/gms]; - for my $e ( @$recvheader ) { s/\n\s+/ /, y/\n\t / /s for $e } + my $re = [$$argv0 =~ /^Received:[ ]*(.*?)\n(?![\s\t])/gms]; + for my $e ( @$re ) { + # 1. Exclude the Received header including "(qmail ** invoked from network)". + # 2. Convert all consecutive spaces and line breaks into a single space character. + next if index($e, ' invoked by uid') > 0; + next if index($e, ' invoked from network') > 0; + + $e =~ s/\n\s+/ /; + $e =~ y/\n\t / /s; + push @$receivedby, $e; + } } - $headermaps->{'received'} = $recvheader; + $headermaps->{'received'} = $receivedby; return $headermaps unless $argv1; return $headermaps unless length $headermaps->{'subject'}; diff --git a/lib/Sisimai/RFC5322.pm b/lib/Sisimai/RFC5322.pm index 0e800469..9d7fc5ed 100644 --- a/lib/Sisimai/RFC5322.pm +++ b/lib/Sisimai/RFC5322.pm @@ -3,6 +3,7 @@ use feature ':5.10'; use strict; use warnings; use Sisimai::String; +use Sisimai::Address; use constant HEADERTABLE => { 'messageid' => ['message-id'], 'subject' => ['subject'], @@ -49,84 +50,148 @@ sub LONGFIELDS { sub received { # Convert Received headers to a structured data # @param [String] argv1 Received header - # @return [Array] Received header as a structured data + # @return [Array] Each item in the Received header order by the following: + # 0: (from) "hostname" + # 1: (by) "hostname" + # 2: (via) "protocol/tcp" + # 3: (with) "protocol/smtp" + # 4: (id) "queue-id" + # 5: (for) "envelope-to address" my $class = shift; my $argv1 = shift || return []; - my $hosts = []; - my $value = { 'from' => '', 'by' => '' }; # Received: (qmail 10000 invoked by uid 999); 24 Apr 2013 00:00:00 +0900 - return [] if index($argv1, '(qmail ') > 0 && index($argv1, ' invoked ') > 0; - - my $p1 = index($argv1, 'from '); - my $p2 = index($argv1, 'by '); - my $p3 = index($argv1, ' ', $p2 + 3); - - if( $p1 == 0 && $p2 > 1 && $p2 < $p3 ) { - # Received: from localhost (localhost) by nijo.example.jp (V8/cf) id s1QB5ma0018057; - # Wed, 26 Feb 2014 06:05:48 -0500 - $value->{'from'} = Sisimai::String->sweep(substr($argv1, $p1 + 5, $p2 - $p1 - 5)); - $value->{'by'} = Sisimai::String->sweep(substr($argv1, $p2 + 3, $p3 - $p2 - 3)); - - } elsif( $p1 != 0 && $p2 > -1 ) { - # Received: by 10.70.22.98 with SMTP id c2mr1838265pdf.3; Fri, 18 Jul 2014 - # 00:31:02 -0700 (PDT) - $value->{'from'} = Sisimai::String->sweep(substr($argv1, $p2 + 3,)); - $value->{'by'} = Sisimai::String->sweep(substr($argv1, $p2 + 3, $p3 - $p2 - 3)); + return [] if ref $argv1; + return [] if index($argv1, ' invoked by uid') > 0; + return [] if index($argv1, ' invoked from network') > 0; + + # - https://datatracker.ietf.org/doc/html/rfc5322 + # received = "Received:" *received-token ";" date-time CRLF + # received-token = word / angle-addr / addr-spec / domain + # + # - Appendix A.4. Message with Trace Fields + # Received: + # from x.y.test + # by example.net + # via TCP + # with ESMTP + # id ABC12345 + # for ; 21 Nov 1997 10:05:43 -0600 + my $recvd = [split(' ', $argv1)]; + my $label = [qw|from by via with id for|]; + my $token = {}; + my $other = []; + my $alter = []; + my $right = 0; + my $range = scalar @$recvd; + my $index = -1; + + for my $e ( @$recvd ) { + # Look up each label defined in $label from Received header + last unless ++$index < $range; my $f = lc $e; + next unless grep { $f eq $_ } @$label; + + $token->{ $f } = lc $recvd->[$index + 1] || next; + $token->{ $f } =~ y/();//d; + + next unless $f eq 'from'; + last unless $index + 2 < $range; + next unless index($recvd->[$index + 2], '(') == 0; + + # Get and keep a hostname in the comment as follows: + # from mx1.example.com (c213502.kyoto.example.ne.jp [192.0.2.135]) by mx.example.jp (V8/cf) + # [ + # "from", # index + 0 + # "mx1.example.com", # index + 1 + # "(c213502.kyoto.example.ne.jp", # index + 2 + # "[192.0.2.135])", # index + 3 + # "by", + # "mx.example.jp", + # "(V8/cf)", + # ... + # ] + # The 2nd element after the current element is NOT a continuation of the current element + # such as "(c213502.kyoto.example.ne.jp)" + push @$other, $recvd->[$index + 2]; $other->[0] =~ y/();//d; + + # The 2nd element after the current element is a continuation of the current element. + # such as "(c213502.kyoto.example.ne.jp", "[192.0.2.135])" + last unless $index + 3 < $range; + push @$other, $recvd->[$index + 3]; + $other->[1] =~ y/();//d; } - if( index($value->{'from'}, ' ') > -1 ) { - # Received: from [10.22.22.222] (smtp.kyoto.ocn.ne.jp [192.0.2.222]) (authenticated bits=0) - # by nijo.example.jp (V8/cf) with ESMTP id s1QB5ka0018055; Wed, 26 Feb 2014 06:05:47 -0500 - my @received = split(' ', $value->{'from'}); - my @namelist; - my @addrlist; - my $hostname = ''; - my $hostaddr = ''; - - for my $e ( @received ) { - # Received: from [10.22.22.222] (smtp-gateway.kyoto.ocn.ne.jp [192.0.2.222]) - my $cv = Sisimai::String->ipv4($e) || []; - if( scalar @$cv > 0 ) { - # [192.0.2.1] or (192.0.2.1) - push @addrlist, @$cv; - - } else { - # hostname - $e =~ y/()//d; - push @namelist, $e; - } - } + for my $e ( @$other ) { + # Check alternatives in $other, and then delete uninformative values. + next unless $e; + next if length $e < 4; + next if $e eq 'unknown'; + next if $e eq 'localhost'; + next if $e eq '[127.0.0.1]'; + next if $e eq '[IPv6:::1]'; + next if index($e, '.') == -1; + next if index($e, '=') > 1; + push @$alter, $e; + } - for my $e ( @namelist ) { - # 1. Hostname takes priority over all other IP addresses - next unless rindex($e, '.') > -1; - $hostname = $e; - last; - } + for my $e ('from', 'by') { + # Remove square brackets from the IP address such as "[192.0.2.25]" + next unless defined $token->{ $e }; + next unless length $token->{ $e }; + next unless index($token->{ $e }, '[') == 0; + $token->{ $e } = shift Sisimai::String->ipv4($token->{ $e })->@* || ''; + } + + $token->{'from'} ||= ''; + while(1) { + # Prefer hostnames over IP addresses, except for localhost.localdomain and similar. + last if $token->{'from'} eq 'localhost'; + last if $token->{'from'} eq 'localhost.localdomain'; + last if index($token->{'from'}, '.') < 0; # A hostname without a domain name + last if scalar Sisimai::String->ipv4($token->{'from'})->@*; - unless( $hostname ) { - # 2. Use IP address as a remote host name - for my $e ( @addrlist ) { - # Skip if the address is a private address - next if index($e, '10.') == 0; - next if index($e, '127.') == 0; - next if index($e, '192.168.') == 0; - next if $e =~ /\A172[.](?:1[6-9]|2[0-9]|3[0-1])[.]/; - $hostaddr = $e; - last; - } + # No need to rewrite $token->{'from'} + $right = 1; + last; + } + while(1) { + # Try to rewrite uninformative hostnames and IP addresses in $token->{'from'} + last if $right; # There is no need to rewrite + last if scalar @$alter == 0; # There is no alternative to rewriting + last if index($alter->[0], $token->{'from'}) > -1; + + if( index($token->{'from'}, 'localhost') == 0 ) { + # localhost or localhost.localdomain + $token->{'from'} = $alter->[0]; + + } elsif( index($token->{'from'}, '.') == -1 ) { + # A hostname without a domain name such as "mail", "mx", or "mbox" + $token->{'from'} = $alter->[0] if index($alter->[0], '.') > 0; + + } else { + # An IPv4 address + $token->{'from'} = $alter->[0]; } - $value->{'from'} = $hostname || $hostaddr || $addrlist[-1]; + last; } - for my $e ('from', 'by') { - # Copy entries into $hosts - next unless defined $value->{ $e }; - $value->{ $e } =~ y/()[];?//d; - push @$hosts, $value->{ $e }; + delete $token->{'by'} unless defined $token->{'by'}; + delete $token->{'from'} unless defined $token->{'from'}; + $token->{'for'} = Sisimai::Address->s3s4($token->{'for'}) if exists $token->{'for'}; + for my $e ( keys %$token ) { + # Delete an invalid value + $token->{ $e } = '' if index($token->{ $e }, ' ') > -1; + $token->{ $e } =~ y/[]//d; # Remove "[]" from the IP address } - return $hosts; + + return [ + $token->{'from'} || '', + $token->{'by'} || '', + $token->{'via'} || '', + $token->{'with'} || '', + $token->{'id'} || '', + $token->{'for'} || '', + ]; } sub part { @@ -203,16 +268,21 @@ Sisimai::RFC5322 provide methods for checking email address. =head2 C)>> -C returns array reference which include host names in the Received header. +C returns array reference including elements in the Received header. - my $v = 'from mx.example.org (c1.example.net [192.0.2.1]) by mx.example.jp'; + my $v = 'from mx.example.org (c1.example.org [192.0.2.1]) by neko.libsisimai.org + with ESMTP id neko20180202nyaan for ; ...'; my $r = Sisimai::RFC5322->received($v); warn Dumper $r; $VAR1 = [ - 'mx.example.org', - 'mx.example.jp' - ]; + 'mx.example.org', + 'neko.libsisimai.org', + '', + 'ESMTP', + 'neko20180202nyaan', + 'michitsuna@nyaan.jp' + ]; =head2 C, I)>> @@ -236,7 +306,7 @@ azumakuniyuki =head1 COPYRIGHT -Copyright (C) 2014-2023 azumakuniyuki, All rights reserved. +Copyright (C) 2014-2024 azumakuniyuki, All rights reserved. =head1 LICENSE diff --git a/set-of-emails/maildir/bsd/lhost-sendmail-60.eml b/set-of-emails/maildir/bsd/lhost-sendmail-60.eml new file mode 100644 index 00000000..a5ba8d00 --- /dev/null +++ b/set-of-emails/maildir/bsd/lhost-sendmail-60.eml @@ -0,0 +1,85 @@ +Return-Path: <> +X-Original-To: nekochan@email.example.jp +Delivered-To: mail@email.example.jp +Received: from mx311.ume.example.ne.jp (ip-192-0-2-25.us-east-1.compute.internal [192.0.2.25]) + by mx1.email.example.jp (Postfix) with ESMTPS id Ln2ZS7LPwxzW4HMF + for ; Wed, 7 Feb 2024 23:34:45 +0900 (JST) +Received: from localhost (localhost) + by mx311.ume.example.ne.jp (8.16.1/8.16.1) id 3LGub1et091679; + Wed, 7 Feb 2024 23:34:45 +0900 (JST) + (envelope-from MAILER-DAEMON) +Date: Wed, 7 Feb 2024 23:34:45 +0900 (JST) +From: Mail Delivery Subsystem +To: +Message-Id: <202402072222.3LGub1et091679@mx311.ume.example.ne.jp> +MIME-Version: 1.0 +Content-Type: multipart/report; report-type=delivery-status; + boundary="3LGub1et091679.1157497350/mx311.ume.example.ne.jp" +Subject: Returned mail: see transcript for details +Auto-Submitted: auto-generated (failure) + +This is a MIME-encapsulated message + +--3LGub1et091679.1157497350/mx311.ume.example.ne.jp + +The original message was received at Wed, 7 Feb 2024 23:34:45 +0900 (JST) +from localhost [127.0.0.1] + + ----- The following addresses had permanent fatal errors ----- + + (reason: 550-5.7.26 The MAIL FROM domain [email.example.jp] has an SPF record with a hard fail) + + ----- Transcript of session follows ----- +... while talking to gmail-smtp-in.l.google.com.: +>>> DATA +<<< 550-5.7.26 The MAIL FROM domain [email.example.jp] has an SPF record with a hard fail +<<< 550-5.7.26 policy (-all) but it fails to pass SPF checks with the ip: +<<< 550-5.7.26 [203.0.113.22]. To best protect our users from spam and phishing, +<<< 550-5.7.26 the message has been blocked. For instructions on setting up +<<< 550-5.7.26 authentication, go to +<<< 550 5.7.26 https://support.google.com/mail/answer/81126#authentication q2.22 - gsmtp +554 5.0.0 Service unavailable + +--3LGub1et091679.1157497350/mx311.ume.example.ne.jp +Content-Type: message/delivery-status + +Reporting-MTA: dns; mx311.ume.example.ne.jp +Arrival-Date: Wed, 7 Feb 2024 23:34:45 +0900 (JST) + +Final-Recipient: RFC822; kijitora-cat@google.example.com +X-Actual-Recipient: rfc822; kijitora-cat@google.example.com +Action: failed +Status: 5.7.26 +Remote-MTA: DNS; gmail-smtp-in.l.google.com +Diagnostic-Code: SMTP; 550-5.7.26 The MAIL FROM domain [email.example.jp] has an SPF record with a hard fail +Last-Attempt-Date: Wed, 7 Feb 2024 23:34:45 +0900 (JST) + +--3LGub1et091679.1157497350/mx311.ume.example.ne.jp +Content-Type: message/rfc822 + +Return-Path: +Received: from mx311.ume.example.ne.jp (localhost [127.0.0.1]) + by mx311.ume.example.ne.jp (8.16.1/8.16.1) with ESMTP id ujmeV2ZG033926 + for ; Wed, 7 Feb 2024 23:34:45 +0900 (JST) + (envelope-from nekochan@email.example.jp) +Received: (from kijitora@localhost) + by mx311.ume.example.ne.jp (8.16.1/8.16.1/Submit) id BJObadmZ060489 + for kijitora-cat@google.example.com; Wed, 7 Feb 2024 23:34:45 +0900 (JST) + (envelope-from nekochan@email.example.jp) +Received: from relay1.email.example.jp (relay1.email.example.jp [192.168.168.168]) + by mx311.ume.example.ne.jp (8.16.1/8.16.1) with ESMTPS id Mf73VCB9P8z4lM3b + (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO) + for ; Wed, 7 Feb 2024 23:34:45 +0900 (JST) + (envelope-from nekochan@email.example.jp) +Content-Type: text/plain; charset=us-ascii +Message-Id: <50ad190.20240207.2202@relay1.email.example.jp> +Content-Transfer-Encoding: 7bit +Subject: Nyaan +From: +To: +Date: 7 Feb 2024 23:34:45 +0900 +MIME-Version: 1.0 + +Nyaan? + +--3LGub1et091679.1157497350/mx311.ume.example.ne.jp-- diff --git a/t/022-mail-maildir.t b/t/022-mail-maildir.t index 5179e164..727b0de8 100644 --- a/t/022-mail-maildir.t +++ b/t/022-mail-maildir.t @@ -8,7 +8,7 @@ my $Methods = { 'class' => ['new'], 'object' => ['path', 'dir', 'file', 'size', 'offset', 'handle', 'read'], }; -my $MaildirSize = 525; +my $MaildirSize = 526; my $SampleEmail = './set-of-emails/maildir/bsd'; my $NewInstance = $Package->new($SampleEmail); diff --git a/t/031-rfc5322.t b/t/031-rfc5322.t index b6995d7c..3de4c966 100644 --- a/t/031-rfc5322.t +++ b/t/031-rfc5322.t @@ -78,14 +78,23 @@ MAKETEST: { ]; for my $e ( @$received00 ) { + # Check each value returned from Sisimai::RFC5322->received + # 0: (from) "hostname" + # 1: (by) "hostname" + # 2: (via) "protocol/tcp" + # 3: (with) "protocol/smtp" + # 4: (id) "queue-id" + # 5: (for) "envelope-to address" my $v = $Package->received($e); - ok length $e, $e; isa_ok $v, 'ARRAY'; ok scalar @$v, 'scalar = '.scalar @$v; - for my $f ( @$v ) { - ok length $f, 'received = '.$f; - ok $f =~ qr{\A[-/:.0-9A-Za-z]+\z}, 'Regular expression'; - } + is scalar @$v, 6; + like $v->[0], qr/\A[^\s\(\)\[\];]+\z/, '->received(from) = '.$v->[0] if length $v->[0]; + like $v->[1], qr/\A[^\s\(\)\[\];]+\z/, '->received(by) = '.$v->[1] if length $v->[1]; + is $v->[2], '', '->received(via) = ""'; + like $v->[3], qr/\A[^\s;]+\z/, '->received(with) = '.$v->[3] if length $v->[3]; + like $v->[4], qr/\A[^\s;]+\z/, '->received(id) = '.$v->[4] if length $v->[4]; + like $v->[5], qr/[^\s;]+[@][^\s;]+/, '->received(for) = '.$v->[5] if length $v->[5]; } my $rfc822body = <<'EOB'; diff --git a/t/500-fact.t b/t/500-fact.t index 09bd2bb0..962534c0 100644 --- a/t/500-fact.t +++ b/t/500-fact.t @@ -150,6 +150,13 @@ MAKETEST: { $ci += 1; } + + # Forwarded and bounced + my $fw = Sisimai->rise('set-of-emails/maildir/bsd/lhost-sendmail-60.eml'); + isa_ok $fw, 'ARRAY'; + isa_ok $fw->[0], 'Sisimai::Fact'; + is $fw->[0]->alias, 'neko@libsisimai.org', '->alias = neko@libsisimai.org'; + is $fw->[0]->recipient->address, 'kijitora-cat@google.example.com', '->recipient = kijitora-cat@google.example.com'; } done_testing; diff --git a/t/600-lhost-code b/t/600-lhost-code index 1a52dfd4..354cf5dd 100644 --- a/t/600-lhost-code +++ b/t/600-lhost-code @@ -174,6 +174,7 @@ my $moduletest = sub { $ct = sprintf("[%s-%02d] ->alias =", $e, $errorindex); ok defined $cv, sprintf("%s %s", $ct, $cv); + ok $cv ne $rr->recipient->address, sprintf("%s %s != %s", $ct, $cv, $rr->recipient->address); } CATCH: { @@ -261,7 +262,7 @@ my $moduletest = sub { LHOST: { $cv = $rr->lhost; - $cr = qr/\A[-.0-9A-Za-z]+\z/; + $cr = qr/\A[^\s\[\]\(\)]+\z/; $ct = sprintf("[%s-%02d] ->lhost =", $e, $errorindex); ok defined $cv, sprintf("%s %s", $ct, $cv); @@ -363,7 +364,7 @@ my $moduletest = sub { RHOST: { $cv = $rr->rhost; - $cr = qr/\A[-.:0-9A-Za-z]+\z/; + $cr = qr/\A[^\s\[\]\(\)]+\z/; $ct = sprintf("[%s-%02d] ->rhost =", $e, $errorindex); ok defined $cv, sprintf("%s %s", $ct, $cv); diff --git a/t/791-lhost-sendmail.t b/t/791-lhost-sendmail.t index c7851a4e..63733799 100644 --- a/t/791-lhost-sendmail.t +++ b/t/791-lhost-sendmail.t @@ -68,6 +68,7 @@ my $isexpected = { '57' => [['5.7.27', '550', 'notaccept', 1]], '58' => [['5.7.1', '550', 'authfailure', 0]], '59' => [['5.7.1', '550', 'authfailure', 0]], + '60' => [['5.7.26', '550', 'authfailure', 0]], }; $enginetest->($enginename, $isexpected); diff --git a/t/800-cannot-parse-yet.t b/t/800-cannot-parse-yet.t index 33677d42..69d1f0c2 100644 --- a/t/800-cannot-parse-yet.t +++ b/t/800-cannot-parse-yet.t @@ -8,8 +8,8 @@ plan 'skip_all', sprintf("%s does not exist", $CannotParse) unless -d $CannotPar MAKETEST: { SISIMAI: { use Sisimai; - my $v = Sisimai->make($CannotParse); - is $v, undef, 'Sisimai->make() returns undef'; + my $v = Sisimai->rise($CannotParse); + is $v, undef, 'Sisimai->rise() returns undef'; } MAILDIR: {