#!/usr/local/bin/perl
#
# qmHandle 1.0.0
#
# Copyright(c) 1998 -> 2001 Michele Beltrame <mick@io.com>
#
# This program is distributed under the GNU GPL.
# For more information have a look at http://www.gnu.org

use strict;
use diagnostics;

#################### USER CONFIGURATION BEGIN ####################

#####
# Set this to your qmail queue directory (be sure to include the final slash!)
my ($queue) = '/var/qmail/queue/';

#####
# If your system has got automated command to start/stop qmail, then
# enter them here.
# ### Be sure to uncomment only ONE of each variable declarations ###

# For instance, this is if you have DJB's daemontools
#my ($stopqmail) = '/usr/local/bin/svc -d /service/qmail-send';
#my ($startqmail) = '/usr/local/bin/svc -u /service/qmail-send';

# While this is if you have a Debian GNU/Linux with its qmail package
my ($stopqmail) = '/etc/init.d/qmail stop';
my ($startqmail) = '/etc/init.d/qmail start';

# If you don't have scripts, leave $stopqmail blank (the process will
# be hunted and killed by qmHandle):
#my ($stopqmail) = '';

# However, you still need to launch qmail in a way or the other. So,
# if you have a standard qmail 1.03 use this:
#my ($startqmail) = "csh -cf '/var/qmail/rc &'";

# While, if you have a standard qmail < 1.03 you should use this:
#my ($startqmail) = '/var/qmail/bin/qmail-start ./Mailbox splogger qmail &';

#####
# Enter here the system command which returns qmail PID. The following
# should work on most Unixes:
my ($pidcmd) = 'pidof qmail-send';

####################  USER CONFIGURATION END  ####################

# Print usage if no arguments
if ($#ARGV == -1) {
    &Usage();
}

# Set "global" variables
my ($norestart) = 0;

# Create a message list for local and remote queues
my (@queues) = ("remote", "local");
my (@msglist) = ();
my (%type) = ();

foreach my $currentqueue (@queues) {

    # Make list of messages each queue (thanks Franky Van Liedekerke)
    opendir(DIR,"${queue}$currentqueue");
    my (@dirlist) = grep !/\./, readdir DIR;
    closedir DIR;
    foreach my $dir (@dirlist) {
	opendir (SUBDIR,"${queue}${currentqueue}/$dir");
	my (@files) = grep !/\./, map "$dir/$_", readdir SUBDIR;
	foreach my $file (@files) {
	    push @msglist, "$file";
	    ($currentqueue eq "remote") ? ($type{"$file"} = 'R') : ($type{"$file"} = 'L');
	}
	closedir SUBDIR;
    }

}
    
# Get options & perform action(s)
my ($color) = 0;
my ($summary) = 0;
my (@actions) = ();
foreach my $arg (@ARGV) {
  SWITCH: {
      $arg eq '-l' and do { push @actions, "&ListMsg('A')"; last SWITCH; };
      $arg eq '-R' and do { push @actions, "&ListMsg('L')"; last SWITCH; };
      $arg eq '-L' and do { push @actions, "&ListMsg('R')"; last SWITCH; };
      $arg eq '-N' and do { $summary = 1; last SWITCH; };
      $arg eq '-c' and do { $color = 1; last SWITCH; };
      $arg eq '-s' and do { push @actions, "&Stats()"; last SWITCH; };
      $arg =~ /^-v(.+)/ and do { push @actions, "&ViewMsg($1)"; last SWITCH; };
      $arg =~ /^-d(.+)/ and do { push @actions, "&DelMsg($1)"; last SWITCH; };
      $arg eq '-D' and do { push @actions, "&DelAll()"; last SWITCH; };
      &Usage();
  }
}

foreach my $action(@actions) {
    eval "$action";
}

exit(0);

# Display message list
# pass parameter of queue NOT to list! i.e. if you want remote only, pass L
# if you want local, pass R  if you want all pass anything else eg A
sub ListMsg {
    my ($q) = shift;
    my (%ret, %date, %from, %subj, %to, %cc, %fsize);
    
    if ($summary == 0) {

	foreach my $msg(@msglist) {

	    # Read return path
	    open (MSG, "${queue}info/$msg");
	    $ret{$msg} = <MSG>;
	    substr($ret{$msg}, 0, 1) = '';
	    chop ($ret{$msg});
	    close (MSG);
	    
	    # Get message (file) size
	    $fsize{$msg} = (stat("${queue}mess/$msg"))[7];
	    
	    # Read something from message header (sender, receiver, subject, date)
	    open (MSG, "${queue}mess/$msg");
	    while (<MSG>) {
		if ($_ =~ /^Date: /) {
		    $date{$msg} = $';
		    chop ($date{$msg});
		} elsif ( $_ =~ /^From: /) {
		    $from{$msg} = $';
		    chop ($from{$msg});
		} elsif ( $_ =~ /^Subject: /) {
		    $subj{$msg} = $';
		    chop ($subj{$msg});
		} elsif ( $_ =~ /^To: /) {
		    $to{$msg} = $';
		    chop ($to{$msg});
		} elsif ( $_ =~ /^Cc: /) {
		    $cc{$msg} = $';
		    chop ($cc{$msg});
		} elsif ( $_ eq "\n") {
		    last;
		}
	    }
	}

    }
    
    if ($color == 1) {

	foreach my $msg(@msglist) {
	    unless ($q eq $type{$msg})  {
		my ($dir, $rmsg) = split (/\//, $msg);
		print chr(27)."[01;34m$rmsg ($dir, $type{$msg})\n";
		if ($summary == 0) {
		    defined($ret{$msg})   and print "  \e[01;31mReturn-path\e[00m: $ret{$msg}\n";
		    defined($from{$msg})  and print "  \e[01;31mFrom\e[00m: $from{$msg}\n";
		    defined($to{$msg})    and print "  \e[01;31mTo\e[00m: $to{$msg}\n";
		    defined($cc{$msg})    and print "  \e[01;31mCc\e[00m: $cc{$msg}\n";
		    defined($subj{$msg})  and print "  \e[01;31mSubject\e[00m: $subj{$msg}\n";
		    defined($date{$msg})  and print "  \e[01;31mDate\e[00m: $date{$msg}\n";
		    defined($fsize{$msg}) and print "  \e[01;31mSize\e[00m: $fsize{$msg} bytes\n\n";
		}
	    }
	}

    } else {

	foreach my $msg(@msglist) {
	    unless ($q eq $type{$msg})  {
		my ($dir, $rmsg) = split (/\//, $msg);
		print "$rmsg ($dir, $type{$msg})\n";
		if ($summary == 0) {
		    defined($ret{$msg})   and print "  Return-path: $ret{$msg}\n";
		    defined($from{$msg})  and print "  From: $from{$msg}\n";
		    defined($to{$msg})    and print "  To: $to{$msg}\n";
		    defined($cc{$msg})    and print "  Cc: $cc{$msg}\n";
		    defined($subj{$msg})  and print "  Subject: $subj{$msg}\n";
		    defined($date{$msg})  and print "  Date: $date{$msg}\n";
		    defined($fsize{$msg}) and print "  Size: $fsize{$msg} bytes\n\n";
		}
	    }
	}

    }
    &Stats();
}

# View a message in the queue
sub ViewMsg {
    my ($rmsg) = shift;
    
    unless ($rmsg =~ /^\d+$/) {
	
	print "$rmsg is not a valid message number!\n";
	
    } else {

	# Search message
	my ($ok) = 0;
	foreach my $msg(@msglist) {
	    if ($msg =~ /$rmsg$/) {
		$ok = 1;
		print "\n --------------\nMESSAGE NUMBER $rmsg \n --------------\n"; 
		open (MSG, "${queue}mess/$msg");
		while (<MSG>) {
		    print $_;
		}
		close (MSG);
		last;
	    }
	}
	
	# If the message isn't found, print a notice
	if ($ok == 0) {
	    print "Message $rmsg not found in the queue!\n";
	    
	}
    }
    
}

# Delete a message in the queue
sub DelMsg {
    my ($rmsg) = shift;
    
    unless ($rmsg =~ /^\d+$/) {
	
	print "$rmsg is not a valid message number!\n";
	
    } else {
	
	# Stop qmail if needed
	&stopQmail() or die "Could not stop qmail: $!";
	
	# Search message
	my ($ok) = 0;
	foreach my $msg(@msglist) {
	    if ($msg =~ /$rmsg$/) {
		$ok = 1;
		unlink "${queue}mess/$msg";
		unlink "${queue}info/$msg";
		if ($type{$msg} eq 'R') {
		    unlink "${queue}remote/$msg";
		} else {
		    unlink "${queue}local/$msg";
		}
		last;
	    }
	}
	
	# If the message isn't found, print a notice
	if ($ok == 0) {
	    print "Message $rmsg not found in the queue!\n";
	}

	# Restart qmail if needed
	&startQmail();
	
    }
}

# Delete all messages in the queue (thanks Kasper Holtze)
sub DelAll {
    my ($rmsg) = shift;

    # Stop qmail if needed
    &stopQmail() or die "Could not kill off qmail: $!";

    # Search messages
    my ($ok) = 0;
    foreach my $msg (@msglist) {
	$ok = 1;
	print "Deleting message $msg...\n";
	unlink "${queue}mess/$msg";
	unlink "${queue}info/$msg";
	if ($type{$msg} eq 'R') {
	    unlink "${queue}remote/$msg";
	} else {
	    unlink "${queue}local/$msg";
	}
    }

    # If no messages are found, print a notice
    if ($ok == 0) {
	print "No messages found in the queue!\n";
    }

    # Restart qmail if needed
    &startQmail();
}

# Make statistics
sub Stats {
    my ($l) = 0;
    my ($r) = 0;

    foreach my $msg(@msglist) {
	if ($type{$msg} eq 'R') { $r++; }
	else { $l++; }
    }

    if ($color == 1) {
	print chr(27)."[01;31mMessages in local queue".chr(27)."[00m: $l\n";
	print chr(27)."[01;31mMessages in remote queue".chr(27)."[00m: $r\n";
    } else {
	print "Messages in local queue: $l\n";
	print "Messages in remote queue: $r\n";
  }
}

# Retrieve pid of qmail-send
sub qmailPid {
    my $qmpid = `$pidcmd`;
    chomp ($qmpid);
    if ($qmpid =~ /^\d+$/) { return $qmpid; }
    return 0;
}

# Stop qmail
sub stopQmail {
    my ($qmpid) = &qmailPid();

    # If qmail is running, we stop it
    if ($qmpid != 0) {

	# If there is a system script available, we use it
	if ($stopqmail ne '') {

	    print "Calling system script to terminate qmail...\n";
	    if (system($stopqmail) > 0) {
		return 0;
	    }

	# Otherwise, we're killers!
	} else {
	    print "Terminating qmail (pid $qmpid)... this might take a while if qmail is working.\n";
	    kill 'TERM', $qmpid;
	    
	    while (&qmailPid() eq "$qmpid\n"){
		sleep 1;
	    }
	}

    # If it isn't, we don't. We also set a flag which assures we don't
    # restart it later either (the user might not want this)
    } else {
	print "Qmail isn't running... no need to stop it.\n";
	$norestart = 1;
    }

    return 1;
}

# Start qmail
sub startQmail {
    my ($qmpid) = &qmailPid();

    # If qmail is running, why restart it?
    if ($qmpid != 0) {
	print "Qmail is already running again, so it won't be restarted.\n";

    # If it wasn't running before qmHandle was launched, it's better leave is this way
    } elsif ($norestart == 1) {
	print "Qmail wasn't running when qmHandle was started, so it won't be restarted.\n";

    # In any other case, we restart it
    } else {
	print "Restarting qmail... ";
	system($startqmail);
	print "done (hopefully).\n";
    }

    return 1;
}

# Print help
sub Usage {
    print "qmHandle v1.0.0\n";
    print "by Michele Beltrame\n\n";
    print "Wrong parameters entered, available ones are:\n";
    print "  -l   : list message queues\n";
    print "  -L   : list local message queue\n";
    print "  -R   : list remote message queue\n";
    print "  -s   : show some statistics\n";
    print "  -vN  : display message number N\n";
    print "  -dN  : delete message number N\n";
    print "  -D   : delete all messages in the queue (local & remote)\n";
    print "Additional (optional) parameters are:\n";
    print "  -c   : display colored output\n";
    print "  -N   : list message numbers only\n";
    print "         (to be used either with -l, -L or -R)\n\n";
    print "You can view/delete multiple message eg -d123 -v456 -d567\n\n";
    exit 1;
}

