#!/bin/sh

echo $$ > /var/run/iup.pid

. /lib/functions.sh
. /lib/functions/network.sh
. /lib/functions/savecfg.sh
. /lib/functions/dhcp_option_relay.sh
. /usr/share/libubox/jshn.sh

include /lib/upgrade
include /lib/network

export IUPCONFFILES=/tmp/iup/sysupgrade.conffiles.tar.gz
export IUPMD5=/etc/configchecksums
export CONFILESLURP='/tmp/iup/*.conf'
export IUPTEMP="/tmp/iup"
export INTERACTIVE=0
export VERBOSE=0
export CONF=1
export DESKEY=
export MAC=
export RANGE=10
export RETRYSTOP=5
export SLEEP=10

json_load "$(ubus call router.system info)"
json_select system
json_get_var MAC basemac
json_select ..
json_select keys
json_get_var DESKEY des
json_select ..

# MAC lowercase to uppercase and remove ':'
MAC=$(echo $MAC | tr '[a-z]' '[A-Z]' | tr -d ':')

# Convert DESKEY to HEX format
DESKEY=$(echo $DESKEY | tr -d '\n' | hexdump -e '16/1 "%02x"')


# no verbose:	no output
# -v		log to system log
# -v -v		log to system log and console
# -v -v -v	log to system log, console and stderr
v() {
	[ "$VERBOSE" -eq 0 ] && return
	[ "$VERBOSE" -eq 1 ] && logger -t $0[$$] "$@" && return
	[ "$VERBOSE" -eq 2 ] && logger -s -t $0[$$] "$@" >/dev/console 2>&1 && return
	[ "$VERBOSE" -eq 3 ] && logger -s -t $0[$$] "$@" 2>&1 | tee /dev/console && return
}

vv() {
	VERBOSE="$(($VERBOSE + 1))"
	v "$@"
	VERBOSE="$(($VERBOSE - 1))"

}

get_packages()
{
	local pack
	pack=$(grep -w 'package' $1)
	pack=${pack//package/}
	#pack=${pack//[\'|\"]/}
	echo $pack | tr -d '\"' | tr -d "'"
}

reload ()
{
	local pack
	pack=$(get_packages $1)
	v "Calling ubus call uci commit for $pack"
	for packname in $pack
	do
		ubus call uci commit '{"config":"'$packname'"}'
		sleep 1
	done
}

save_usercfg()
{
	savecfg_save_config fw_redirect fw_parental wifi
}

apply_usercfg()
{
	# decide to save the user changes based on keepuserconfig received through provisioning
	local keep
	config_load provisioning
	config_get_bool keep configserver keepuserconfig "0"
	v "apply_usercfg(): keepuserconfig = $keep"
	if [ "$keep" != "1" ] ; then
		savecfg_rm_files
		return
	fi

	savecfg_apply_config fw_redirect fw_parental wifi
}

handle_provisioning() {
	local config="$1"
	local default="$2"
	local enabled
	local url
	local tftpfile
	local host
	local md5
	local defaultreset
	local pack
	local packname
	local number=$RANDOM
	local retry=1
	local number
	local incsleep=$SLEEP
	let "number %= $RANGE"
	if [[ ! -e $IUPTEMP ]]; then
		mkdir $IUPTEMP
	fi
	touch $IUPCONFFILES
	config_get_bool enabled "$config" enabled "$default"
	config_get_bool defaultreset "$config" defaultreset
	config_get deckey "$config" deckey
	if [ "$enabled" -eq 1	]; then
		if [ "$config" == "iup" ]; then
			v "Using url received in dhcp options"
			config_get url "$config" urliup
		else
			config_get url "$config" url
		fi
		url=${url//\$MAC/$MAC}
		v "Downloading from url \"$url\""

		while [ $retry -le $RETRYSTOP ]
		do
		  if [ ${url%%:*} == "tftp" ]; then
			tftpfile=${url#*\/\/}
			host=${tftpfile%%\/*}
			tftpfile=${tftpfile#*\/}

			/usr/bin/tftp -l $IUPCONFFILES -r "$tftpfile" -g "$host"
		  else
			get_image "$url" "cat" > $IUPCONFFILES
		  fi
		  if [ -s $IUPCONFFILES ]; then
			v "Download finished"
			retry=$((RETRYSTOP + 1))
		  else
		  v "Download failed, retrying in $incsleep seconds"
		  sleep $incsleep
		  incsleep=$((incsleep * retry + number))
		  retry=$((retry+1))
		  fi
		done
	fi
	if [ ! -s "$IUPCONFFILES" ] && [ "$enabled" -eq 1 ]; then
		echo "File not Found"
		reboot="off"
		CONF=0
	else
		handle_Downloaded_file $deckey
	fi
}

handle_Downloaded_file()
{
	local DECKEY
	local KEY
	[ -n "$1" ] && DECKEY=$(echo $1 | tr -d '\n' | hexdump -e '16/1 "%02x"')
	KEY=${DECKEY:-$DESKEY}
	local img_type
	case "$(get_image_type "$IUPCONFFILES")" in
                "INTENO") img_type=2 ;;
                "CFE+FS") img_type=1 ;;
                "FS")     img_type=0 ;;
                *)      img_type="UNKNOWN";;
        esac

        if [ "$img_type" == "UNKNOWN" ] ; then
		case "$(hexdump -v -n 2 -e '1/1 "%02x"' $IUPCONFFILES)" in
			1f8b)
				v "Downloaded file is an unencrypted config"
				md5=$(md5sum $IUPCONFFILES)
				md5="${md5%% *}" # remove the first space and everything after it
				md5sum "$IUPCONFFILES" >> "$IUPMD5.temp"
				if grep -q "$md5" "$IUPMD5"; then
					v "Config is already up to date, nothing to do"
					#because config is up to date we need to disable last step reboot if set
					reboot="off"
					rm -rf $IUPTEMP
				else
					save_usercfg
					cd $IUPTEMP
					tar xvzf $IUPCONFFILES
					for f in $CONFILESLURP
					do
						v "File to be applied $f and config $(cat $f)"
						uci -f $f import
					done
					pack=$(get_packages $CONFILESLURP)
					v "Packages to be commited: $pack"
					for packname in $pack
					do
						uci commit $packname
					done
					apply_usercfg
					reboot=`uci -q  get provisioning.configserver.reboot`
					if [ "$reboot" != "on" ]; then
						sync
						reload $CONFILESLURP
					fi
					rm -rf $IUPTEMP

					cp /rom/etc/uci-defaults/* /etc/uci-defaults/
					sync
				fi
			;;
			*)
				v "Downloaded file is an encrypted config"
				if [ $KEY ]; then
					openssl enc -d -des-ede -nosalt -K $KEY -iv "0000000000000000" -in $IUPCONFFILES -out $IUPCONFFILES.tmp
					if [ $? -eq 0 ]; then
						v "Decryption successful"
						mv $IUPCONFFILES.tmp $IUPCONFFILES
						handle_Downloaded_file
					else
						v "Decryption Failed! Exiting"
						rm -rf $IUPTEMP
						exit 1
					fi
				else
					v "No Key Defined"
					rm -rf $IUPTEMP
					exit 1
				fi
			;;
		esac
	else
		v "Downloaded file is a firmware, will start reflashing"
		if [ "$defaultreset" -eq 1 ]; then
			v "Not saving configuration over reflash"
			 /sbin/sysupgrade -v -n $IUPCONFFILES
		else
			v "Saving configuration over reflash"
			/sbin/sysupgrade -v $IUPCONFFILES
		fi
	fi
}

# function: change_to_vlan
# arg 1 = vlan id
# arg 2 = vlan priority
# arg 3 = $INTERFACE
change_to_vlan() {
	# local variables.
	local name="vlan$1"	# construct name = vlan + vlanid.
	local base_dev		# varible holding device name of wan port.
	local wan_if		# variable holding wan interfaces.
	local new_wan_if	#

	# extract device name.
	network_get_device base_dev $3

	# bring down old interface/device.
	ifdown "${base_dev}"
	v "option 132: bringing down old if: ${base_dev}"

	# extract base name of device.
	base_dev=$(echo ${base_dev} | cut -d. -f1)

	# add "VLAN interface" to the "end of the file".
	uci add network device
	uci rename network.@device[-1]=${name}
	uci set network.@device[-1].type=8021q
	uci set network.@device[-1].ifname=${base_dev}
	uci set network.@device[-1].name="${base_dev}.${1}"
	uci set network.@device[-1].vid=${1}
	uci set network.@device[-1].priority=${2}
	# and commit the change.
	uci commit network
	v "option 132: committed update to file network"

	# reload configuration files.
	ubus call network reload
	v "option 132: reload network config"

	# replace old interface (like eth0.1 with new eth0.101).
	wan_if=$(uci get network.$3.ifname)
	v "option 132: old network.${3}.ifname: $wan_if"

	# loop through the string, remove any word close to "base_dev".
	for word in ${wan_if}; do
	#echo $word
	case $word in
		${base_dev}*) # don't add if.
		;;
		*)	new_wan_if=${word}' '${new_wan_if}  # add if.
			#new_wan_if+=" ${word}"  # probably not working in ash.
		;;
	esac
	done
	# append new interface.
	new_wan_if=${new_wan_if}${base_dev}.${1}
	uci set network.wan.ifname="${new_wan_if}"
	# and commit the change.
	uci commit network
	v "option 132: committed update to file network: ifname=${new_wan_if}"

	# reboot into new if configuration.
	vv "Rebooting"
	export REBOOT_REASON=iup
	/sbin/reboot
}

handle_option224()
{
	if [ -z "$1" ] ; then
		echo "No argument"
		return 1
	fi

	if [ $(echo $1|grep -o "," | wc -l) -eq 0 ] ; then
		url=$1
	else
		url=$(echo $1|cut -d',' -f1)
		a=$(echo $1|cut -d',' -f2)
		b=$(echo $1|cut -d',' -f3)
		c=$(echo $1|cut -d',' -f4)
	fi

	currdate=$(date +"%Y-%m-%d")

	active=0
	if [ -z $a ] ; then
		active=1
	elif [ $a ] && [ $b ] && [ $a -lt 25 ] ; then
		#Time
		begin=$(date +%s -d"$currdate $a")
		now=$(date +%s)
		end=$((begin+3600*$b))
		if [ $now -gt $begin ] && [ $now -lt $end ] ; then
			active=1
		fi
	elif [ $a ] && [ $b ] && [ $c ] && [ $a -gt 25 ]  ; then
		#Date
		y=$(echo $a| cut -c1-4)
		m=$(echo $a| cut -c5-6)
		d=$(echo $a| cut -c7-8)
		begin=$(date +%s -d"$y-$m-$d $b")
		now=$(date +%s)
		end=$((begin+3600*$c))
		if [ $now -gt $begin ] && [ $now -lt $end ] ; then
			active=1
		fi
	else
		echo "Bad format"
		return 1
	fi

	softwareminuspath=${url##*/}

	if [ $url ] && [ $active -eq 1 ]; then
		v "Software version to download \"$softwareminuspath\""
		local sysinfo=$(ubus call router.system info)
		json_load "$sysinfo"
		json_select system
		json_get_var firmware firmware
		local firmware_new=${softwareminuspath%.*}	# remove extension (.w, .y or .y2) from filename
		if [ "$firmware_new" != "$firmware" ] ; then
			v "Firmware found $url will start flashing"
			v "Currently running firmware: \"$firmware\""
			v "Newly received firmware:    \"$firmware_new\""
			wait_for_dns $url
			v "Start flashing"
			/sbin/sysupgrade -v $url &
			return 1
		else
			v "Firmware is up to date, nothing to do"
			return 0
		fi
	fi
}


parse_dhcp_options()
{
	local the_json="$@"

	# process the dhcp_option_relay sections
	dhcp_option_relay_parse "$the_json"

	# Process IUP related DHCP options #
	local privopt224 privopt225 privopt226 vendorspecinf httpurl128
	local tftp bootfile vlanid vlanpriority interface

	json_load "$the_json"
	json_get_var interface interface
	json_get_var privopt224 privopt224
	json_get_var privopt225 privopt225
	json_get_var privopt226 privopt226
	json_get_var vendorspecinf vendorspecinf # option 43
	json_get_var httpurl128 httpurl128
	json_get_var tftp tftp # option 66
	json_get_var bootfile bootfile #option 67
	json_get_var vlanid vlanid # option 132
	json_get_var vlanpriority vlanpriority # option 133

	if [ $privopt224 ]; then
		v "dhcp option 224 firmware url $privopt224"
		handle_option224 $privopt224
		[ $? -eq 1 ] && exit
	fi

	if [ $vendorspecinf ]; then
		v "dhcp option 43 tr69 url $vendorspecinf"
		url=${vendorspecinf%%,*}; rest=${vendorspecinf#*,}
		provisioningcode=${rest%%,*};
		[ -f /etc/config/cwmp ] && uci_set_state cwmp acs dhcp_url "$url"
		uci_set_state provisioning iup urlcwmp "$url"
		uci_set_state provisioning iup url "$url"
		uci_set_state provisioning iup provisioningcode "$provisioningcode"
	elif [ $httpurl128 ]; then
		v "dhcp option 128 http config url $httpurl128"
		uci_set_state provisioning iup urliup "$httpurl128"
	elif [ $tftp ]; then
		v "dhcp option 66 tftp config url $tftp"
		if [ ${bootfile:0:1} == '/' ]; then
			uci_set_state provisioning iup urliup "tftp://$tftp$bootfile"
		else
			uci_set_state provisioning iup urliup "tftp://$tftp/$bootfile"
		fi
	fi

	# vlanid (and vlanpriority)
	if [ -n "$vlanid" -a -n "$vlanpriority" ]; then
		v "dhcp option 132 vlanid: ${vlanid} vlanpriority: ${vlanpriority}"
		change_to_vlan ${vlanid} ${vlanpriority} ${interface}
	elif [ -n "$vlanid" ]; then
		v "dhcp option 132 vlanid: ${vlanid}"
		change_to_vlan ${vlanid} 0 ${interface}
	elif [ -n "$privopt225" ] || [ -n "$privopt226" ]; then
		# opt225 and opt226 can be used together or separatly
		if [ -n "$privopt225" ]; then
			v "dhcp option 225: $privopt225"
			# option225 is allowd to change only once per CPE
			if [ "$(uci get -q ice.cloud.frozen)" != "1" ] ; then
				uci set ice.cloud.frozen="1"
				uci set ice.cloud.enabled="1"
				uci set ice.cloud.server="$privopt225"
			fi
		fi
		if [ -n "$privopt226" ]; then
			v "dhcp option 226: $privopt226"
			[ -z "$(uci get -q ice.dhcp)" ] && uci set ice.dhcp="dhcp"
			uci set ice.dhcp.opt226connectionid="$privopt226"
			ubus send dhcp.opt226connectionid '{"opt226connectionid":"","value":"'$privopt226'"}'
		fi
		uci commit
	fi
}

# wait_for_default_gateway to become reachable
# return 0 if the default gateway is reachable
# return 1 if the default gateway is not reachable after $wait_time
wait_for_default_gateway()
{
	local gateway
	local device
	local wait_time=120
	local wait_interval=10

	while [ true ] ; do

		gateway=""
		device=""
		network_flush_cache
		network_get_gateway gateway wan #true
		network_get_device device wan
		device="${device:+-I }$device"
		if ping -q -w 1 -c 1 $device $gateway >/dev/null 2>&1 ; then
			[ "$wait_time" -lt "60" ] && v "Default gateway $gateway is reachable"
			sleep $wait_interval
			return 0
		fi

		# try the nameservers too
		for ns in $(grep nameserver /var/resolv.conf.auto | awk '{print $2}'); do
			if ping -q -w 1 -c 1 $device $ns >/dev/null 2>&1 ; then
				[ "$wait_time" -lt "60" ] && v "Name server $ns is reachable"
				sleep $wait_interval
				return 0
			fi
		done

		v "Waiting for default gateway or name server. Countdown $wait_time seconds"
		sleep $wait_interval
		wait_time=$((wait_time - wait_interval))
		[ "$wait_time" -le "0" ] && break # timer expired
	done

	return 1 # default gateway and name server are not reachable
}


main()
{

	while [ -n "$1" ]; do
		case "$1" in
			-v) export VERBOSE="$(($VERBOSE + 1))";;
			-q) export VERBOSE="$(($VERBOSE - 1))";;
			--dhcp-options)
				shift
				parse_dhcp_options "$@"
				exit 0
				;;
			-*)
				echo "Invalid option: $1"
				exit 1
			;;
			*) break;;
		esac
		shift;
	done

	if ! wait_for_default_gateway ; then
		v "Neither default gateway nor name server are reachable. Aborting iup."
		exit 1
	fi

	if [ ! -f $IUPMD5 ]; then
		v "Creating file $IUPMD5"
		touch $IUPMD5
	fi


	local iupurl
	local configurl
	local software
	local sofwareminuspath

	config_load provisioning
	#check if iup should be used or if its overridden by /etc/config
	config_get configurl configserver	url
	config_get reboot configserver reboot
	config_get iupurl iup	urliup

	if [ $configurl ]; then
		handle_provisioning configserver "0"
	elif [ $iupurl ]; then
		handle_provisioning iup "1"
	else
		v "No provisioning server configured"
		exit
	fi

	config_load provisioning
	config_foreach handle_provisioning subconfig "0"
	config_get software uppgradeserver url
	sofwareminuspath=${software##*/}

	if [ $software ]; then
		v "Software version to download \"$sofwareminuspath\""
		local sysinfo=$(ubus call router.system info)
		json_load "$sysinfo"
		json_select system
		json_get_var firmware firmware
		json_get_var filesystem filesystem
	        if [ "$filesystem" == "JFFS2" ] ; then
	                firmware=$firmware.w
		else
			firmware=$firmware.y3
		fi
		if [ "$sofwareminuspath" == "${sofwareminuspath/$firmware/}" ] ; then
			v "Software \"$software\""
			handle_provisioning uppgradeserver "0"
		else
			v "Will not update software, already up to date"
		fi
	fi

	if [ $CONF -eq 1 ]; then
		mv "$IUPMD5.temp" $IUPMD5
	fi

	if [ "$reboot" == "on" ]; then
		vv "Rebooting"
		export REBOOT_REASON=iup
		/sbin/reboot
	fi

	rm -rf /var/run/iup.pid
}

main $@
