#!/bin/bash
if [ "$1" = 'debug'  ]; then set -x;	_DEBUG=$1; shift; fi
if [ "$1" = 'debug2' ]; then set -xvT;	_DEBUG=$1; shift; fi
if [ "$1" = 'trace'  ]; then 		_TRACE=$1; shift; fi
# - WatchMX -
##############################################
# Don't write with 'logger' to the maillog 
# ... which ends up in recursion and crash ...
##############################################
#-------------------------
REALPATH=`realpath $0`
WHERE=`dirname $REALPATH`
ME=`basename $REALPATH`
cd $WHERE
#------ Core stuff -----------
. ../../system.conf
. ../../watchermap.conf
. ../../common.conf
. ../../common.bashlib
#------ API stuff -------------
. ../../api/bash/sql.bashlib
#------ Private stuff ---------
. $ME.conf
. private.bashlib

trap reload_private		USR1		# Reload private.bashlib
trap refresh			HUP		# React on a 'HUP' for the filter refresh
trap stopservice		TERM INT	# React on a 'KILL'
trap rerun			KILL 9		# React on crash 
trap leave			TRAP		# Leave program on serious errors
trap 'errorexit $? $_'		EXIT		# React on script error

reload_private() {
local funtag="[${FUNCNAME[0]}]"

	source private.bashlib
	trace	"$funtag - Reloaded private.bashlib, $ME[$$]"
}	

refresh() {
local funtag="[${FUNCNAME[0]}]"

	trace	"$funtag Reloading filters for $ME ..."
	logger  "$ME[$$]: $funtag Reloading filters for $ME ..."
	load_filter "$funtag `date --iso=seconds`" $COMPRESS_FILTER
}

errorexit() {
local	funtag="[${FUNCNAME[0]}]"

	logger	"$ME[$$]: $funtag Trapped with exit code: ''$1', Cmd: '$2' ... Loop: $LOOP"
	trace	"$funtag Trapped with exit code '$1', Cmd: '$2' ... Loop:  $LOOP"
        trace   "$funtag BASH Source: $BASH_SOURCE"
	trace	"$funtag Caller     : $(caller)"
	trace	"$funtag BASH line 0: ${BASH_LINENO[0]}"
	trace	"$funtag BASH line 1: ${BASH_LINENO[1]}"
	trace	"$funtag BASH line 2: ${BASH_LINENO[2]}"
	exit 255
}	

stopservice() {
local	funtag="[${FUNCNAME[0]}]"

	logger	 "$ME[$$]: $funtag Terminating on request ..."
	trace	 "$funtag Terminating on request ..."
	exit
}

leave() {
local funtag="[${FUNCNAME[0]}]"
local	rc=$?
local	lastcmd="$BASH_COMMAND"
local	lineno=${BASH_LINENO[0]}

	trace "$funtag $ME leaving at line $lineno: '$lastcmd' (exit code: $rc)"
	logger "$ME[$$]: $funtag leaving at line $lineno: '$lastcmd' (exit code: $rc)"
	exit 254
}

rerun() {
local	funtag="[${FUNCNAME[0]}]"

	log 	"Abnormal termination, Cmd: '$_' ... Loop: $LOOP"
	log 	"Line was: '$REPLY'"
	log	"Rule: $RULE, Bandit: $BANDIT"
	logger 	"$ME[$$] $funtag Restarting in a minute ..."
	trace	"$funtag Restarting in a minute ..."
	echo	"$WHERE/$ME" | at "now + 1 minute"
	exit
}

load_filter() {
# $1 .. filter status
# $2 .. compress flag
local funtag="[${FUNCNAME[0]}]"
	unset -f filter

	[[ -f filter	   	]]  && mv filter filter.old
	[[ -f filter.compress 	]]  && rm -f filter.compress

	./mkfilter $1
	source filter	# Must load here at first for the compress to work

	if [ -f filter.old ]
	then	diff -c filter filter.old >filter-diffs
	else	[[ -f filter-diffs ]] && rm -f filter-diffs
	fi

	if [ ! -z "$2" ]
	then	# If the 'compress filter' flag is set
		# Compress filter; i.e. remove comments for runtime
		compress_filter
		. filter.compress
		trace "$funtag Using filter compressed"
	else	trace "$funtag Using filter plain"
	fi

	# (Re)load superfluous_map as well
	SUPERFLUOUS=`mk_superfluous`
}


#---------------------- Main program ----------------
dump_runtime
config_table $DB

#
# Start companion process 'WatchMB' ...
#
COMPANION=`ps -ef| grep -v "grep"| grep -o "modules/WatchMB" | uniq`
if [ -z "$COMPANION" ]
then	(cd $MASTER_PATH; modules/WatchMB/WatchMB &)
	logger "$ME[$$]: Companion process 'WatchMB' started as process $! ..."
else	logger "$ME[$$]: Companion process 'WatchMB' already running ..."
fi

THIS_IP=`dig +short $HOSTNAME`	# Bash knows HOSTNAME

ln -sf $WHERE/MXinjector $TOOLS_LINK

trace	"--- $ME started on $HOSTNAME ... $SYSTEM $SYSVERS"
trace   "Watcher: $PRODUCT, $REVISION $1"

# Set 'time slice' if missing or empty
#if [ -z "$TIME_SLICE" ]; then TIME_SLICE=60; fi
: "${TIME_SLICE:=60}"

if [ ! -p $PIPE ]
then mkfifo -m 600 $PIPE
fi

touch $LOG		;	chmod 600 $LOG
touch $ME.trace		;	chmod 600 $ME.trace
touch $ME.sql_trace	;	chmod 600 $ME.sql_trace

load_filter Initial $COMPRESS_FILTER

# Here we go ...
log	"Started logging to $LOG ..."

logger  "$ME[$$]: Started listening on pipe $PIPE using dynamic filters"
log	"Started listening on $PIPE using dynamic filters"
trace	"Started listening on $PIPE using dynamic filters"

# Some data structures ...
declare -A	PFtransaction

renice -10 $$
trace "DB calls $SQL ..."
trace "Starting scanner ..."
while :	# Only a crash can stop us ....
do

 	[ -e $DB-new ] && pickDB-new

	# Set loop conditions
	CLASS=''

	read < $PIPE
	(( LOOP++ ))
	: echo "-------Loop: $LOOP----------------"
	LOOP_START=`date +%s%3N`
	#DYN_ADDR=`$DYN_CMD`
	read DYN_ADDR < $LOG_DIR/DYN_IP 
	: echo "$DYN_ADDR"

	# Empty lines kicked-off before we trace anything 
	if [ -z "$REPLY" ]
	then 	: echo "Eeek! Empty line ..."
		continue
	fi

# ------ Internal Rule set (Pre-Processor)--------------
	STAGE="[prepro]"
        # Log lines w/o IP make no sense
	# » Must come FIRST of all «
	RULE=IPpresent
		BANDIT=`get_bandit`
		if [ -z "$BANDIT" ]
		then    : trace "[No IP addr] ... skipped"
			continue
		fi

	#
	# Kick junk off ...
	#
	RULE=Superfluous
		BASH_REMATCH=()
		[[ "$REPLY" =~ ($SUPERFLUOUS) ]] 
		if [ -n "${BASH_REMATCH[0]}" ]
		then	: echo "Superfluous stuff ... ignored ..."
			continue
		fi

	RULE=Freehost
		if [[ "$REPLY" =~ "@Freehost:" ]]
		then
			DEL_ADDR=`get_bandit`
			: trace "$ME/$RULE Deleting $DEL_ADDR from $TABLE"
			freehost $DEL_ADDR
			continue
		fi

       	RULE=Self
		if [[ "$REPLY" =~ "$THIS_IP" ]]
		then    : trace "$ME/$RULE Oops ... me $THIS_IP"
			continue
		fi
		
       	RULE=DynIP
		if [[ "$REPLY" =~ "$DYN_ADDR" ]]
		then    : trace "$ME/$RULE Oops ... my dyn IP $DYN_ADDR"
			continue
		fi

	# Careful here $TRUSTED is a LIST OF IP addresses
       	RULE=Trusted
		if [[ "$REPLY" =~ ($TRUSTLIST) ]]
		then    : trace "$ME/$RULE Oops ... ${BASH_REMATCH[0]} in trusted hosts ... skipping"
			continue
                fi

# ------------[ End pre-processor ]--------------------------

	STAGE="[filter]"
	dump_loadrate
	trace	"[Loop: $LOOP] '$REPLY'"
	trace	"Debuging: Debug: '$_DEBUG', Trace: '$_TRACE'"

	FILT_START=`date +%s%3N`
	filter
	FILT_RESULT=$?
	FILT_END=`date +%s%3N`
	FILT_TIME=$((FILT_END-FILT_START))
	LOOP_TIME=$((FILT_END-LOOP_START))

	STAGE="[postpro]"
	#
	# Post processor ...
	# Check return code of 'filter' ...
	case $FILT_RESULT in
		0)	trace "[postpro] $RULE ... fall through, $LOOP_TIME/$FILT_TIME ms"
		;;
		1)	trace "[postpro] Finished for rule [$RULE], $LOOP_TIME/$FILT_TIME ms"
			continue
		;;
		2)	: echo "[postpro] Passed to WatchMB"
			trace "[postpro] Passed to WatchMB ... $LOOP_TIME/$FILT_TIME ms"
			continue
		;;
		3)	: echo "[postpro] $BANDIT whitelisted ... $LOOP_TIME/$FILT_TIME ms"
			trace "[postpro] $BANDIT whitelisted ... $LOOP_TIME/$FILT_TIME ms"
			continue
		;;
		4)	: echo "[postpro] RC: $FILT_RESULT ... RBL blocked, $LOOP_TIME/$FILT_TIME ms"
			trace "[postpro] RC: $FILT_RESULT ... RBL blocked, $LOOP_TIME/$FILT_TIME ms"
			continue
		#====== Return codes 240...255 reserved for internal use =========================
		;;
		241)	trace "[postpro] Debuging set $Pattern"
			continue
		;;
		254)    trace "[postpro] Ignored ... [$RULE] '$Pattern' $LOOP_TIME/$FILT_TIME ms"
			continue
		;;
		255)	: echo "[postpro] Trash in rule: $RULE, RETCODE '$FILT_RESULT'"
		;;
		*)	: echo "[postpro] Retcode '$FILT_RESULT' unhandled ... fall through"
	;;
	esac

# --------------------	
	# Must be the last rule in chain  just report what is uncovered
	RULE=UNTREATED
	log	"[$RULE] '$REPLY'"
	trace	"[$RULE] '$REPLY'"
done

#  Normally never reached - except on erratic termination
logger  "$ME[$$] Terminated erratically"
log	"Erratically terminated, exit status: $?, Loop: $LOOP"
trace	"Erratically terminated, exit status: $?, Loop: $LOOP"

#
# We restart ourselves ...
#
rerun
