#!/usr/bin/perl
##########################################################################
# $Header: /etc/log.d/scripts/services/RCS/cyrus,v 1.4 2004/02/04 16:02:13 root Exp $
##########################################################################

########################################################
#
# This was written and is maintained by:
#    Sebastian Hagedorn <Hagedorn@uni-koeln.de>
#
########################################################

$Detail = $ENV{'LOGWATCH_DETAIL_LEVEL'};

sub numerically { $a <=> $b }

while (defined($ThisLine = <STDIN>)) {
   chomp($ThisLine);
   if ($ThisLine =~ /ERROR/) { # collect error messages
	if ($ThisLine =~ /DBERROR db4: (\d+) lockers/) {
		if ($1 > $max_lockers) {
			$max_lockers = $1;
		}
		next;
	}
	push @ErrorList, "$ThisLine\n";
	next;
   }

   # store info to calculate average and maximal delay between accepting a connection and login
   if (($ThisLine =~ /(\S+) \w+ (\w+)\[(\d+)\]: accepted connection/) and ($2 !~ /lmtp/)) { # LMTP doesn't have logins
      $process{$3}=$2;
      $accepttime{$3}=$1;
   } elsif ($ThisLine =~ /(\S+) \w+ (\w+)\[(\d+)\]: badlogin:/) {
      if ((defined $process{$3}) and ($process{$3} eq $2)) {
         $process{$3} = undef; # badlogin screws up maxdelay
      }
   } elsif ($ThisLine =~ /(\S+) \w+ (\w+)\[(\d+)\]: login:/) {
      if ((defined $process{$3}) and ($process{$3} eq $2)) {
         $logintime{$3}=$1;
      }
   }

   # ignore the following
   if ( ($ThisLine =~ /lmtpunix\[/) or
	($ThisLine =~ /master\[\d+\]: process \d+ exited, status 0/) or
	($ThisLine =~ /master\[\d+\]: about to exec/) or
	($ThisLine =~ /last message repeated \d+ times/) or
	($ThisLine =~ /ctl_cyrusdb\[/) or
	($ThisLine =~ /ctl_deliver\[\d+\]: mydelete/) or
	($ThisLine =~ /tls_prune\[\d+\]: mydelete/) or
	($ThisLine =~ /sieve\[/)
      )
   {
	# we ignore these lines
	next;
   }

   # count abnormal exits
   if ($ThisLine =~ /master\[\d+\]: process \d+ exited, status 75/) {
	$err_75++;
	next;
   }

   # count message numbers
   if ($ThisLine =~ /lmtpd\[/) {
      if ($ThisLine =~ /mystore: committing txn/) {
	$LMTPStore++;
      } elsif ($ThisLine =~ /sieve: discarded message/) {
	$LMTPDiscard++;
      } elsif ($ThisLine =~ /dupelim: eliminated duplicate message to/) {
	$LMTPDupelim++;
      }
   } elsif ($ThisLine =~ /squatter\[\d+\]:/) {
      if ($ThisLine =~ /indexing mailbox /) {
	$Squatter++;
      }
   # count POP logins
   } elsif ($ThisLine =~ /pop3.?\[/) {
	if ($ThisLine =~ /badlogin/) {
	    $POPbadlogin++;
#	} elsif ($ThisLine =~ /login: \S+ \w+ (\S+) User logged in/) {
	} elsif ($ThisLine =~ /login: \S+ \w+ (\S+) /) { # plaintext doesn't log "User logged in"
	    $POPlogin++;
	    $POPmech{$1}++;
	} elsif ($ThisLine =~ /starttls/) {
	    $POPTLS++;
	}
   # count IMAP logins
   } elsif ($ThisLine =~ /imap.?\[/) {
	if ($ThisLine =~ /badlogin/) {
	    $IMAPbadlogin++;
#	} elsif ($ThisLine =~ /login: \S+ \w+ (\S+) User logged in/) {
	} elsif ($ThisLine =~ /login: \S+ \w+ (\S+) /) { # plaintext doesn't log "User logged in"
	    $IMAPlogin++;
	    $IMAPmech{$1}++;
	} elsif ($ThisLine =~ /starttls/) {
	    $IMAPTLS++;
	}
   # count sieve logins
   } elsif ($ThisLine =~ /timsieved\[/) {
	if ($ThisLine =~ /badlogin/) {
	    $sievebadlogin++;
#	} elsif ($ThisLine =~ /login: \S+ \w+ (\S+) User logged in/) {
	} elsif ($ThisLine =~ /login: \S+ \w+ (\S+) /) { # plaintext doesn't log "User logged in"
	    $sievelogin++;
	    $sievemech{$1}++;
	} elsif ($ThisLine =~ /starttls/) {
	    $sieveTLS++;
	}
   } else {
      # Report any unmatched entries...
      push @OtherList, "$ThisLine\n";
   }
}

if ($LMTPStore) {
	print "   Mails stored: " . $LMTPStore . "\n";
}

if ($LMTPDiscard) {
	print "   Mails discarded (by sieve): " . $LMTPDiscard . "\n";
}

if ($LMTPDupelim) {
	print "   Duplicates suppressed: " . $LMTPDupelim . "\n";
}

if ($max_lockers) {
	print "   Maximum number of lockers: " . $max_lockers . "\n";
}

if ($Squatter) {
	print "   Mailboxes indexed by squatter: " . $Squatter . "\n";
}

# calculate average and maximal delay between accepting a connection and login
foreach $pid (sort numerically keys %process) {
        if ((defined $accepttime{$pid}) and (defined $logintime{$pid})) {
                my ($accepth, $acceptm, $accepts) = split /:/, $accepttime{$pid};
                my ($loginh, $loginm, $logins) = split /:/, $logintime{$pid};
                if ($accepth <= $loginh) { # don't deal with date change
                        $delay = ((3600 * $loginh ) + (60 * $loginm) + $logins) 
				- ((3600 * $accepth) + (60 * $acceptm) + $accepts);
                        if (($delay > 0) and ($delay < 120)) { # not perfect, but delays > 2m seem unlikely
                                print "PID: $pid, Delay: $delay\n" if $Detail >= 5;
                                $maxdelay = $delay if $delay > $maxdelay;
                                $delaysum += $delay;
                                $count++;
                        }
                }

        }
}

print "\n   Max delay between accept and login: $maxdelay seconds\n";
printf "   Average delay: %4.2f seconds\n", $delaysum/$count;

if ($POPlogin) {
	print "\n   POP:\n";
	print "     Number of logins: " . $POPlogin . "\n";
	foreach $mech (keys %POPmech) {
	    print "	... using " . $mech . ": " . "$POPmech{$mech}\n";
	}
	if ($POPbadlogin) {
	    print "\n     Number of failed logins: " . $POPbadlogin . "\n";
	}
	if ($POPTLS) {
	    print "\n     Number of sessions using TLS: " . $POPTLS . "\n";
	}
}

if ($IMAPlogin) {
	print "\n   IMAP:\n";
	print "     Number of logins: " . $IMAPlogin . "\n";
	foreach $mech (keys %IMAPmech) {
	    print "	... using " . $mech . ": " . "$IMAPmech{$mech}\n";
	}
	if ($IMAPbadlogin) {
	    print "\n     Number of failed logins: " . $IMAPbadlogin . "\n";
	}
	if ($IMAPTLS) {
	    print "\n     Number of sessions using TLS: " . $IMAPTLS . "\n";
	}
	if ($err_75) {
	    print "\n     Number of sessions aborted with error 75: " . $err_75 . "\n";
	}
}

if ($sievelogin) {
	print "\n   sieve:\n";
	print "     Number of logins: " . $sievelogin . "\n";
	foreach $mech (keys %sievemech) {
	    print "	... using " . $mech . ": " . "$sievemech{$mech}\n";
	}
	if ($sievebadlogin) {
	    print "\n     Number of failed logins: " . $sievebadlogin . "\n";
	}
	if ($sieveTLS) {
	    print "\n     Number of sessions using TLS: " . $sieveTLS . "\n";
	}
}


if ($#ErrorList >= 0) {
   print "\n**Error Messages**\n";
   print @ErrorList;
}

if ($#OtherList >= 0) {
   print "\n**Unmatched Entries**\n";
   print @OtherList;
}

exit(0);
