Mail-DMARC-Iterator

 view release on metacpan or  search on metacpan

lib/Mail/DMARC/Iterator.pm  view on Meta::CPAN

		# skip quoted text
		m{\G (?:[^"\\]+|\\.)* \"}xgc or last; # missing final \"
		pop @state;
	    } elsif ($state[-1] eq '(') {
		# skip comments (can be nested)
		m{\G .*? ([()]) }xsgc or last; # missing final ')'
		if ($1 eq ')') {
		    pop @state;
		} else {
		    push @state,'('
		}
	    }
	}
	$DEBUG && debug("extract: $_ -> ".join(" ",sort keys %domains));
	return sort keys %domains;
    }
}

{
    # Parse Received-SPF header into (result,\%hash).
    my %res;
    $res{ lc($_) } = $_ for(SPF_Pass, SPF_Fail, SPF_SoftFail, SPF_Neutral,
	SPF_None, SPF_TempError, SPF_PermError);
    my $res = join("|",keys %res);
    $res = qr{$res}i;
    my $fws = qr{(?:[ \t]*\r?\n)?[ \t]+};
    my $key = qr{\w[\w\-]*};
    my $atext = qr{[0-9a-zA-Z!#$%&'*+\-/=?^_`{|}~]+};
    my $dotatom = qr{$atext(?:\.$atext)*};
    my $qstring = qr{"(?:[^"\\]+|\\.)*"};
    my $val = qr{$dotatom|$qstring};

    sub _parse_spfreceived {
	local $_ = shift;
	m{\G($res)\s+}igc or return;
	my $result = $res{ lc($1) };
	my %hash;
	my $comment;
	while (1) {
	    if ($comment) {
		last if ! m{\G[^()]*([()])\s*}gc; # no end of comment found
		$comment += $1 eq '(' ? +1:-1;
	    } elsif (m{\G($key)$fws?=$fws?($val)\s*(;\s*)?}gc) {
		my ($k,$v,$delim) = ($1,$2,$3);
		$v =~s{\\(.)}{$1}g if $v =~s{\A\"(.*)\"\z}{$1};
		$hash{$k} = $v;
		last if ! $delim; # no delimeter-> end
	    } elsif (!%hash && !defined $comment && m{\G\(}gc) {
		$comment++;
	    } else {
		last
	    }
	}
	return ($result,\%hash);
    }
}

{
    # Define function organizational_domain based on which package we have to
    # calculate the public suffix.
    if (eval { require IO::Socket::SSL::PublicSuffix }) {
	my $ps = IO::Socket::SSL::PublicSuffix->default;
	*organizational_domain = sub {
	    return $ps->public_suffix($_[0],1) || $_[0];
	};
    } elsif (eval { require Domain::PublicSuffix }) {
	my $ps = Domain::PublicSuffix->new;
	*organizational_domain = sub {
	    return $ps->get_root_domain($_[0]) || $_[0];
	};

    } elsif (eval { require Mozilla::PublicSuffix }) {
	*organizational_domain = sub {
	    my $domain = shift;
	    if (my $suffix = Mozilla::PublicSuffix::public_suffix($domain)) {
		return $1 if $domain =~m{([^\.]+\.\Q$suffix\E$)}i;
	    }
	    return $domain;
	}
    } else {
	die "failed to find any package for calculating the public suffix";
    }
}

1;

__END__

=head1 NAME

Mail::DMARC::Iterator - Iterativ DMARC validation for mails.

=head1 SYNOPSIS

    use Mail::DMARC::Iterator;
    use Net::DNS::Resolver;

    my $resolver = Net::DNS::Resolver->new;
    my $dmarc = Mail::DMARC::Iterator->new(

	# data from SMTP dialog - used for SPF
	ip => '10.11.12.13',
	mailfrom => 'foo@example.com',
	helo => 'mx.example.com',

	# alternatively add predefined results from your own SPF validation
	# spf_result => [ 'pass',undef, { 'envelope-from' => ... } ]

	# or set to undef so that it tries to use Received-SPF header fields
	# spf_result => undef,

	# you can optionally use a global DNS cache
	# dnscache => \%global_cache
    );

    open( my $fh,'<','mail.eml');      # open the file
    my ($result,@todo) = $dmarc->next; # initial result

    while (!defined $result && @todo) {
	my $todo = shift(@todo);
	if (!ref($todo)) {
	    # no reference - indicator that we need more mail data



( run in 3.317 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )