Blob Blame History Raw
###########################################################################
# $Id$
###########################################################################

###########################################################################
# This was written and is maintained by:
#    Stefan Jakobs <logwatch at localside.net>
#
# Please send all comments, suggestions, bug reports,
#    etc, to logwatch at localside.net.
###########################################################################
# Copyright (c) 2011 Stefan Jakobs
# Covered under the included MIT/X-Consortium License:
#    http://www.opensource.org/licenses/mit-license.php
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
###########################################################################

#use warnings;
use strict;

my $Detail 	= $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0;
my $Version	= "0.1-20110109";

# initialize logwatch variables 
my $ThisLine	= "";
my %OtherList 	= ();

# initialize variables which save the stats
my ($Starts,%Stops,$Reloads,$Crashs,$Upgrades);
my (%Warnings);
my (%RSSfeeds, %RSSerrors);
my (%SMTPclientRelay, %SMTPclientStats, %SMTPclientCMDS);
my (%SMTPclientDelivery, %SMTPclientBounce, %SMTPclientConnect);
my ($SMTPclient_queuerun, $SMTPclient_messages, $SMTPclientBounces) = (0, 0, 0);
my ($SMTPclientTime) = (0);
my (%SMTPserverStats, %SMTPserverRelay, %SMTPserverCMDS);
my (%SMTPserverHELO, %SMTPserverRCPT, %SMTPserverFROM);
my (%SMTPserverEval, %SMTPserverAuth, %SMTPSSLError);
my ($SMTPBytesAccepted, $SMTPAccepted, $SMTPRejected) = (0, 0 ,0, 0);
my ($SMTPserverStatsSum) = (0);
my ($serv_extnotify_queuerun) = (0);
my (%Threads);
my (%IMAPCmds, %IMAPexpunge, %IMAPUserLogin);
my ($IMAPCompletedCmds, $IMAPCmdDuration) = (0, 0);
my (%Ctdlcmds, %CtdlCleanup, %Ctdlqp_encode, %CtdlValRcpt, %CtdlMsgCorrupted);
my (%CtdlReplChecks, %CtdlAddContact, %CtdlFileOp, %CtdlGenerate);
my (%CtdlWriteFail);
my ($CtdlMsgDeleted) = (0);
my (@CtdlLogDeleted, @CtdlUserDeleted, @CtdlUserCreated, @CtdlRoomDeleted);
my ($NetProcessingTime, $NetProcessingCount) = (0, 0);
my (%NetStarts, %NetNodes, %NetNoConnect, %NetFilesRemoved);
my (%WebClientEngine, %WebClientHost, %WebLoginFailure, %WebUserLogin);
my ($SieveMsgID, $SieveName, $SieveStarts, $SieveNoProcessing);
my (%SieveMsg, %SieveExecute, %SieveProcFor);
my (%POPCmds, %POPClientConnects, %POPErrors, %POPUserLogin);
my ($POPCompletedCmds, $POPClientStarted, $POPClientEnded) = (0, 0, 0);
my ($POPClientFetched, $POPClientProcessTime) = (0, 0);
my (%POPDauth, %POPDCmds);
my (%SessionStarted);
my (%ClamdConnects);
my ($ClamdIP) = "";

# status variables
my ($NetNode, $NetHost);
my $Session = "";

### Functions ###

sub print_asterisks() {
   printf "\n  ******** Details (%2i) ***************************************\n", $Detail;
}

sub print_line() {
   print  "  --------   --------------------------------------------------\n";
}

sub print_doubleline() {
   print  "  ========   ==================================================\n\n";
}


sub print_hash($$) {
   my $out = "";
   my $sum = 0;
   my %hash = %{$_[0]};
   my $desc = $_[1];
   foreach my $item (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
      $out .= sprintf "  %8i      %s\n", $hash{$item}, $item;
      $sum += $hash{$item};
   }
   printf "\n  %8i   %-36s\n", $sum, $desc;
   if ($Detail > 4) {
      printf "$out";
   }
}

sub print_2xhash($$) {
   my $out = "";
   my $sum = 0;
   my %hash = %{$_[0]};
   my $desc = $_[1];
   foreach my $item (sort {$a cmp $b} keys %hash) {
         my $out2 = "";
         my $sum2 = "";
         foreach my $item2 (sort {$hash{$item}{$b} <=> $hash{$item}{$a}} keys %{$hash{$item}}) {
            $out2 .= sprintf "  %8i         %s\n", $hash{$item}{$item2}, $item2;
            $sum2 += $hash{$item}{$item2};
         }
         $out .= sprintf "  %8i      %s\n", $sum2, $item;
         $sum += $sum2;
         if ($Detail > 9) { $out .= $out2; }
      }
   printf "\n  %8i   %-36s\n", $sum, $desc;
   if ($Detail > 4) {
      printf "$out";
   }
}

sub print_3xhash($$) {
   my $out = "";
   my $sum = 0;
   my %hash = %{$_[0]};
   my $desc = $_[1];
   foreach my $item (sort {$a cmp  $b} keys %hash) {
         my $out2 = "";
         my $sum2 = 0;
         foreach my $item2 (sort {$a cmp $b} keys %{$hash{$item}}) {
            my $out3 = "";
            my $sum3 = 0;
            foreach my $item3 (sort {$hash{$item}{$item2}{$b} <=> $hash{$item}{$item2}{$a}} keys %{$hash{$item}{$item2}}) {
##         foreach my $nr (sort {$a cmp $b} keys %{$Threads{$action}{$thread}}) {
##            printf "\t\t%-40s: %5i Time(s)\n", $nr, $Threads{$action}{$thread}{$nr};
               $out3 .= sprintf "  %8i            %s\n", $hash{$item}{$item2}{$item3}, $item3;
               $sum3 += $hash{$item}{$item2}{$item3};
            }
            $out2 .= sprintf "  %8i         %s\n", $sum3, $item2;
            $sum2 += $sum3;
            if ($Detail > 9) { $out2 .= $out3; }
         }
         $out .= sprintf "  %8i      %s\n", $sum2, $item;
         $sum += $sum2;
         if ($Detail > 4) { $out .= $out2; }
      }
      printf "\n  %8i   %-36s\n", $sum, $desc;
      if ($Detail > 4) {
         printf "$out";
      }
}

### Parse the lines ###

while (defined($ThisLine = <STDIN>)) {
   chomp($ThisLine);

   # ignore general messages
   if ( ($ThisLine =~ /^-- db checkpoint --$/) ||
        ($ThisLine =~ /^\s*$/) ||
        ($ThisLine =~ /^This program is distributed under the terms/) ||
        ($ThisLine =~ /^Copyright \(C\) [-\d]+ by the Citadel/) ||
        ($ThisLine =~ /^(?:Sieve: )?libSieve is written and maintained by Aaron Stone/) ||
        ($ThisLine =~ /^(?:CC\[\d+\])?<.*> \d+ new of \d+ total messages$/) ||
        ($ThisLine =~ /^(?:TDAP_)?AdjRefCount\(\) msg -?\d+/) ||
        ($ThisLine =~ /^Closing the AdjRefCount queue file/) ||
        ($ThisLine =~ /^Closing (?:-\d+ )listener on/) ||
        ($ThisLine =~ /^Called as: /) ||
        ($ThisLine =~ /(?:zlib| libcurl|libssh2)\/\d/) ||
#        ($ThisLine =~ /^\[\d+\]\[.+\]/) ||  # this conflics with the web stats
        ($ThisLine =~ /^Caught signal 15; shutting down/) ||
        ($ThisLine =~ /^\$Id\$/) ||
        ($ThisLine =~ /^errno is \d+$/) ||
        ($ThisLine =~ /\[\d+\]\[\w+\(\d+\)\]\s+$/)
   ) {
      # ignore these lines
   }

   ### Start, Stop, Reload ###
   elsif ($ThisLine =~ /^\*\*\* Citadel server engine/) {
      $Starts++;
   }
   
   #TD: citserver: Exiting with status 15
   elsif ($ThisLine =~ /^citserver: Exiting with status (\d+)$/) {
      $Stops{$1}++;
   }

   #TD: Posting crash message 
   elsif ($ThisLine =~ /^Posting crash message$/) {
      $Crashs++;
   }

   #TD: upgrade
   elsif ($ThisLine =~ /^upgrade$/) {
      $Upgrades++;
   }

   ### Warnings (message) (reason)
   #TD: unable to change into directory [/var/run/citadel/]: No such file or directory
   elsif ($ThisLine =~ /^unable to change into directory \[(.*)\]: (.*)$/) {
      $Warnings{$2}{$1}++;
   }

   #TD: citserver: unable to lock /var/lib/citadel/data/citadel.control.
   elsif ($ThisLine =~ /^citserver: (unable to lock .+)\.$/ ) {
      $Warnings{$1}{""}++
   }

   #TD: Is another citserver already running?
   elsif ($ThisLine =~ /^(Is another citserver already running\?)$/ ) {
      $Warnings{$1}{""}++;
   }

   #TD: This message has a zero length.  Probable data corruption.
   elsif ($ThisLine =~ /^(This message has a zero length)\. {1,2}(Probable data corruption)\.$/) {
      $Warnings{$2}{$1}++;
   }

   #TD: pthread_create() : Cannot allocate memory
   elsif ($ThisLine =~ /^pthread_create\(\) : (Cannot allocate memory)$/) {
      $Warnings{$1}{""}++;
   }

   ### Thread processing ###
   elsif ( ($ThisLine =~ /^\S+_thread\(\) exiting/) ||
           ($ThisLine =~ /^Garbage collection on own thread/) ||
           ($ThisLine =~ /^Startup thread \d+ becoming garbage collector/) ||
           ($ThisLine =~ /^Interrupted CtdlThreadSelect/) 
   ) {
      # ignore lines
   }
   
   elsif ($ThisLine =~ /^(?:Thread|Created a new thread|Garbage Collection for thread|Waiting for thread)/) {

      if ( ($ThisLine =~ /^Thread system stopping thread/) ||
           ($ThisLine =~ /^Waiting for thread/) ||
           ($ThisLine =~ /^Thread .* caught signal/)
      ) {
         # ignore these lines
      } 

      #TD: Created a new thread "SMTP Send" (0x40504950).
      elsif ($ThisLine =~ /Created a new thread "(.+)" \(([x0-9a-fA-F]+)\)/) {
         $Threads{"created"}{$1}{$2}++;
      }

      #TD: Thread "SMTP Send" (0x40504950) exited.
      #TD: Thread "RSS Client" (0x40605950) exited.
      elsif ($ThisLine =~ /^Thread "(.+)" \(([x0-9a-fA-F]+)\) exited/) {
         $Threads{"exited"}{$1}{$2}++;
      }

      #TD: Garbage Collection for thread "RSS Client" (0x40605950).
      elsif ($ThisLine =~ /^Garbage Collection for thread "(.+)" \(([x0-9a-fA-F]+)\)/) {
         $Threads{"garbage collection"}{$1}{$2}++;
      }

      else {
         # Report any unmatched entries...
         chomp($ThisLine);
         $OtherList{$ThisLine}++;
      }
   }

   ### Sessions ###
   elsif ( ($ThisLine =~ /^\[[ \d]+\] Session ended/) ||
           ($ThisLine =~ /^Context: \[[ \d]+\]SRV\[(?:citadel-(?:TCP|UDS)|CitNetworker|rssclient|POP1aggr|POP3|POP3S|DICT_TCP|IMAP|IMAPS|LMTP|SMTP_Send|SMTP-MSA)\] Session ended/) ||
           ($ThisLine =~ /^C(?:itadel c)?lient disconnected: ending session\.$/) ||
           ($ThisLine =~ /^New client socket \d+$/) ||
           ($ThisLine =~ /^Terminated \d+ idle sessions$/) ||
           ($ThisLine =~ /^Context: Scheduled \d+ idle sessions for termination$/) ||
           ($ThisLine =~ /^Context: Flushed \d+ stuck sessions$/) ||
           ($ThisLine =~ /^Context: terminate_all_sessions\(\) is murdering/) ||
           ($ThisLine =~ /^(Modules: )?\[(?:citadel-TCP|DICT_TCP)\] closing service$/) 
   ) {
      # ignore these lines
   }
   #TD: Session (IMAPS) started from myhost (192.168.36.150)
   #TD: Session (citadel-TCP) started from localhost.localdomain (127.0.0.1).
   #TD: Session (LMTP) started via local socket UID:101.
   elsif ($ThisLine =~ /^Session \(([\w-]+)\) started (?:from (\S+) \(([\da-fA-F.:]+)\)|via (local socket) (UID:-?\d+))/) { 
      $SessionStarted{$1}{"$2$4 [$3$5]"}++;
      $Session = "$1";
   }


   ### RSS feed processing ###
   elsif ( ($ThisLine =~ /^\S+ has already been seen/) ||
           ($ThisLine =~ /^RSS: XML Status \[\(null\)\]/) ||
           ($ThisLine =~ /^RSS: This is an (?:RSS|RDF) feed/) ||
           ($ThisLine =~ /^RSS: saving item/) ||
           ($ThisLine =~ /^rssclient (?:started|ended)/)
   ) {
      # ignore these lines
   }

   #TD: Fetching RSS feed <http://www.heise.de/open/news/news.rdf>
   elsif ($ThisLine =~ /Fetching RSS feed <(\S+)>/) {
      $RSSfeeds{$1}++;
   }

   #TD: IO[968]CC[968][72]RSSPneed a 200, got a 302 !
   elsif ($ThisLine =~ /^IO\[[ \d]+\]CC\[[ \d]+\]\[\d+\](RSS.?need a \d+, got a \d+)/) {
      $RSSerrors{"$1 (libcurl too old?)"}++;
   }

   ### serv_something processing ###
   elsif ($ThisLine =~ /^serv_extnotify: queue run completed/)
   {
      # ignore these lines
   }

   elsif ($ThisLine =~ /^serv_extnotify: processing notify queue/) {
      $serv_extnotify_queuerun++;
   }


   ### SMTP Client ###
   elsif ( ($ThisLine =~ /^SMTP(?:CQ| client): processing outbound queue/) ||
           ($ThisLine =~ /^SMTP client: smtp_do_procmsg\(\d+\)$/) ||
           ($ThisLine =~ /^SMTP(?:C| client): Trying <.*>/) ||
           ($ThisLine =~ /^SMTP client: Attempting delivery to /) ||
           ($ThisLine =~ /^SMTP client: connected!/) ||
           ($ThisLine =~ /Number of MX hosts for /) ||
           ($ThisLine =~ /^<?\d{3} \w/) ||
           ($ThisLine =~ /^smtp_do_bounce\(\) called$/) ||
           ($ThisLine =~ /^key=<(?:msgid|submitted)?> addr=<.*> status=\d+ dsn=<.*>$/) ||
           ($ThisLine =~ /^Done processing bounces$/) ||
           ($ThisLine =~ /^SMTPCQ: Msg No \d+: already in progress!$/) 
   ) {
      # ignore these lines
   }

   #TD: SMTP client: connecting to localhost : 25 ...
   #TD: SMTPC:IO[1025]CC[1025]S[198261][0] connecting to localhost [127.0.0.1]:25 ...
   elsif ($ThisLine =~ /^SMTP(?:C| client):(?:IO\[[ \d]+\]CC\[\d+\]S\[\d+\]\[\d+\])? connecting to (\S+) (?:\[[\.:\da-fA-F]*\])?: ?(\d+)/) {
      $SMTPclientConnect{"$1:$2"}++;
   }

   #TD: >EHLO valaskjalf.localside.net
   #TD: >MAIL FROM:<stefan@localside.net>
   #TD: >QUIT
   elsif ($ThisLine =~ /^>([A-Z ]+)(?::<(.+)>)?/) {
      $SMTPclientCMDS{$1}{$2}++;
   }
 
   #TD: SMTP client: delivery to <useraddr> @ <gmail.com> (user) succeeded
   elsif ($ThisLine =~ /SMTP client: delivery to <(.*)> @ <(.*)> \(.*\) (\w+)$/) {
      $SMTPclientDelivery{$3}{$2}{$1}++;
   }

   #TD: SMTPC:IO[1025]CC[1025]S[198261][0] Delivery successful. Time[19.466858s] Recipient <a> @ <example.net> (name) Status message: 2.0.0 from MTA(smtp:[127.0.0.1]:10025): 250 2.0.0 Ok: queued as A61BE66B8008
   elsif ($ThisLine =~ /^SMTPC:IO\[[ \d]+\]CC\[\d+\]S\[\d+\]\[\d+\] Delivery ([^.]+)\. Time\[([\d\.]+)s\] Recipient <(.*)> @ <(.*)> \(.*\) Status message: ([\d\.]{3,5}|[\s\w]+)/) {
      $SMTPclientDelivery{$1}{$4}{$3}++;
      $SMTPclientStats{$5}++;
      $SMTPclientTime+=$2;
   }

   #TD: 108319: to=<user@gmail.com>, relay=localhost, stat=2.0.0 Ok, id=10168-06, from MTA([127.0.0.1]:10025): 250 2.0.0 Ok: queued as 2901498D0082
   elsif ($ThisLine =~ /^\d+: to=<(.*)>, relay=(\S*), stat=([\d\.]{3,5}.*?),/) {
      if ($2 != "") { $SMTPclientRelay{$2}{$1}++; }
      $SMTPclientStats{$3}++;
   }

   #TD: key=<bounceto> addr=<User@valaskjalf> status=0 dsn=<>
   elsif ($ThisLine =~ /^key=<bounceto> addr=<(.*)> status=(\d+) dsn=<(.*)>$/) {
      $SMTPclientBounce{$1}{"$2: $3"}++;
   }

   #TD: num_bounces = 0
   elsif ($ThisLine =~ /^num_bounces = (\d+)$/) {
      $SMTPclientBounces += $1;
   }

   #TD: SMTPCQ: queue run completed; 1 messages processed 1 activated
   elsif ($ThisLine =~ /^(?:SMTPCQ|SMTP client): queue run completed; (\d+) messages processed/) {
      $SMTPclient_queuerun++;
      $SMTPclient_messages += $1;
   }

   ### SMTP Server ###
   elsif ( ($ThisLine =~ /^Directory key is <.*>$/) ||
           ($ThisLine =~ /is being forwarded to/) ||
           ($ThisLine =~ /^[:\[] get \S*\]?$/) ||
           ($ThisLine =~ /^<\d{3}[ -]\w+/) ||
           ($ThisLine =~ /^SSL\/TLS using /) ||
           ($ThisLine =~ /Ending SSL\/TLS$/) ||
           ($ThisLine =~ /^(?:Performing|Finished) SMTP cleanup hook$/) ||
           ($ThisLine =~ /^SMTP module clean up for shutdown/) ||
           ($ThisLine =~ /^(Modules: )?\[LMTP(?:-UnF)?\] Closed UNIX domain socket/) ||
           ($ThisLine =~ /^(Modules: )?\[SMTPs?-(?:MTA|MSA)\] closing service$/) ||
           ($ThisLine =~ /^SMTP: client disconnected: ending session\.$/) ||
           ($ThisLine =~ /^sending \d+ [A-Z]+ for the room/) 	# this belongs to validate_recipients()
   ) {
      # ignore these lines
   }

   elsif ($ThisLine =~ /^SMTP server:/) {

      #TD: SMTP server: LHLO vs243073.vserver.de
      if ($ThisLine =~ /^SMTP server: (?:LHLO|HELO|EHLO) (\S+)/i) {
         $SMTPserverHELO{$1}++;
      }
 
      #TD: SMTP server: RCPT TO:<room_spamassassin-user@localside.net>
      elsif ($ThisLine =~ /^SMTP server: RCPT TO: *<?([^\s<>]+)>?$/i) {
         $SMTPserverRCPT{$1}++;
      }

      #TD: SMTP server: MAIL FROM:<users@spamassassin.apache.org> SIZE=2982 BODY=7BIT
      elsif ($ThisLine =~ /^SMTP server: MAIL FROM: *<?([^\s<>]*)>?(?: SIZE=(\d+))?/i) {
         $SMTPserverFROM{$1}++;
         $SMTPBytesAccepted += $2;
      }

      #TD: SMTP server: DATA
      elsif ($ThisLine =~ /^SMTP server: (DATA|QUIT|STARTTLS|AUTH PLAIN|RSET)/i) {
         $SMTPserverCMDS{$1}++;
      }

      else {
         # Report any unmatched entries...
         chomp($ThisLine);
         $OtherList{$ThisLine}++;
      }
   }

   #TD: SMTP authenticated Stefan
   elsif ($ThisLine =~ /^SMTP authenticated (.*)$/) {
      $SMTPserverAuth{$1}++;
   }

   #TD: 108347: from=<postfix@postfix.org>, nrcpts=1, relay= [], stat=250 Message accepted
   elsif ($ThisLine =~ /^\d+: from=<(.*)>, nrcpts=(\d+), relay=(.*) \[(\S*)\], stat=(\d{3})(.*)\./) {
##      $SMTPserverNumRCPTs += $2;
      if ($4 != "") { $SMTPserverRelay{"$4 ($3)"}{$1}++; }
      $SMTPserverStats{$5}{$6}++;
      $SMTPserverStatsSum++;
      if ($5 =~ /^2\d\d$/) { $SMTPAccepted++; }
      if ($5 =~ /^[45]\d\d$/) { $SMTPRejected++; }
   }

   #TD: Evaluating recipient #0: stefan@localside.net
   elsif ($ThisLine =~ /^Evaluating recipient #\d+: (?:.*<)?([^\s<>]+)>?$/) {
      $SMTPserverEval{$1}++;
   }

   #TD: SSL_read got error 5
   #TD: SSL_accept failed: retval=0, errval=5, err=error:00000005:lib(0):func(0):DH lib
   elsif ($ThisLine =~ /^SSL_(\S+) (?:got error |failed: retval=[-0-9]+, errval=)(\d+)/) {
      $SMTPSSLError{$1}{$2}++;
   }

   ### IMAP processing ###
   elsif ( ($ThisLine =~ /^\(That translates to/) ||
           ($ThisLine =~ /^imap_do_expunge\(\) called/) ||
           ($ThisLine =~ /^[\w ]+ already exists\.$/) ||
           ($ThisLine =~ /^(?:before| after) update:/) ||
           ($ThisLine =~ /^(?:Performing|Finished) IMAP cleanup hook$/) ||
           ($ThisLine =~ /^Section is: \[.*\]/) || 
           ($ThisLine =~ /^Converting CRLF to LF$/) ||
           ($ThisLine =~ /^IMAP(?:CC\[[ \d]+\]|:) client disconnected: ending session/) ||
           ($ThisLine =~ /^(Modules: )?\[IMAPS?\] closing service$/)
   ) {
      # ignore these lines
   }
 
   elsif ($ThisLine =~ /^IMAP/) {
   
      if ( ($ThisLine =~ /^IMAP(?:CC\[[ \d]+\]|:) <plain_auth>$/) ||
           ($ThisLine =~ /^IMAPCC\[[ \d]+\] not subordinate to inbox$/) 

      ) {
         # ignore these lines
      }
   # improve: IMAPCmdDuration per Command.
      #TD: IMAP command completed in 0.1437 seconds
      elsif ($ThisLine =~ /^IMAP command completed in (\d+\.\d+) seconds/)
      {
         $IMAPCompletedCmds++;
         $IMAPCmdDuration += $1;
      }

      #TD: IMAP: 10117 NOOP
      #TD: IMAP: 10120 LIST "" "Server Level/%"
      #TD: IMAP: a003 LOGOUT
      elsif ($ThisLine =~ /^IMAP: \w?\d+ ([A-Z ]+)/) {
         $IMAPCmds{$1}++;
      }
  
      #TD: IMAP: LOGIN...
      #TD: IMAPCC[51121] LOGIN...
      elsif ($ThisLine =~ /^IMAP(?:CC\[[ \d]+\]|:) (LOGIN)/) {
         $IMAPCmds{$1}++;
      }

      else {
         # Report any unmatched entries...
         chomp($ThisLine);
         $OtherList{$ThisLine}++;
      }
   }

   #TD: <Stefan> logged in
   #TD: Context: <Stefan> logged in
   elsif ($ThisLine =~ /^(?:Context: )?<(.+)> logged in$/) {
      # this can be a POP or IMAP login
      my $user = lc($1);
      if ($Session =~ /^IMAPS?$/) {
         $IMAPUserLogin{$user}++;
      } elsif ($Session =~ /^POPS?/) {
         $POPUserLogin{$user}++;
      } elsif ($Session =~ /^(?:citadel-TCP|LMTP)$/) { #ignore 
      } else { 
         chomp($ThisLine);
         $OtherList{$ThisLine}++;
      }
      # $Session = ""; ## this may be wrong
   }

   #TD: Expunged 0 messages from <SpamAssassin-user>
   elsif ($ThisLine =~ /^Expunged (\d+) messages from <(.+)>/) {
      if ($1 > 0) { $IMAPexpunge{$2} += $1; } 
   }

   ### Citadel internal processing ###
   elsif ( ($ThisLine =~ /^Selected room/) ||
           ($ThisLine =~ /^Performing before-save hooks$/) ||
           ($ThisLine =~ /^Skipping hooks$/) ||
           ($ThisLine =~ /^Saving to disk$/) ||
           ($ThisLine =~ /^Creating MetaData record$/) ||
           ($ThisLine =~ /^Storing pointers$/) ||
           ($ThisLine =~ /^Updating user$/) ||
           ($ThisLine =~ /^\d+ unique messages to be merged$/) ||
           ($ThisLine =~ /^Performing room hooks for <.+>$/) ||
           ($ThisLine =~ /^Performing after-save hooks$/) ||
           ($ThisLine =~ /^Wordbreaking message \d+/) ||
           ($ThisLine =~ /^Purge use table: /) ||
           ($ThisLine =~ /^Purge (?:euid|EUID) index: /) ||
           ($ThisLine =~ /^dead_session_purge\(\): purging session/) ||
           ($ThisLine =~ /^RemoveContext\([\w-]+\) session/) ||
           ($ThisLine =~ /^---- Looking up \[[A-Z]+\] -----$/) ||
           ($ThisLine =~ /^(?:CC\[[ \d]+\])?Changed to <.*>$/) ||
           ($ThisLine =~ /^do_fulltext_indexing\(\)/) ||
           ($ThisLine =~ /^Indexed \d+ of \d+ messages/) ||
           ($ThisLine =~ /^ft_index_message\(\) (?:adding|removing) msg/) ||
           ($ThisLine =~ /^fixed_output(?:_pre|_post)?\(\) (?:part|type)/) ||
           ($ThisLine =~ /^Skipping part \d+/) ||
           ($ThisLine =~ /^Indexing message \d+ \[\d+ tokens\]/) || 
           ($ThisLine =~ /^Indexing message #\d+ <.*>/) ||
           ($ThisLine =~ /^Flush(?:ing|ed) index cache to disk/) ||
           ($ThisLine =~ /^Delivering to room <.*>$/) ||
           ($ThisLine =~ /^Returning to original room/) ||
           ($ThisLine =~ /^(?:Context: )?User \d+ maps to/) ||
           ($ThisLine =~ /^Auto-purger: (?:starting|finished)/) ||
           ($ThisLine =~ /^Auto purger found a user \d+ with name <.*>$/) ||
           ($ThisLine =~ /^Delivering private local mail to <.*>$/) ||
           ($ThisLine =~ /^(?:CC\[[ \d]+\])?Final selection: /) ||
           ($ThisLine =~ /^Processed \d+ message reference count adjustments/) ||
           ($ThisLine =~ /^Generating delivery instructions$/) ||
           ($ThisLine =~ /^Initializing (?:server extensions|ipgm secret)/) ||
           ($ThisLine =~ /^Seeding the pseudo-random number generator/) ||
	   ($ThisLine =~ /^Registered (?:server command|a new .+ function)/) ||
           ($ThisLine =~ /^Creating base rooms/) ||
           ($ThisLine =~ /^Floor \d{1,3}: \d+ rooms/) || 
           ($ThisLine =~ /^Server-hosted upgrade level is/) || 
           ### report this as status ???
           ($ThisLine =~ /^(?:Sieve: )?Extensions:/) || 
           ($ThisLine =~ /registered\.$/) ||
           ($ThisLine =~ /^Initiali(?:s|z)e modules, /) ||
           ($ThisLine =~ /(?:_thread|startup|databases)\(\) (?:initializing|finished|started)/) ||
           ($ThisLine =~ /^(?:Modules: )?Service \S+ has been manually disabled, skipping/) || 
           ($ThisLine =~ /^Checking floor reference counts$/) ||
           ($ThisLine =~ /^Changing uid to \d+$/) ||
           ### report these files ???
           ($ThisLine =~ /^Loading (?:citadel.config|\S+\/public_clients)$/) ||
           ($ThisLine =~ /^Checking\/re-building control record$/) ||
           ($ThisLine =~ /^Acquiring control record$/) ||
           ($ThisLine =~ /^Path is /) ||
           ($ThisLine =~ /^Cleaning up fulltext noise words\.$/) ||
           ($ThisLine =~ /^Saving calendar UID <\S+>$/) ||
           ($ThisLine =~ /^Raw length is \d+$/) ||
           ($ThisLine =~ /^Allocating$/) ||
           ($ThisLine =~ /Server hosted updates need to be processed at this time/) ||
           ($ThisLine =~ /^User (?:0|-\d+) not found$/) ||
           ($ThisLine =~ /^Deleted \d+ other msgs of this type$/) ||
           ($ThisLine =~ /^diff length is \d+ bytes$/) ||
           ($ThisLine =~ /^diff cmd/) ||
           ($ThisLine =~ /^History page not being historied$/) ||
           ($ThisLine =~ /^UID of vNote is: [0-9a-f-]+$/) ||
           ($ThisLine =~ /^Part \d+ contains a vNote/) ||
           ($ThisLine =~ /^\.\.\. yes its local\.$/) ||
           ($ThisLine =~ /^Loaded module: /) ||
           ($ThisLine =~ /^Waiting \d+ seconds for \d+ worker threads to exit$/) ||
           ($ThisLine =~ /^Existing database version on disk is/) ||
           ($ThisLine =~ /^Upgrad(?:ing|e) modules/) || 
           ($ThisLine =~ /^Modules: (?:upgrade|Upgrade modules|Destroyed service)/) ||
           ### report this as warning ???
           ($ThisLine =~ /^Modules: Initializing\. CtdlThreads not yet enabled\.$/) ||
           ($ThisLine =~ /^libcitadel\(unnumbered\)$/) ||
           ($ThisLine =~ /^(Modules: )?\[citadel-UDS\] Closed UNIX domain socket/) ||
           ($ThisLine =~ /^Found\.$/) ||
           ($ThisLine =~ /^No external notifiers configured on system\/user$/)
   ) {
      # ignore these lines
   }

   #TD: validate_recipients()
   #TD:  local: 1 <Stefan>
   #TD:   room: 0 <>
   #TD:   inet: 0 <>
   #TD:  ignet: 0 <>
   #TD:  error: -1 <No recipient specified>
   elsif ($ThisLine =~ /^(validate_recipients)\(\)$/) {
      $Ctdlcmds{$1}++;
   }

   elsif ($ThisLine =~ /^\s{1,2}(local|room|inet|ignet|error): -?(\d+) <(.*)>$/) {
      if ($2 > 0) { $CtdlValRcpt{$1}{$3} += $2; }
   }

   #TD: Deleting message <103425>
   elsif ($ThisLine =~ /Deleting message <(\d+)>$/) {
      $CtdlMsgDeleted++; 
   }
   #TD: 7 message(s) deleted.
   elsif ($ThisLine =~ /(\d+) message\(s\) deleted\.$/) {  # This belongs to CtdlDeleteMessages
      if ($1 > 0) { $CtdlMsgDeleted += $1; }
   }

   #TD: Expired 147 messages.
   #TD: Expired 0 rooms.
   #TD: Purged 0 visits.
   elsif ($ThisLine =~ /^(Expired|Purged) (\d+) (messages|rooms|visits|users|entries from the EUID index|stale OpenID associations|entries from the use table)/) {
      $CtdlCleanup{$1}{$3} += $2;
   }

   #TD: Context: New user <gh> created
   elsif ($ThisLine =~ /^(?:Context: )?New user <(.*)> created$/) {
      push(@CtdlUserCreated, $1);
   }

   #TD: Deleting log: /var/lib/citadel/data/log.0000004270
   elsif ($ThisLine =~ /Deleting log: (.*)$/) {
      push(@CtdlLogDeleted, $1);
   }

   #TD: Deleting user <SYS_Citadel> 
   #TD: Context: Deleting user <SYS_Citadel>
   elsif ($ThisLine =~ /^(?:Context: )?Deleting user <(.*)>$/) {
      push(@CtdlUserDeleted, $1);
   } 

   #TD: Deleting room <00000000033.Tasks>
   elsif ($ThisLine =~ /^(?:Context: )?Deleting room <(.*)>$/) {
      push(@CtdlRoomDeleted, $1);
   }

   #TD: PurgeMessages() called
   elsif ($ThisLine =~ /(Purge(?:Messages|Rooms|Users))\(\) called$/) {
      $Ctdlcmds{$1}++;
   }

   #TD: qp_encode_email_addrs: [postfix-users@postfix.org]
   elsif ($ThisLine =~ /qp_encode_email_addrs: \[(.+)\]$/) {
      $Ctdlqp_encode{$1}++;
   }

   #TD: Message 0 appears to be corrupted
   elsif ($ThisLine =~ /Message (\d+) appears to be corrupted/) {
      $CtdlMsgCorrupted{$1}++;
   }

   #TD: Performing replication checks in <Global Address Book>
   elsif ($ThisLine =~ /Performing replication checks in <(.+)>$/) {
      $CtdlReplChecks{$1}{$2}++;
   }

   #TD: Adding contact: "Full Name" <user@example.com>
   elsif ($ThisLine =~ /Adding contact: (.*)$/) {
      $CtdlAddContact{$1}++;  
   } 

   elsif ($ThisLine =~ /^Ctdl/) {

      if ($ThisLine =~ /^$/) 
      {
         # ignore these lines
      }

      # ToDo: This can be done better
      #TD: CtdlFetchMessage(108265, 1)
      #TD: CtdlOutputPreLoadedMsg(TheMessage=not null, 1, 0, 0, 1
      #TD: CtdlDeleteMessages(SpamAssassin-user, 1 msgs, )
      #TD: CtdlCreateRoom(name=Contacts, type=4, view=2)
      #    Contacts already exists.
      elsif ($ThisLine =~ /^Ctdl(\w+)\(/) {
         $Ctdlcmds{$1}++;
      }

   }

   #TD: chmod(/srv/citadel/data//cdb.03, 0600) returned 0
   #TD: chown(/var/lib/citadel/data//cdb.08, CTDLUID, -1) returned 0
   elsif ($ThisLine =~ /^(chown|chmod)\((.*), [A-Z0-9]+(?:, -?\d+)?\) returned (\d+)/) {
      $CtdlFileOp{$1}{$2}++;
   }

   #TD: Generating a self-signed certificate.
   #TD: Generating RSA key pair.
   elsif ($ThisLine =~ /^Generating(?: a)? (.+)\.$/ ) {
      $CtdlGenerate{$1}++;
   }

   #TD: client_write(13 bytes) failed: Broken pipe (32)
   elsif ($ThisLine =~ /^client_write\((\d+) bytes\) failed: (.+)$/) {
      $CtdlWriteFail{$2} += $1;
   }

   ### IGnet Networking ###
   elsif ( ($ThisLine =~ /^network: (?:running|loading) outbound queue$/) ||
           ($ThisLine =~ /^network: (?:nothing in|processing) inbound queue/) ||
           ($ThisLine =~ /^network: queue run completed/) ||
           ($ThisLine =~ /^network: polling/) ||
           ($ThisLine =~ /^NW\[\w*\]\[\d+\]: polling/) ||
           ($ThisLine =~ /^>[0-9]{3} \w* (?:Citadel|as network|talking to)/) ||
           ($ThisLine =~ /^nttlist=</) ||
           ($ThisLine =~ /^Compare <\S+> to <\S+>$/) ||
           ($ThisLine =~ /^network_process_buffer\(\) processing \d+ bytes$/) ||
           ($ThisLine =~ /^Expecting to transfer \d+ bytes$/) ||
           ($ThisLine =~ /^network_usetable\(\) : /) ||
           ($ThisLine =~ /^(?:CC\[[ \d]+\])?(?:Not )?(?:s|S)ending to \S+$/) ||
           ($ThisLine =~ /^(?:Appending to|Consolidate) \//) ||
           ($ThisLine =~ /^(?:CC\[[ \d]+\])?Invalid node (?:name )?<\S+>$/) ||
           ($ThisLine =~ /^(?:CC\[[ \d]+\])?skipping Spoolcleanup because of \d+ files unprocessed\.$/)
   ) {
      # ignore these lines
   }

   #TD: Networking started for <0000000007.Mail>
   elsif ($ThisLine =~ /^Networking started for <(.+)>$/) {
      $NetStarts{$1}++;
   }

   #TD: Network full processing in 1021 seconds.
   elsif ($ThisLine =~ /^Network full processing in (\d+) seconds/) {
      $NetProcessingCount++;
      $NetProcessingTime += $1;
   }

   #TD: Network node <valaskjalf> logged in from example.com [10.0.0.1]
   elsif ($ThisLine =~ /^Network node <(.+)> logged in from (.*)/) {
      $NetNodes{"Logins from"}{$1}{$2}++;
   }

   #TD: Connecting to <valaskjalf> at example.com:504
   #TD: IO[13221]CC[13221]NW[genux][8624]Connecting to <genux> at example.com:504
   elsif ($ThisLine =~ /^(?:IO\[\d+\]CC\[\d+\]NW\[\w+\]\[\d+\])?Connecting to <(.+)> at (.*)/) {
      $NetNodes{"Connects to"}{$1}{$2}++;
      $NetNode=$1;
      $NetHost=$2;
   }

   #TD: Sent 0 octets to <valaskjalf> 
   #TD: IO[49977]CC[49977]NW[valaskjalf][49972]Sent 0 octets to <valaskjalf>
   elsif ($ThisLine =~ /^(?:IO\[\d+\]CC\[\d+\]NW\[\w+\]\[\d+\])?Sent (\d+) octets to <(.+)>/) {
      $NetNodes{"Bytes sent to"}{$2}{""} += $1;
   }

   #TD: Can't connect to example.com:504: Connection timed out
   elsif ($ThisLine =~ /^Can't connect to (.+): (.+)$/) {
      $NetNoConnect{"Can't connect to"}{$1}{$2}++;
   }

   #TD: connect() failed: Connection timed out
   elsif ($ThisLine =~ /^connect\(\) failed: Connection timed out$/) {
      $NetNoConnect{"Can't connect to"}{$NetNode}{$NetHost}++;
   }

   #TD: Can't get example.com host entry: Connection timed out
   elsif ($ThisLine =~ /^Can't get (.+) host entry: (.+)$/) {
      $NetNoConnect{"Can't get host entry"}{$1}{$2}++;
   }

   #TD: EVCURL:IO[4515]CC[4515] error description: Failed to connect to 2a00:1450:8005::79: Network is unreachable
   elsif ($ThisLine =~ /^EVCURL:IO\[[ \d]+\]CC\[\d+\] error description: (Failed (?:to )?connect to) (.*)(?::|;) (.*)/) {
      $NetNoConnect{"$3"}{$1}{$2}++;
      $NetNode=$1;
      $NetHost=$2;
   }

   #TD: EVCURL:IO[4515]CC[4515] error description: Recv failure: Connection reset by peer
   #TD: EVCURL:IO[2152]CC[2752] error description: The requested URL returned error: 404
   elsif ($ThisLine =~ /^EVCURL:IO\[[ \d]+\]CC\[\d+\] error description: (Recv failure: |Couldn't resolve host |Empty reply from server|The requested URL returned error: )(.*)/) {
      $NetNoConnect{"$2"}{"$1"}{""}++;
   }

   #TD: EVCURL:IO[4515]CC[4515] error performing request: Couldn't connect to server
   elsif ($ThisLine =~ /^EVCURL:IO\[[ \d]+\]CC\[\d+\] error performing request: (.*)/) {
      $NetNoConnect{"$1"}{$NetNode}{$NetHost}++;
      # That isn't correct !!!
   }

   #TD: network: processing 0 bytes from /var/spool/citadel/network/spoolin//genux.0e73.012a
   #TD: CC[0]network: processing 426944 bytes from /var/spool/citadel/network/spoolin//wk.64c1.000a
   elsif ($ThisLine =~ /^(?:CC\[[ \d]+\])?network: processing (\d+) bytes from \/\S+\/spool\/citadel\/network\/spoolin\/+(\w+)\.[\.0-9a-f]+$/) {
      $NetNodes{"Bytes download from"}{$2}{""} += $1
   }

   #TD: Duplicate session for network node <genux>
   elsif ($ThisLine =~ /^Duplicate session for network node <(\S+)>$/) {
      $NetNodes{"Duplicate session"}{$1}{""}++;
   }

   #TD: IO[50917]CC[50917]NW[wk][31235]Already talking to wk; skipping this time.
   elsif ($ThisLine =~ /^IO\[[ \d]+\]CC\[\d+\]NW\[(\w+)\]\[\d+\]Already talking to \w+; skipping this time\.$/) {
      $NetNodes{"Already talking to"}{$1}{""}++;
   }

   #TD: Removing </var/spool/citadel/network/spoolout//valaskjalf>
   elsif ($ThisLine =~ /^Removing <(\S+)>$/) {
      $NetFilesRemoved{$1}++;
   }

   #TD: >540 authentication failed
   elsif ($ThisLine =~ /^>5\d\d authentication failed$/) {
      $NetNodes{"Authentication failed to"}{$NetNode}{$NetHost}++;
   }

   #TD: An unknown Citadel server called "node" attempted to connect from name [1.1.1.1].
   elsif ($ThisLine =~ /^An unknown Citadel server called "(\S*)" attempted to connect from (\S*) \[(\S+)\]/) {
      $NetNodes{"Attempted connects"}{$1}{"$2 [$3]"}++;
   }

   #TD: Connected to node "" but I was expecting to connect to node "node".
   elsif ($ThisLine =~ /^Connected to node "(\S*)" but I was expecting to connect to node "(\S*)"/) {
      $NetNodes{"Connects to server with wrong node name"}{"$2 (as $1)"}{""}++;
   }

   #TD: A Citadel server at example.com [10.0.0.1] failed to authenticate as network node "node".
   elsif ($ThisLine =~ /A Citadel server at (\S+) \[(\S+)\] failed to authenticate as network node "(\S*)"/) {
      $NetNodes{"Authentication failed from"}{$3}{"$1 [$2]"}++;
   }

   ### web access ###
   elsif ( ($ThisLine =~ /^New client socket \d+/) ||
           ($ThisLine =~ /^Closing socket -?\d+/) ||
           ($ThisLine =~ /^Checking whether [0-9a-fA-F:.]+ is a local or public client/) ||
           ($ThisLine =~ /^\.\.\. yes it is/) ||
           ($ThisLine =~ /^Looking up hostname '/) ||
           ($ThisLine =~ /^Client \d\/\d\/[\d.]+ \(.*\)/) ||
           ($ThisLine =~ /^(?:\[\d+\]\[.*\] )?<password command hidden from log>$/) ||
           ($ThisLine =~ /^cmd_user\(\S+\)$/) ||
           ($ThisLine =~ /^username: /) ||
           ($ThisLine =~ /^Setting chosen part: <[\.\d]+>$/) ||
           # ToDo: these are commands from webcit, count them ??
           ($ThisLine =~ /^(?:\[\d+\]\[.*\] )?(?:ICAL|INFO|MSGP|QUIT|GOTO|MSGS|MSG\d|EUID|MESG|CHEK|READ|OPEN|SEEN|NOOP|DLAT|RDIR|MOVE|OIMG|NDOP|NETP|GTSN|LKR[AN]|LFLR|RWHO|CLOS|UCLS|SLRP|TIME|NUOP|RINF|IPGM|DOWN|UOPN|ENT0|DELF|WRIT|GVSN|GVEA|OPNA|GPEX|SPEX|AUTO|GNUR|LIST|ECHO|CONF|CREU|DELE|GNET|LOUT|AGUP|LBIO|ASUP|ISME|SEXP|GEXP|RBIO|NEWU|GREG|VIEW|CULL|GETR|MSIV|GETA|WHOK|MRTG|RCHT)/i) ||
           ($ThisLine =~ /^Done with RemoveContext\(\)/) ||
           ($ThisLine =~ /^RemoveContext\(\) session/) ||
           ($ThisLine =~ /^Purging session \d+/) ||
           ($ThisLine =~ /^Searching for EUID/) || 
           ($ThisLine =~ /^returning msgnum = -?\d+/) 
   ) {
      # ignore these lines
   }

   #TD: [3115][(not logged in)(0)] IDEN 0|4|814||::ffff:72.52.147.163
   elsif ($ThisLine =~ /^(?:\[\d+\]\[.*\] )?IDEN \d\|\d\|\d+\|(.*)\|(.*)/) {
      $WebClientEngine{$1}++;
      $WebClientHost{$2}++;
   }

   #TD: Bad password specified for <Stefan>
   #TD: Context: Bad password specified for <Stefan> Service <citadel-TCP> Port <504> Remote <example.com / >
   elsif ($ThisLine =~ /^(?:Context: )?Bad password specified for <(\w*)>(?: Service <\w+> Port <\d+> Remote <(.*)>)?/) {
      $WebLoginFailure{lc($1)}{$2}++;
   }

   #TD: USER stefan
   #TD: [3231][(not logged in)(0)] USER Stefan
   elsif ($ThisLine =~ /^(?:\[\d+\]\[.*\] )?USER (\S*)$/) {
      $WebUserLogin{lc($1)}++;
   }

   ### XMPP ###
   elsif ( ($ThisLine =~ /^xmpp_queue_event/) ||
           ($ThisLine =~ /^(Modules: )?\[XMPP\] closing service$/) 
   ) {
      # ignore these lines
   }

   ### Sieve processing ###
   elsif ( ($ThisLine =~ /^Calling sieve2_execute/) ||
           ($ThisLine =~ /^ctdl_getscript\(\) is using script/) ||
           ($ThisLine =~ /^ctdl_getheaders\(\) was called$/) ||
           ($ThisLine =~ /^<.*> queued for Sieve processing$/) ||
           ($ThisLine =~ /^(Modules: )?\[ManageSieve\] closing service$/) 
   ) {
      # ignore these lines
   }

   #TD: Begin Sieve processing
   elsif ($ThisLine =~ /^Begin Sieve processing$/) {
      $SieveStarts++;
   }

   #TD: Rules found.  Performing Sieve processing for <0000000007.Mail>
   elsif ($ThisLine =~ /^Rules found.  Performing Sieve processing for <(\d+)\.(\S+)>$/ ) {
      $SieveProcFor{$1}{$2}++
   }

   #TD: sieve2_execute() returned 11: Sieve Error: header could not be parsed
   #TD: Sieve: sieve2_execute() returned 11: Sieve Error: header could not be parsed
   elsif ($ThisLine =~ /^(?:Sieve: )?sieve2_execute\(\) returned \d+: (.+)$/) {
      $SieveExecute{$1}++;
   }

   #TD: Performing sieve processing on msg <108269>
   elsif ($ThisLine =~ /^Performing sieve processing on msg <(\d+)>$/) {
      $SieveMsgID = $1;
   }

   #TD: Completed sieve processing on msg <108269>
   elsif ($ThisLine =~ /^Completed sieve processing on msg <\d+>$/) {
      undef $SieveMsgID;
   }

   elsif ($ThisLine =~ /^Sieve: /) {

      if ( ($ThisLine =~ /^Sieve: Prepending a new headerlist and header struct$/) ||
           ($ThisLine =~ /^Sieve: (?:Begin|body:|header:) (?:NAME|TEXT|WRAP)/) ||
           ($ThisLine =~ /^Sieve: (?:body: body )?WRAP: /) ||
           ($ThisLine =~ /^Sieve: Entering name and body into header struct/) ||
           ($ThisLine =~ /^Sieve: Prepending a new headerlist and header struct/) ||
           ($ThisLine =~ /^Sieve: starting into libsieve_eval$/) ||
           ($ThisLine =~ /^Sieve: the commandlist type is \[\d+\]$/) ||
           ($ThisLine =~ /^Sieve: top of the eval loop$/) ||
           ($ThisLine =~ /^Sieve: Doing a header comparison$/) ||
           ($ThisLine =~ /^Sieve: Header parse error, returning null$/) ||
           ($ThisLine =~ /^Sieve: Relation is \[\d+\]$/) || 
           ($ThisLine =~ /^Sieve: the commandlist is null$/) ||
           ($ThisLine =~ /^Sieve: Eat some whitespace and return COLON, forget TEXT$/) ||
           ($ThisLine =~ /^Sieve: Doing a fileinto$/) ||
           ($ThisLine =~ /^Sieve: Testing \[(?:Yes|No)\] \[\d+\] \[(?:YES|NO)\]/)
      ) {
         # ignore these lines
      }

      #TD: Sieve: NAME: Content-type
      elsif ($ThisLine =~ /^Sieve: NAME: (\S+)/) {
         $SieveName = $1;
      }
      #TD: Sieve: TEXT: WebCit 7.85
      elsif ($ThisLine =~ /^Sieve: TEXT: (.*)$/) {
         $SieveMsg{$SieveMsgID}{"Items"}{$SieveName} = $1;
      }
      #TD: Sieve: Asking for header [X-Spam-Flag]
      elsif ($ThisLine =~ /^Sieve: Asking for header \[(\S+)\]$/) {
         $SieveMsg{$SieveMsgID}{"Checks"}{$1}++;
      }
      #TD: Sieve: test HEADER comparing [room_Citadel@uncensored.citadel.org] with [stefan.jakobs@gmx.de]
      elsif ($ThisLine =~ /^Sieve: test HEADER comparing (\[.+\]) with (\[.+\])$/) {
         $SieveMsg{$SieveMsgID}{"Header tests"}{$1} = $2;
      }
      #TD: Sieve: Header parse error on line 56: syntax error, unexpected NAME, expecting COLON
      elsif ($ThisLine =~ /^Sieve: Header parse error on (.+)$/) {
         $SieveMsg{$SieveMsgID}{"Header parse error"}{$1}++;
      }

      else {
         # Report any unmatched entries...
         chomp($ThisLine);
         $OtherList{$ThisLine}++;
      }
   }
   
   #TD: Action is FILEINTO, destination is <Citadel Support>
   elsif ($ThisLine =~ /^Action is ([A-Z]+), destination is <(.+)>$/) {
      $SieveMsg{$SieveMsgID}{"Action"}{$1} = $2;
   }

   #TD: keep is 0 -- deleting message from inbox
   elsif ($ThisLine =~ /^keep is 0 -- deleting message from (.*)/) {
      $SieveMsg{$SieveMsgID}{"deleting from"}{$1}++;
   }

   #TD: No Sieve rules exist.  No processing is required.
   elsif ($ThisLine =~ /^No Sieve rules exist\.  No processing is required/) {
      $SieveNoProcessing++;
   }

   ### POP3 server ###
   elsif ( ($ThisLine =~ /^Performing POP3 cleanup hook$/) ||
           ($ThisLine =~ /^POP3 client disconnected: ending session/) ||
           ($ThisLine =~ /^(Modules: )?\[POP3S?\] closing service$/) 
   ) {
      # ignore these lines
   }

   #TD: POP3 authenticated stefan
   elsif ($ThisLine =~ /^POP3 authenticated (.+)$/) {
      $POPDauth{lc($1)}++;
   }

   #TD: POP3: LIST
   #TD: POP3: PASS... 
   elsif ($ThisLine =~ /^POP3: ([.A-Z]+) ?(\S+)?/) {
      if ("$2" eq "") {
         $POPDCmds{$1}++;
      } else {
         $POPDCmds{"$1 ($2)"}++;
      }
   } 

   ### POP3 client ###
   elsif ( ($ThisLine =~ /^>\d+ \d+$/) ||
           ($ThisLine =~ /^>\+OK(?: \d+| POP server ready| mailbox)?/) ||
           ($ThisLine =~ /^>\.$/) ||
           ($ThisLine =~ /^Converting message/) ||
           ($ThisLine =~ /^Converted to <\S*>/) ||
           ($ThisLine =~ /^POP3: .* <password>$/) ||
           ($ThisLine =~ /^Could not connect:/) ||
           ($ThisLine =~ /^Connected!$/)
   ) {
      # ignore these lines
   }

   #TD: pop3client started
   elsif ($ThisLine =~ /^pop3client started$/) {
      $POPClientStarted++;
   }

   #TD: pop3client started
   elsif ( ($ThisLine =~ /^pop3client ended$/) ||
           ($ThisLine =~ /^Context: \[[ \d]+\]SRV\[POP3aggr\] Session ended/) ) {
      $POPClientEnded++;
   }

   #TD: Connecting to <pop3.web.de>
   elsif ($ThisLine =~ /^Connecting to <(\S+)>$/) {
      $POPClientConnects{$1}++;
   }
 
   #TD: 
   elsif ($ThisLine =~ /^<([A-Z]+)/) {
      my $cmd = $1;
      # exclude IGnet and clamav commands
      if ($1 != /^(?:NETP|PORT)$/) {
         $POPCompletedCmds++;
         $POPCmds{$cmd}++;
      }
   }

   elsif ($ThisLine =~ /^>-ERR (.*)$/) {
      $POPErrors{$1}++;
   }

   elsif ($ThisLine =~ /(.+): Name or service not known$/) {
      $POPErrors{"Name or service not know: $1"}++;
   }

   #Citadel >v8.12
   #TD: IO[1965]CC[1965][39]xxx@example.net: fetched 0 new of 0 messages in 3.445628s. bye.
   elsif ($ThisLine =~ /^IO\[[ \d]+\]CC\[[ \d]+\]\[\d+\][^\s]+: fetched (\d+) new of \d+ messages in ([.\d]+)s/) {
      $POPClientStarted++;
      $POPClientFetched+=$1;
      $POPClientProcessTime+=$2;
   }

   ### Databases ###
   elsif ( ($ThisLine =~ /^DB:/) ||
           ($ThisLine =~ /^(?:Closing|Opening) databases?/) ||
           ($ThisLine =~ /^Destroyed/) ||
           ($ThisLine =~ /^(?:bdb\(\)|dbenv->)/) ||
           ($ThisLine =~ /dbversion:/) ||
           ($ThisLine =~ /^ *(?:Linked|Compiled) (?:zlib|db):/) ||
           ($ThisLine =~ /^Starting up DB/) 
   ) {
      # ignore these lines
   }

   ### clamd
   elsif ( ($ThisLine =~ /^Waiting for PORT number$/ ) ||
           ($ThisLine =~ /^STREAM socket connected!$/ ) ||
           ($ThisLine =~ /^Transmitting STREAM command$/ ) ||
           ($ThisLine =~ /^<stream: OK$/ ) ||
           ($ThisLine =~ /^Awaiting response$/ )
   ) {
        # ignore these lines
   }

   #TD: Connecting to clamd at <127.0.0.1>
   elsif ($ThisLine =~ /^Connecting to clamd at <([a-fA-F0-9.:]+)>$/ ) {
      $ClamdConnects{$1}++;
   }

   ### vCard
   elsif ( ($ThisLine =~ /^vCard beforesave hook running for/ ) ||
           ($ThisLine =~ /^Part 1 contains a vCard!  Loading/) ||
           ($ThisLine =~ /^(?:Delete|Create) directory entry: \S+ --> \S+ @ \S+$/) ||
           ($ThisLine =~ /^Adding \S+ @ \S+ \(\S+\) to directory$/) ||
           ($ThisLine =~ /^Checking for <\S+>\.\.\.$/) ||
           ($ThisLine =~ /^vcard client disconnected: ending session/)  
   ) {
      # ignore these
   }

   ### ical
   elsif ( ($ThisLine =~ /^ical_saving_vevent\(\) has been called!$/) ||
           ($ThisLine =~ /^<\d+> attendees: <.*>$/ )
   ) {
      # ignore these
   }

   else {
      # Report any unmatched entries...
      chomp($ThisLine);
      $OtherList{$ThisLine}++;
   }

}

### generate the output ###

# \t = 8 chars
# %-56s: %5i Time(s)

if ($Starts) {
   printf "  %8i   %-36s  %12i\n", $Starts, "Citadel starts", 1000;
}
if (keys %Stops) {
   print_hash(\%Stops, "Citadel exited with");
}
if ($Reloads) {
   printf "  %8i   %-36s  %12i\n", $Reloads, "Citadel reloads", 1000;
}
if ($Crashs) {
   printf "  %8i   %-36s  %12i\n", $Crashs, "Citadel crashs", 1000;
}
if (keys %Warnings) {
   print_2xhash(\%Warnings, "Warnings"); 
}

if ($Starts or keys %Stops or $Reloads) { print_doubleline();  }

if (keys %Threads) {
#   print "\nTHREADS:";
#   print "\n--------\n";
   print_3xhash(\%Threads, "Threads");
}


if (keys %SessionStarted) {
   print "\nSESSIONS:";
   print "\n---------";
### it would be nice to change the Detail level here. So we see all in $MED
   print_2xhash(\%SessionStarted, "Sessions started");
#   print "\n";
}

if (keys %SMTPserverHELO or keys %SMTPserverEval) {
   print  "\nSMTP SERVER:";
   print  "\n------------\n";
   if ($SMTPBytesAccepted > 0 || $SMTPRejected > 0) {
      printf "  %8.3fK  %-36s  %12i\n", $SMTPBytesAccepted/1024, "Bytes accepted", $SMTPBytesAccepted;
      print_doubleline();
      printf "  %8i   %-36s  %12i\n", $SMTPAccepted, "Accepted", 100/($SMTPRejected+$SMTPAccepted)*$SMTPAccepted;
      printf "  %8i   %-36s  %12i\n", $SMTPRejected, "Rejected", 100/($SMTPRejected+$SMTPAccepted)*$SMTPRejected;
      print_line();
      printf "  %8i   %-36s  %12i\n", $SMTPAccepted+$SMTPRejected, "Total", 100;
      print_doubleline();
   }
   if (keys %SMTPserverStats) {
      my %out = ();
      my %sum = ();
      foreach my $stat (sort {$a cmp $b} keys %SMTPserverStats) {
         my $Nxx =~ /^(\d)/;
         foreach my $reason (sort {$a cmp $b} keys %{$SMTPserverStats{$stat}}) {
            $out{$Nxx} .= sprintf "  %8i   %-36s  %12i\n", $SMTPserverStats{$stat}{$reason}, "$stat $reason", 100/$SMTPserverStatsSum*$SMTPserverStats{$stat}{$reason};
            $sum{$Nxx} += $SMTPserverStats{$stat}{$reason};
         }
      }
      foreach my $line (sort {$a cmp $b} keys %out) {
         print  "$out{$line}";
         print_line();
         printf "  %8i   %-36s  %12i\n", $sum{$line}, "Total", 100;
         print_doubleline();
      }
   }
## add connecitons
   print_asterisks();
   print_hash(\%SMTPserverFROM, "Envelope senders");
   print_hash(\%SMTPserverRCPT, "Recipients");
   print_hash(\%SMTPserverHELO, "Connections");
   print_hash(\%SMTPserverCMDS, "Other Commands");
   if (keys %SMTPserverEval) {
      print_hash(\%SMTPserverEval, "Recipient Verifications");
   }
   if (keys %SMTPserverAuth) {
      print_hash(\%SMTPserverAuth, "User Authentications");
   }
   if (keys %SMTPserverRelay) {
      print_2xhash(\%SMTPserverRelay, "Messages Relayed");
   }
   if (keys %SMTPSSLError) {
      print_2xhash(\%SMTPSSLError, "SSL Errors");
   }
#   print "\n";
} 

if ($SMTPclient_queuerun or $SMTPclient_messages or 
    keys %SMTPclientCMDS or keys %SMTPclientDelivery) {
   print "\nSMTP CLIENT:";
   print "\n------------\n";
   if ($SMTPclientTime > 0 && $SMTPclient_messages > 0) {
      printf "  %8is  %-36s  %12i\n", $SMTPclientTime, "Time to send messages", 1000;
      printf "  %8is  %-36s  %12i\n", $SMTPclientTime/$SMTPclient_messages, "avg. time to send one message", 1000;
   }
   if ($SMTPclient_queuerun > 0 || $SMTPclient_messages > 0) {
      printf "  %8i   %-36s  %12i\n", $SMTPclient_queuerun, "Queue runs", 1000;
      printf "  %8i   %-36s  %12i\n", $SMTPclient_messages, "Messages processed", 1000;
      print_line();
   }
   if ($SMTPclientBounces > 0) {
      printf "  %8i   %-36s  %12i\n", $SMTPclientBounces, "Messages bounced", 100/($SMTPclientBounces)*$SMTPclientBounces;
      print_doubleline();
   }
   if (keys %SMTPclientConnect) {
      print_hash(\%SMTPclientConnect, "Connections to");
   }
   if (keys %SMTPclientDelivery) {
      print_3xhash(\%SMTPclientDelivery, "Messages delivered");
#      print "  Message delivery:\n";
#      foreach my $status (sort {$a cmp $b} keys %SMTPclientDelivery) {
#         printf "     %-50s: %5i Time(s)\n", $status, scalar keys %{$SMTPclientDelivery{$status}};
#         foreach my $domain (sort {$a cmp $b} keys %{$SMTPclientDelivery{$status}}) {
#            printf "\t%-48s: %5i Time(s)\n", $domain, scalar keys %{$SMTPclientDelivery{$status}{$domain}};
#            foreach my $user (sort {$a cmp $b} keys %{$SMTPclientDelivery{$status}{$domain}}) {
#               printf "\t  %-46s: %5i Time(s)\n", $user, $SMTPclientDelivery{$status}{$domain}{$user};
#            }
#         }
#      }
   }
   if (keys %SMTPclientCMDS) {
      print_2xhash(\%SMTPclientCMDS, "Commands send");
   }
#   print "  Commands send:\n";
#   foreach my $cmd (sort {$a cmp $b} keys %SMTPclientCMDS) {
#      printf "     %-50s: %5i\n", $cmd, scalar keys %{$SMTPclientCMDS{$cmd}};
#      foreach my $addr (sort {$a cmp $b} keys %{$SMTPclientCMDS{$cmd}}) {
#         if ($addr != "") { printf "\t%-48s: %5i\n"; $addr, $SMTPclientCMDS{$cmd}{$addr}; }
#      } 
#   }
   if (keys %SMTPclientRelay) {
      print_2xhash(\%SMTPclientRelay, "Messages relayed");
#      print "  Messages relayed:\n";
#      foreach my $relay (sort {$a cmp $b} keys %SMTPclientRelay) {
#         printf "     %-50s\n", $relay;
#         foreach my $rcpt (sort {$a cmp $b} keys %{$SMTPclientRelay{$relay}}) {
#            printf "\t%-48s: %5i\n", $rcpt, $SMTPclientRelay{$relay}{$rcpt};
#         }
#      }
   }
   if (keys %SMTPclientStats) {
      print_hash(\%SMTPclientStats, "Message status");
#      print "  Message status:\n";
#      foreach my $stat (sort {$a cmp $b} keys %SMTPclientStats) {
#         printf "\t%-48s: %5i Time(s)\n", $stat, $SMTPclientStats{$stat};
#      }
   }
   if ($SMTPclientBounces) {
      print_2xhash(\%SMTPclientBounce, "Messages bounced");
#      foreach my $bounce (sort {$a cmp $b} keys %SMTPclientBounce) {
#         printf "     %-50s: %5i Time(s)\n", $bounce, scalar keys %{$SMTPclientBounce{$bounce}};
#         foreach my $status (sort {$a cmp $b} keys %{$SMTPclientBounce{$bounce}}) {
#            printf "\t%-48s: %5i Time(s)\n", $status, $SMTPclientBounce{$bounce}{$status};
#         }
#      }
   }
#   print "\n";
}

if (keys %IMAPCmds or keys %IMAPexpunge or keys %IMAPUserLogin) {
   print "\nIMAP PROCESSING:";
   print "\n----------------\n";
   if ($IMAPCompletedCmds > 0) {
      printf "  %8i   %-36s  %12i\n", $IMAPCompletedCmds, "IMAP Commands processed", 1000;
      printf "  %8.3fs  %-36s  %12i\n", $IMAPCmdDuration/$IMAPCompletedCmds, "avg time per IMAP command", 1000;
      print_line();
   }
   if (keys %{$SessionStarted{"IMAP"}} ) {
      print_hash(\%{$SessionStarted{"IMAP"}}, "Connections");
   }
   if (keys %{$SessionStarted{"IMAPS"}} ) {
      print_hash(\%{$SessionStarted{"IMAPS"}}, "Secure Connections");
   }
   if (keys %IMAPUserLogin) {
      print_hash(\%IMAPUserLogin, "Users logged in");
   }
   if ($IMAPCompletedCmds > 0) {
       print_hash(\%IMAPCmds, "IMAP commands processed");
   }
   if (keys %IMAPexpunge) {
      print_hash(\%IMAPexpunge, "Messages expunged from"); 
   }
#   print "\n";
}

if (keys %POPDauth or keys %POPDCmds or keys %POPUserLogin) {
   print "\nPOP3 SERVER:";
   print "\n------------\n";
   if (keys %{$SessionStarted{"POP3"}} ) {
      print_hash(\%{$SessionStarted{"POP3"}}, "Connections");
   }
   if (keys %{$SessionStarted{"POP3S"}} ) {
      print_hash(\%{$SessionStarted{"POP3S"}}, "Secure Connections");
   }
   if (keys %POPUserLogin) {
      print_hash(\%POPUserLogin, "Users logged in");
   }
   print_hash(\%POPDauth, "Users authenticated");
   print_hash(\%POPDCmds, "POP3 commands processed");

#   print "\n";
}

if ($POPClientStarted or keys %POPCmds or keys %POPErrors) {
   print "\nPOP3 CLIENT:";
   print "\n------------\n";
   printf "  %8i   %-36s  %12i\n", $POPClientStarted, "POP3 client started", 1000;
   printf "  %8i   %-36s  %12i\n", $POPClientEnded, "POP3 client ended", 1000;
   if ($POPClientFetched > 0) {
      print_line();
      printf "  %8i   %-36s\n", $POPClientFetched, "Messages fetched via POP3"; 
   }
   if ($POPClientProcessTime > 0 and $POPClientFetched > 0) {
      print_line();
      # Log says it seconds, but it must be milliseconds
      printf "  %8.3fs  %-36s\n", $POPClientProcessTime/1000, "Time for message download";
      printf "  %8.3fs  %-36s\n", $POPClientProcessTime/$POPClientFetched/1000, "avg. time to fetch one message";
   }
   print_doubleline();
### this one is the same as the sum over %POPCmds (see later)
   if ($POPCompletedCmds > 0) {
      printf "  %8i   %-36s  %12i\n", $POPCompletedCmds, "POP3 Commands send", 1000;
   print_asterisks();
   }
   if (keys %POPClientConnects) {
      print_hash(\%POPClientConnects, "POP3 Client Connections");
   }
   if (keys %POPCmds) {
      print_hash(\%POPCmds, "POP3 Commands send");
   }
   if (keys %POPErrors) {
      print_hash(\%POPErrors, "POP3 Errors");
   }
#   print "\n";
}

if (keys %Ctdlcmds or keys %CtdlMsgCorrupted or keys %CtdlReplChecks or
    keys %CtdlAddContact or %CtdlFileOp or keys %RSSfeeds or keys %RSSerrors) {
   print "\nCITADEL INTERNAL MESSAGES:";
   print "\n--------------------------";
   if ($serv_extnotify_queuerun) {
      printf "\n  %8i   %-36s  %12i\n", $serv_extnotify_queuerun, "serv extnotify queue run", 1000;
   }
   if (@CtdlUserCreated) {
      printf "\n  %8i   %-36s  %12i\n", scalar @CtdlUserCreated, "User created", 1000;
      foreach my $user (sort {$a cmp $b} @CtdlUserCreated) {
         printf "  %8i      %-36s\n", 1, $user;
      }
   }
   if (@CtdlUserDeleted) {
      printf "\n  %8i   %-36s  %12i\n", scalar @CtdlUserDeleted, "User deleted", 1000;
      foreach my $user (sort {$a cmp $b} @CtdlUserDeleted) {
         printf "  %8i      %-36s\n", 1, $user;
      }
   }
   if (@CtdlRoomDeleted) {
      printf "\n  %8i   %-36s  %12i\n", scalar @CtdlRoomDeleted, "Room deleted", 1000;
      foreach my $room (sort {$a cmp $b} @CtdlRoomDeleted) {
         printf "  %8i      %-36s\n", 1, $room;
      }
   }
   if ($CtdlMsgDeleted) {
      printf "\n  %8i   %-36s  %12i\n", $CtdlMsgDeleted, "Messages deleted", 1000;
   }
   if (@CtdlLogDeleted) {
      printf "\n  %8i   %-36s  %12i\n", scalar @CtdlLogDeleted, "Logs deleted", 1000;
      foreach my $log (sort {$a cmp $b} @CtdlLogDeleted) {
         printf "  %8i      %-36s\n", 1, $log; 
      }
   }
   if (keys %RSSfeeds) {
      print_hash(\%RSSfeeds, "RSS feeds fetched");
   }
   if (keys %RSSerrors) {
      print_hash(\%RSSerrors, "RSS client errors");
   }
   if (keys %CtdlGenerate) {
      print_hash(\%CtdlGenerate, "Generated");
   }
   if (keys %CtdlCleanup) {
      print_2xhash(\%CtdlCleanup, "Actions");
   }
   if (keys %Ctdlcmds) {
      print_hash(\%Ctdlcmds, "Commands processed");
   }
   if (keys %CtdlFileOp) {
      print_2xhash(\%CtdlFileOp, "File operations");
   }
   if (keys %CtdlMsgCorrupted) {
      print_hash(\%CtdlMsgCorrupted, "Corrupted messages");
#      print "  Corrupted messages:\n";
#      foreach my $msg (sort {$a cmp $b} keys %CtdlMsgCorrupted) {
#         printf "\t%-48s: %5i\n", $msg, $CtdlMsgCorrupted{$msg};
#      }
   }
   if (keys %Ctdlqp_encode) {
      print_hash(\%Ctdlqp_encode, "qp_encode addresses");
   }
   if (keys %CtdlValRcpt) {
      print_2xhash(\%CtdlValRcpt, "Recipients validated");
   }
   if (keys %CtdlReplChecks) {
      print_2xhash(\%CtdlReplChecks, "Replication checks");
#      print "\n  Replication checks:\n";
#      foreach my $user (sort {$a cmp $b} keys %CtdlReplChecks) {
#         printf "     %s\n", $user;
#         foreach my $mbox (sort {$a cmp $b} keys %{$CtdlReplChecks{$user}}) {
#            printf "\t%-48s: %5i Time(s)\n", $mbox, $CtdlReplChecks{$user}{$mbox}; 
#         }
#      }
   }
   if (keys %CtdlAddContact) {
      print_hash(\%CtdlAddContact, "Contacts added");
#      print "\n  Contacts added:\n";
#      foreach my $contact (sort {$a cmp $b} keys %CtdlAddContact) {
#         printf "     %-50s: %5i Time(s)\n", $contact , $CtdlAddContact{$contact};
#      }
   }
   if (keys %ClamdConnects) {
      print_hash(\%ClamdConnects, "Clamd connections");
   }
   if (keys %CtdlWriteFail) {
      print_hash(\%CtdlWriteFail, "Client failed to write bytes");
   }
#   print "\n";
}

if ($NetProcessingTime or $NetProcessingCount or keys %NetNodes or
    keys %NetNoConnect) {
   print "\nNETWORK PROCESSING";
   print "\n------------------\n";
   if ($NetProcessingCount > 0 or $NetProcessingTime > 0) {
      printf "  %8i   %-36s  %12i\n", $NetProcessingCount, "Full processings completed", 1000;
      printf "  %8.3fs  %-36s  %12i\n", $NetProcessingTime/1000, "Full processing time", 1000;
      print_doubleline();
   }

   if (keys %NetStarts) {
      print_hash(\%NetStarts, "Networking started for");
   }
   if (keys %NetNodes) {
### this doesn't make much sense
      print_3xhash(\%NetNodes, "Network nodes");  
   }
   if (keys %NetNoConnect) {
      print_3xhash(\%NetNoConnect, "Connections failed");
#      foreach my $what (sort {$a cmp $b} keys %NetNoConnect) {
#         print "  $what:\n";
#         foreach my $host (sort {$a cmp $b} keys %{$NetNoConnect{$what}}) {
#            print "     $host:\n";
#            foreach my $reason (sort {$a cmp $b} keys %{$NetNoConnect{$what}{$host}}) {
#               printf "\t%-48s: %-5i Time(s)\n", $reason, $NetNoConnect{$what}{$host}{$reason};
#            }
#         }
#      }
   }
   if (keys %NetFilesRemoved) {
      print_hash(\%NetFilesRemoved, "Queue files removed");
   }
}

if (keys %WebClientHost or keys %WebClientEngine or keys %WebUserLogin) {
   print "\nWEBCIT:";
   print "\n-------";
   if (keys %WebUserLogin) {
      print_hash(\%WebUserLogin, "Users logged in");
   }
   if (keys %WebLoginFailure) {
      print_2xhash(\%WebLoginFailure, "Login failures");
   }
   if (keys %WebClientHost) {
      print_hash(\%WebClientHost, "Connects from hosts");
   }
   if (keys %WebClientEngine) {
      print_hash(\%WebClientEngine, "Connects with engines");
   }
}


if (keys %SieveMsg or keys %SieveExecute or %SieveProcFor or $SieveStarts) {
   print "\nSIEVE PROCESSING:";
   print "\n-----------------\n";
   if ($SieveStarts > 0) {
      printf "  %8i   %-36s  %12i\n", $SieveStarts, "Sieve processing started", 1000;
      printf "  %8i   %-36s  %12i\n", scalar keys %SieveMsg, "Messages processed", 1000;
      if ($SieveNoProcessing > 0) {
         printf "  %8i   %-36s  %12i\n", $SieveNoProcessing, "No processing required", 1000;
      }
      print_doubleline();
   }
   if (keys %SieveExecute) {
      print_hash(\%SieveExecute, "Sieve execute returned");
   }
   if (keys %SieveProcFor) {
      print_2xhash(\%SieveProcFor, "Sieve processed for user");
   }
   if ($Detail > 5) {
### this needs a rework
   print_3xhash(\%SieveMsg, "Sieve details");
#   foreach my $id (sort {$a cmp $b} keys %SieveMsg) {
#      printf "  Message %8i:\n", $id;
#      foreach my $stage (sort {$a cmp $b} keys %{$SieveMsg{$id}}) {
#         if ($Detail >= 10 || ($stage ne "Header tests" && $stage ne "Items") ) {
#            printf "     %s:\n", $stage;
#            foreach my $item (sort {$a cmp $b} keys %{$SieveMsg{$id}{$stage}}) {
#               printf "\t%-48s: %s\n", $item, $SieveMsg{$id}{$stage}{$item};
#            }
#         }
#      }
#   }
   }
   print "\n";
}

if (keys %OtherList) {
   print "\n**** Unmatched entries ****\n";
   foreach my $Error (keys %OtherList) {
      print "    $Error : $OtherList{$Error} Time(s)\n";
   }
}

### return without a failure ###
exit(0);

# vi: shiftwidth=3 tabstop=3 syntax=perl et