#!/usr/bin/perl -w
# Clam-AV plugin.
 
use File::Temp qw(tempfile);
 
sub register {
  my ($self, $qp, @args) = @_;
  $self->register_hook("data_post", "clam_scan");

  if (@args > 0) {
    # Untaint scanner location
    if ($args[0] =~ /^(\/[\/\-\_\.a-z0-9A-Z]*)$/) {
      $self->{_clamscan_loc} = $1;
    } else {
      $self->log(1, "FATAL ERROR: Unexpected characters in clamav argument 1");
      exit 3;
    }
    $self->log(1, "WARNING: Ignoring additional arguments.") if (@args > 1);
  } else {
    $self->{_clamscan_loc} = "/usr/local/bin/clamscan";
  }
}
 
sub clam_scan {
  my ($self, $transaction) = @_;
 
  my ($temp_fh, $filename) = tempfile();
  print $temp_fh $transaction->header->as_string;
  print $temp_fh "\n";
  $transaction->body_resetpos;
  while (my $line = $transaction->body_getline) {
    print $temp_fh $line;
  }
  seek($temp_fh, 0, 0);
 
  # Now do the actual scanning!
  my $cmd = $self->{_clamscan_loc}." --stdout -i --max-recursion=50 --disable-summary $filename 2>&1";
  $self->log(1, "Running: $cmd");
  my $output = `$cmd`;
 
  my $result = ($? >> 8);
  my $signal = ($? & 127);
 
  unlink($filename);
  chomp($output);
 
  $output =~ s/^.* (.*) FOUND$/$1 /mg;
 
  $self->log(1, "clamscan results: $output");
 
  if ($signal) {
    $self->log(1, "clamscan exited with signal: $signal");
    return (DECLINED);
  }
  if ($result == 1) {
    $self->log(1, "Virus(es) found");
    # return (DENY, "Virus Found: $output");
    $transaction->header->add('X-Virus-Found', 'Yes');
    $transaction->header->add('X-Virus-Details', $output);
  }
  elsif ($result) {
    $self->log(1, "ClamAV error: $result\n");
  }
  $transaction->header->add('X-Virus-Checked', 'Checked');
  return (DECLINED);
}