#!/usr/bin/perl -w
# uvscan plugin. Based off of the Clam-AV plugin.
#
# currently takes two optional arguments (defaults):
# uvscan_location <path-to-uvscan> (/usr/local/bin/uvscan)
# deny_viruses yes|no (no)
#

sub register {
  my ($self, $qp, @args) = @_;
  $self->register_hook("data_post", "uvscan");

  while (@args) {
  	$self->{"_uvscan"}->{pop @args}=pop @args;
  }
  $self->{"_uvscan"}->{"uvscan_location"}||="/usr/local/bin/uvscan";
}
 
sub uvscan {
  my ($self, $transaction) = @_;
 
  return (DECLINED)
    if $transaction->body_size > 250_000;

  my $filename = $transaction->body_filename;
  return (DECLINED) unless $filename;
 
  # Now do the actual scanning!
  my @cmd =($self->{"_uvscan"}->{"uvscan_location"},
	'--mime', '--unzip', '--secure', '--noboot',
	$filename, '2>&1 |');
  $self->log(LOGINFO, "Running: ",join(' ', @cmd));
  open(FILE, join(' ', @cmd)); #perl 5.6 doesn't properly support the pipe
  # mode list form of open, but this is basically the same thing. This form
  # of exec is safe(ish).
  my $output;
  while (<FILE>) { $output.=$_; }
  close FILE;

  my $result = ($? >> 8);
  my $signal = ($? & 127);
 
  unlink($filename);

  my $virus;
  if ($output && $output =~ m/.*\W+Found (.*)\n/m) {
    $virus=$1;
  }
  if ($output && $output =~ m/password-protected/m) {
    return (DENY, 'We do not accept password-protected zip files!');
  }

  if ($signal) {
    $self->log(LOGWARN, "uvscan exited with signal: $signal");
    return (DECLINED);
  }
  if ($result == 2) {
      $self->log(LOGERROR, "Integrity check for a DAT file failed.");
      return (DECLINED);
  } elsif ($result == 6) {
      $self->log(LOGERROR, "A general problem has occurred.");
      return (DECLINED);
  } elsif ($result == 8) {
      $self->log(LOGERROR, "The program could not find a DAT file.");
      return (DECLINED);
  } elsif ($result == 15) {
      $self->log(LOGERROR, "The program self-check failed");
      return (DECLINED);
  } elsif ( $result ) { # all of the possible virus returns
    if ($result == 12) {
      $self->log(LOGERROR, "The program tried to clean a file but failed.");
    } elsif ($result == 13) {
      $self->log(LOGERROR, "One or more virus(es) found");
    } elsif ($result == 19) {
      $self->log(LOGERROR, "Successfully cleaned the file");
    }

    if (lc($self->{"_uvscan"}->{"deny_viruses"}) eq "yes") {
       return (DENY, "Virus Found: $virus");
    }
    $transaction->header->add('X-Virus-Found', 'Yes');
    $transaction->header->add('X-Virus-Details', $virus);
    return (DECLINED);
  }
  
  $transaction->header->add('X-Virus-Checked', 'Checked');
  return (DECLINED);
} 
