#!/usr/bin/perl
require 5.000;

use POSIX qw ( WEXITSTATUS WIFEXITED WTERMSIG WIFSIGNALED );

#
# FBB F_Filter
#
# Author:  Stewart Wilkinson G0LGS
#
$Vers="0.60";
$VDate="29/03/2004";
$VData="F_Filter for XFBB 7.01+ (G0LGS V${Vers} ${VDate}).";

# Various Virus Scanners
# All will be used if present.

# NAI uvscan
$uvscan="/usr/local/uvscan/uvscan";

# H+BEDV AntiVir
$antivir="/usr/lib/AntiVir/antivir";

# FRISK f-prot
$fprot="/usr/local/bin/f-prot";

#
# Nothing beyond here should need changing (unless its broken)
#

# Read Settings from FBBCONFIG
&ReadFBB_CFG;

# Our Config file.
$Config="${CONF}/f_filter.cfg";

# Wrong number of Parameters ?
if( $#ARGV < 3 ){
   printf STDERR "%s\n", ${VData};
   if( ! -f ${Config} ){
      printf STDERR "WARNING: The Config File %s required by f_filter is missing or unreadable.", ${Config};
      printf STDERR " Please correct the problem, as File Filtering will not work without it.\n";
   }
   exit 0;
}

# The Parameters
$Callssid=$ARGV[0];
$UNK=$ARGV[1];
$RecNo=$ARGV[2];
$DatName=$ARGV[3];

$Callssid =~ /(\S*)\-\d/;
$Callsign = $1;

# Some Default settings
$MAILSYSOP=1;

# Read Our Config file 

if(! open(CFG, "<${Config}") ) {
   if( ! -f "${CONF}/f_filter.warn" ){
      if( open(LOCK,"> ${LOCK}" ) ) {
         printf LOCK "$$";
         if( open(MAIL,">> ${MAIL}" ) ){
            printf MAIL "SP %s < FILTER\n", ${SYSCALL};
            printf MAIL "WARNING: F_Filter Config Error\n\n";
            printf MAIL "The Config File %s required by f_filter is missing or unreadable.\n", ${Config};
            printf MAIL "Please correct the problem, as File Filtering will not work without it.\n";
            printf MAIL "/EX\n";
            close(MAIL);
         }
         close(LOCK);
         unlink(${LOCK});
      }

      if( open(WRN,"> ${CONF}/f_filter.warn" ) ){
         print WRN "f_filter is not correctly configured !";
         close WRN;
      }
   }

   exit 0;
}

if( -f "${CONF}/f_filter.warn" ){
   unlink( "${CONF}/f_filter.warn" );
}


while(<CFG>) {

   chomp;

   if(/^\s*\;/) {
      next;

   }elsif(/^\s*$/) {
      next;

   }elsif(/^\s*\#\S*/){
      next;

   }elsif(/^\s*\!\s*MAIL\s*\=\s*(\S*)/){
      $MAILSYSOP = &IsEnabled($1);
      next;

   # Ignore unknown config lines.
   }elsif(/^\s*\!\S*/i) {
      next;

   }
}
close(CFG);

# Get Some information from INF.SYS

# Open inf.sys
if( open(INF, "< ${INFSYS}" ) ) {

   # Move to the appropriate Record
   seek(INF, ${RecNo} * 360, 0);

   # Read the Record
   if( read( INF, $Rec, 360 ) == 360 ) {

      ($call,$digis,$lastmes,$nbcon,$hcon,$lastyap,$flags,$on,
      $nbl,$lang,$newbanner,$download,$free,$thema,$Nom,$Name,
      $Addr,$City,$Phone,$Jphone,$Home,$QTH,$Pri,$Lc,$Pass,$Zip) = "";

      # Extract Data from Record.
      ($call,$digis,$lastmes,$nbcon,$hcon,$lastyap,$flags,$on,
      $nbl,$lang,$newbanner,$download,$free,$thema,$Nom,$Name,
      $Addr,$City,$Phone,$Jphone,$Home,$QTH,$Pri,$Lc,$Pass,$Zip) =
      unpack("Z8 Z64 L L L L s s c c L s Z20 c Z18 Z13 Z61 Z31 Z13 Z13 Z41 Z7 Z13 Z7 Z13 Z9", $Rec);

      close(INF);
   }

}else{
   printf STDERR "\nUnable to open ${INFSYS}\n\n";
}

if( defined(${Name}) ){
   printf STDOUT "\nThank you ${Name},\n\n";
}else{
   printf STDOUT "\nThank you ${Callsign},\n\n";
}

# Get Info about upload
if( open( DAT, "< ${DatName}" ) ) {

   while(<DAT>) {
      chomp;

      if(/^\s*\;/) {
         next;

      }elsif(/^\s*$/) {
         next;

      }elsif(/^\s*\#\S*/){
         next;

      }elsif(/^\s*TempName\s*\=\s*(\S*)/){
         $TempName = $1;
         next;

      }elsif(/^\s*FileName\s*\=\s*(\S*)/){
         $Filename = $1;
         next;

      }elsif(/^\s*Label\s*\=\s*(\S*)/){
         $Label = $1;
         next;

      # Ignore unknown lines.
      }elsif(/^\s*\!\S*/i) {
         next;
      }
   }
   close(DAT);

}else{
   printf STDERR "\nUnable to check your upload - Please inform the Sysop\n\n";
   exit 0;
}

printf STDOUT "Please wait a moment ...\n";

$infected=0;

# NAI uvscan (untested)
if( defined($uvscan) && -x $uvscan ){
   printf STDOUT "\nThe system is checking your upload with NAI uvscan ...\n";

   chop($SC = `${uvscan} --noboot --unzip ${TempName}` );
   $ScanRes = exitcode($?);

   # No Error, No Virus.
   if ($ScanRes == 0) {
      printf STDOUT "\nThe Virus Check Completed OK - No Viruses/Threats were found.\n";
   # No Error, Virus.
   } elsif ($ScanRes == 1) {
      $infected++;
         printf STDOUT "\nA Virus/Alert was detected in %s\n", ${Filename};
   } else {
         printf STDOUT "An Error occured while Checking your upload for Viruses/Threats.\n";
         $Err=1;
   }
}

# H+BEDV AntiVir
if( defined($antivir) && -x $antivir ){
   printf STDOUT "\nThe system is checking your upload with H+BEDV AntiVir ...\n";

   chop($SC = `$antivir -allfiles -noboot --scan-in-archive ${TempName}` );
   $ScanRes = exitcode($?);

   # No Error, No Virus.
   if ($ScanRes == 0) {
      printf STDOUT "\nThe Virus Check Completed OK - No Viruses/Threats were found.\n";
   # No Error, Virus.
   } elsif ($ScanRes == 1) {
      $infected++;
      # AntiVir < 2.0.4
      if ( $SC =~ /VIRUS:/ ) {
         @virusname = ($SC =~ /VIRUS: .* virus (.+)/ig);
         printf STDOUT "\nThe '%s' Virus was detected in %s\n",  @virusname, ${Filename};
      # AntiVir > 2.0.4
      } elsif ( $SC =~ /ALERT:/ ) {
         @virusname = ($SC =~ /ALERT: \[(\S+)\s.*?\]/g);
         printf STDOUT "\nThe '%s' Alert was detected in %s\n",  @virusname, ${Filename};
      }
   } else {
         printf STDOUT "An Error occured while Checking your upload for Viruses/Threats.\n";
         $Err=1;
   }
}

# FRISK f-prot
if( defined($fprot) && -x $fprot ){
   printf STDOUT "\nThe system is checking your upload with FRISK f-prot ...\n";

   chop($SC = `$fprot -packed -archive ${TempName}` );
   $ScanRes = exitcode($?);

   # No Error, No Virus.
   if ($ScanRes == 0) {
      printf STDOUT "\nThe Virus Check Completed OK - No Viruses/Threats were found.\n";
   # No Error, Virus.
   } elsif ($ScanRes < 128) {
      $infected++;
      if ( $SC =~ /Infection:/ ) {
         @virusname = ($SC =~ /Infection: (.*)/ig);
         printf STDOUT "\nThe '%s' Virus was detected in %s\n",  @virusname, ${Filename};
      }
   } else {
         printf STDOUT "An Error occured while Checking your upload for Viruses/Threats.\n";
         $Err=1;
   }
}


if( $infected >= 1 ){
   unlink( ${TempName} );
   printf STDOUT "The file has been deleted, Please Check your system for Viruses/Threats\n";
   printf STDOUT "and upload the file again another time.\n";
}

if( ( defined(${MAILSYSOP}) && ${MAILSYSOP} ) ) {

   if( open(LOCK,"> ${LOCK}" ) ) {
      printf LOCK "$$";

      if( open(MAIL,">> ${MAIL}" ) ){
         printf MAIL "#\nSP %s < FILTER\n", ${SYSCALL};
         printf MAIL "New File '${Filename}'\n\n";

         printf MAIL "A new file '${Filename}' was uploaded by : %s (%s)\n", ${Name}, ${Callsign};

         if( defined($Err) ){
            printf MAIL "\nNote:\tThe System was unable to Virus Check the upload.\n";
            printf MAIL "\tYou are advised to check the upload manually.\n";
         }

         if( defined($ScanRes) && (${ScanRes} >= 128) ){
            printf MAIL "An Error occured while Checking ${Filename} for Viruses\n";
            printf MAIL "Virus Scanner Return Value = ${ScanRes}\n";
         }
         if( $infected >= 1 ){
            printf MAIL "One or more Viruses/Threats was detected in the upload\n";
            printf MAIL "The file was deleted\n";
         }

         printf MAIL "\n\n%s\n", ${VData};
         printf MAIL "/EX\n";
         close(MAIL);
      }

      close(LOCK);
      unlink(${LOCK});

      print STDOUT "\nThe Sysop has been advised of your Upload\n";
   }
}

print STDOUT "\n";

exit 0;

sub ReadFBB_CFG
{
   # Where is FBB Setup File
   $INIT="/etc/ax25/fbb.conf";

   # Read things we need from Setup File
   if(! open(CONFIG, $INIT) ) {
      printf STDERR "Unable to open $INIT\n";
      exit 0;
   }

   while($line = <CONFIG>) {

      chomp($line);

      if( $line =~ /^\s*\#/) {
         next;

      }elsif( $line =~ /^\s*$/) {
         next;

      }else{
         $line =~ /^\s*(\S*)\s*\=\s*(.*)$/;
         $item = $1;
         $value = $2;

         if( $item =~ /callsign/ ){
            $value =~ /^(\w*)\.(.*)$/;
            $BBSCALL = $1;
            $BBSHR = $2;

         }elsif( $item =~ /data/ ) {
            $value =~ /^(\S*)$/;
            $INFSYS = $1 . "/inf.sys";

         }elsif( $item =~ /config/ ){
            $value =~ /^(\S*)$/;
            $CONF = $1;

         }elsif( $item =~ /name/ ){
            $value =~ /^(\S*)$/;
            $NAME = $1;

         }elsif( $item =~ /sysop/ ){
            $value =~ /^(\S*)$/;
            $SYSCALL = $1;

         }elsif( $item =~ /import/ ){
            $value =~ /^([^\.]\S*)\.(\S*)$/;
            $MAIL = "$1.$2";
            $LOCK = "$1.lck";
         }
      }
   }

   close(CONFIG);
}

sub IsEnabled
{
   local($Ret) = 0;
   local($P) = $_[0];

   if( ($P =~ /^0/) || ($P =~ /^n\S*/i) || ($P =~ /^off/i) || ($P =~ /^di\S*/i) ){
         ${Ret}=0;

   }elsif( ($P =~ /^1/) || ($P =~ /^y\S*/i) || ($P =~ /^ok/i) || ($P =~/^on/i) || ($P =~ /^en\S*/i) ){
         ${Ret}=1;

   }

   return ${Ret};
}

sub exitcode
{
    my $code = shift;
    return WEXITSTATUS($code) if WIFEXITED($code);
    return 128+WTERMSIG($code) if WIFSIGNALED($code);
    return 255;
}
