Qpsmtpd-Plugin-Quarantine
view release on metacpan or search on metacpan
lib/Qpsmtpd/Plugin/Quarantine/Spam.pm view on Meta::CPAN
my $x = read_file($file);
my %results;
TEST:
for(;;) {
if (($defaults{clamd} || $defaults{clamav}) &&
($headers->get('Content-Type') =~ /$defaults{virus_content}/
|| $headers->get('Content-Disposition') =~ /$defaults{virus_content}/
|| ($headers->get('Content-Type') =~ /multipart/
&& $transaction->body_as_string() =~ /^Content-Type: $defaults{virus_content}/m)))
{
last if
clamav($qp, $transaction, \%results, ($defaults{clamd} || $defaults{clamav}), 'stream');
unless (exists $results{clamav}) {
last if
clamav($qp, $transaction, \%results, ($defaults{clamd} || $defaults{clamav}), $file);
}
}
for my $spamd3 (@spamd3) {
last TEST if
spamc($qp, $transaction, \%results, $spamd3, 'SPAMASSASSIN3');
last if $results{spamc};
}
last if $results{filtered};
if ($defaults{clamd}) {
unless (exists $results{clamav}) {
last if
clamav($qp, $transaction, \%results, $defaults{clamd}, 'stream');
}
}
if ($defaults{clamav}) {
unless (exists $results{clamav}) {
last if
clamav($qp, $transaction, \%results, $defaults{clamav}, $file);
}
}
last;
}
$qp->log(LOGDEBUG, "Filtered? $results{filterd}");
$qp->log(LOGDEBUG, "Details: $results{hlines}");
return $results{filtered};
}
sub clamav
{
my ($qp, $transaction, $r, $command, $input) = @_;
my $results;
my $file = $transaction->body_filename();
eval {
local($SIG{__DIE__}) = 'DEFAULT';
local($SIG{ALRM}) = sub {
$qp->log(LOGWARN, "ClamAV timeout");
die "timeout\n";
};
alarm($defaults{subcommand_timeout}) if $defaults{subcommand_timeout};
$results = `$command $file`;
};
unless ($results =~ /-- SCAN SUMMARY --/ && $results =~ /^\Q$input\E: (OK|.*FOUND)\n/) {
$qp->log(LOGINFO, "ClamAV failed: $results");
$r->{clamav} = undef;
return 0;
}
my $return = 0;
my $status = $1;
if ($status =~ /\n\n/) {
$qp->log(LOGINFO, "ClamAV Failed: $results");
$r->{clamav} = undef;
return 0;
} elsif ($status eq "OK") {
$status = "X-ClamAV-Status: No\n";
} else {
$status = "X-ClamAV-Status: Yes, $status\n";
$r->{filtered} .= " CLAMAV";
$return = 1;
}
$r->{clamav} = $status;
$r->{hlines} .= $status;
return $return;
}
sub results_in_headers
{
my ($qp, $transaction, $r, $command, $lookfor) = @_;
$qp->log(LOGDEBUG, "running $command...");
local($/) = "\n\n";
my $file = $transaction->body_filename();
unless (open(RESULTS, "$command < $file|")) {
$qp->log(LOGWARN, "Could not open $command: $!");
return '';
}
my $h = <RESULTS>;
$/ = "\n";
chomp($h);
close(RESULTS);
my $fileheader = $transaction->header()->as_string();
my $fileheaderlen = length($fileheader);
if (substr($h, 0, $fileheaderlen) eq $fileheader) {
substr($h, 0, $fileheaderlen) = '';
} elsif ($h =~ /^(From .*\n)/g) {
# weird, with qpsmtpd we don't have a "From " line
} elsif ($h =~ /^[A-Z][-\w]*:/) {
# that's good
} else {
$qp->log(LOGWARN, "Could not parse output from $command");
return '';
}
my $found = '';
my %found;
pos($h) = 0;
while ($h =~ /$headerrx/gco) {
my ($k, $v) = ($1, $2);
next unless $lookfor->{$k};
$found{$k} = $v;
$found .= "$k:$v\n";
}
if (pos($h) != length($h)) {
$qp->log(LOGWARN, "Unparsed header from $command");
return '';
}
return ($found, %found);
}
sub spamc
{
my ($qp, $transaction, $r, $command, $tag) = @_;
my $file = $transaction->body_filename();
$qp->log(LOGDEBUG, "running $command...");
my $results;
eval {
local($SIG{__DIE__}) = 'DEFAULT';
local($SIG{ALRM}) = sub {
$qp->log(LOGWARN, "TIMEOUT $command $file");
die "timeout\n";
};
alarm($defaults{subcommand_timeout}) if $defaults{subcommand_timeout};
$results = `$command $file`;
};
$results =~ m!^(-?\d+\.\d+)/(\d+\.\d+)\n!;
my ($score, $thresh) = ($1 || '', $2 || '');
unless ($thresh && $thresh > 0 && ($results =~ s/.*^[- ]{25,300}\n//sm)) {
$results = substr($results, 0, 30);
$results =~ s/\n/\\n/g;
$qp->log(LOGDEBUG, "'$command $file' failed $thresh: $results");
return '';
}
my @tests;
my $report = '';
$thresh = 5.0;
while ($results =~ /\G {0,5}(-?\d+(?:\.\d+)?) (\S+)\s+(\S(?:.+(?=\n)|\n(?= {6}))+)\n/g) {
my ($pts, $rule, $desc) = ($1, $2, $3);
$desc =~ s/\n {5,60}(\S)/\n\t* $1/g;
push(@tests, $rule);
$report .= sprintf("\t* % 4s %s %s\n", $pts, $rule, $desc);
}
my $xss;
my $tests = join(',', @tests);
if ($score >= $thresh) {
$xss = "X-Spam-Status: Yes, hits=$score required=$thresh tests=$tests\n";
$r->{filtered} .= " $tag";
$report = "X-Spam-Report:\n$report";
} else {
$xss = "X-Spam-Status: No, hits=$score required=$thresh tests=$tests\n";
$report = "";
}
1 while $xss =~ s/^(.{60}.*?,)(?=.)/$1\n\t/m;
my $rout = "$xss$report";
if ($rout =~ /\n\n/) {
$qp->log(LOGWARN, "$command $file: failed doubleNL $results");
return '';
}
$r->{spamc} = $rout;
$r->{hlines} .= $rout;
return $report;
}
1;
( run in 1.408 second using v1.01-cache-2.11-cpan-39bf76dae61 )