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;
}