#! /usr/bin/perl

#------------------------------------------------------------------------------
use strict;

#------------------------------------------------------------------------------
eval( "use String::CRC32;" );
if( $@ )
{
  print( "\nError: 'String::CRC32' module is not found! Please, download this module from\n" );
  print( "CPAN site (http://www.cpan.org) and install it before running this script.\n"  );
  exit;
}

#------------------------------------------------------------------------------
my $TEMP = "/tmp";

my $system = '';
my $sdaemon = ':Daemon'; # '' if scanner update
$system = "linux" if( $^O =~ /Linux/i );
$system = "freebsd4" if( $^O =~ /FreeBSD/i );
$system = "solaris" if( $^O =~ /Solaris/i );
die "Unsupported OS!\n" unless $system;

my @urls = 
(
  # main URL:
  "http://www.drweb.ru/ftp/update_free/$system/drweb32.lst",
  # emergency URL's:
  "http://www.dials.ru/drweb/$system/drweb32.lst"
  # ...
  # ...
);

my $ini = $ARGV[0];
if( $ini eq '' )
{
  $ini = "/usr/local/drweb/drweb32.ini";
}

my $fetch = "fetch -q -o";
#my $fetch = "wget -q -O";
   $fetch = "wget -q -O" if( $^O =~ /Linux/i );   

my $engine = '';
my $bases = '';
my $update = '';
my $pidFile = '';

   readIniFile( $ini );
   die "Engine is not defined!" unless $engine;
   die "Bases path is not defined!" unless $bases;
   die "Update path is not defined!" unless $update;
   
   $pidFile = "/usr/local/drweb/run/drwebd.pid" if( $pidFile eq '' );

my $version = readVersion( $engine ); 
   die "Engine version error (invalid engine?)" unless $version;

my %Paths;
   $engine =~ s/\/[\d\w\*\?\.]*$//;
   $Paths{ "vdb\$" } = "$bases";
   $Paths{ "dll\$" } = "$engine";

   foreach my $url ( @urls )
   { 
     next if $url !~ m/^(.*)\/([\w.]+)$/o; # invalid URL
     my ( $b_url, $l_url ) = ( $1, $2 );
     open FH, "$fetch - $url |" or next;
     my $my_section = 0;

     while ( <FH> ) 
     {
       tr/\012\015//d;
       $my_section = ($1 eq $version) if /^\[(.+)\]/o;
       next unless $my_section;
       last; # section is found
     }

     my $mask = ".*";   # all files in your version section
     my %del; my %add; my %added;

     while ( <FH> ) 
     {
       tr/\012\015//d;
       last if /^\[---\]/o;  # no more sections for this versions
       $mask = 'drw.*\.vdb', next if /^\[(.+)\]/o; # new version, *.vdb only
       if( /$mask/ )
       {
         $del{$1} = hex($2) || '', next
         if /^\-(\w[\w\.\-]*)[, \t]*([a-fA-F\d]*)/o;
         $del{$1} = $add{$1} = hex($2) || '', next
           if /^\=(\w[\w\.\-]*)[, \t]*([a-fA-F\d]*)/o;
         $add{$1} = hex($2) || '', next
           if /^\+?(\w[\w\.\-]*)[, \t]*([a-fA-F\d]*)/o;
       }
     }

     close FH;

     foreach my $file ( keys %del ) 
     {
        my $full = fullPath( $file );
        next unless -e $full;
        unlink $full or warn "Can't delete $full $!";
     }

     foreach my $file ( keys %add ) 
     {
        my $full = fullPath( $file );

        if ( -e $full ) 
        { 
           next if $add{$file} ne '' and $add{$file} == &fileCRC($full);
        }

        my $newfile = "$TEMP/$file".rand();
        system( "$fetch $newfile $b_url/$file" );
        warn "Can't $fetch $file" unless -e "$newfile";
        $added{ fullPath( $file ) } = $newfile, next if $add{$file} ne '' and $add{$file} == &fileCRC( "$newfile" );
        warn "Can't $fetch $file with valid CRC32";
        unlink "$newfile";
     }

     my $updated = 0;
     foreach my $file ( keys %added )
     {
       if( -e( $added{ $file } ) ) { `mv $added{ $file } $file` };
       $updated++;
     }
     
     if( $updated > 0 )
     {
        open( FH, "$pidFile" ) || die "Can not open $pidFile: $!\n";
        my $pid = <FH>;
        close( FH );
        chomp( $pid );
        kill 'HUP', $pid;
     }	

     exit;
   }


#------------------------------------------------------------------------------
sub readVersion
{
  open( FH, "$_[0]" ) || die "Can not open $_[0]!";
  seek( FH, 0x400, 0 );
  my $ver = 0;
  read( FH, $ver, 4 );
  close( FH );
  return unpack( "I", $ver );
}

#------------------------------------------------------------------------------
sub readIniFile
{
  my $section = '';

  $section = "Linux$sdaemon" if( $^O =~ /Linux/i );
  $section = "BSD$sdaemon" if( $^O =~ /FreeBSD/i );
  $section = "Solaris/i86$sdaemon" if( $^O =~ /Solaris/i );

  open( FH, "$_[0]" ) || die "Can not open $_[0]!";

  my $my_section = '';

  while( <FH> )
  {
    tr/\012\015//d;
    s/^[\t ]*//g;
    $my_section = ($1 eq $section), next if /^\[(.+)\]/o;
    next unless $my_section;
    if( /^EnginePath/ )
    {
      my ( $k, $v ) = split( 'EnginePath' );
      if( $v ne '' )
      {
        $engine = $v;
        $engine =~ s/^[\"\t =]*//g;
        $engine =~ s/[\" \t]*$//g;       
      }
    }
    elsif( /^PidFile/ )
    {
      my ( $k, $v ) = split( 'PidFile' );
      if( $v ne '' )
      {
        $pidFile= $v;
        $pidFile =~ s/^[\"\t =]*//g;
        $pidFile =~ s/[\" \t]*$//g;       
      }
    }
    elsif( /^VirusBase/ && $bases eq '' ) # first base path only
    {
      my ( $k, $v ) = split( 'VirusBase' );
      if( $v ne '' )
      {
        $bases = $v;

        $bases =~ s/^[\"\t =]*//g;
        $bases =~ s/,.*$//;
        $bases =~ s/[\" \t]*$//g;	
	$bases =~ s/\/[\d\w\*\?\.]*$//;
      }
    }
    elsif( /^UpdatePath/ )
    {
      my ( $k, $v ) = split( 'UpdatePath' );
      if( $v ne '' )
      {
        $update = $v;
        $update =~ s/^[\"\t =]*//g;
        $update =~ s/[\" \t]*$//g;       
      }
    }
  } 
  close( FH );
}

#------------------------------------------------------------------------------
sub fullPath
{
  my $file = $_[0];

  foreach my $template ( keys %Paths ) 
  {
     if( $file =~ /$template/ )
     {
          return "$Paths{ $template }/$file";
     }
  }

  return "$update/$file";
}

#------------------------------------------------------------------------------
sub fileCRC 
{
    open FH, $_[0] or return '';
    binmode( FH );
    my $crc32 = crc32( *FH );
    close FH; 
    return $crc32;
}

#----------------------------------------------------------------------------
