#
# This software is Copyright 2005 by Elsevier Inc.  You may use it
# under the terms of the license at http://perl.plover.com/hop/LICENSE.txt .
#



###
### logfile-process
###

## Chapter 6 section 5.3.1

sub _devino {
  my $f = shift;
  my ($dev, $ino) = stat($f);
  return unless defined $dev;
  "$dev;$ino";
} 
sub _next_record {
  while (1) {
    my ($fh, $filename, $devino, $wait) = @_;
    $wait = 1 unless defined $wait;
    my $rec = <$fh>;
    return $rec if defined $rec;
    if (_devino($filename) eq $devino) {
      # File has not moved
      sleep $wait;
    } else {
      # $filename refers to a different file
      open $_[0], "<", $filename or return;
      $_[2] = _devino($_[0]);
    }
  }
}
sub follow_file {
  my $filename = shift;
  my ($devino, $fh);
  tail(iterate_function(sub { _next_record($fh, $filename, $devino) }));
}
 
my $raw_mail_log = follow_file('/service/qmail/log/main/current');
sub tai64n_to_unix_time {
  my $rec = shift;
  return [undef, $rec] unless s/^\@([a-f0-9]{24})\s+//;
  [hex(substr($1, 8, 8)) + 10, $rec];
}
my $mail_log = &transform(\&tai64n_to_unix_time, $raw_mail_log);


## Chapter 6 section 5.3.1

sub digest_maillog {
  my ($s, $msg, $del) = @_;
  for ($msg, $del) { $_ = {} unless $_ }
  while ($s) {
    my ($date, $rec) = @{drop($s)};

    next unless defined $date;
    if ($rec =~ /^new msg (\d+)/) {
      $msg->{$1} = {start => $date, id => $1,
                   success => 0, failure => 0, deferral => 0};

    } elsif ($rec =~ /^info msg (\d+): bytes (\d+) from (<[^\>]*>)/) {
      next unless exists $msg->{$1};
      $msg->{$1}{bytes} = $2;
      $msg->{$1}{from} = $3;

    } elsif ($rec =~ /^starting delivery (\d+): msg (\d+)/) {
      next unless exists $msg->{$2};
      $del->{$1} = $2;
      push @{$msg->{$2}{deliveries}}, $1;

    } elsif ($rec =~ /^delivery (\d+): (success|failure|deferral)/) {
      next unless exists $del->{$1} && exists $msg->{$del->{$1}};
      $msg->{$del->{$1}}{$2}++;

    } elsif ($rec =~ /^end msg (\d+)/) {
      next unless exists $msg->{$1};
      my $m = delete $msg->{$1};
      $m->{total_deliveries} = @{$m->{deliveries}};
      for (qw(success failure deferral)) { $m->{$_} += 0 }
      for (@{$m->{deliveries}}) { delete $del->{$_} };
      $m->{end} = $date;
      return node($m, promise { digest_maillog($s, $msg, $del) });
    }
  }
  return;
}
use POSIX 'strftime';

sub format_digest {
  my $h = shift;
  join " ", 
    $h->{id},
    strftime("%d/%b/%Y:%T", localtime($h->{start})),
    strftime("%d/%b/%Y:%T", localtime($h->{end})),
    $h->{from},
    $h->{total_deliveries},
    $h->{success},
    $h->{failure},
    $h->{deferral},
      ;
}

show(&transform(\&format_digest, digest_maillog($mail_log)));
