#! /bin/bash

set -e

# Copyright (C) 2022-2023 Pädagogisches Landesinstitut Rheinland-Pfalz
# Copyright (C) 2022-2023 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.

# This script has been localized...
export TEXTDOMAIN="debian-edu-router-fai"
. gettext.sh
. /etc/environment.FAI-dialogs

mask2cdr() {
   # Assumes there's no "255." after a non-255 byte in the mask
   local x=${1##*255.}
   set -- 0^^^128^192^224^240^248^252^254^ $(( (${#1} - ${#x})*2 )) ${x%%.*}
   x=${1%%$3*}
   echo $(( $2 + (${#x}/4) ))
}

#
#   Check if IPv4 network is available and offer manual configuration otherwise.
#
#   Set env variable TEST_MODE to some value if you want to test the dialogs of
#   this script as a developer.
#

if [ -z "${TEST_MODE}" ]; then
	URL="http://deb.debian.org/"
	_tmp="/tmp"
else
	URL="http://some.unknown.server/"
	_tmp="$(mktemp -d)"
fi

# The network configuration part in this script will only do the
# absolutely necessary. The real network setup (external, internal,
# VLANs, etc.) is done by DEB package debian-edu-router-config...

# make sure, we can create ${_tmp}/network-check-v4
touch ${_tmp}/network-check-v4

red=$(mktemp)
echo 'screen_color = (CYAN,RED,ON)' > $red

function show_dialogs_manual_ipv4_conf() {
	uplink_iface=""

	STATE=1
	QCOUNT=5

	while [ "$STATE" != 0 -a "$STATE" != "$(($QCOUNT+1))" ]; do
		case "$STATE" in
			1)
				# Pick / Discover the NIC for uplink internet connectivity
				while [ -z "${uplink_iface}" ]; do

					ifaces_connected=`ip a | grep -E "^[0-9]+:.*" | grep -v "NO-CARRIER" | awk '{ print $2}' | sed -e "s/:.*//" | grep -E -v "(lo|vnet[0-9]+|virbr[0-9]+|lxcbr[0-9]+|docker[0-9]+|br-[a-f0-9]{12})"`

					# No interfaces connected? If so, throw a warning and offer retry or cancel
					if [ -z "${ifaces_connected}" ]; then

						if ! { dialog --stdout --backtitle " $(eval_gettext "Configure Internet Access") " \
							--title " $(eval_gettext "No network adapter connected!") " \
							--yes-label "$(eval_gettext "Try again")" --no-label "$(eval_gettext "Cancel installation")" \
							--yesno "\n$(eval_gettext "Please plug in a network cable to the uplink network interface and try again.")" 10 78; } ; then
							export LANG=C.UTF-8
							exit 1
						fi

					else

						# Only one interface found? Then use it. Otherwise, ask which NIC to use as uplink.
						ifaces_tagnitems=""
						for iface in ${ifaces_connected}; do
							field="ID_MODEL_FROM_DATABASE"
							set +e
							iface_description=`udevadm info /sys/class/net/${iface} | grep "${field}" | sed -rn -e "s/^E: $field=(.+)/\1/p" | sed -e "s/ /_/g"`
							set -e
							ifaces_tagnitems="${ifaces_tagnitems} ${iface} ${iface_description}"
						done
						uplink_iface=`dialog --stdout --backtitle " $(eval_gettext "Configure Internet Access") " \
							--title " $(eval_gettext "Please choose uplink [external] network interface") " \
							--ok-label "$(eval_gettext "Select")" --cancel-label "$(eval_gettext "Re-scan network interfaces")" \
							--menu "\n$(eval_gettext "Please plugin a network cable to the uplink network interface and try again.")" 15 78 5 ${ifaces_tagnitems} ` || true

					fi

					if [ ! -z ${TEST_MODE} ]; then
						reset
						echo
						echo "hooks/install.GATEWAY.sh: $(eval_gettext "Uplink NIC chosen:") ${uplink_iface}"
						echo
						sleep 2
					fi

				done

				# if we get here, we can continue...
				cont=0
				;;
			2)
				set +e
				uplink_address_v4=`dialog --stdout --backtitle " $(eval_gettext "Configure Internet Access") " --title " $(eval_gettext "External IPv4 address") " --cancel-label "$(eval_gettext "Cancel installation")" --inputbox \
									"\n$(eval_gettext "Please specify the external IPv4 address of this system.")" 9 78`
				cont=$?
				set -e

				# provide an exit point here
				if [ ! ${cont} -eq 0 ]; then
					reset
					echo
					echo "hooks/install.GATEWAY.sh: $(eval_gettext "Manual network setup cancelled.")"
					echo
					export LANG=C.UTF-8
					exit 1
				fi

				# valid IPv4 address syntax?
				if [ ${cont} -eq 0 ] && ! echo ${uplink_address_v4} | grep -qE '^(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$'; then

					DIALOGRC=${red} \
					dialog --stdout \
						--backtitle " $(eval_gettext "Configure Internet Access") " \
						--title " $(eval_gettext "External IPv4 address") " \
						--msgbox "\n`printf "$(eval_gettext "IPv4 address '%s' is of invalid format, please try again.")" "${uplink_address_v4}"`" 8 45

					cont=10
				fi
				;;
			3)
				set +e
				uplink_netmask_v4=`dialog --stdout --backtitle " $(eval_gettext "Configure Internet Access") " --title " $(eval_gettext "External IPv4 netmask") " --cancel-label "$(eval_gettext "Back")" --inputbox \
									"\n$(eval_gettext "Please specify the external interface's IPv4 netmask.")" 9 78`
				cont=$?
				set -e

				# valid IPv4 netmask syntax?
				if [ ${cont} -eq 0 ] && ! echo ${uplink_netmask_v4} | grep -qE '^(((255\.){3}(255|254|252|248|240|224|192|128+))|((255\.){2}(255|254|252|248|240|224|192|128|0+)\.0)|((255\.)(255|254|252|248|240|224|192|128|0+)(\.0+){2})|((255|254|252|248|240|224|192|128|0+)(\.0+){3}))$'; then

					DIALOGRC=${red} \
					dialog --stdout \
						--backtitle " $(eval_gettext "Configure Internet Access") " \
						--title " $(eval_gettext "External IPv4 netmask") " \
						--msgbox "\n`printf "$(eval_gettext "IPv4 netmask '%s' is of invalid format, please try again.")" "${uplink_netmask_v4}"`" 8 45

					cont=10
				fi
				;;
			4)
				set +e
				uplink_gateway_v4=`dialog --stdout --backtitle " $(eval_gettext "Configure Internet Access") " --title " $(eval_gettext "External IPv4 gateway") " --cancel-label "$(eval_gettext "Back")" --inputbox \
									"\n$(eval_gettext "Please specify the external interface's IPv4 gateway address.")" 9 78`
				cont=$?
				set -e

				# valid IPv4 address syntax?
				if [ ${cont} -eq 0 ] && ! echo ${uplink_gateway_v4} | grep -qE '^(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$'; then

					DIALOGRC=${red} \
					dialog --stdout \
						--backtitle " $(eval_gettext "Configure Internet Access") " \
						--title " $(eval_gettext "External IPv4 gateway") " \
						--msgbox "\n`printf "$(eval_gettext "IPv4 address '%s' is of invalid format, please try again.")" "${uplink_gateway_v4}"`" 8 45

					cont=10
				fi

				# FIXME/TODO: validate IPv4 gateway; Same subnet?
				;;
			5)
				set +e
				dns_nameservers_v4=`dialog --stdout --backtitle " $(eval_gettext "Configure Internet Access") " --title " $(eval_gettext "Upstream DNS servers") " --cancel-label "$(eval_gettext "Back")" --inputbox \
									"\n$(eval_gettext "Please specify available IPv4 DNS servers on the external network. Use commas or blanks to separate several DNS server addresses.")" 10 78`
				cont=$?
				set -e

				if [ ${cont} -eq 0 ]; then

					# remove commas and superfluous white-spaces
					dns_nameservers_v4="$(echo ${dns_nameservers_v4} | sed -E -e "s/,/ /g" -e "s/\s+/ /g")"
					for dns_v4_addr in ${dns_nameservers_v4}; do
						if ! echo ${dns_v4_addr} | grep -qE '^(\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$'; then

							DIALOGRC=${red} \
							dialog --stdout \
								--backtitle " $(eval_gettext "Configure Internet Access") " \
								--title " $(eval_gettext "Upstream DNS servers") " \
								--msgbox "\n`printf "$(eval_gettext "DNS server's IPv4 address '%s' is of invalid format, please try again.")" "${dns_v4_addr}"`" 8 45

							cont=10
							break 1
						fi
					done
				fi
				;;
		esac

		if [ $cont -eq 0 ]; then
			STATE=$(($STATE + 1))
		elif [ $cont -eq 1 ]; then
			STATE=$(($STATE - 1))
		elif [ $cont -eq 10 ]; then
			:
		else
			export LANG=C.UTF-8
			exit 1
		fi

	done
}

checked_inet_connection_once_before=false

# If IPv4 connectivity is not yet available (i.e. DHCPv4 address retrieval failed) we offer a manual IPv4 setup
while ! curl --ipv4 --fail --silent $URL > ${_tmp}/network-check-v4; do

	if [ "$checked_inet_connection_once_before" == true ]; then
		dialog --stdout --backtitle " $(eval_gettext "Configure Internet Access") " --title " $(eval_gettext "Manual IPv4 network setup [required]") " --yesno \
			"\n$(eval_gettext "Internet access still fails using the IPv4 networking data you entered manually.")\n\n$(eval_gettext "Do you want to retry setting up IPv4 internet access manually?")" 9 78;
	else
		dialog --stdout --backtitle " $(eval_gettext "Configure Internet Access") " --title " $(eval_gettext "Manual IPv4 network setup [required]") " --yesno \
			"\n$(eval_gettext "The system failed to obtain an IPv4 address via DHCP automatically.")\n\n$(eval_gettext "Do you want to set up IPv4 internet access manually?")" 9 78;
	fi

	# Check if user cancelled the dialog before.
	if [ ! "$?" -eq 0 ]; then
		if ! { dialog --stdout --backtitle " $(eval_gettext "Configure Internet Access") " \
			--title " $(eval_gettext "No IPv4 network / internet access available!") " \
			--yes-label "$(eval_gettext "Try again")" --no-label "$(eval_gettext "Cancel installation")" \
			--yesno "\n$(eval_gettext "Could not access test URL '$URL' via IPv4. Please make sure internet access is available during installation.")\n\n\
			$(eval_gettext "Fix this problem [Ctrl-Alt-F2] and try again.")" 10 78; }
		then
			reset
			echo
			echo "hooks/install.GATEWAY.sh: $(eval_gettext "Uplink network setup cancelled, installation can't proceed.")"
			echo
			export LANG=C.UTF-8
			exit 1
		fi
	fi

	show_dialogs_manual_ipv4_conf

	checked_inet_connection_once_before=true

	if [ -z "${TEST_MODE}" ]; then
		# only execute this if not in (dialog) TEST_MODE

		# use above data to set up networking and test again...
		if [ -n "$uplink_iface" ] && \
		   [ -n "$uplink_address_v4" ] && \
		   [ -n "$uplink_netmask_v4" ] && \
		   [ -n "$uplink_gateway_v4" ] && \
		   [ -n "$dns_nameservers_v4" ]; then

			ip address add ${uplink_address_v4}/${uplink_netmask_v4} dev ${uplink_iface}
			ip link set ${uplink_iface} up
			ip -4 route add default via ${uplink_gateway_v4} dev ${uplink_iface}
			mv /etc/resolv.conf /etc/resolv.conf.FAI-orig
			for dns_nameserver_v4 in $dns_nameservers_v4; do
				echo "nameserver $dns_nameserver_v4" >> /etc/resolv.conf
			done

			# tear down above network config, if not usable for internet connectivity
			if ! curl --ipv4 --fail --silent $URL > ${_tmp}/network-check-v4; then
				mv /etc/resolv.conf.FAI-orig /etc/resolv.conf
				ip -4 route del default via ${uplink_gateway_v4} dev ${uplink_iface}
				ip address delete ${uplink_address_v4}/${uplink_netmask_v4} dev ${uplink_iface}
				ip link set ${uplink_iface} down
			else
				# we don't need this anymore...
				rm /etc/resolv.conf.FAI-orig
			fi

			# fill uplink_full_address_v4
			uplink_full_address_v4="${uplink_address_v4}/$(mask2cdr $uplink_netmask_v4)"
		fi
	else
		break 1
	fi
done

if [ -z "${TEST_MODE}" ]; then

	# only execute this if not in (dialog) TEST_MODE

	if [ -n "$uplink_iface" ] && \
	   [ -n "$uplink_address_v4" ] && \
	   [ -n "$uplink_full_address_v4" ] && \
	   [ -n "$uplink_netmask_v4" ] && \
	   [ -n "$uplink_gateway_v4" ] && \
	   [ -n "$dns_nameservers_v4" ]; then

		echo "UPLINK_IFACE=$uplink_iface"                       >> $LOGDIR/additional.var
		echo "UPLINK_ADDRESS_V4=${uplink_address_v4}"           >> $LOGDIR/additional.var
		echo "UPLINK_FULL_ADDRESS_V4=${uplink_full_address_v4}" >> $LOGDIR/additional.var
		echo "UPLINK_NETMASK_V4=${uplink_netmask_v4}"           >> $LOGDIR/additional.var
		echo "UPLINK_GATEWAY_V4=${uplink_gateway_v4}"           >> $LOGDIR/additional.var
		echo "DNS_NAMESERVERS_V4=${dns_nameservers_v4}"         >> $LOGDIR/additional.var

	fi

else

	reset
	echo
	echo "hooks/install.GATEWAY.sh: $(eval_gettext "Uplink ifupdown config will look like this:")"
	echo
	echo '```'
	echo "auto ${uplink_iface}"
	echo "iface ${uplink_iface} inet static"
	echo "        address ${uplink_address_v4}"
	echo "        netmask ${uplink_netmask_v4}"
	echo "        gateway ${uplink_gateway_v4}"
	echo "        dns-nameservers ${dns_nameservers_v4}"
	echo '```'
	echo

fi

export LANG=C.UTF-8
set +e
