#!/bin/sh

# Allow only one instance
lockfile="/var/lock/${0##*/}.lock"
if ! lock -n "$lockfile"; then
	echo "Only one instance of $0 allowed."
	exit 1
fi
trap "lock -u \"$lockfile\"" INT TERM EXIT

. /usr/share/libubox/jshn.sh
. /lib/functions/fff/keyxchange
. /lib/functions/fff/network
. /lib/functions/fff/wireless
. /lib/functions/fff/timeserver
. /usr/lib/functions/fff/hoodfile


hoodfilelocal=/etc/hoodfile

rm -f "$hoodfiletmp"

# Gatewaycheck function
isGatewayAvailable() {
	if [ -n "$(batctl gwl -H)" ]; then
		return 0
	else
		return 1
	fi
}

# Ping test
hasInternet() {
	test_ipv4_host1="keyserver.freifunk-franken.de" # Freifunk-Franken keyserver
	test_ipv4_host2="8.8.8.8"        # Google DNS
	test_ipv6_host1="heise.de"       # heise Zeitschriftenverlag
	if ping -w5 -c3 "$test_ipv4_host1" &>/dev/null ||
		ping -w5 -c3 "$test_ipv4_host2" &>/dev/null ||
		ping6 -w5 -c3 "$test_ipv6_host1" &>/dev/null ; then
		return 0
	fi
	return 1
}

# Hidden AP check

if [ -s "$hoodfilewww" ] && isGatewayAvailable ; then
	needwifi="0"
	for radio in $(uci show wireless | sed -n 's,.*\.\([a-z0-9]*\)=wifi-device,\1,p'); do
		freq="2"
		if [ "$(uci get "wireless.${radio}.channel")" -gt "14" ]; then
			freq="5"
		fi
		# Break: wXconfig is up
		uci -q get "wireless.w${freq}configap" > /dev/null && continue
		# Break: No mesh interface
		(uci -q get "wireless.w${freq}mesh" > /dev/null || uci -q get "wireless.w${freq}ibss" > /dev/null) || continue
		
		# Create configap
		iface="configap$freq"
		uci set network.${iface}=interface
		uci set network.${iface}.proto='static'
		uci set network.${iface}.ip6addr='fe80::1/64'
		uci commit network
		reload_config
		if ! wifiAddAP "$radio" "config.franken.freifunk.net" "$iface" "configap" "1"; then
			echo "Can't add Config interface on $radio."
			exit 1
		fi
		needwifi="1"
	done

	if [ "$needwifi" = "1" ] ; then
		wifi
		sleep 10
	fi
fi

if [ -s "$hoodfilelocal" ]; then
	# remove webroot hoodfile to prevent offering local hoodfiles to neighbours
	rm -f "$hoodfilewww"

	cp "$hoodfilelocal" "$hoodfiletmp"
	echo "Use local hood file"
else
	# if we have Internet, we download the Hoodfile from the keyxchangev2
	if hasInternet ; then
		getKeyserverHoodfile "$hoodfiletmp" && cp "$hoodfiletmp" "$hoodfilewww"

		#if no Internet, we connect to the hidden AP and download the file from another Node in range
	else
		# connect to wireless hidden ap here and download the json File from the nearest router
		# Only do that, when we have no gateway in range. If the Uplinkrouter changed the hood, we lost the GW and do this automatically again, I think! Nice idea?
		if ! isGatewayAvailable ; then
			#now we haven't a gateway in Range, we search for a hidden AP to get a keyxchangev2data file!
			#first we delete all wifi settings
			rm -f "$hoodfileref" # delete this, so interfaces are recreated if reconnect with unchanged hood file takes place
			rm -f "$hoodfilewww" # delete this, so wrong hood file is not broadcasted anymore

			uci -q del "system.@system[0].hood"
			uci -q del "system.@system[0].hoodid"
			uci -q commit system
			reload_config
		
			sleep 30 # Wait for the config AP, which may be created at the same time as this script has started

			getEthernetHoodfile "$hoodfiletmp" || getWirelessHoodfile "$hoodfiletmp"
		else
			echo "We have a Gateway in Range, we load the keyxchangev2data from fe80::1"
			getGatewayHoodfile "$hoodfiletmp" && cp "$hoodfiletmp" "$hoodfilewww"
		fi
	fi
fi

if [ -s "$hoodfiletmp" ]; then

	# we get a json file in this format: 
	# https://pw.freifunk-franken.de/patch/205/
	# but without signature, every hood file we get is valid!

	sumnew=$(cat "$hoodfiletmp" 2>/dev/null | sha256sum | cut -f1 -d " ")
	sumold=$(cat "$hoodfileref" 2>/dev/null | sha256sum | cut -f1 -d " ")

	json_load "$(cat "$hoodfiletmp")"

	if [ "$sumnew" != "$sumold" ] ; then
		echo "New file detected, we reconfigure the Node";

		json_select hood

		json_get_var hood name
		json_get_var hoodid id
		[ -n "$mesh_id" ] || json_get_var mesh_id mesh_id
		[ -n "$mesh_bssid" ] || json_get_var mesh_bssid mesh_bssid
		[ -n "$mesh_essid" ] || json_get_var mesh_essid mesh_essid
		[ -n "$essid" ] || json_get_var essid essid
		json_get_var ntpip ntp_ip
		[ -n "$chan2ghz" ] || json_get_var chan2ghz channel2
		[ -n "$mesh_type2" ] || json_get_var mesh_type2 mesh_type2
		[ -n "$chan5ghz" ] || json_get_var chan5ghz channel5
		[ -n "$mesh_type5" ] || json_get_var mesh_type5 mesh_type5
		# Additional parameters may be activated in future versions
		#json_get_var mode2 mode2
		#json_get_var mode5 mode5
		#json_get_var protocol protocol
		
		json_select ".." # back to root
		
		if ! ([ -n "$chan2ghz" ] && [ -n "$chan5ghz" ]) ; then
			# If channel is missing, do nothing
			exit 0
		fi

		echo "Setting hood name: $hood (ID $hoodid)"
		uci -q set "system.@system[0].hood=$hood"
		uci -q set "system.@system[0].hoodid=$hoodid"
		uci -q commit system
		reload_config

		if ! wifiDelIface; then
			echo "Can't delete current wifi setup"
			exit 1
		fi

		for phy in $(iw phy | awk '/^Wiphy/{ print $2 }'); do
			radio="$(wifiAddPhyCond "$phy" "2" "$chan2ghz")"
			radio5="$(wifiAddPhyCond "$phy" "5" "$chan5ghz")"
			[ -n "$radio5" ] && radio="$radio5"

			if ! wifiAddAP "$radio" "$essid" "mesh" "ap" "0"; then
				echo "Can't add AP interface on $radio."
				exit 1
			fi

			# add 802.11s mesh if type = "802.11s"
			if ( [ -n "$radio5" ] && [ "$mesh_type5" = "802.11s" ] ) || [ "$mesh_type2" = "802.11s" ]; then
				if ! wifiAddMesh "$radio" "$mesh_id"; then
					echo "Can't add Mesh interface on $radio."
					exit 1
				fi
			fi

			# add IBSS mesh if type = "ibss"
			if ( [ -n "$radio5" ] && [ "$mesh_type5" = "ibss" ] ) || [ "$mesh_type2" = "ibss" ]; then
				if ! wifiAddAdHocMesh "$radio" "$mesh_essid" "$mesh_bssid"; then
					echo "Can't add AdHocMesh interface on $radio."
					exit 1
				fi
			fi
		done

		echo "Loading wifi"
		wifi

		oldntp="$(uci -q get system.ntp.server)"
		newntp="${ntpip}" # requires routable address, no link-local
		[ "$newntp" = "$oldntp" ] || setTimeserver "${newntp}" # only rewrite if changed

		# copy the file to webroot so that other mesh routers can download it;
		# copy only after all other steps so IF can be reentered if something goes wrong
		cp "$hoodfiletmp" "$hoodfileref"

		# This is a workaround to enable alfred on devices which do not see a configap during initial setup
		/etc/init.d/alfred restart
	else
		echo "We have no new file. We do nothing. We try it again in 5 minutes...";
	fi
	
	# and now we get to vpn-select script and load VPNs directly from /tmp/keyxchangev2data
	
	if hasInternet ; then
		/usr/sbin/vpn-select "$hoodfiletmp"
	else
		/usr/sbin/vpn-stop
	fi

	# now we load the prefix from the hoodfile and set this to br-mesh
	json_select network
	json_get_var prefix ula_prefix
	# Set $prefix::MAC as IP
	if [ -n "$prefix" ] ; then
		prefix="$(echo "$prefix" | sed -e 's,\\,,')"
		mac="$(cat "/sys/class/net/br-mesh/address")"
		addr="$(ipMacAssemble "$prefix" "$mac")"
		addr="$(ipTidyColon "$addr")"
		addr_eui="$(ipEUIAssemble "$prefix" "$mac")"
		addr_eui="$(ipTidyColon "$addr_eui")"
		for ip in $(ip -6 addr show dev br-mesh | grep inet6 | grep -v -e " $addr" -e " $addr_eui" -e " fe80::" -e " fdff::" | cut -f6 -d " "); do
			ip -6 addr del "$ip" dev br-mesh
		done
		if ! ( ip -6 addr show dev br-mesh | grep -q "$addr" ) ; then
			ip -6 addr add "$addr" dev br-mesh
			echo "Set ULA address to br-mesh: $addr"
		else
			echo "Address already set."
		fi
		# Set $prefix::link-local as IP
		if ! ( ip -6 addr show dev br-mesh | grep -q "$addr_eui" ) ; then
			ip -6 addr add "$addr_eui" dev br-mesh
			echo "Set ULA EUI-64 address to br-mesh: $addr_eui"
		else
			echo "Address already set."
		fi
		if ! ( ip -6 route show dev br-mesh | grep -q "fc00::" ) ; then
			ip -6 route add fc00::/7 via fe80::1 dev br-mesh
			echo "Set ULA route to br-mesh."
		else
			echo "Route already set."
		fi
	fi
	json_select ".." # back to root
else
	echo "We haven't got a file. We do nothing. We try it again in 5 minutes...";
	exit 0
fi
