#!/usr/bin/perl
#
# qmHandle
#
# Copyright(c) 1998 -> 2003 Michele Beltrame <mb@italpro.net>
#
# This program is distributed under the GNU GPL.
# For more information have a look at http://www.gnu.org

use strict;
use warnings;
use diagnostics;

my $version = '1.2.0';

#################### 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();
}

# Get command line options

my $color = 0;
my $summary = 0;
my @actions = ();
my $dactions = 0;

foreach my $arg (@ARGV) {
  SWITCH: {
      $arg eq '-a' and do { push @actions, "SendMsgs()"; last 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 =~ /^-m(.+)/ and do { push @actions, "ViewMsg($1)"; last SWITCH; };
      $arg =~ /^-d(.+)/ and do { push @actions, "DelMsg($1)"; $dactions++; last SWITCH; };
      $arg =~ /^-S(.+)/ and do { push @actions, "DelMsgSubj(\"$1\")"; $dactions++; last SWITCH; };
      $arg eq '-D' and do { push @actions, "DelAll()"; $dactions++; last SWITCH; };
      $arg eq '-V' and do { push @actions, "Version()"; last SWITCH; };
      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;
    }

}

# In case of deletion actions, stop qmail
if ($dactions) {
    stopQmail() or die "Could not stop qmail: $!";
}

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

# In case of deletion actions, restart qmail
if ($dactions) {
    startQmail() or die "Could not stop qmail: $!";
}

# ##### SERVICE FUNCTIONS #####

# 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()){
		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;
}

# Returns the subject of a message
sub getSubject {
    my $msg = shift;
    my $msgsub;
    open (MSG, "${queue}mess/$msg") or die("cannot open message $msg");
    while (<MSG>) {
	if ( $_ =~ /^Subject: /) {
	    $msgsub = $';
	    chop ($msgsub);
	} elsif ( $_ eq "\n") {
	    last;
	}
    }
    close (MSG);
    return $msgsub;
}


# ##### MAIN FUNCTIONS #####

# Tries to send all queued messages now 
# This is achieved by sending an ALRM signal to qmail-send
sub SendMsgs {
    my ($qmpid) = qmailPid();

    # If qmail is running, we force sending of messages
    if ($qmpid != 0) {

	kill 'ALRM', $qmpid;

    } else {

	print "Qmail isn't running, can't send messages!\n";

    }
}

# 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 {
	
	# Search message
	my ($ok) = 0;
	foreach my $msg(@msglist) {
	    if ($msg =~ /\/$rmsg$/) {
		$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";
		}
		last;
	    }
	}
	
	# If the message isn't found, print a notice
	if ($ok == 0) {
	    print "Message $rmsg not found in the queue!\n";
	}

    }
}

sub DelMsgSubj {
    my $subject = shift;
    my $msgsub;
    my $delnum = 0;

    print "Looking for messages with Subject: $subject\n";

    # Search messages
    my ($ok) = 0;
    foreach my $msg (@msglist) {
	$msgsub = getSubject($msg);

	if ($msgsub and $msgsub =~ /$subject/) {
	    $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";
	    }
	    $delnum++;
	}

    }

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

}


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

    # 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";
    }

}

# 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;
}

# Print help
sub Usage {
    print "qmHandle v$version\n";
    print "Copyright 1998-2003 Michele Beltrame\n\n";
    print "Available parameters:\n";
    print "  -a     : try to send queued messages now (qmail must be running)\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 "  -mN    : display message number N\n";
    print "  -dN    : delete message number N\n";
    print "  -Stext : delete all messages that have/contain text as Subject\n";
    print "  -D     : delete all messages in the queue (local and remote)\n";
    print "  -V     : print program version\n";
    print "\n";
    print "Additional (optional) parameters:\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";
    print "\n";
    print "You can view/delete multiple message i.e. -d123 -v456 -d567\n\n";
    exit;
}

# Print help
sub Version {
    print "qmHandle v$version\n";
}

