From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

use v5.26;
use strict;
state $RhostClass = {
"Aol" => [".mail.aol.com", ".mx.aol.com"],
"Apple" => [".mail.icloud.com", ".apple.com", ".me.com"],
"Cloudflare" => [".mx.cloudflare.net"],
"Cox" => ["cox.net"],
"Facebook" => [".facebook.com"],
"FrancePTT" => [".laposte.net", ".orange.fr", ".wanadoo.fr"],
"GoDaddy" => ["smtp.secureserver.net", "mailstore1.secureserver.net"],
"Google" => ["aspmx.l.google.com", "gmail-smtp-in.l.google.com"],
"GSuite" => ["googlemail.com"],
"IUA" => [".email.ua"],
"KDDI" => [".ezweb.ne.jp", "msmx.au.com"],
"MessageLabs" => [".messagelabs.com"],
"Microsoft" => [".prod.outlook.com", ".protection.outlook.com", ".onmicrosoft.com", ".exchangelabs.com",],
"Mimecast" => [".mimecast.com"],
"NTTDOCOMO" => ["mfsmax.docomo.ne.jp"],
"Outlook" => [".hotmail.com"],
"Spectrum" => ["charter.net"],
"Tencent" => [".qq.com"],
"YahooInc" => [".yahoodns.net"],
};
sub name {
# Detect the rhost class name
# @param [Sisimai::Fact] argvs Decoded email object
# @return [String] rhost class name
my $class = shift;
my $argvs = shift || return "";
my $rhostclass = "";
my $clienthost = lc $argvs->{"lhost"} || "";
my $remotehost = lc $argvs->{"rhost"} || "";
my $domainpart = lc $argvs->{"destination"} || "";
FINDRHOST: while( $rhostclass eq "" ) {
# Try to match the hostname patterns with the following order:
# 1. destination: The domain part of the recipient address
# 2. rhost: remote hostname
# 3. lhost: local MTA hostname
for my $e ( keys %$RhostClass ) {
# Try to match the domain part with each value of RhostClass
next unless grep { index($_, $domainpart) > -1 } $RhostClass->{ $e }->@*;
$rhostclass = $e; last FINDRHOST;
}
for my $e ( keys %$RhostClass ) {
# Try to match the remote host with each value of RhostClass
next unless grep { index($remotehost, $_) > -1 } $RhostClass->{ $e }->@*;
$rhostclass = $e; last FINDRHOST;
}
# Neither the remote host nor the destination did not matched with any value of RhostClass
for my $e ( keys %$RhostClass ) {
# Try to match the client host with each value of RhostClass
next unless grep { index($clienthost, $_) > -1 } $RhostClass->{ $e }->@*;
$rhostclass = $e; last FINDRHOST;
}
last;
}
return $rhostclass;
}
sub find {
# Detect the bounce reason from certain remote hosts
# @param [Sisimai::Fact] argvs Decoded email object
# @return [String] The value of bounce reason
my $class = shift;
my $argvs = shift || return undef;
my $rhost = __PACKAGE__->name($argvs) || return "";
$rhost = __PACKAGE__."::".$rhost; (my $modulepath = $rhost) =~ s|::|/|g;
require $modulepath.'.pm';
return $rhost->find($argvs);
}
1;
__END__
=encoding utf-8
=head1 NAME
Sisimai::Rhost - Detect the bounce reason returned from certain remote hosts.
=head1 SYNOPSIS
use Sisimai::Rhost;
=head1 DESCRIPTION
C<Sisimai::Rhost> detects the bounce reason from the content of C<Sisimai::Fact> object as an argument
of C<find()> method when the value of C<rhost> of the object is listed in C<$RhostClass> variable.
This class is called only C<Sisimai::Fact> class.
=head1 CLASS METHODS
=head2 C<B<name(I<Sisimai::Fact Object>)>>
C<name()> method returns the rhost class name.
=head2 C<B<find(I<Sisimai::Fact Object>)>>
C<find()> method detects the bounce reason.
=head1 AUTHOR
azumakuniyuki
=head1 COPYRIGHT
Copyright (C) 2014-2020,2022-2025 azumakuniyuki, All rights reserved.
=head1 LICENSE
This software is distributed under The BSD 2-Clause License.
=cut