#!/bin/sh

. /lib/functions.sh
. /usr/share/libubox/jshn.sh

CLASS=""
OUI=""
SERIAL=""
GW_DISCOVERED=0
_json_no_warning=1

get_vivsoi() {
	# opt125 environment variable has data in below format
	# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	# |	 enterprise-number1	  |
	# |				  |
	# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	# |   data-len1   |		  |
	# +-+-+-+-+-+-+-+-+ option-data1  |
	# /				  /
	# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -----
	# |	 enterprise-number2	  |   ^
	# |				  |   |
	# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+   |
	# |   data-len2   |		  | optional
	# +-+-+-+-+-+-+-+-+ option-data2  |   |
	# /				  /   |
	# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+   |
	# ~	       ...		  ~   V
	# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -----

	#  Enterprise Id  Len  Sub Op  SLen  Data Sub Op  SLen Data	  Sub Op  SLen Data
	# +-------------+-----+------+------+----+------+-----+----+-----+------+-----+----+
	# |	id    |  n  |	1  |  n1  | D1 |   2  |  n2 | D2 | ... |   6  |  n6 | D6 |
	# +-------------+-----+------+------+----+------+-----+----+-----+------+-----+----+

	local opt125="$1"
	local len="$2"
	local ent_id

	#hex-string 2 character=1 Byte
	# length in hex string will be twice of actual Byte length
	[ "$len" -gt "8" ] || return

	data="${opt125}"
	rem_len="${len}"
	while [ $rem_len -gt 0 ]; do
		ent_id=${data:0:8}
		ent_id=$(printf "%d\n" "0x$ent_id")

		if [ $ent_id -ne  3561 ]; then
			len_val=${data:8:2}
			data_len=$(printf "%d\n" "0x$len_val")
			# add 4 byte for ent_id and 1 byte for len
			data_len=$(( data_len * 2 + 10 ))
			# move ahead data to next enterprise id
			data=${data:"${data_len}":"${rem_len}"}
			rem_len=$(( rem_len - $data_len ))
			continue
		fi

		# read the length of enterprise data
		len_val=${data:8:2}
		data_len=$(printf "%d\n" "0x$len_val")
		# add 4 byte for ent_id and 1 byte for len
		data_len=$(( data_len * 2 + 10 ))

		opt_len=$(printf "%d\n" "0x$len_val")
		[ $opt_len -eq 0 ] && return

		# populate the option data of enterprise id
		sub_data_len=$(( opt_len * 2))
		# starting 10 means ahead of length field
		sub_data=${data:10:"${sub_data_len}"}

		# parsing of suboption of option 125
		while [ $sub_data_len -gt 0 ]; do
			# get the suboption id
			sub_opt_id=${sub_data:0:2}
			sub_opt_id=$(printf "%d\n" "0x$sub_opt_id")

			# get the length of suboption
			sub_opt_len=${sub_data:2:2}
			sub_opt_len=$(printf "%d\n" "0x$sub_opt_len")
			sub_opt_len=$(( sub_opt_len * 2 ))

			# get the value of sub option starting 4 means starting after length
			sub_opt_val=${sub_data:4:${sub_opt_len}}

			# assign the value found in sub option
			case "${sub_opt_id}" in
				"4")
				OUI=$(echo -n $sub_opt_val | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf && echo '')
				GW_DISCOVERED=1
				;;
				"5")
				SERIAL=$(echo -n $sub_opt_val | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf && echo '')
				GW_DISCOVERED=1
				;;
				"6")
				CLASS=$(echo -n $sub_opt_val | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf && echo '')
				GW_DISCOVERED=1
				;;
			esac

			# add 2 bytes for sub_opt id and sub_opt len field
			sub_opt_end=$(( sub_opt_len + 4 ))

			# update the remaining sub option hex string length
			sub_data_len=$((sub_data_len - sub_opt_end))

			# fetch next sub option hex string
			sub_data=${sub_data:${sub_opt_end}:${sub_data_len}}
		done

		# move ahead data to next enterprise id
		data=${data:"${data_len}":"${rem_len}"}
		rem_len=$(( rem_len - data_len ))
	done
}

send_host_query() {
	intf="${1}"
	resp=1
	loop=3
	usp_serv_found=0
	sent_host=" "

	ubus call umdns update
	while [ "${loop}" -ne 0 ]; do
		sleep 5

		json_load "$(ubus call umdns browse)"
		json_select discovered_services
		if [ "${?}" -ne 0 ]; then
			json_cleanup
			loop=$(( loop - 1 ))
			continue
		fi

		json_select _usp-agt-mqtt._tcp
		if [ "${?}" -ne 0 ]; then
			json_cleanup
			loop=$(( loop - 1 ))
			continue
		fi

		usp_serv_found=1
		break
	done

	if [ "${usp_serv_found}" -eq 0 ]; then
		echo "${resp}"
		return 0
	fi

	json_get_keys keys
	for key in $keys; do
		json_select $key
		json_get_var _host host ""

		if [ -z "${_host}" ] || [[ "${sent_host}" =~ " ${_host}" ]]; then
			json_select ..
			continue
		fi

		sent_host="${sent_host} ${_host}"
		cmd="ubus call umdns query '{\"question\":\"$_host\",\"interface\":\"$intf\"}'"
		eval $cmd
		resp=0
		json_select ..
	done

	json_cleanup
	echo "${resp}"
}

get_usp_agent_id() {
	dhcp_ip="${1}"
	family="ipv4"
	ID=""

	if [[ "${dhcp_ip}" =~ ":" ]]; then
		family="ipv6"
	fi

	json_load "$(ubus call umdns browse)"
	json_select discovered_services
	if [ "${?}" -ne 0 ]; then
		json_cleanup
		echo ${ID}
		return 0
	fi

	json_select _usp-agt-mqtt._tcp
	if [ "${?}" -ne 0 ]; then
		json_cleanup
		echo ${ID}
		return 0
	fi

	json_get_keys keys
	for key in $keys; do
		json_select $key
		json_select $family
		if [ "${?}" -ne 0 ]; then
			json_select ..
			continue
		fi

		json_get_keys ips
		for ip in $ips; do
			json_get_var ip_val $ip
			if [ "${ip_val}" != "${dhcp_ip}" ]; then
				continue
			fi

			json_select ..
			json_select txt
			json_get_keys txts
			for _txt in $txts; do
				json_get_var text_val $_txt
				if [[ "${text_val:0:3}" == "ID=" ]]; then
					ID="${text_val:3}"
					break
				fi
			done

			break
		done

		json_select ..
		json_select ..

		if [ -n "${ID}" ]; then
			break
		fi
	done

	json_cleanup
	echo ${ID}
}

get_mac_address() {
	ip="${1}"
	device="${2}"

	mac="$(cat /proc/net/arp | grep $ip | awk '{print $4}')"
	if [ -z "${mac}" ]; then
		arp_resp="$(arping -b -f -c 5 -I $device $ip | grep 'Unicast reply from' | awk '{print $5}')"
		if [ -n "${arp_resp}" ]; then
			mac=${arp_resp:1:-1}
		fi
	fi

	echo "${mac}"
}

send_unknown_gw_event() {
	mac="${1}"

	cmd="ubus -t 5 send gateway-info.gateway.unknown '{\"hwaddr\":\"$mac\"}'"
	eval $cmd
}

send_cwmp_gw_event() {
	oui="${1}"
	class="${2}"
	serial="${3}"

	cmd="ubus -t 5 send gateway-info.gateway.cwmp '{\"oui\":\"$oui\",\"class\":\"$class\",\"serial\":\"$serial\"}'"
	eval $cmd
}

send_usp_gw_event() {
	endpoint="${1}"

	cmd="ubus -t 5 send gateway-info.gateway.usp '{\"endpoint\":\"$endpoint\"}'"
	eval $cmd
}

config_load gateway
config_get_bool enable global enable '1'
config_get wan_intf global wan_interface "wan"

if [ "${enable}" -eq 0 ]; then
	return 0
fi

if [ "${wan_intf}" == "${INTERFACE}" ]; then
	if [ "${1}" == "deconfig" ]; then
		rm -rf /var/state/gwinfo
		return 0
	fi

	json_load "$(ifstatus ${INTERFACE})"
	json_get_var dev_name device ""
	json_select data
	json_get_var dhcp_ip dhcpserver ""
	json_cleanup

	if [ -z "${dhcp_ip}" ] || [ -z "${dev_name}" ]; then
		return 0
	fi

	MAC="$(get_mac_address $dhcp_ip $dev_name)"

	mkdir -p /var/state
	touch /var/state/gwinfo
	sec=$(uci -q -c /var/state get gwinfo.gatewayinfo)
	if [ -z "${sec}" ]; then
		sec=$(uci -q -c /var/state add gwinfo gatewayinfo)
		uci -q -c /var/state rename gwinfo."${sec}"="gatewayinfo"
	fi

	uci -q -c /var/state set gwinfo.gatewayinfo.hwaddr="$MAC"
	uci -q -c /var/state set gwinfo.gatewayinfo.endpoint=""
	uci -q -c /var/state set gwinfo.gatewayinfo.class=""
	uci -q -c /var/state set gwinfo.gatewayinfo.oui=""
	uci -q -c /var/state set gwinfo.gatewayinfo.serial=""
	uci -q -c /var/state set gwinfo.gatewayinfo.proto=""
	uci -q -c /var/state commit gwinfo

	if [ -z "$opt125" ]; then
		send_unknown_gw_event "${MAC}"
		return 0
	fi

	len=$(printf "$opt125"|wc -c)
	get_vivsoi "$opt125" "$len"

	if [ "${GW_DISCOVERED}" -eq 0 ]; then
		send_unknown_gw_event "${MAC}"
		return 0
	fi

	uci -q -c /var/state set gwinfo.gatewayinfo.class="$CLASS"
	uci -q -c /var/state set gwinfo.gatewayinfo.oui="$OUI"
	uci -q -c /var/state set gwinfo.gatewayinfo.serial="$SERIAL"
	uci -q -c /var/state set gwinfo.gatewayinfo.proto="CWMP"
	uci -q -c /var/state commit gwinfo

	# Check for USP parameters
	ubus -t 15 wait_for umdns
	if [ "${?}" -ne 0 ]; then
		send_cwmp_gw_event "${OUI}" "${CLASS}" "${SERIAL}"
		return 0
	fi

	resp=$(send_host_query $dev_name)
	if [ "${resp}" -ne 0 ]; then
		send_cwmp_gw_event "${OUI}" "${CLASS}" "${SERIAL}"
		return 0
	fi

	ID="$(get_usp_agent_id $dhcp_ip)"
	if [ -z "${ID}" ]; then
		send_cwmp_gw_event "${OUI}" "${CLASS}" "${SERIAL}"
		return 0
	fi

	uci -q -c /var/state set gwinfo.gatewayinfo.endpoint="$ID"
	uci -q -c /var/state set gwinfo.gatewayinfo.proto="USP"
	uci -q -c /var/state commit gwinfo

	send_usp_gw_event "${ID}"
fi
