#!/bin/bash

# **************************************************************************
# *                                                                        *
# *  Pointtools v.0.3 by Alexey Khromov AKA zxalexis AKA 2:5030/723        *
# *                                                                        *
# **************************************************************************
# *                                                                        *
# * pointadd - add new point to Links, Passwords and Userlist files        *
# *     inserting first spare (with -i option) or adding next to max       *
# *                                                                        *
# **************************************************************************

NMSBASE=""
FIDOBASE=""

if [ -f /etc/fidonms ]; then
    source /etc/fidonms
fi
if [ -z "$NMSBASE" ]; then
    #Try to guess
    NMSBASE=/opt/fidonms
fi
if [ -z "$FIDOBASE" ]; then
    FIDOBASE=/var/spool/ftn
fi

# --------------------------------------------------------------------------
# Here comes a script
# --------------------------------------------------------------------------

# Function find_spare must return a string in format:
#  errorlvl|^next_pnum|first_spnum|next_spnum|passwd_index|links_index|userlist_index|bforce_index
#  errorlvl is 0 if all ok, 1 if need sorting, 2 for nonconsistent (need check)
#  next_spnum - next num after spareif exist. else 0
#  next_pnum - maximum pointnum +1
#  idices are the first lines of NEXT point definition (0 if next_spnum=0) 
#  in passwords, links and userlist files respectively

function find_spare() {
	# echo "function find_spare"
	errorlvl=0
	next_upnum=0
	next_ppnum=0
	next_lpnum=0
	next_bpnum=0
	max_upnum=0
	max_ppnum=0
	max_lpnum=0
	max_bpnum=0
	declare -a upntarri=()    # userlist point numbers
	declare -A upntarrs=()    # userlist point indices
	declare -a dpntarri=()    # Deleted points numbers
	declare -A dpntarrs=()    # Deleted points indices
	declare -a ppntarri=()    # binkd-password num
	declare -A ppntarrs=()
	declare -a bpntarri=()    # bforce-password num
	declare -A bpntarrs=()
	declare -a lpntarri=()    # husky-points-links num
	declare -A lpntarrs=()

	if [[ -f ${USERLIST} ]]; then
		lnum=0
		while read -r line; do
			if [[ "$(echo -ne ${line}| awk -F',' '{ print $1 }')" =~ ^[Pp][Oo][Ii][Nn][Tt]$ ]]; then
				next_upnum=$(echo -ne ${line}| awk -F',' '{print $2}')
				upntarri+=(${next_upnum})
				upntarrs+=([${next_upnum}]=${lnum})
				if [[ ${max_upnum} < ${next_upnum} ]]; then
					max_upnum="${next_upnum}"
				fi
			fi
			if [[ "$(echo -ne ${line}| awk -F',' '{ print $1 }')" =~ ^[Dd][Oo][Ww][Nn]$ ]]; then
                                next_upnum=$(echo -ne ${line}| awk -F',' '{print $2}')
                                dpntarri+=(${next_upnum})
                                dpntarrs+=([${next_upnum}]=${lnum})
                                if [[ ${max_upnum} < ${next_upnum} ]]; then
                                        max_upnum="${next_upnum}"
                                fi
                        fi
			lnum=$(( ${lnum} + 1 ))
		done < ${USERLIST}
		max_upnum=$(( ${max_upnum} + 1 ))
	else
		errorlvl=0
	fi
	
	# Binkdpasswd

	if [[ -f ${BINKD_PASSWD} ]]; then
		lnum=0
		while read -r line; do
			
			tmpaka="$(echo -ne ${line}| awk '{ print $1 }'| awk -F'.' '{ --NF; print }'| sed -e 's/ /./')"

			if [[ ${tmpaka} == ${AKA} ]]; then
				next_ppnum=$(echo -ne ${line}| awk '{ print $1 }'| awk -F'.' '{ print $NF }')
				ppntarri+=(${next_ppnum})
				ppntarrs+=([${next_ppnum}]=${lnum})
				if [[ ${max_ppnum} < ${next_ppnum} ]]; then
					max_ppnum="${next_ppnum}"
				fi
			fi
			# Adding after to store previous lines
			lnum=$(( ${lnum} + 1 ))
		done < ${BINKD_PASSWD}
		max_ppnum=$(( ${max_ppnum} + 1 ))
		# echo "Max num is ${max_ppnum}, array is ${ppntarri[@]}"
		# echo "strings array is ${ppntarrs[@]}"
	else
		# echo "No Binkd passwd file found"
		errorlvl=2
	fi
	
	# Binkleyforce

	if [[ -f ${NMSBASE}/etc/bforce.passwd ]]; then
		lnum=0
		while read -r line; do
			# echo "bforce.passwd processing line $line" >&2
			if [[ $line =~ ^[Pp]assword ]]; then
				# echo "found passwd line" >&2
				tmpaka="$(echo -ne ${line}| awk '{ print $2 }'| awk -F'.' '{ --NF; print }'| sed -e 's/ /./')"
				# echo "tmpaka $tmpaka" >&2
				if [[ ${tmpaka} == ${AKA} ]]; then
					next_bpnum=$(echo -ne ${line}| awk '{ print $2 }'| awk -F'.' '{ print $NF }')
					bpntarri+=(${next_bpnum})
					bpntarrs+=([${next_bpnum}]=${lnum})
					# echo "bforce pnt ${next_bpnum} at line ${lnum}" >&2
					if [[ ${max_bpnum} < ${next_bpnum} ]]; then
						max_bpnum="${next_bpnum}"
					fi
				# Adding after to store previous lines
				fi
			fi	
			lnum=$(( ${lnum} + 1 ))
		done < ${NMSBASE}/etc/bforce.passwd
		max_bpnum=$(( ${max_bpnum} + 1 ))
		#echo "Max num is ${max_bpnum}, array is ${bpntarri[@]}"
		#echo "strings array is ${bpntarrs[@]}"
	else
		# echo "No Binkleyforce passwd file found"
		errorlvl=0
	fi
	
	if [[ -f ${HPT_POINTS} ]]; then
		lnum=0
		loclink=0
		while read -r line; do
			if [[ "$(echo -ne ${line}| awk '{ print $1 }')" =~ ^[Ll][Ii][Nn][Kk]$ ]]; then
				# echo "Link begins at ${lnum}"
				loclink=${lnum}
			fi
			if [[ "$(echo -ne ${line}| awk '{print $1 }')" =~ ^[Aa][Kk][Aa]$ ]]; then

				next_lpnum=$(echo -ne ${line}| awk '{print $2}'| awk -F'.' '{ print $NF }')
				lpntarri+=(${next_lpnum})
				lpntarrs+=([${next_lpnum}]=${loclink})
				if [[ ${max_lpnum} < ${next_lpnum} ]]; then
					max_lpnum="${next_lpnum}"
				fi
			fi
			lnum=$(( ${lnum} + 1 ))
		done < ${HPT_POINTS}
		max_lpnum=$(( ${max_lpnum} + 1 ))
		# echo "Max num is ${max_lpnum}, array is ${lpntarri[@]}"
		# echo "strings array is ${lpntarrs[@]}"
	else
		# echo "No LINKS file found"
		errorlvl=2
	fi

	# Now we have indexes from all 3/4 files (userlist is optional, but preferrable
	# We can a) compare them for consistency, b) find spare one c) throw out 
	# indices for insertion
	declare -a lsortedlst=()
	declare -a usortedlst=()
	declare -a psortedlst=()
	declare -a bsortedlst=()
	declare -a flist=()
	declare -a fsortedlst=() # Full list of *pntarri and dpntarri
	readarray -t lsortedlst < <(printf '%s\n' "${lpntarri[@]}"| sort -g)
	readarray -t psortedlst < <(printf '%s\n' "${ppntarri[@]}"| sort -g)
	readarray -t bsortedlst < <(printf '%s\n' "${bpntarri[@]}"| sort -g)
	
	flist+=( ${lpntarri[@]} )
	flist+=( ${dpntarri[@]} )
	readarray -t fsortedlst < <(printf '%s\n' "${flist[@]}" | sort -g)

	if [ -f "${USERLIST}" ]; then
		readarray -t usortedlst < <(printf '%s\n' "${upntarri[@]}"| sort -g)
		if [[ ${usortedlst[@]} != ${upntarri[@]} ]] && [[ ${errorlvl} = 0 ]]; then
			errorlvl=1
		elif [[ ${usortedlst[@]} != ${psortedlst[@]} ]] || [[ ${usortedlst[@]} != ${lsortedlst[@]} ]]; then
			errorlvl=2
		fi

	fi
	if [[ ${lsortedlst[@]} = ${psortedlst[@]} ]]; then
	    	if [[ ${lsortedlst[@]} != ${lpntarri[@]} ]] || [[ ${psortedlst[@]} != ${ppntarri[@]} ]] ; then
			errorlvl=1
			# echo "LS: ${lsortedlst[@]}"
			# echo "LPNT: ${lpntarri[@]}"
			# echo "PS: ${psortedlst[@]}"
			# echo "PPNT: ${ppntarri[@]}"
		fi
	else
		errorlvl=2
	fi
	
	# Let's find minimal spare
	#
	cntr=1
	for cnt in ${fsortedlst[@]}; do
		if [ ${cntr} != ${cnt} ] && [ ${cnt} != ${#fsortedlst[@]} ]; then
		        sparenum=${cntr}
			nextnum=${cnt}
	 	elif [ ${cnt} = ${cntr} ] && [ ${cnt} = ${#fsortedlst[@]} ]; then
			sparenum=$(( ${cntr} + 1 ))
			nextnum=0
		fi
		cntr=$(( ${cntr} +1 ))		
	done

	echo -ne "${errorlvl}|${max_lpnum}|${sparenum}|${nextnum}|"
	if [ ${nextnum} = 0 ]; then
		echo "0|0|0|0"
	else
		echo -ne "${ppntarrs[$nextnum]}|${lpntarrs[$nextnum]}|"
		if [[ -f ${USERLIST} ]]; then
			echo -n "${upntarrs[$nextnum]}|"
		else
			echo -n "0|"
		fi
		if [[ -f "$NMSBASE/etc/bforce.conf" ]]; then
			echo "${bpntarrs[$nextnum]}"
		else
			echo "0"
		fi

	fi


}


# Initialize variables
cmdaka=""
cmdpnt=""
cmdstr=""
cmdlg=""
cmdag=""
cmdpass=""
cmdins=""
cmdyes=""
cmdfind=""

#TODO If no parameters - call interactive subroutine.

# Read parameters
while [ -n "$1" ]
do
	case "$1" in
		-f)	cfgfile="$2"
			shift;;
		-s)	cmdstr="$2"
			shift;;
		-lg)	cmdlg="$2"
			shift;;
		-ag)	cmdag="$2"
			shift;;
		-pass)	cmdpass="$2"
			shift;;
		-i)	cmdins="1";;
		-q)	cmdquiet="1";;
		-y)	cmdyes="1";;
		*)	cmdusage="1";;
	esac
	shift
done

pwdsource=${BASH_SOURCE[0]}
while [ -L "$pwdsource" ]; do
        pwddir=$( cd -P "$( dirname "$pwdsource" )" > /dev/null 2>&1 && pwd )
        pwdsource=$( readlink "$pwdsource" )
        [[ $pwdsource != /* ]] && pwdsource=$pwddir/$pwdsource
done
pwddir=$( cd -P "$( dirname "$pwdsource" )" > /dev/null 2>&1 && pwd )

if [[ -z ${cfgfile} ]]; then
        if [[ -f "${NMSBASE}/etc/pointcfg" ]]; then
                cfgfile="${NMSBASE}/etc/pointcfg"
        elif [[ -f "/etc/pointcfg" ]]; then
                cfgfile="/etc/pointcfg"
        elif [[ -f "$pwddir/etc/pointcfg" ]]; then
                cfgfile="$pwddir/etc/pointcfg"
        else
                echo "Config file not found. Please specify one"
                exit -2
        fi
fi

# echo "Applying config: ${cfgfile}" >&2
source ${cfgfile}

if [ -f ${NMSBASE}/etc/bforce.passwd ]; then
    BFORCE_PASSWD=${NMSBASE}/etc/bforce.passwd
fi

if [ -f ${FIDOCONFIG} ];
then
    AKA=$(grep -ie ^Address ${FIDOCONFIG}| head -n1 | awk -F'[ @]' '{ print $2 }')
else
    echo "No FIDOCONFIG var found..."
    break;
fi

FCONFIG=${FIDOCONFIG}

if [[ "${cmdusage}" = "1" ]]; then
	echo -ne "Usage: \n\
pointadd {-f cfgfile} {-i} {-q} {-y} -s <pointstr> {-pass <passwd>} {-lg <linkgroup>} \n\
{-ag <accessgroup>}. If any of parameters absent - they are taken from default settings.\n"
	exit -2
fi

if [ -f ${BINKD_PASSWD} ] && [ -f ${HPT_POINTS} ]; then
	
	if [ "${cmdstr}" != "" ]; then
		param=$(echo -ne "${cmdstr}"| awk -F',' '{ print $1 }')
		if [[ "${param}" =~ ^[Pp][Oo][Ii][Nn][Tt]$ ]]; then
			cmdstr=$(echo -ne "${cmdstr}"| cut -d, -f2- )
		fi
		
		cmdpnt=$(echo -ne "${cmdstr}"| awk -F',' '{ print $1 }')
		cmdaka=$(echo -ne "${AKA}.${cmdpnt}")

		if [[ "${cmdpnt}" = "" ]] || [[ ${cmdpnt} = 999 ]]; then
			cmdfind=1
		fi

		spareline=$( find_spare )
		# echo "SpareLine: $spareline" >&2
		sperr=$(echo -ne ${spareline}| awk -F'|' '{ print $1 }')

		if [[ ${sperr} != 2 ]]; then
			# Main adding procedure ---
			# begins here
			if [ "${cmdpass}" = "" ]; then
				cmdpass=${DefPassword}
			fi
			if [ "${cmdlg}" = "" ]; then
				cmdlg=${DefLinkGroup}
			fi
			if [ "${cmdag}" = "" ]; then
				cmdag=${DefAccessGroup}
			fi
			if [ "${cmdfind}" = "1" ]; then
				spspare=$(echo -ne ${spareline}| awk -F'|' '{ print $3 }')
				spmax=$(echo -ne ${spareline}| awk -F'|' '{ print $2 }')
				if [[ "${cmdins}" = "1" ]]; then
					cmdpnt=${spspare}
				else
					cmdpnt=${spmax}
				fi
				cmdaka=$(echo -ne "${AKA}.${cmdpnt}")
			fi
			cmdstr=$(echo -ne "${cmdstr}"| awk -F',' "{ \$1=${cmdpnt}; print }"|sed -e 's/ /,/g')
			if [[ ${cmdyes} != 1 ]]; then
				echo "Add new Point ${cmdaka}: ${cmdstr} ? [y/n]"
				stty_old_cfg=$(stty -g)
				stty raw -echo ; answer=$(head -c 1) ; stty ${stty_old_cfg}
				if [[ "${answer}" != "${answer#[Yy]}" ]]; then
					echo "Adding point..."
				else
					exit -10
				fi
			fi

			
			# TODO: check cmdpnt for non-existance in binkd_passwd (it's
			# easier
			if [[ -f ${USERLIST} ]]; then
				exist=$(cat ${USERLIST}| sed -e '/^\s*#.*$/d' | awk -F',' '{ print $2 }'|grep ${cmdpnt}|wc -l)
			else
				exist=$(cat ${BINKD_PASSWD}| awk '{ print $1 }'|grep ${cmdaka}|wc -l)
			fi
			if [[ "${exist}" != "0" ]]; then
				echo "Point ${cmdaka} already exists!. Can not add another!"
				exit -1
			fi
			
			
			spnext=$(echo -ne "${spareline}" | awk -F'|' '{ print $4 }')
			if [[ "${cmdins}" != "1" ]] || [[ "${spnext}" = "0" ]]; then
				echo "Adding to end of file(s)"
				# TODO: check \n at end of file and add it
				echo -ne "${cmdaka}\t\t${cmdpass}\n" >> ${BINKD_PASSWD}

				if [ -n "$BFORCE_PASSWD" ]; then
					echo -e "password\t\t${cmdaka}\t\t${cmdpass}" >> ${BFORCE_PASSWD}
				fi

				# TODO: check \n at EOF
				# echo "\n" >> ${HPT_POINTS}
				echo -ne "Link $(echo -ne "${cmdstr}"| awk -F',' '{ print $2 }')\n" >> ${HPT_POINTS}
				echo "Aka ${cmdaka}" >> ${HPT_POINTS}
				echo "OurAka ${AKA}" >> ${HPT_POINTS}
				echo -e "Packer ${DefPacker}\nPassword ${cmdpass}" >> ${HPT_POINTS}
				echo -e "NetMailFlavour hold\nEchoMailFlavour hold\nFileEchoFlavour hold" >> ${HPT_POINTS}
				echo "packNetMail ${DefPackNetMail}" >> ${HPT_POINTS}
				echo "arcNetMail ${DefArcNetMail}" >> ${HPT_POINTS}
				echo "LinkGrp ${cmdlg}" >> ${HPT_POINTS}
				echo "AccessGrp ${cmdag}" >> ${HPT_POINTS}
				echo -e "\n" >> ${HPT_POINTS}

				# TODO check for \n in EOF
				if [[ -f "${USERLIST}" ]]; then
					echo "Point,${cmdstr}|${cmdpass}|${cmdlg}|${cmdag}" >> ${USERLIST}
				fi

			else
				# Adding in center of file(s)
				pindex=$(echo -ne "${spareline}"| awk -F'|' '{ print $5 }')
				cat ${BINKD_PASSWD} | sed -e "${pindex} a ""${cmdaka}\t\t${cmdpass}""" > ${BINKD_PASSWD}.new
				mv -f ${BINKD_PASSWD} ${BINKD_PASSWD}.old
				mv -f ${BINKD_PASSWD}.new ${BINKD_PASSWD}
				
				#TODO BFORCE password inj
				if [ -n "$BFORCE_PASSWD" ]; then
					bindex=$(echo -ne "${spareline}"| awk -F'|' '{ print $8 }')
					cat ${BFORCE_PASSWD} | sed -e "${bindex} a ""password\t\t${cmdaka}\t\t${cmdpass}""" > ${BFORCE_PASSWD}.new
					mv -f ${BFORCE_PASSWD} ${BFORCE_PASSWD}.old
					mv -f ${BFORCE_PASSWD}.new ${BFORCE_PASSWD}
				fi
				
				lindex=$(echo -ne "${spareline}"| awk -F'|' '{ print $6 }')
				lblock="Link $(echo -ne ${cmdstr}| awk -F',' '{ print $2 }')\n\
Aka ${cmdaka}\nOurAka ${AKA}\nPacker ${DefPacker}\nPassword ${cmdpass}\nNetMailFlavour hold\n\
EchoMailFlavour hold\nFileEchoFlavour hold\npackNetMail ${DefPackNetMail}\narcNetMail ${DefArcNetMail}\n\
LinkGrp ${cmdlg}\nAccessGrp ${cmdag}\n\n"
				cat ${HPT_POINTS} | sed -e "${lindex} a ""${lblock}""" > ${HPT_POINTS}.new
				mv -f ${HPT_POINTS} ${HPT_POINTS}.old
				mv -f ${HPT_POINTS}.new ${HPT_POINTS}

				if [[ -f ${USERLIST} ]]; then
					uindex=$(echo -ne "${spareline}"| awk -F'|' '{ print $7 }')
					ublock="Point,${cmdstr}|${cmdpass}|${cmdlg}|${cmdag}"
					cat ${USERLIST} | sed -e "${uindex} a ""${ublock}""" > ${USERLIST}.new
					mv -f ${USERLIST} ${USERLIST}.old
					mv -f ${USERLIST}.new ${USERLIST}
				fi

			fi
			if [[ ${sperr} = 1 ]] && [[ ${cmdyes} != 1 ]]; then
				echo "Your configs are not sorted (yet consistent).\n\
Would you like to sort it now? [y/n] "
				# TODO add choice and pointsort call
				old_stty_cfg=$(stty -g)
				stty raw -echo ; answer=$(head -c 1) ; stty ${old_stty_cfg}
				if [ "$answer" != "${answer#[Yy]}" ]; then
					sorting=1
					echo "Sorting..."
				else
					sorting=0
				fi
			fi
					
			if [[ ${sorting} = 1 ]] || [[ ${cmdyes} = 1 ]]; then
				$NMSBASE/bin/pointsort
			fi
		else
			echo "We found errors in pointlinks and passwords files: they are not defining the same points!"
			break
		fi

	else
		echo "Can not add point without Point definition"
	fi
    
else
    echo "No userlists found! Aborting."
fi
