#!/usr/bin/perl -w
# -*-CPerl-*-
# clamd client
#
use POE;
use POE::Component::Client::TCP;
use Getopt::Long;

my $foldername;
my $verbose = 0; # does nothing right now
my $server = "127.0.0.1";
GetOptions( "folder:s" => \$foldername,
            "verbose!" => \$verbose,
            "server:s" => \$server,);

if ( defined( $foldername )) {
    require Mail::Folder; import Mail::Folder;
    require Mail::Folder::Mbox; import Mail::Folder::Mbox;

    my $folder = new Mail::Folder('mbox', $foldername ) || die $!;
    my $f2 = new Mail::Folder('mbox', "virus", Create => 1 ) || die $!;
    #$folder->set_readonly();
    $count = $folder->qty();
    my $v = 0;
    for ( $i = 1; $i <= $count; $i++ ) {
        my $msg = $folder->get_message( $i );
        my $head = $msg->head();
        print "$i of $count: From " . $head->get( "Mail-From" ) if $verbose;
        my $msgtxt = $msg->as_string;
        # silly
        my @msg = split( /\n/, $msgtxt );
        my $status = scan_one( \@msg, 1 );
        if ( $status ) {
            print "  Found virus $status\n";
            $folder->refile( $i, $f2 );
            $v++;
        } else {
            print "  Clean\n" if $verbose;
        }
    }
    $folder->sync();
    $f2->sync();
    $folder->close();
    $f2->close();

    print "handled $count messages, found $v viruses\n";
} else {
    # read in the entire bucket of data.
    my @data = <>;
    my $status = scan_one(\@data);
    my $header = 0;
    while ( @data ) {
        my $line = shift @data;
        if ( $line =~ /^$/ and !$header ) {
            print "X-CLAMAV: scanned\n";
            # insert a header marking this mail as infected
            if ( $status ) {
                print "X-CLAMAV: found virus $status\n";
            }
            $header = 1;
        }
        print $line;
    }
}

sub scan_one {
    my $file = shift;
    my $needsnl = shift;
    my $result = "";

    POE::Component::Client::TCP->new
        (
         RemoteAddress => $server,
         RemotePort => 3310,
         Started => sub {
             $poe_kernel->alias_set( "controller" );
         },
         Connected => sub {
             $_[HEAP]->{server}->put( "STREAM" );
         },
         ServerInput => sub {
             my $input = $_[ARG0];
             if ( $input =~ /^PORT\s+(\d+)/ ) {
                 doscan( $1, $file, $needsnl );
             } elsif ( $input =~ /^stream: (.*) FOUND/ ) {
                 $result = $1;
             } elsif ( $input =~ /^stream: OK/ ) {
                 # clean
             } else {
                 print "> $input\n";
             }
         },
         ServerError => sub {
             $poe_kernel->post( $_[SESSION] => "shutdown" );
         },
        );

    $poe_kernel->run();

    $result;
}


sub doscan {
    my $port = shift;
    my $data = shift;
    my $needsnl = shift;
    my $nl = 0;

    POE::Component::Client::TCP->new
        (
         RemoteAddress => $server,
         RemotePort => $port,
         Connected => sub {
             $poe_kernel->post( $_[SESSION] => "nextline" );
         },
         ServerInput => sub {
         },
         InlineStates =>
         {
          nextline => sub {
              my $line;
              if ( !defined( $data )) {
                  $line = <>;
              } else {
                  $line = $data->[$nl];
                  $nl++;
                  if ( defined( $line )) {
                      $line .= "\n" if $needsnl;
                  } else {
                      $line ||= "";
                  }
              }
              if ( $line ) {
                  $_[HEAP]->{server}->put($line);
                  $poe_kernel->post( $_[SESSION] => "nextline" );
              } else {
                  $poe_kernel->post( $_[SESSION] => "shutdown" );
              }
          },
         }
        );
}

