#!/usr/bin/perl
# 
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1
# 
# The contents of this file are subject to the Mozilla Public License
# Version 1.1 ("License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://www.zimbra.com/license
# 
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and limitations
# under the License.
# 
# The Original Code is: Zimbra Collaboration Suite Server.
# 
# The Initial Developer of the Original Code is Zimbra, Inc.
# Portions created by Zimbra are Copyright (C) 2006 Zimbra, Inc.
# All Rights Reserved.
# 
# Contributor(s):
# 
# ***** END LICENSE BLOCK *****
# 

use strict;

use IO::File;
use File::Find;

my $readfiles = 0;

my %queue_stats = ();

my $hash_queue_depth;
my $queue_directory;

my $qfiles = 0;

sub getLocalConfig {
	my $key = shift;
	if (defined ($ENV{zmsetvars})) {
		return $ENV{$key};
	}
	open CONF, "/opt/zimbra/bin/zmlocalconfig -q -m shell |" or die "Can't open local config: $!";
	my @conf = <CONF>;
	close CONF;

	chomp @conf;

	foreach (@conf) {
		my ($key, $val) = split '=', $_, 2;
		$val =~ s/;$//;
		$val =~ s/'$//;
		$val =~ s/^'//;
		$ENV{$key} = $val;
	}
	$ENV{zmsetvars} = 'true';
	return $ENV{$key};
}

sub get_record {  # Borrowed from qshape
	my ($fh) = shift;
	my $rec = getc($fh) || return;
	my $len = 0;
	my $shift = 0;
	while (defined(my $lb = getc($fh))) {
		my $dig = ord($lb);
		$len |= ($dig & 0x7f) << $shift ;
		last if (($dig & 0x80) == 0);
		$shift += 7;
		return if ($shift > 14); # XXX: max rec len of 2097151
	}
	my $data = "";
	return unless ($len == 0 || read($fh,$data,$len) == $len);
	#print STDERR "Returning $rec, $len, $data\n";
	return ($rec, $len, $data);
}


sub getHashPath {
	my $fn = shift;
	my $p = "";
	for (my $i = 0; $i < $hash_queue_depth; $i++) {
		$p .= substr($fn, $i, 1);
		$p .="/";
	}
	return $p;
}

sub processQ {
	if (! -f $_ ) {return;}
	if (! m{(^|/)[A-F0-9]{6,}$} ) { return; }
	$qfiles++;
	my ($cdir) = ($File::Find::dir =~ m|([^/]*)|);
	#print STDERR "Processing $cdir - $_\n";
	$queue_stats{$cdir}{COUNT}++;
	
	if ($readfiles) {
		my %qf = ();
		my @st = lstat($_);
		if (@st == 0) { return; }
		my $fh = new IO::File ($_, "r") || return;
		my ($rec, $len, $data) = get_record($fh);
		$qf{TO} = ();
		if ($rec eq "C") { # Size
			($qf{SIZE}) = ($data =~ m/\s*(\d+)\s*\d+\s*\d+/);
			($rec, $len, $data) = get_record($fh);
			if ($rec eq "T") {
				$qf{TIME} = $data;
			} else {
				return;
			}
		} else {
			return;
		}
		while (my ($rec, $len, $data) = get_record($fh)) {
			#print STDERR "got $rec, $len, $data\n";
			if ($rec eq "R") { 
                                push(@{$qf{TO}}, $data);
                        } elsif ($rec eq "S") { 
                                $qf{FROM} = $data?$data:'MAILER-DAEMON'; 
                        } elsif ($rec eq "L") {
                                $qf{FILTER} = $data;
                        } elsif ($rec eq "A") {
                                my ($aname, $avalue) = ($data =~ /^([^=]+)=(.*)$/);
                                if ($aname eq "client_address" && defined($avalue)) {
                                        $qf{ADDR} = $avalue;
                                } elsif ($aname eq "client_name" && defined($avalue)) {
                                        $qf{HOST} = $avalue;
                                }
                        }
		}
		$fh->close();
		if ($cdir eq "deferred") {
			my $dfile = getHashPath($_);
			$fh = new IO::File ("$queue_directory/defer/$dfile/$_","r") || die "Can't open $dfile/$_: $!";
			my @reasons = grep /^reason=/, <$fh>;
			$qf{REASON} = $reasons[0];
			chomp $qf{REASON};
			$qf{REASON} =~ s/reason=//;
		}

		print "id=", $_, "\n";
		print "time=", $qf{TIME}, "\n";
		print "size=", $qf{SIZE}, "\n";
		print "from=", $qf{FROM}, "\n";
		if (defined $qf{ADDR}) { print "addr=", $qf{ADDR}, "\n"; }
		if (defined $qf{HOST}) { print "host=", $qf{HOST}, "\n"; }
		if (defined $qf{FILTER}) { print "filter=", $qf{FILTER},"\n"; }
		if ($qf{REASON}) {
			print "reason=", $qf{REASON}, "\n";
		}
                print "to=", join(',', @{$qf{TO}}), "\n";
		print "\n";

	}
}

getLocalConfig('zimbra_home');

$queue_directory = `$ENV{zimbra_home}/bin/postconf -h queue_directory`;
chomp $queue_directory;

$hash_queue_depth = `$ENV{zimbra_home}/bin/postconf -h hash_queue_depth`;
chomp $hash_queue_depth;

#$queue_directory="/opt/zimbra/postfix/spool";
#print STDERR "$queue_directory\n";

my @queues = qw/incoming hold active deferred corrupt/;

if ($ARGV[0]) { 
	@queues = $ARGV[0]; 
	$readfiles = 1;
};

map {$queue_stats{$_} = ()} @queues;

foreach (map "$queue_directory/$_", @queues) {
	chdir $_ or die "Can't chdir to $_";
}

chdir $queue_directory;

find (\&processQ, @queues);

if (!$readfiles) {
	map { printf "%s=%d\n", $_, $queue_stats{$_}{COUNT} } keys %queue_stats;
}
