sub check_senders_reputation()

in lib/Mail/SpamAssassin/Plugin/TxRep.pm [1275:1452]


sub check_senders_reputation {
###########################################################################
  my ($self, $pms) = @_;

# just for the development debugging
# use Data::Printer;
# dbg("TxRep: DEBUG DUMP of pms: %s, %s", $pms, p($pms));

  my $autolearn = defined $self->{autolearn};
  $self->{last_pms} = $self->{autolearn} = undef;
  $self->{pms} = $pms;

  # Cases where we would not be able to use TxRep
  if(not $self->{conf}->{use_txrep}) {
    dbg("TxRep is disabled, quitting");
    return 0;
  }
  if ($self->{conf}->{use_auto_welcomelist}) {
    warn("TxRep: cannot run when Auto-Welcomelist is enabled. Please disable it!\n");
    return 0;
  }
  if ($autolearn && !$self->{conf}->{txrep_autolearn}) {
    dbg("TxRep: autolearning disabled, no more reputation adjusting, quitting");
    return 0;
  }
  my @from = $pms->all_from_addrs();
  if (@from && $from[0] eq 'ignore@compiling.spamassassin.taint.org') {
    dbg("TxRep: no scan in lint mode, quitting");
    return 0;
  }

  my $delta    = 0;
  my $timer    = $self->{main}->time_method("total_txrep");
  my $msgscore = (defined $self->{learning})? $self->{learning} : $pms->get_autolearn_points();
  my $date     = $pms->{msg}->receive_date() || $pms->{date_header_time};
  my $msg_id   = $self->{msgid} || $pms->{msg}->generate_msgid();

  my $from   = lc $pms->get('From:addr') || $pms->get('EnvelopeFrom:addr');
  return 0 unless $from =~ /\S/;
  my $domain = $from;
  $domain =~ s/^.+@//;

  # Find the last untrusted relay and populate helo and original IP
  my ($origip, $helo);
  if (defined $pms->{relays_trusted} || defined $pms->{relays_untrusted}) {
    my $trusteds = @{$pms->{relays_trusted}};
    foreach my $rly ( @{$pms->{relays_trusted}}, @{$pms->{relays_untrusted}} ) {
	# Get the last found HELO, regardless of private/public or trusted/untrusted
	# Avoiding a redundant duplicate entry if HELO is equal/similar to another identificator
	if (defined $rly->{helo} &&
            $rly->{helo} !~ /^\[?\Q$rly->{ip}\E\]?$/ &&
            $rly->{helo} !~ /^\Q$domain\E$/i &&
            $rly->{helo} !~ /^\Q$from\E$/i ) {
	    $helo   = $rly->{helo};
	}
	# use only trusted ID, but use the first untrusted IP (if available) (AWL bug 6908)
	# at low spam scores (<2) ignore trusted/untrusted
	# set IP to 127.0.0.1 for any internal IP, so that it can be distinguished from none (AWL bug 6357)
	if ((--$trusteds >=  0 || $msgscore<2) && !$msg_id && $rly->{id})            {$msg_id = $rly->{id};}
	if (($trusteds   >= -1 || $msgscore<2) && !$rly->{ip_private} && $rly->{ip}) {$origip = $rly->{ip};}
	if ( $trusteds   >=  0     && !$origip &&  $rly->{ip_private} && $rly->{ip}) {$origip = '127.0.0.1';}
    }
  }

  # Look for previous scores of the same message, for instance when doing re-learning
  if ($self->{conf}->{txrep_track_messages}) {
    if ($msg_id) {
        my $msg_rep = $self->check_reputations($pms, 'MSG_ID', $msg_id, undef, $date, undef);
        if (defined $msg_rep && ($self->count() > 0)) {
            if (defined $self->{learning} && !defined $self->{forgetting}) {
                # already learned, forget only if already learned (count>1), and relearn
                # when only scanned (count=1), go ahead with normal rep scan
                if ($self->count() > 1) {
                    $self->{last_pms} = $pms;                   # cache the pmstatus
                    $self->forget_message($pms->{msg},$msg_id); # sub reentrance OK
                }
            } elsif ($self->{forgetting}) {
                $msgscore = $msg_rep;   # forget the old stored score instead of the one got now
                dbg("TxRep: forgetting stored score %0.3f of message %s", $msgscore || 'undef', $msg_id);
            } else {
                # calculating the delta from the stored message reputation
                $delta = ($msgscore + $self->{conf}->{txrep_factor}*$msg_rep) / (1+$self->{conf}->{txrep_factor}) - $msgscore;
                if ($delta != 0) {
                    $pms->got_hit("TXREP", "TXREP: ", ruletype => 'eval', score => sprintf("%0.3f", $delta));
                }
                dbg("TxRep: message %s already scanned, using old data; post-TxRep score: %0.3f", $msg_id, $pms->{score} || 'undef');
                if (!defined $self->{txKeepStoreTied}) {
                  $self->finish();
                }
                return 0;
            }
        }       # no stored reputation found, go ahead with normal rep scan
    } else {dbg("TxRep: no message-id available, parsing forced");}
  }             # else no message tracking, go ahead with normal rep scan

  # welcomelists recipients at senders from internal networks after checking MSG_ID only
  if ( $self->{conf}->{txrep_welcomelist_out} &&
          defined $pms->{relays_internal} &&  @{$pms->{relays_internal}} &&
        (!defined $pms->{relays_external} || !@{$pms->{relays_external}})
     ) {
    foreach my $rcpt ($pms->all_to_addrs()) {
        if ($rcpt) {
            dbg("TxRep: internal sender, welcomelisting recipient: $rcpt");
            $self->modify_reputation($rcpt, -1*$self->{conf}->{txrep_welcomelist_out}, undef);
        }
    }
  }

  # Get the signing domain
  my $signedby = ($self->{conf}->{auto_welcomelist_distinguish_signed})? $pms->get_tag('DKIMDOMAIN') : undef;

  # Summary of all information we've gathered so far
  dbg("TxRep: active, %s pre-score: %s, autolearn score: %s, IP: %s, address: %s %s",
    $msg_id       || '',
    $pms->{score} || '?',
    $msgscore     || '?',
    $origip       || '?',
    $from         || '?',
    $signedby ? "signed by $signedby" : '(unsigned)'
  );

  my $ip = $origip;
  my $spf_domain;
  if ($signedby) {
    $ip       = undef;
    $domain   = $signedby;
  } elsif ($pms->{spf_pass} && $self->{conf}->{txrep_spf} && defined $pms->{spf_sender}) {
    $ip       = undef;
    $spf_domain = $pms->{spf_sender};
    $spf_domain =~ s/^.+@//;
    $signedby   = 'spf-'.$spf_domain;
    dbg("TxRep: email signed by spf domain $spf_domain");
  } elsif ($pms->{spf_pass} && $self->{conf}->{txrep_spf}) {
    $ip       = undef;
    $signedby = 'spf';
  }

  my $totalweight      = 0;
  $self->{totalweight} = $totalweight;

  # Get current reputation info
  $delta += $self->check_reputations($pms, 'EMAIL_IP', $from, $ip, $signedby, $msgscore);

  if ($domain) {
    $delta += $self->check_reputations($pms, 'DOMAIN', $domain, $ip, $signedby, $msgscore);
  }
  if ($helo) {
    $delta += $self->check_reputations($pms, 'HELO', $helo, undef, 'HELO', $msgscore);
  }
  if ($origip) {
    if (!$signedby) {
      $delta += $self->check_reputations($pms, 'EMAIL', $from, undef, undef, $msgscore);
    }
    $delta += $self->check_reputations($pms, 'IP', $origip, undef, undef, $msgscore);
  }

  # Learn against this message and store reputation
  if (!defined $self->{learning}) {
    $delta = ($self->{totalweight})? $self->{conf}->{txrep_factor} * $delta / $self->{totalweight}  :  0;
    if ($delta) {
      $pms->got_hit("TXREP", "TXREP: ", ruletype => 'eval', score => sprintf("%0.3f", $delta));
    }
    $msgscore += $delta;
    if (defined $pms->{score}) {
      dbg("TxRep: post-TxRep score: %.3f", $pms->{score});
    }
  }
  # Track message ID
  if ($self->{conf}->{txrep_track_messages} && $msg_id) {
    $self->check_reputations($pms, 'MSG_ID', $msg_id, undef, $date, $msgscore);
  }
  # Close any open resources
  if (!defined $self->{txKeepStoreTied}) {
    $self->finish();
  }

  return 0;
}