=head1 NAME

Mail::SpamAssassin::Plugin::WhitelistRcvdIP - perform whitelist verification over IP address.

=head1 SYNOPSIS

 loadplugin Mail::SpamAssassin::Plugin::WhitelistRcvdIP [/path/to/WhitelistRcvdIP.pm]

 whitelist_from_rcvd_ip email_address ip.ad.re.ss/mask

=head1 DESCRIPTION

This plugin complements whitelist_from_rcvd leting the ip address be specified
instead of the revers DNS name.

=cut

package Mail::SpamAssassin::Plugin::WhitelistRcvdIP;

use Mail::SpamAssassin::Plugin;

use base 'Mail::SpamAssassin::Plugin';

use strict;
use warnings;
use bytes;

our $VERSION="0.1";

# constructor: register the eval rule
sub new {
  my ($proto, $mailsa) = @_;
  my $class = ref($proto) || $proto;

  my $self = $class->SUPER::new($mailsa);
  bless ($self, $class);

  $self->register_eval_rule ("check_for_ip_whitelist_from");

  $self->set_config($mailsa->{conf});

  return $self;
}


###########################################################################

sub set_config {
  my($self, $conf) = @_;
  my @cmds = ();

=head1 USER SETTINGS

=over 4

=item whitelist_from_rcvd_ip email_address ip.add.re.ss[/mask]

Same as whitelist_from_rcvd but using the ip address of the remote sender instead of the
reverse DNS name.

Note that the ip address can be one ip, a list of ips (space separated or multiple entries),
can have (or not) a mask and can be negated with !.  It can be specified in the same way as 
trusted_networks.

Please see the discussion on whitelist_from_rcvd and trusted_networks in C<Mail::SpamAssassin::Conf>.

Example:

  whitelist_from_rcvd_ip *@yahoo.com !64.3.23.1 64.3.0.0/16

This will whitelist any email @yahoo.com that comes from 64.3.*.* but not 64.3.23.1.

OBS: The entry used is the last external relay ip.  So if your internal/trusted networks entries are 
incorrect, you might have some problems.

=cut

  push (@cmds, 
  {
    setting => 'whitelist_from_rcvd_ip',
    code => sub 
    {
      my ($self, $key, $value, $line) = @_;

      return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE
        unless (defined $value && $value !~ /^$/);
      
      return $Mail::SpamAssassin::Conf::INVALID_VALUE
	unless ($value =~ /^(\S+)\s+(.+)$/);

      my $address = lc $1;
      my $ips = $2;

      $self->{parser}->{whitelist_from_rcvd_ip} = {}
	unless defined $self->{parser}->{whitelist_from_rcvd_ip};

      unless (defined $self->{parser}->{whitelist_from_rcvd_ip}->{$address}) 
      {
	$self->{parser}->{whitelist_from_rcvd_ip}->{$address} = {};
	my $re = $address;
    	$re =~ s/[\000\\\(]/_/gs;                   # paranoia
    	$re =~ s/([^\*\?_a-zA-Z0-9])/\\$1/g;        # escape any possible metachars
    	$re =~ tr/?/./;                             # "?" -> "."
    	$re =~ s/\*+/\.\*/g;                        # "*" -> "any string"
	$self->{parser}->{whitelist_from_rcvd_ip}->{$address}->{re} = "^${re}\$";
	$self->{parser}->{whitelist_from_rcvd_ip}->{$address}->{net} =  Mail::SpamAssassin::NetSet->new;
      }

      foreach my $net (split (/\s+/, $ips)) 
      {
	$self->{parser}->{whitelist_from_rcvd_ip}->{$address}->{net}->add_cidr ($net);
      }
    }
  });

  $conf->{parser}->register_commands(\@cmds);
}



sub check_for_ip_whitelist_from {
  my ($self, $scanner) = @_;
  $self->_check_whitelist_from_ip($scanner) unless $scanner->{whitelist_from_rcvd_ip_checked};
  return $scanner->{whitelist_from_rcvd_ip};
}


sub _check_whitelist_from_ip {
  my ($self, $scanner) = @_;

  $scanner->{whitelist_from_rcvd_ip} = 0;
  $scanner->{whitelist_from_rcvd_ip_checked} = 0;

  my $header = $scanner->get("X-Spam-Relays-External");
  return 0
    unless defined $header;
  
  return 0
    unless ( $header =~ /ip=(.*?)\s/ );
 
  my $ip = $1;

  #check all addresses
  for my $waddr (keys ( %{ $scanner->{conf}->{parser}->{whitelist_from_rcvd_ip} } ))
  {
      if ($address =~ $regexp)
      {
	#matched the email address, now check the ip
	if ( $scanner->{conf}->{parser}->{whitelist_from_rcvd_ip}->{$waddr}->{net}->contains_ip( $ip ) )
        {
    	  $scanner->{whitelist_from_rcvd_ip} = 1;
      	  $scanner->{whitelist_from_rcvd_ip_checked} = 1;
      	  return 1;
        }
      }
    }
  }

  return 0; 
}

=head1 SEE ALSO

C<Mail::SpamAssassin::Conf>

=head1 AUTHOR

Raul Dias <raul@dias.com.br>

=cut


1;
