#!/bin/bash -e
#set -e
#Copyright (c) 1999, William L. Stearns <wstearns@pobox.com>
#See top of Mason script for copyright and licensing information.
#Last YYYY code used: 0019, use 0020 next

CKPTMGT=" mgt: Ground0" ; #ckpt $CKPTMGT

MASONDIR=${MASONDIR:-"/var/lib/mason/"}
MASONCONF=${MASONCONF:-"/etc/masonrc"}
MASONLIB=${MASONLIB:-"${MASONDIR}masonlib"}
#MASONLIB=${MASONLIB:-"${MASONDIR}masonlib"}	#My editor of choice (mcedit) gets quoting backwards if there is an odd number of these.

if [ -f $MASONLIB ]; then
	. $MASONLIB
else
	echo Missing $MASONLIB library file.  Please get a complete copy of Mason from >/dev/stderr
	echo http://www.pobox.com/~wstearns/mason/ .  Exiting. >/dev/stderr
	sleep 10
	#FIXME - exit 1?
	exit 0
fi

trap preexit 0		#This gets nullified if we get a SIGHUP or SIGINT -  see mgtcleanup.

killrunningmason () {
	#A backgrounded Mason is a pain in the %$^#^%$ to kill...
	#I think I may have found my wooden stake.  Have the equivalent of a pipe to mason; a 
	#file which is tailed along with /var/log/messages into mason when it's run.  When 
	#we need to kill mason, echo '!!EXIT!!' into it and have mason look for it on stdin.
	#Until I'm sure this works, I'll keep the old "bludgeon it to death with sigs" 
	#approach as a backup.
	if [ -n "$KILLFILES" ]; then
		for ONEKILLFILE in $KILLFILES ; do
			echo '!!EXIT!!' >>$ONEKILLFILE
		done
		sleep 5
		for ONEKILLFILE in $KILLFILES ; do
			rm -f $ONEKILLFILE 2>/dev/null || :
		done
		KILLFILES=""
	fi

	if [ -n "$MASONTAILPID" ]; then	kill -TERM $MASONTAILPID 2>/dev/null || : ;	MASONTAILPID="" ; sleep 1 ;	fi
	if [ -n "$MASONPID" ]; then
		#FIXME - check if it's still running first?
		#echo Killing active $MASONPID >/dev/stderr
		kill -HUP $MASONPID 2>/dev/null || :
		sleep 2
		kill -TERM $MASONPID 2>/dev/null || :
		MASONPID=""
	fi

	if [ -f "$MASONPIDFILE" ]; then
		for ONEPID in `cat $MASONPIDFILE` ; do
			if type -path ps >/dev/null 2>/dev/null && type -path grep >/dev/null 2>/dev/null ; then
				if [ -n "`ps ax | grep "^[[:space:]]*$ONEPID[[:space:]]" | grep mason`" ]; then
					#echo Killing background $ONEPID >/dev/stderr
					kill -TERM $ONEPID 2>/dev/null || :
					sleep 2
					kill -9 $ONEPID 2>/dev/null || :
				fi
			else
				kill -TERM $ONEPID 2>/dev/null || :
			fi
		done
		cat /dev/null >$MASONPIDFILE || :
	fi
}

CKPTMGT=" mgt: trap" ; #ckpt $CKPTMGT

mgtcleanup () {
	#FIXME - should this be 'trap 0'?
	trap - 0	#If we received a signal, no need to process a "crash".
	unset CKPTMGT CKPTMASON CKPTCHECKSYS CKPTCLIENTPORTRANGE \
	 CKPTGENERALIZEIP CKPTIPLE CKPTIPLT CKPTISNUMERICIP CKPTLOADCONF \
	 CKPTSERVERPORTRANGE CKPTADDCOUNTS CKPTNAMEOF CKPTBROADCASTOF \
	 CKPTCHECKCONF CKPTDELCOUNTS CKPTFLUSHFIREWALL CKPTPORT2CISCOPORT \
	 CKPTPROTONUM2NAME CKPTRULETAG CKPTRUNFIREWALL \
	 CKPTSETTOS CKPTSORTRULEFILE \
	 CKPTUNIQRULEFILE CKPTUPDATECOUNTS CKPTNETWORKOF || :
	if [ -n "$VIEWTAILPID" ]; then	kill -TERM $VIEWTAILPID 2>/dev/null || : ;	VIEWTAILPID="" ;			fi
	killrunningmason
	CKPTMGT=""
	echo
	echo Exiting - you may need to reset the firewall...
	exit 0
}
trap mgtcleanup SIGINT		#Ctrl-C generates this


if [ -f $MASONCONF ]; then
	. $MASONCONF
fi

CKPTMGT=" mgt: start" ; #ckpt $CKPTMGT

checksys
checkconf


EXITCONTROL="NO"
MASONPID=""
MASONTAILPID=""
VIEWTAILPID=""
KILLFILES=""

killrunningmason

while [ ! "$EXITCONTROL" = "YES" ]; do
	clear
	echo "---- Mason firewall builder for Linux                         ----"
	echo "---- Learning shell for Mason.                                ----"
	echo "---- see http://www.pobox.com/~wstearns/mason/ for more info. ----"
	echo "---- William Stearns <wstearns@pobox.com>                     ----"
	echo "(This is release $MASONVER )"
	if [ -n "$MASONPID$MASONTAILPID" ]; then
		echo Mason is currently in learn mode.
		echo
		echo EL: End Learning.
	else
		echo Mason IS NOT currently learning.
		echo
		echo BL: Begin Learning.
	fi
	echo EB: Edit Base firewall rule file.	#=> end learn
	echo EN: Edit New firewall rule file.	#=> end learn, ask to merge rulefiles when done
	echo MR: Merge Rules from new to base.
	echo CS: Change Mason settings.			#=> sigusr1 mason when done
	echo LC: Lock this terminal and display new rules.
	echo Q: Quit.
	#CKPTMGT=" mgt: Waiting for main menu choice" ; #ckpt $CKPTMGT
	CKPTMGT="" ; #ckpt $CKPTMGT
	read CHOICE
	CKPTMGT=" mgt: Calling $CHOICE" ; #ckpt $CKPTMGT
	case $CHOICE in
	[Bb][Ll])
		echo Begin Learning.
		echo 
		CKPTMGT=" mgt: BL, flushing" ; #ckpt $CKPTMGT
		flushfirewall
		CKPTMGT=" mgt: BL, learning" ; #ckpt $CKPTMGT
		runfirewall LEARN
		CKPTMGT=" mgt: BL, set killfile" ; #ckpt $CKPTMGT
		NEWKILLFILE="$MASONDIR$RANDOM.tempflagfile"
		touch $NEWKILLFILE || logfail $LINENO mason-gui-text: YYYY 0019
		KILLFILES="$KILLFILES $NEWKILLFILE"
		CKPTMGT=" mgt: BL, tail" ; #ckpt $CKPTMGT
		#Is there _any_ _friggin'_ _way_ to handle this cleanly and catch a non-zero return code?
		#Should we set +e here?
		( tail -q -f --lines=0 $PACKETLOGFILE $KILLFILES | $MASONEXE >>$NEWRULEFILE ) &		#nohup just before tail removed
		MASONTAILPID="$MASONTAILPID `ps axf | grep -v grep | grep -A 1 '^[[:space:]]*$![[:space:]]' | tail -1 | awk '{print $1}' || logfail $LINENO mason-gui-text: YYYY 0001`"
		#I am open to ideas on better ways to figure out the pid of the tail command.  Anyone?  Bueller?
		MASONPID="$MASONPID `ps axf | grep -v grep | grep -A 2 '^[[:space:]]*$![[:space:]]' | tail -1 | awk '{print $1}' || logfail $LINENO mason-gui-text: YYYY 0002`"
		#FIXME - display this first so as not to mix output?
		echo Mason is now running in the background.  I will be showing the log 
		echo output.  Press Enter when you want to return to the main menu - Mason
		echo will continue to run in the background until you choose \"EL\" from the
		echo main menu.
		#tail -f --lines=0 $PACKETLOGFILE $NEWRULEFILE &
		CKPTMGT=" mgt: BL, background tail" ; #ckpt $CKPTMGT
		set +e      #Turn off failure checking - I don't know how to handle a failure return code from it.
		tail -f --lines=0 $NEWRULEFILE &
		VIEWTAILPID="$VIEWTAILPID $!"
		set -e
		echo Press ENTER to return to the main menu
		CKPTMGT=" mgt: BL, press enter" ; #ckpt $CKPTMGT
		read JUNK
		CKPTMGT=" mgt: BL, killtail" ; #ckpt $CKPTMGT
		kill -TERM $VIEWTAILPID 2>/dev/null || : ; VIEWTAILPID=""
																;;
	[Ee][Ll])
		if [ -n "`cat $NEWRULEFILE | sed -e 's/#.*//' | grep -v '^[[:space:]]*$' | grep -v '^export' || :`" ]; then		#formerly YYYY 0003
			echo You still have unmerged rules in $NEWRULEFILE.  If you continue, your 
			echo firewall will cease to contain these rules.  Instead of continuing, you should
			echo finish deleting or merging these rules.  Do you want to continue Y/N?
			read CONTINUE
		else
			CONTINUE="Y"
		fi
		case $CONTINUE in
		[Yy]*)
			echo End Learning.
			killrunningmason
			flushfirewall
			runfirewall STANDARD
																	;;
		esac
																;;
	[Ee][Bb])
		echo Edit Base rule file.
		if [ -z "$EDITOR" ]; then
			echo EDITOR was not set in your environment
			echo Please set it with something like \"export EDITOR=mcedit\"
		else
			cp -pf $BASERULEFILE $BASERULEFILE.bak || logfail $LINENO mason-gui-text: YYYY 0004
			addcounts $BASERULEFILE
			$EDITOR $BASERULEFILE || logfail $LINENO mason-gui-text: YYYY 0005
			if diff -bB <(sed -e 's/#.*//' -e 's/[[:space:]]//g' $BASERULEFILE | uniq || logfail $LINENO mason-gui-text: YYYY 0006) <(sed -e 's/#.*//' -e 's/[[:space:]]//g' $BASERULEFILE.bak | uniq || logfail $LINENO mason-gui-text: YYYY 0007) >/dev/null ; then
				echo No changes made.
			else
				echo Changes made, restarting firewall
				flushfirewall
				if [ -n "$MASONPID$MASONTAILPID" ]; then
					runfirewall LEARN
				else
					runfirewall STANDARD
				fi
			fi
		fi
																;;
	[Ee][Nn])		
		echo Edit the new rule file.
		if [ -z "$EDITOR" ]; then
			echo EDITOR was not set in your environment
			echo Please set it with something like \"export EDITOR=mcedit\"
		else
			if [ -n "$MASONPID$MASONTAILPID" ]; then echo Ending the learn process. ;										fi
			killrunningmason
			uniqrulefile $NEWRULEFILE
			$EDITOR $NEWRULEFILE || logfail $LINENO mason-gui-text: YYYY 0008
			if diff -bB <(sed -e 's/#.*//' -e 's/[[:space:]]//g' $NEWRULEFILE | uniq || logfail $LINENO mason-gui-text: YYYY 0009) <(sed -e 's/#.*//' -e 's/[[:space:]]//g' $NEWRULEFILE.bak | uniq || logfail $LINENO mason-gui-text: YYYY 0010) >/dev/null ; then
				echo No changes made.
			else
				if [ -n "$MASONPID$MASONTAILPID" ]; then
					echo Changes made, restarting firewall
					flushfirewall
					runfirewall LEARN
				else
					echo Changes made, but no need to restart firewall as Mason is not running.
				fi
			fi
		fi
																;;
	[Mm][Rr])
		CKPTMGT=" mgt: start mr" ; #ckpt $CKPTMGT
		echo Do you want to merge some, all, or none of the rules in this file
		echo to the base rule file?  Enter \"some\", \"all\", or \"none\".
		unset WHATTOMERGE || :
		read WHATTOMERGE || :
		case $WHATTOMERGE in
		[Aa][Ll][Ll])
			CKPTMGT=" mgt: mr all" ; #ckpt $CKPTMGT
			cp -pf $BASERULEFILE $BASERULEFILE.bak || logfail $LINENO mason-gui-text: YYYY 0011
#FIXME - should we or not?
			#uniqrulefile $NEWRULEFILE
			echo "#Rules merged from the new rule file:"	>>$BASERULEFILE
			#cat $NEWRULEFILE | sed -e 's/^/#/'				>>$BASERULEFILE #Use this version if you want to force them commented
			cat $NEWRULEFILE								>>$BASERULEFILE || logfail $LINENO mason-gui-text: YYYY 0012
			cat /dev/null >$NEWRULEFILE || logfail $LINENO mason-gui-text: YYYY 0013
			echo You will probably want to edit the base rule file next.
																	;;
		[Ss][Oo][Mm][Ee])
			CKPTMGT=" mgt: mr some" ; #ckpt $CKPTMGT
			echo What string identifies the rules to merge?
			unset MERGEID || :
			read MERGEID || :
			echo There are `grep "$MERGEID" $NEWRULEFILE | wc -l` rules with that ID, and
			echo `grep -v "$MERGEID" $NEWRULEFILE | wc -l` lines without it, `cat $NEWRULEFILE | wc -l` total.
			echo Do you want to continue \(yes/no\)?
			unset YESNO || :
			read YESNO || :
			case $YESNO in
			[Yy][Ee][Ss]|[Yy])
				CKPTMGT=" mgt: mr some continue" ; #ckpt $CKPTMGT
				cp -pf $BASERULEFILE $BASERULEFILE.bak || logfail $LINENO mason-gui-text: YYYY 0014
#FIXME - should we or not?
				#uniqrulefile $NEWRULEFILE
				CKPTMGT=" mgt: mr some continue header" ; #ckpt $CKPTMGT
				echo "#Rules merged from the new rule file:"	>>$BASERULEFILE
				#cat $NEWRULEFILE | grep "$MERGEID" | sed -e 's/^/#/'	>>$BASERULEFILE
				CKPTMGT=" mgt: mr some continue append to base" ; #ckpt $CKPTMGT
				cat $NEWRULEFILE | grep "$MERGEID"						>>$BASERULEFILE || :
				CKPTMGT=" mgt: mr some continue remove from new" ; #ckpt $CKPTMGT
				cat $NEWRULEFILE | grep -v "$MERGEID" >$NEWRULEFILE.tmp || :	#Grep returns false if we happen to merge everything.
				CKPTMGT=" mgt: mr some continue overwrite new" ; #ckpt $CKPTMGT
				cat $NEWRULEFILE.tmp >$NEWRULEFILE || :
				CKPTMGT=" mgt: mr some continue remove tmp" ; #ckpt $CKPTMGT
				rm -f $NEWRULEFILE.tmp || :
				echo You will probably want to edit the base rule file next.
																		;;
			*)	echo Aborting merge.									;;
			esac
																	;;
		esac
																;;
	[Cc][Ss])
		if [ -z "$EDITOR" ]; then
			echo EDITOR was not set in your environment
			echo Please set it with something like \"export EDITOR=mcedit\"
		else
			#echo Here are the non-comment lines in $MASONCONF:
			#echo
			#cat $MASONCONF | sed -e 's/#.*//' | grep -v '^$' | uniq
			#echo
			#echo Press enter to edit this file
			#read JUNK
			cp -pf $MASONCONF ${MASONDIR}masonrc.bak || :
			$EDITOR $MASONCONF || logfail $LINENO mason-gui-text: YYYY 0015
			if diff -bB <(sed -e 's/#.*//' -e 's/[[:space:]]//g' $MASONCONF | uniq || logfail $LINENO mason-gui-text: YYYY 0016) <(sed -e 's/#.*//' -e 's/[[:space:]]//g' ${MASONDIR}masonrc.bak | uniq || logfail $LINENO mason-gui-text: YYYY 0017) >/dev/null ; then
				echo No changes made.
				rm -f ${MASONDIR}masonrc.bak || :
			else
				if [ -f $MASONCONF ]; then
					. $MASONCONF
				fi
				checkconf
				if [ -n "$MASONPID$MASONTAILPID" ]; then
					echo Changes made, signalling mason to reread configuration.
					kill -USR1 $MASONPID 2>/dev/null || :
				else
					echo Changes made.
				fi
			fi
		fi
																;;
	[Ll][Cc])
		if ! type -path ps >/dev/null 2>/dev/null ; then
			echo The vlock utility is not on this system.
			echo It is required to be able to lock the console.
			echo Once installed, this option will be available again.
			sleep 15
		else
			if [ -n "$MASONPID$MASONTAILPID" ]; then
				#tail -f --lines=0 $PACKETLOGFILE $NEWRULEFILE &
				#FIXME - 'set +e' here?
				tail -f --lines=0 $NEWRULEFILE &
				VIEWTAILPID="$VIEWTAILPID $!"
			else
				echo Mason is not currently learning, but I will lock this terminal anyways.
			fi	
			vlock || :
			if [ -n "$VIEWTAILPID" ]; then	kill -TERM $VIEWTAILPID 2>/dev/null || : ; VIEWTAILPID="" ;			fi
		fi
																;;
	[Qq])
		if [ -n "`cat $NEWRULEFILE | sed -e 's/#.*//' | grep -v '^[[:space:]]*$' | grep -v '^export' || :`" ]; then		#formerly YYYY 0018
			echo You may still have unmerged rules in $NEWRULEFILE.  If you continue, your 
			echo firewall will cease to contain these rules.  Instead of continuing, you should
			echo finish deleting or merging these rules.  Do you want to continue Y/N?
			unset CONTINUE || :
			read CONTINUE || :
		else
			CONTINUE="Y"
		fi
		case $CONTINUE in
		[Yy]*)
			EXITCONTROL="YES"
			echo Exiting.  Returning to the standard firewall.
			if [ -n "$VIEWTAILPID" ]; then	kill -TERM $VIEWTAILPID 2>/dev/null || : ;	VIEWTAILPID="" ;			fi
			killrunningmason
			flushfirewall
			runfirewall STANDARD
																	;;
		esac
																;;
	*)	echo Unknown choice.  Please enter the one or two character code.
																;;
	esac

	sleep 2
done

CKPTMGT=""

