File-VirusScan
view release on metacpan or search on metacpan
lib/File/VirusScan/Engine/Daemon/FPROT/V4.pm view on Meta::CPAN
return $result;
}
}
}
# TODO FIXME
# This is unbelievably ugly code, but as I have no way of testing it
# against an F-PROT daemon, it's been ported nearly verbatim from
# MIMEDefang. It is in desperate need of cleanup!
sub _scan
{
my ($self, $item) = @_;
my $host = $self->{host};
my $baseport = $self->{base_port};
# Default error message when reaching end of function
my $errmsg = "Could not connect to F-Prot Daemon at $host:$baseport";
# Try 5 ports in order to find an active scanner; they may
# change the port when they find and spawn an updated demon
# executable
SEARCH_DEMON: foreach my $port ($baseport .. ($baseport + 4)) {
# TODO: Timeout value?
# TODO: Why aren't we using a HTTP client instead of
# rolling our own HTTP?
my $sock = IO::Socket::INET->new(
PeerAddr => $host,
PeerPort => $port
);
next if !defined $sock;
# The arguments (following the '?' sign in the HTTP
# request) are the same as for the command line F-Prot,
# the additional -remote-dtd suppresses the unuseful
# XML DTD prefix
my @args = qw( -dumb -archive -packed -remote-dtd );
my $uri = "$item?" . join('%20', @args);
if(!$sock->print("GET $uri HTTP/1.0\n\n")) {
my $err = $!;
$sock->close;
return File::VirusScan::Result->error("Could not write to socket: $err");
}
if(!$sock->flush) {
my $err = $!;
$sock->close;
return File::VirusScan::Result->error("Could not flush socket: $err");
}
# Fetch HTTP Header
## Maybe dropped, if no validation checks are to be made
while (my $output = $sock->getline) {
if($output =~ /^\s*$/) {
last; # End of headers
#### Below here: Validating the protocol
#### If the protocol is not recognized, it's assumed that the
#### endpoint is not an F-Prot demon, hence,
#### the next port is probed.
} elsif($output =~ /^HTTP(.*)/) {
my $h = $1;
next SEARCH_DEMON unless $h =~ m!/1\.0\s+200\s!;
} elsif($output =~ /^Server:\s*(\S*)/) {
next SEARCH_DEMON if $1 !~ /^fprotd/;
}
}
# Parsing XML results
my $xml = HTML::TokeParser->new($sock);
my $t = $xml->get_tag('fprot-results');
unless ($t) { # This is an essential tag --> assume a broken demon
$errmsg = 'Demon did not return <fprot-results> tag';
last SEARCH_DEMON;
}
if($t->[1]{'version'} ne '1.0') {
$errmsg = "Incompatible F-Protd results version: " . $t->[1]{'version'};
last SEARCH_DEMON;
}
my $curText; # temporarily accumulated information
my $virii = ''; # name(s) of virus(es) found
my $code; # overall exit code
my $msg = ''; # accumulated message of virus scanner
while ($t = $xml->get_token) {
my $tag = $t->[1];
if($t->[0] eq 'S') { # Start tag
# Accumulate the information temporarily
# into $curText until the </detected> tag is found
my $text = $xml->get_trimmed_text;
# $tag 'filename' of no use in MIMEDefang
if($tag eq 'name') {
$virii .= (length $virii ? " " : "") . $text;
$curText .= "Found the virus: '$text'\n";
} elsif($tag eq 'accuracy' || $tag eq 'disinfectable' || $tag eq 'message') {
$curText .= "\t$tag: $text\n";
} elsif($tag eq 'error') {
$msg .= "\nError: $text\n";
} elsif($tag eq 'summary') {
$code = $t->[2]{'code'} if defined $t->[2]{'code'};
}
} elsif($t->[0] eq 'E') { # End tag
if($tag eq 'detected') {
# move the cached information to the
# accumulated message
$msg .= "\n$curText" if $curText;
undef $curText;
} elsif($tag eq 'fprot-results') {
last; # security check
}
}
}
$sock->close;
## Check the exit code (man f-protd)
## NOTE: These codes are different from the ones of the command line version!
( run in 1.537 second using v1.01-cache-2.11-cpan-df04353d9ac )