#!/usr/bin/perl -w
use strict;

=head1 NAME

collapse_qpsmtpd_conn - collapse connections from qpsmtpd logs to a
single line

=SYNOPSIS

collapse_qpsmtpd_conn [files...]

=DESCRIPTION

collapse_qpsmtpd_conn extracts commands and responses from qpsmtpd log
files and prints a single line for each connection with the entire SMTP
dialog. The output is intended to be easily filtered with grep or awk.

Connections are identified by their PID. To avoid concatenating two
connections with the same PID only state about the last 100 connections
is kept.

=cut

use POSIX qw(strftime);

my @a;
my %h;
while (<>) {
    chomp();

    my @f = split();
    my $ts = $f[0];
    my $id = $f[1];
    my $s;

    $f[2] = "" unless defined($f[2]);
    if ($f[2] eq "dispatching") {
	$s = join(" ", @f[3..$#f]);
    } elsif ($f[2] =~ /^\d\d\d(-|$)/) {
	$s = join(" ", @f[2..$#f]);
    } else {
	next;
    }

    if ($h{$id}) {
	# we already have this entry. Append
	push @{$h{$id}->{s}}, $s;
    } else {
	# a new one. 
	if ($#a >= 100) {
	    # print oldest connection and forget about it
	    my $c = shift(@a);
	    print tai64local($c->{ts}), " $c->{id}: ", join("|", @{$c->{s}}), "\n";
	    delete $h{$id};
	}

	my $c = { ts => $ts, id => $id, s => [ $s ] };
	push @a, $c;
	$h{$id} = $c;
    }
}

for my $c (@a) {
    print tai64local($c->{ts}), " $c->{id}: ", join("|", @{$c->{s}}), "\n";
}

sub tai64local {
    my ($s) = @_;

    # @400000003f6c7bc5253bf98c
    # 0123456789012345678901234
    # 0         1         2
    #   |-------------||------|
    if (substr($s, 0, 2) eq '@4') {
	my $ts = hex(substr($s, 2, 15));
	my $tf = hex(substr($s, 17, 8));
	$s = strftime('%Y-%m-%dT%H:%M:%S', localtime($ts));
	$s .= sprintf(".%09d", $tf);
    }
    return $s;
}
