--- Message.pm.old	Fri Jan 23 12:31:07 2004
+++ Message.pm	Thu Feb 12 08:46:16 2004
@@ -838,10 +843,14 @@
 
   # Add the spam stars if they want that. Limit it to 60 characters to avoid
   # a potential denial-of-service attack.
-  my($stars,$starcount);
+  my($stars,$starcount,$scoretext,$minstars);
   $starcount = int($this->{sascore}) + 0;
   $starcount = 0 if $this->{spamwhitelisted}; # 0 stars if white-listed
+  $scoretext = $starcount;
   if (MailScanner::Config::Value('spamstars', $this) =~ /1/ && $starcount>0) {
+    $minstars = MailScanner::Config::Value('minstars', $this);
+    $starcount = $minstars if $this->{isrblspam} && $minstars &&
+                              $starcount<$minstars;
     if (MailScanner::Config::Value('spamscorenotstars', $this)) {
       $stars = int($starcount);
     } else {
@@ -860,7 +869,7 @@
   # if it's spam AND they want to modify the subject line AND it's not
   # already been modified by another of your MailScanners.
   my $spamtag = MailScanner::Config::Value('spamsubjecttext', $this);
-  $spamtag =~ s/_SCORE_/$starcount/;
+  $spamtag =~ s/_SCORE_/$scoretext/;
   if ($this->{isspam} && !$this->{ishigh} &&
       MailScanner::Config::Value('spamprependsubject',$this) &&
       !$global::MS->{mta}->TextStartsHeader($this, 'Subject:', $spamtag)) {
@@ -868,7 +877,7 @@
   }
   # If it is high-scoring spam, then add a different bit of text
   $spamtag = MailScanner::Config::Value('highspamsubjecttext', $this);
-  $spamtag =~ s/_SCORE_/$starcount/;
+  $spamtag =~ s/_SCORE_/$scoretext/;
   if ($this->{isspam} && $this->{ishigh} &&
       MailScanner::Config::Value('highspamprependsubject',$this) &&
       !$global::MS->{mta}->TextStartsHeader($this, 'Subject:', $spamtag)) {
@@ -1003,11 +1012,80 @@
     }
   }
 
+  $this->ExplodePart($explodeinto);
+
   # Set the owner and group on all the extracted files
   chown $workarea->{uid}, $workarea->{gid}, glob "$explodeinto/*"
     if $workarea->{changeowner};
 }
 
+# Try to parse all the text bits of each message, looking to see if they
+# can be parsed into files which might be infected.
+# How I treat these in the MIME structure is anyone's guess!
+sub ExplodePart {
+  my($this, $explodeinto) = @_;
+
+  my($dir, $part);
+
+  $dir = new DirHandle;
+
+  $dir->open($explodeinto);
+  while($part = $dir->read) {
+    #print STDERR "Reading $part\n";
+    next unless $part =~ /^msg.*txt/;
+    
+    # Try and find hidden messages in the text files
+    my $file = new FileHandle;
+    #print STDERR "About to read $explodeinto/$part\n";
+    $file->open("$explodeinto/$part") or next;
+
+    # Try reading the first few lines to see if they look like mail headers
+    my($linenum, $foundheader, $prevline, $line, $position, $prevpos);
+    $foundheader = 0;
+    $prevpos = 0;
+    for ($linenum=0; $linenum<100 && !$file->eof(); $linenum++) {
+      $position = $file->getpos();
+      $line = <$file>;
+      # Must have 2 lines of header
+      if ($prevline =~ /^[^:]+: / && $line =~ /(^\s+)|(^[^:]+ )|(^\s+.*=)/) {
+        #print STDERR "Found start of a header at \"$prevline\"\n and \"$line\"\n";
+        $foundheader = 1;
+        last;
+      }
+      $prevline = $line;
+      $prevpos  = $position;
+    }
+
+    unless ($foundheader) {
+      $file->close();
+      $dir->close();
+      return;
+    }
+
+    # Rewind to the start of the header
+    $file->setpos($prevpos);
+
+    # Setup everything for the MIME parser
+    my $parser = MIME::Parser->new;
+    my $filer  = MIME::Parser::FileInto::MailScanner->new($explodeinto);
+
+    # Over-ride the default default character set handler so it does it
+    # much better than the MIME-tools default handling.
+    MIME::WordDecoder->default->handler('*' => \&WordDecoderKeep7Bit);
+
+    #print STDERR "Exploding message " . $this->{id} . " into " .
+    #             $explodeinto . "\n";
+    $parser->filer($filer);
+    $parser->extract_uuencode(1); # uue is off by default
+    $parser->output_to_core('NONE'); # everything into files
+
+    # Do the actual parsing
+    my $entity = eval { $parser->parse($file) };
+
+    $file->close;
+  }
+}
+
 
 # Print the infection reports for this message
 sub PrintInfections {
@@ -1163,10 +1241,13 @@
 # them some time later, but I wanted to keep them separate if possible
 # in case anyone wanted a feature in the future which would be easier
 # with separate reports.
+# If safefile2file does not map for a filename, ban the whole message
+# to be on the safe side.
 sub CombineReports {
   my $this = shift;
 
   my($file, $text, $Name);
+  my(%reports, %types);
   #print STDERR "Combining reports for " . $this->{id} . "\n";
 
   # If they want to include the scanner name in the reports, then also
@@ -1176,7 +1257,7 @@
   # *not* be inserted at the start of the reports.
   $Name = MailScanner::Config::LanguageValue($this, 'mailscanner')
     if MailScanner::Config::Value('showscanner', $this);
-  $Name .= ': ' if $Name ne "";
+  $Name .= ': ' if $Name ne "" && $Name !~ /:/;
 
   # Or the flags together
   $this->{infected} = $this->{virusinfected} |
@@ -1187,28 +1268,48 @@
   while (($file, $text) = each %{$this->{virusreports}}) {
     #print STDERR "Adding file $file report $text\n";
     $this->{allreports}{$file} .= $text;
+    $reports{$file} .= $text;
   }
   while (($file, $text) = each %{$this->{virustypes}}) {
     #print STDERR "Adding file $file type $text\n";
     $this->{alltypes}{$file} .= $text;
+    $types{$file} .= $text;
   }
   while (($file, $text) = each %{$this->{namereports}}) {
-    #print STDERR "Adding file $file report $text\n";
+    #print STDERR "Adding file \"$file\" report \"$text\"\n";
     $text =~ s/\n(.)/\n$Name:  $1/g; # Make sure name is at the front of this
+    #print STDERR "report is now \"$text\"\n";
     $this->{allreports}{$file} .= $Name . $text;
+    $reports{$file} .= $Name . $text;
   }
   while (($file, $text) = each %{$this->{nametypes}}) {
     #print STDERR "Adding file $file type $text\n";
     $this->{alltypes}{$file} .= $text;
+    $types{$file} .= $text;
   }
   while (($file, $text) = each %{$this->{otherreports}}) {
     #print STDERR "Adding file $file report $text\n";
     $this->{allreports}{$file} .= $Name . $text;
+    $reports{$file} .= $Name . $text;
   }
   while (($file, $text) = each %{$this->{othertypes}}) {
     #print STDERR "Adding file $file type $text\n";
     $this->{alltypes}{$file} .= $text;
+    $types{$file} .= $text;
   }
+
+  # Now look for the reports we can't match anywhere and make them
+  # map to the entire message.
+  my($key, $value);
+  while(($key, $value) = each %reports) {
+    if (!exists($this->{safefile2file}{$key})) {
+      delete $this->{allreports}{$key};
+      delete $this->{alltypes}{$key};
+      $this->{allreports}{""} .= $value;
+      $this->{alltypes}{""} .= $types{$key};
+    }
+  }
+
   #print STDERR "Finished combining reports\n";
 }
 
@@ -1960,10 +2061,14 @@
 
   # Add the spam stars if they want that. Limit it to 60 characters to avoid
   # a potential denial-of-service attack.
-  my($stars,$starcount);
+  my($stars,$starcount,$scoretext,$minstars);
   $starcount = int($this->{sascore}) + 0;
   $starcount = 0 if $this->{spamwhitelisted}; # 0 stars if white-listed
+  $scoretext = $starcount;
   if (MailScanner::Config::Value('spamstars', $this) =~ /1/ && $starcount>0) {
+    $minstars = MailScanner::Config::Value('minstars', $this);
+    $starcount = $minstars if $this->{isrblspam} && $minstars &&
+                              $starcount<$minstars;
     if (MailScanner::Config::Value('spamscorenotstars', $this)) {
       $stars = int($starcount);
     } else {
@@ -1983,7 +2088,7 @@
   # already been modified by another of your MailScanners.
   my $subjectchanged = 0;
   my $spamtag = MailScanner::Config::Value('spamsubjecttext', $this);
-  $spamtag =~ s/_SCORE_/$starcount/;
+  $spamtag =~ s/_SCORE_/$scoretext/;
   if ($this->{isspam} && !$this->{ishigh} &&
       MailScanner::Config::Value('spamprependsubject',$this) &&
       !$global::MS->{mta}->TextStartsHeader($this, 'Subject:', $spamtag)) {
@@ -1992,7 +2097,7 @@
   }
   # If it is high-scoring spam, then add a different bit of text
   $spamtag = MailScanner::Config::Value('highspamsubjecttext', $this);
-  $spamtag =~ s/_SCORE_/$starcount/;
+  $spamtag =~ s/_SCORE_/$scoretext/;
   if ($this->{isspam} && $this->{ishigh} &&
       MailScanner::Config::Value('highspamprependsubject',$this) &&
       !$global::MS->{mta}->TextStartsHeader($this, 'Subject:', $spamtag)) {
@@ -2099,10 +2204,14 @@
 
   # Add the spam stars if they want that. Limit it to 60 characters to avoid
   # a potential denial-of-service attack.
-  my($stars,$starcount);
+  my($stars,$starcount,$scoretext,$minstars);
   $starcount = int($this->{sascore}) + 0;
   $starcount = 0 if $this->{spamwhitelisted}; # 0 stars if white-listed
+  $scoretext = $starcount;
   if (MailScanner::Config::Value('spamstars', $this) =~ /1/ && $starcount>0) {
+    $minstars = MailScanner::Config::Value('minstars', $this);
+    $starcount = $minstars if $this->{isrblspam} && $minstars &&
+                              $starcount<$minstars;
     if (MailScanner::Config::Value('spamscorenotstars', $this)) {
       $stars = int($starcount);
     } else {
@@ -2123,7 +2232,7 @@
   # already been modified by another of your MailScanners.
   my $subjectchanged = 0;
   my $spamtag = MailScanner::Config::Value('spamsubjecttext', $this);
-  $spamtag =~ s/_SCORE_/$starcount/;
+  $spamtag =~ s/_SCORE_/$scoretext/;
   if ($this->{isspam} && !$this->{ishigh} &&
       MailScanner::Config::Value('spamprependsubject',$this) &&
       !$global::MS->{mta}->TextStartsHeader($this, 'Subject:', $spamtag)) {
@@ -2132,7 +2241,7 @@
   }
   # If it is high-scoring spam, then add a different bit of text
   $spamtag = MailScanner::Config::Value('highspamsubjecttext', $this);
-  $spamtag =~ s/_SCORE_/$starcount/;
+  $spamtag =~ s/_SCORE_/$scoretext/;
   if ($this->{isspam} && $this->{ishigh} &&
       MailScanner::Config::Value('highspamprependsubject',$this) &&
       !$global::MS->{mta}->TextStartsHeader($this, 'Subject:', $spamtag)) {