=head1 NAME ClamAV - ClamAV anti-virus check =head1 SYNOPSIS loadplugin Mail::SpamAssassin::Plugin::ClamAV full CLAMAV eval:check_clamav() score CLAMAV 5 add_header all Virus _CLAMAV_ =head1 DESCRIPTION This plugin sends the mail to locally running ClamAV daemon for virus detection. =cut package Mail::SpamAssassin::Plugin::ClamAV; use Mail::SpamAssassin::Plugin; use Mail::SpamAssassin::Logger; use IO::Socket::UNIX; use strict; use warnings; use vars qw(@ISA); @ISA = qw(Mail::SpamAssassin::Plugin); sub new { my $class = shift; my $mailsa = shift; $class = ref($class) || $class; my $self = $class->SUPER::new($mailsa); bless ($self, $class); dbg("ClamAV: Registering ClamAV plugin rules"); $self->register_eval_rule("check_clamav"); $self->set_config($mailsa->{conf}); return $self; } sub set_config { my ($self, $conf) = @_; my @cmds; =head1 ADMINISTRATOR SETTINGS =over =item clamav_socket STRING (default: /var/run/clamav/clamd.sock) Full path to locally running ClamAV daemon socket. =cut push (@cmds, { setting => "clamav_socket", is_admin => 1, default => "/var/run/clamav/clamd.sock", type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING, }); $conf->{parser}->register_commands(\@cmds); } sub check_clamav { my ($self, $permsg, $fulltext) = @_; my $socket = IO::Socket::UNIX->new( Type => SOCK_STREAM, Peer => $self->{main}->{conf}->{clamav_socket} ) or do { $permsg->set_tag("CLAMAV", "Error"); info("ClamAV: Failed to connect socket, skipping virus check"); return; }; $socket->send("nINSTREAM\n"); $socket->send(pack("N", length($$fulltext))); $socket->send($$fulltext); $socket->send(pack("N", 0)); my $result = $socket->getline(); if ($result =~ /^stream: (.+) FOUND/i) { info("ClamAV: Found virus " . $1); $permsg->set_tag("CLAMAV", $1); my $rulename = $permsg->get_current_eval_rule_name(); $permsg->{conf}->{descriptions}->{$rulename} = "Found virus " . $1; $result = 1; } elsif ($result =~ /^(.*) ERROR/i) { $permsg->set_tag("CLAMAV", "Error"); info("ClamAV: Error in scanning: " . $1); $result = 0; } else { $permsg->set_tag("CLAMAV", "Clean"); dbg("ClamAV: Message clean"); $result = 0; } $socket->send("QUIT\n"); close $socket; return $result; } 1;