#!/bin/sh
#
# procdiag.sh - a quick procmail diagnostic
#
#  File: 	procdiag.sh
#  Description:	a quick procmail diagnostic
#  Author:	Sean B. Straw
#  Source:	<http://www.professional.org/procmail/procdiag.sh>
#  Copyright:	Portions copyright (c) 2001-2003, Sean B. Straw
#  Disclaimer:	<http://www.professional.org/procmail/disclaimer.html>
#  Licensing:	Free for use by the procmail community.
#  Support:	Visit the official procmail discussion list to ask
#		procmail questions.  If you need custom procmail
#		work performed, the author is available for paid consulting.
#
# Usage: the script takes no arguments and simply emits all collected
# results to STDOUT.  If you need explanation of how to pipe it to a pager
# or redirect it to a file to provide to someone else, please learn some
# basic UNIX before attempting to use procmail.  Before diving into procmail,
# you should gain a fundamental understanding of unix - shell operations, how 
# to check/set file permissions, etc.
#
# If you run a non-compliant shell, you may find this script is executed
# more reliably if you manually run it under a shell:
#		sh ./procdiag.sh
# rather than assuming that your shell will obey the shellcmd definition at
# the top of this script.
#
# The procmail list IS NOT an intro to UNIX discussion list, so basic 
# questions about UNIX are inappropriate there.
#
# This script relies on other tools in your system, primarily a working perl
# (which it uses in an attempt to be much less reliant on less standard 
# versions of other tools).
#
# If you are running a personally compiled version of procmail, one should
# hope that it is in your path...
#
# This script was scrawled up by Sean B. Straw to assist with collecting
# up the various bits of data necessary to assist newbie users who cannot
# figure out what is wrong with their procmail configuration.  It was
# developed with contributions from several members of the procmail mailing
# list.
#
# The author of this script reserves the copyright on behalf of procmail
# users worldwide, but declares this script free for public use.  Don't sell
# stuff that ain't yours.  Do not republish without express permission.
#

# report procdiag version
echo "# procdiag v20031105.1723"
# report when this was run
echo "# procdiag run at `date`"
echo

# function generously provided by Don Hammond
# this reports file permissions in a chmod-like numeric form.
# SBS modified to check optional perms via mask, shows dirs with trailing
# slash, removed 'function ' prefix for compatibility with non-bash sh, and
# take arguments on commandline rather than via environment.
lscmd()
{
	perl -Mstrict -we'
		my $file = $ARGV[0];
		my $perms = $ARGV[1];	# optional permission mask
		unless( -r $file ) 
		{ 
			print "$file: file not found or insufficient permission\n";
			exit;
		}
		my @s = (stat _)[2..9];
		$s[0] = unpack("x2A4",sprintf("%06o",$s[0]));
		$s[2] = getpwuid($s[2]);
		$s[3] = getgrgid($s[3]);  
		splice(@s,4,1);
		splice(@s,5,1);
		$s[5] = scalar localtime($s[5]);
		printf "%04d %2d %-8s %-8s %7d %s %s%s\n", @s, $file, ( -d $file ) ? "/" : "" ;
		if ( $perms )
		{
			# if permission mask was passed, ensure that the file perms
			# do not exceed the perms in the mask.
			if ( ( "$s[0]" & "$perms" ) != "$s[0]" )
			{
				print "CAUTION: $file perms exceed $perms: " .
							"curb back to " . ( "$s[0]" & "$perms" ) . "\n";
				exit;
			}
		}
' -- $1 $2
}

# SBS: perlgrep - since archaic greps which may be encountered on equally
# archaic *nix distributions don't offer features consistent with gnu'er
# grep, we resort to using perl to work around those limitations.
# NOTE: This is *REALLY* rough, and is intended to handle just those handful
# of specific grep usages we use in this script.  Don't complain about
# efficiency unless you want to rewrite a portable version.
pgrep()
{
	perl -Mstrict -we'
		my ($notins, $exlines, $exlinec, $file, $string);
		my $count=0;
		my $match=0;
		# cheezy - we know we have a handful of grep flags we use, so check for
		# them individually.  I do not feel like writing a complete grep in perl.
		if ( $ARGV[0] =~ /-vi/ )
		{
				$notins=1;
				$count++;
		}
		elsif ( $ARGV[0] =~ /-A2/ )
		{
				$exlines=2;
				$count++;
		}

		$string = $ARGV[$count++];
		$file = $ARGV[$count];

		if ( $file )
		{
			unless( -r $file ) 
			{ 
				print "pgrep: $file: No such file or directory\n";
				exit ( 1 );
			}
		}
		else
		{
			# no filename specified, use stdin
			$file = "-"
		}

		# open the file (or stdin), read until done, taking each line and
		# checking it for matching text (or nonmatching).
		open( SCAN, $file ) || die "could not open $file\n" ;

		while ( <SCAN> )
		{
			if ( $notins )
			{
				if ( ! m/$string/i )
				{
					$match = 1;
					print $_;
				}
			}
			else
			{
				if ( m/$string/ )
				{
					$match = 1;
					print $_;
					if ( $exlines )
					{
						$exlinec = $exlines;
						while ( $exlinec-- && ( $_ = <SCAN> ) )
						{
							print $_;
						}
					}
				}
			}
		}
		close( SCAN );
		exit !$match;
' -- $@
}

# report general account details
echo "# general account information:"

# report who this user is, and to what groups they belong
echo "USER: $USER (`whoami`)"
echo "GROUPS: `groups`"
echo "SHELL: $SHELL"
echo "MAIL: $MAIL"
# report hostname / FQDN
# don't know how to reliably determine if hostname can return a FQDN.
echo "hostname: `hostname -s` ($HOSTNAME)"
echo "FQDN: `perl -e '($fqdn) = gethostbyname("$ARGV[0]"); print $fqdn;' \`hostname -s\``"

# Deal with $HOME = "/" (usu. for user root, which some people may, for some
# reason or another, choose to run this script as).  Since
echo "HOME: $HOME"
# if $HOME is trailed by a backslash, this could be the source of some
# user problems (usual uses of $HOME would have it separated from the next
# element by a slash).  This produces a copy of $HOME which doesn't have this
# trailing backslash (which we can then use within this script to still
# access files in the home dir, since the tilde syntax doesn't work
# universally).
PD_HOME=`echo $HOME|sed -e 's/\/$//'`
if [ "$PD_HOME" != "$HOME" ];then
	echo "CAUTION: \$HOME definition has trailing backslash."
fi

echo

echo "# user info from /etc/passwd (password is masked):"
# Just in case the system is in the dark ages and isn't using shadow
# passwords, we blast the password field as if it were shadowed - this
# is because the results of this script might be made available to others
# and we don't want to knowingly compromise the security of the users
# account.
pgrep ^$USER: /etc/passwd|sed -e 's/^\([^:]*:\)\([^:]*:\)/\1x:/'
echo

# report system type (what's available on linux at least)
# how to do this generically?
echo "# system identifiers (if discernible):"
echo "OSTYPE: $OSTYPE"
echo "MACHTYPE: $MACHTYPE"
echo "UNAME: `uname -a`"
#dmesg|head -1
echo

# display the current path
echo "# The current shell path is (not to be confused with the MTA-defined path):"
echo "PATH: $PATH"
echo

# Report the running procmail and formail locations (in path) AND report
# versions.  Because procmail and formail both emit version info to STDERR,
# we must redirect STDOUT to STDERR in order to access it.
# We also nab the bottom three lines from the procmail version banner, since
# they typically show the locking, rcfile, and default system mailbox.
echo "# formail and procmail information (as per versions in the current path):"
lscmd `which formail`
formail -v 2>&1 | head -1
echo

lscmd `which procmail`
procmail -v 2>&1 | head -1
procmail -v 2>&1 | tail -3
echo

echo "# various procmail configuration elements:"
eval `procmail < /dev/null 2>&1 DEFAULT=/dev/null \
 LOG='pm_mailbox=$ORGMAIL pm_mta=$SENDMAIL \
 pm_sendmailflags=$SENDMAILFLAGS \
 pm_host=$HOST pm_version=$PROCMAIL_VERSION \
 pm_linebuf=$LINEBUF pm_shell=$SHELL pm_path=$PATH \
 pm_shellmetas=\"$SHELLMETAS\"' /dev/null`
echo ORGMAIL=\"$pm_mailbox\"
echo SENDMAIL=\"$pm_mta\"
echo SENDMAILFLAGS=\"$pm_sendmailflags\"
echo HOST=\"$pm_host\"
echo PROCMAIL_VERSION=\"$pm_version\"
echo LINEBUF=\"$pm_linebuf\"
echo PATH=\"$pm_path\"
echo SHELL=\"$pm_shell\"
echo SHELLMETAS=\"$pm_shellmetas\"
echo

# take the pm_mailbox and strip what is presumed to be the username from it
# so that we simply have a dirname:
mailspool=`echo $pm_mailbox|sed -e 's/\/[^\/]*$//'`

# report the usage banner from grep (archaic version, or something gnu?)
echo "# usage banner from grep"
grep 2>&1
echo

# report the version of sed (since it is so often used)
echo "# sed version or other identifier"
lscmd `which sed`
which what > /dev/null
if [ $? != 0 ];then
 # gnu?
 sed --version
else
 # BSD? 
 what `which sed`
fi
echo


# report the file listing for 'sh' - is it a symlink or a real sh?
echo "# sh info (intended to show whether sh is sh or a symlink to another shell)"
ls -l `which sh`
echo

# report sendmail info
# (assumes sendmail is the MTA - how to determine?)
echo "# sendmail program information (from procmail's \$SENDMAIL):"
lscmd $pm_mta
# get the version and options.  pipe through fgrep to eliminate a message
# which might be misinterpreted by some as an error, but which is really
# just a side effect of the address testing ruleset invocation.
# Here, we use the $SENDMAIL variable from procmail to locate the MTA:
$pm_mta -d0 < /dev/null | pgrep -vi Recipient\\snames
echo

# report sendmail local delivery program
# 20020619 SBS	changed to use a sendmail diagnostic to obtain the LDA
#	config, thus eliminating the need for us to know where the sendmail.cf is
#	located (standard or otherwise).  The script still assumes that sendmail
#	is the MTA - hopefully at some point, someone with postfix, qmail, exim,
#	etc will provide some useful feedback.
echo "# Determining Mlocal via sendmail diagnostic invocation"
$pm_mta -d0.15 < /dev/null | pgrep \(local\) | pgrep /procmail
if [ "$?" != "0" ];then
 echo "NOTE: procmail doesn't appear to be the LDA"
else
 echo "NOTE: procmail appears to be the LDA"
 PROCLDA=1
fi
echo

#dump the contents of the .forward file
echo "# contents of $PD_HOME/.forward (if it exists)"
if [ -e $PD_HOME/.forward ]; then
	cat $PD_HOME/.forward
	echo "# An alternative .forward, not checked against the above, but merely"
	echo "# offered here, is:"
	echo "\"|IFS=' ' && exec "`which procmail`" -f- || exit 75 #$USER\""
	if [ "$PROCLDA" == "1" ]; then
		echo "NOTE: Since procmail appears to be your LDA, use of the .forward file should"
		echo "      be unnecessary unless you have some special invocation requirement."
	fi
else
	echo "NOTE: no $PD_HOME/.forward"
	if [ "$PROCLDA" == "1" ]; then
		echo "NOTE: This is not unexpected since procmail appears to be the LDA, and thus"
		echo "      will be invoked directly by the MTA."
	fi
fi
echo

# show perms of various file for which permissions are potentially
# significant.
# ~/Mail and ~/mail are included as _common_ destinations for filtered mail.
# old sh implementations don't handle tilde, so use $HOME
echo "# file permissions and ownership:"
# specific security risks
for FILE in $PD_HOME/.procmailrc $PD_HOME/.forward $PD_HOME /etc/procmailrc /etc/procmailrcs ; do
 if [ -e $FILE ]; then
  lscmd $FILE 7755
 else
  echo "NOTE: There is no $FILE file."
 fi
done
# general security risks
for FILE in $PD_HOME/Mail $PD_HOME/mail $PD_HOME/.procmail $pm_mailbox ; do
 if [ -e $FILE ]; then
  lscmd $FILE 7755
 else
  echo "NOTE: There is no $FILE file."
 fi
done
# general info
for FILE in $mailspool ; do
 if [ -e $FILE ]; then
  lscmd $FILE 7777
 else
  echo "NOTE: There is no $FILE file."
 fi
done
echo

# signify end of report
echo "# procdiag report end"
