~ubuntu-branches/ubuntu/utopic/mailscanner/utopic

« back to all changes in this revision

Viewing changes to bin/MailScanner

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2005-01-02 01:09:43 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20050102010943-wh8bxrc4962i9amu
Tags: 4.37.7-1
New upstream version.

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
#   MailScanner - SMTP E-Mail Virus Scanner
4
4
#   Copyright (C) 2002  Julian Field
5
5
#
6
 
#   $Id: mailscanner,v 1.142.2.133 2004/05/01 12:32:26 jkf Exp $
 
6
#   $Id: mailscanner,v 1.142.2.188 2004/12/30 14:33:26 jkf Exp $
7
7
#
8
8
#   This program is free software; you can redistribute it and/or modify
9
9
#   it under the terms of the GNU General Public License as published by
36
36
use POSIX;
37
37
require 5.005;
38
38
 
 
39
# Awkard BEGIN block so that we pick up MIME::Base64 from the right place!
 
40
BEGIN {
 
41
  my(@oldinc,@safecopy,$path);
 
42
 
 
43
  # Look in /usr/local/MailScanner/utils for the modules
 
44
  @oldinc = @INC;
 
45
  @safecopy = @INC;
 
46
 
 
47
  # Duplicate path with /usr/local/MailScanner/utils stuck on the front
 
48
  # of each element
 
49
  foreach $path (reverse @oldinc) {
 
50
    next unless $path =~ /\//;
 
51
    $path =~ s/^\/usr/\/usr\/lib\/MailScanner\/utils/;
 
52
    unshift @INC, $path;
 
53
  }
 
54
 
 
55
  require MIME::Base64;
 
56
  require MIME::QuotedPrint;
 
57
 
 
58
  @INC = @safecopy;
 
59
}
 
60
 
39
61
use FileHandle;
40
62
use File::Path;
41
63
use IO::Handle;
101
123
#
102
124
# First production release will be 4.00.1.
103
125
#
104
 
$MailScanner::Config::MailScannerVersion = '4.30.3';
 
126
$MailScanner::Config::MailScannerVersion = '4.37.7';
 
127
 
 
128
# Are we just printing version numbers and exiting?
 
129
if (grep /^-+[vV]/, @ARGV) {
 
130
  my @Modules = qw/AnyDBM_File Archive::Zip Carp Convert::BinHex Convert::TNEF Data::Dumper DirHandle Fcntl File::Basename File::Copy FileHandle File::Path File::Temp HTML::Entities HTML::Parser HTML::TokeParser IO IO::File IO::Pipe Mail::ClamAV Mail::SpamAssassin MIME::Base64 MIME::Decoder MIME::Decoder::UU MIME::Head MIME::Parser MIME::QuotedPrint MIME::Tools MIME::WordDecoder Net::CIDR POSIX SAVI Socket Sys::Syslog Time::localtime/;
 
131
  my @Optional = qw#DB_File.pm Digest.pm Digest/HMAC.pm Digest/MD5.pm Digest/SHA1.pm Inline.pm Mail/ClamAV.pm Mail/SpamAssassin.pm Mail/SPF/Query.pm Net/CIDR/Lite.pm Net/DNS.pm Net/LDAP.pm Parse/RecDescent.pm SAVI.pm Sys/Hostname/Long.pm Test/Harness.pm Test/Simple.pm Text/Balanced.pm URI.pm#;
 
132
  my($module, $s, $v, $m);
 
133
 
 
134
  printf("Running on\n%s", `uname -a`);
 
135
  printf("This is %s", `cat /etc/redhat-release`)   if -f "/etc/redhat-release";
 
136
  printf("This is %s", `head -1 /etc/SuSE-release`) if -f "/etc/SuSE-release";
 
137
  printf("This is Perl version %f (%vd)\n", $], $^V);
 
138
  print "\nThis is MailScanner version " . $MailScanner::Config::MailScannerVersion . "\n";
 
139
  print "Module versions are:\n";
 
140
  foreach $module (@Modules) {
 
141
    $s = '$' . "$module" . '::VERSION';
 
142
    $v = eval("$s");
 
143
    print "$v\t$module\n" if $v ne "";
 
144
  }
 
145
  print "\nOptional module versions are:\n";
 
146
  foreach $module (@Optional) {
 
147
    $m = $module;
 
148
    $m =~ s/\//::/g;
 
149
    $m =~ s/\.pm$//;
 
150
    $s = '$' . "$m" . '::VERSION';
 
151
    $v = eval("require \"$module\"; $s") || "missing";
 
152
    print "$v\t$m\n";
 
153
  }
 
154
  exit;
 
155
}
105
156
 
106
157
# Work out what directory we're in and add it onto the front
107
158
# of the include path so that we can work if we're just chucked
129
180
my $ConfFile = $ARGV[0];
130
181
$ConfFile = '/opt/MailScanner/etc/MailScanner.conf' unless $ConfFile;
131
182
 
 
183
# Check the MailScanner version number against what is in MailScanner.conf
 
184
my $NeedVersion = MailScanner::Config::QuickPeek($ConfFile,
 
185
                                              'mailscannerversionnumber');
 
186
if ($NeedVersion) {
 
187
  my($ConfMajor, $ConfMinor, $ConfRelease);
 
188
  my($Error, $AreMajor, $AreMinor, $AreRelease);
 
189
  $Error = 0;
 
190
  $NeedVersion =~ /^(\d+)\.(\d+)\.(\d+)$/;
 
191
  ($ConfMajor, $ConfMinor, $ConfRelease) = ($1+0, $2+0, $3+0);
 
192
  $ConfMajor   = 0 unless $ConfMajor;
 
193
  $ConfMinor   = 0 unless $ConfMinor;
 
194
  $ConfRelease = 0 unless $ConfRelease;
 
195
  $MailScanner::Config::MailScannerVersion =~ /^(\d+)\.(\d+)\.(\d+)$/;
 
196
  ($AreMajor, $AreMinor, $AreRelease) = ($1+0, $2+0, $3+0);
 
197
  $AreMajor   = 0 unless $AreMajor;
 
198
  $AreMinor   = 0 unless $AreMinor;
 
199
  $AreRelease = 0 unless $AreRelease;
 
200
  if ($ConfMajor > $AreMajor) {
 
201
    $Error = 1;
 
202
  } elsif ($ConfMajor == $AreMajor) {
 
203
    if ($ConfMinor > $AreMinor) {
 
204
      $Error = 1;
 
205
    } elsif ($ConfMinor == $AreMinor) {
 
206
      if ($ConfRelease > $AreRelease) {
 
207
        $Error = 1;
 
208
      }
 
209
    }
 
210
  }
 
211
  if ($Error) {
 
212
    print STDERR "The configuration file $ConfFile\nis too new for this version of MailScanner.\nThis is version " . $MailScanner::Config::MailScannerVersion . " but the config file is for at least version $NeedVersion\n";
 
213
    exit 1;
 
214
  }
 
215
}
 
216
 
132
217
# Load the MTA modules we need
133
218
my($MTAmod, $MTADSmod);
134
219
# LEOH:if (MailScanner::Config::QuickPeek($ConfFile, 'mta') =~ /exim/i) {
184
269
#      WorkDir     to be able to clean up after killed children
185
270
#      BayesRebuildPeriod to be able to rebuild the Bayes database regularly
186
271
#
 
272
use vars qw($RunInForeground);
 
273
$RunInForeground= MailScanner::Config::QuickPeek($ConfFile, 'runinforeground');
187
274
my $MaxChildren = MailScanner::Config::QuickPeek($ConfFile, 'maxchildren');
188
275
my $Debug       = MailScanner::Config::QuickPeek($ConfFile, 'debug');
189
276
my $WorkDir     = MailScanner::Config::QuickPeek($ConfFile, 'incomingworkdir');
193
280
# to critical system files, or create pidfiles that point to critical
194
281
# system processes)
195
282
$Debug = 0 unless $Debug =~ /yes|1/i;
 
283
$RunInForeground = 0 unless $RunInForeground =~ /yes|1/i;
 
284
 
 
285
# Enable STDOUT flushing if running in foreground
 
286
# to be able to actively capture it with a logger
 
287
$| = 1 if $RunInForeground;
196
288
 
197
289
# Give the user their shell back
198
290
ForkDaemon($Debug);
223
315
# Set when the next rebuild is due if regular rebuilds are being done
224
316
$NextRebuildDueTime = time + $BayesRebuildPeriod if $BayesRebuildPeriod;
225
317
 
 
318
# If we run in foreground, SIGKILL to the parent will try to reload
 
319
# by SIGKILLing its children
 
320
$SIG{'HUP'} = 'ReloadParent' if $RunInForeground;
 
321
 
226
322
for (;;) {
227
323
  while($NumberOfChildren < $MaxChildren) {
228
324
    # Trigger 1 Bayes rebuild if the period has expired
231
327
      $RebuildDue = 1;
232
328
      $NextRebuildDueTime = time + $BayesRebuildPeriod;
233
329
    }
 
330
    print STDOUT sprintf("About to fork child #%d of %d...\n",
 
331
                         $NumberOfChildren+1, $MaxChildren)
 
332
      if $RunInForeground;
234
333
    my $born_pid = fork();
235
334
    if (!defined($born_pid)) {
236
335
      die "Cannot fork off child process, $!";
242
341
      WorkForHours($RebuildDue);
243
342
      exit 0;
244
343
    }
 
344
    print STDOUT "\tForked OK - new child is [$born_pid]\n" if $RunInForeground;
245
345
    # I am the parent process.
246
346
    $Children{$born_pid} = 1;
247
347
    $NumberOfChildren++;
377
477
  $StartTime = time;
378
478
  $RestartTime = $StartTime + MailScanner::Config::Value('restartevery');
379
479
 
 
480
  my $FirstCheck = MailScanner::Config::Value('firstcheck');
 
481
  MailScanner::Log::WarnLog("First Check must be set to MCP or spam")
 
482
    unless $FirstCheck =~ /mcp|spam/i;
 
483
  my $VirusBeforeSpamMCP = MailScanner::Config::Value('virusbeforespammcp');
 
484
 
380
485
  while (time>=$StartTime && time<$RestartTime) {
381
486
    $workarea->Clear();
382
487
    $batch = new MailScanner::MessageBatch();
412
517
    # Archive untouched incoming messages to directories
413
518
    $batch->ArchiveToFilesystem();
414
519
 
415
 
    # Do the MCP checks
416
 
    $batch->StartTiming('mcp', 'MCP Checks');
417
 
    $batch->MCPChecks();
418
 
    $batch->HandleMCP();
419
 
    $batch->HandleNonMCP();
420
 
    $batch->StopTiming('mcp', 'MCP Checks');
421
 
 
422
 
    # Do the spam checks
423
 
    $batch->StartTiming('spam', 'Spam Checks');
424
 
    $batch->SpamChecks();
425
 
    $batch->HandleSpam();
426
 
    $batch->HandleHam();
427
 
    $batch->StopTiming('spam', 'Spam Checks');
 
520
    # Have to do this very early as it's needed for MCP and spam bouncing
 
521
    $global::MS->{work}->BuildInDirs($batch);
 
522
 
 
523
    # Yes I know this isn't elegant, but it's very short so it will do :-)
 
524
    if ($FirstCheck =~ /mcp/i) {
 
525
      # Do the MCP checks
 
526
      $batch->StartTiming('mcp', 'MCP Checks');
 
527
      $batch->MCPChecks();
 
528
      $batch->HandleMCP();
 
529
      $batch->HandleNonMCP();
 
530
      $batch->StopTiming('mcp', 'MCP Checks');
 
531
 
 
532
      # Do the spam checks
 
533
      $batch->StartTiming('spam', 'Spam Checks');
 
534
      $batch->SpamChecks();
 
535
      $batch->HandleSpam();
 
536
      $batch->HandleHam();
 
537
      $batch->StopTiming('spam', 'Spam Checks');
 
538
    } else {
 
539
      # Do the spam checks
 
540
      $batch->StartTiming('spam', 'Spam Checks');
 
541
      $batch->SpamChecks();
 
542
      $batch->HandleSpam();
 
543
      $batch->HandleHam();
 
544
      $batch->StopTiming('spam', 'Spam Checks');
 
545
 
 
546
      # Do the MCP checks
 
547
      $batch->StartTiming('mcp', 'MCP Checks');
 
548
      $batch->MCPChecks();
 
549
      $batch->HandleMCP();
 
550
      $batch->HandleNonMCP();
 
551
      $batch->StopTiming('mcp', 'MCP Checks');
 
552
    }
428
553
 
429
554
    # Deliver all the messages we are not scanning at all,
430
555
    # and mark them for deletion.
434
559
 
435
560
    # Extract all the attachments
436
561
    $batch->StartTiming('virus', 'Virus Scanning');
437
 
    $global::MS->{work}->BuildInDirs($batch);
 
562
    # Moved upwards: $global::MS->{work}->BuildInDirs($batch);
438
563
    $batch->Explode();
439
564
 
440
565
    # Report all the unparsable messages, but don't delete anything
467
592
    # Quarantine all the infected attachments
468
593
    $batch->QuarantineInfections();
469
594
 
 
595
    # Remove any infected spam from the spam+mcp archives
 
596
    $batch->RemoveInfectedSpam();
 
597
 
470
598
    # Clean all the infections out of the messages
471
599
    $batch->Clean();
472
600
 
553
681
  MailScanner::Log::InfoLog("MailScanner child caught a SIG%s", $sig);
554
682
  # Finish off any incoming queue file deletes that were pending
555
683
  MailScanner::SMDiskStore::DoPendingDeletes();
 
684
  # Kill off any commercial virus scanner process groups that are still running
 
685
  kill -15, $MailScanner::SweepViruses::ScannerPID
 
686
    if $MailScanner::SweepViruses::ScannerPID;
556
687
  # Destroy the incoming work dir
557
688
  $global::MS->{work}->Destroy() if $global::MS && $global::MS->{work};
558
689
  # Close down all the user's custom functions
562
693
  exit 0;
563
694
}
564
695
 
565
 
 
566
 
#
567
 
# SIGTERM handler for parent process.
568
 
# HUP all the children, then commit suicide.
569
 
# Cannot log as no logging in the parent.
570
 
#
571
 
sub ExitParent {
572
 
  my($sig) = @_; # Arg is the signal name
 
696
sub KillChildren {
573
697
  my($child, @dirlist);
574
698
 
575
699
  #print STDERR "Killing child processes...\n";
 
700
  if ($RunInForeground) {
 
701
    print STDOUT "Killing child processes ";
 
702
    print STDOUT join( '/', keys %Children);
 
703
  }
576
704
  kill 1, keys %Children;
 
705
  print STDOUT " and giving them time to die...\n" if $RunInForeground;
 
706
 
577
707
  sleep 3; # Give them time to die peacefully
 
708
  print STDOUT "Cleaning up..." if $RunInForeground;
578
709
 
579
710
  # Clean up after the dying processes in case they left a mess.
580
711
  foreach $child (keys %Children) {
581
712
    #push @dirlist, "$WorkDir/$child" if -d "$WorkDir/$child";
582
713
    rmtree("$WorkDir/$child", 0, 1) if -d "$WorkDir/$child";
583
714
  }
584
 
 
585
 
  #system($global::rm . " -rf \"" . join("\" \"", @dirlist) . "\"")
586
 
  #  if @dirlist;
 
715
  print STDOUT "Done\n" if $RunInForeground;
 
716
}
 
717
 
 
718
#
 
719
# SIGKILL handler for parent process.
 
720
# HUP all the children, then keep working.
 
721
#
 
722
sub ReloadParent {
 
723
  my($sig) = @_; # Arg is the signal name
 
724
 
 
725
  print STDOUT "MailScanner parent caught a SIG$sig - reload\n"
 
726
    if $RunInForeground;
 
727
 
 
728
  KillChildren();
 
729
 
 
730
  print STDOUT "MailScanner reloaded.\n" if $RunInForeground;
 
731
}
 
732
 
 
733
 
 
734
 
 
735
#
 
736
# SIGTERM handler for parent process.
 
737
# HUP all the children, then commit suicide.
 
738
# Cannot log as no logging in the parent.
 
739
#
 
740
sub ExitParent {
 
741
  my($sig) = @_; # Arg is the signal name
 
742
 
 
743
  print STDOUT "MailScanner parent caught a SIG$sig\n" if $RunInForeground;
 
744
 
 
745
  KillChildren();
 
746
 
 
747
  print STDOUT "Exiting MailScanner - Bye.\n" if $RunInForeground;
587
748
 
588
749
  exit 0;
589
750
}
630
791
    # Get current debugging flag, and invert it:
631
792
    my $current = config MIME::ToolUtils 'DEBUGGING';
632
793
    #config MIME::ToolUtils DEBUGGING => !$current;
 
794
  } elsif ($RunInForeground) {
 
795
    # PERT-BBY we don't close STDXX neither fork() nor setsid()
 
796
    #          if we want to run in the foreground
 
797
    print STDOUT "MailScanner $MailScanner::Config::MailScannerVersion " .
 
798
                 "starting in foreground mode - pid is [$$]\n";
633
799
  } else {
634
800
    $SIG{'CHLD'} = \&Reaper;
635
801
    if (fork==0) {
750
916
# Check the versions of the MIME and SpamAssassin modules
751
917
#
752
918
sub CheckModuleVersions {
753
 
  my($varname, $module_version);
754
 
 
755
 
  # These version numbers are what come in the MIME-tools v5.410 package,
756
 
  # which I (nwp) use.
757
 
  my %mime_required = (
758
 
                       Parser     => "5.406",
759
 
                       Entity     => "5.404",
760
 
                       Tools      => "5.410",
761
 
                       Words      => "5.404",
762
 
                       Head       => "5.403",
763
 
                       Decoder    => "5.403",
764
 
                       Body       => "5.403",
765
 
  );
766
 
 
767
 
  no strict 'refs';
768
 
 
769
 
  foreach (keys %mime_required) {
770
 
    $varname = "MIME::". ucfirst lc($_) ."::VERSION";
771
 
    defined $$varname or next;
772
 
    $module_version = $$varname;
773
 
    $module_version >= $mime_required{$_} or
774
 
      MailScanner::Log::DieLog("FATAL: Newer MIME-tools module needed: %s" .
775
 
                               " is only MIME::$_ -- %s required",
776
 
                               $module_version, $mime_required{$_});
777
 
  }
 
919
  my($module_version);
 
920
 
 
921
  # Check the MIME-tools version
 
922
  MailScanner::Log::DieLog("FATAL: Newer MIME::Tools module needed: " .
 
923
                           "MIME::Tools is only %s -- 5.412 required",
 
924
                           $MIME::Tools::VERSION)
 
925
    if defined $MIME::Tools::VERSION &&
 
926
       $MIME::Tools::VERSION<"5.412";
 
927
 
778
928
 
779
929
  # And check the SpamAssassin version
780
 
  $varname = "Mail::SpamAssassin::VERSION";
781
930
  MailScanner::Log::DieLog("FATAL: Newer Mail::SpamAssassin module needed: " .
782
931
                           "Mail::SpamAssassin is only %s -- 2.1 required",
783
932
                           $Mail::SpamAssassin::VERSION)