#!/bin/sh

g_pslfile=/usr/share/public_suffix_list.dat.gz
[ -f "$g_pslfile" ] || g_pslfile="$(dirname $0)/public_suffix_list.dat.gz"

g_pslerr=0
g_cfgfile="ddns"

# modify "cloudflare.com-v1" domain to new syntax
# returns "host[.subdom]@domain.TLD" of given FQDN #############################
mod_cloudflare_v1_domain() {
	# $1	entry to validate/split
	[ -f "$g_pslfile" ] || return 1

	[ $# -ne 1 -o -z "$1" ] && \
		{ printf "%s\\n" "mod_cloudflare_v1_domain() - Invalid number of parameters" >&2; return 1; }

	local mcd_fqdn=$1
	local mcd_fsub=""
	local mcd_fdom=""
	local mcd_ctld=""
	local mcd_ftld=""

	# check if already new syntax, "@" inside string
	if [ $( printf "%s" "$mcd_fqdn" | grep -cF "@" 2>/dev/null ) -gt 0 ]; then
		# already done
		printf "%s" "$mcd_fqdn"
		return 0
	fi

	# we need to do in one line because otherwise sh doesn't work correctly
	# to lower | replace "." to " " | awk invert word order
	set -- $(printf %s "$mcd_fqdn" | tr [A-Z] [a-z] | tr "." " " \
			| awk '{do printf "%s"(NF>1?OFS:ORS),$NF;while (--NF)}' )

	while [ -n "${1:-}" ] ; do			# as long we have parameters
		if [ -z "$mcd_ctld" ]; then 		# first loop
			mcd_ctld="$1"			# CURRENT TLD to look at
			shift
		else
			mcd_ctld="$1.$mcd_ctld"		# Next TLD to look at
			shift
		fi
		# check if TLD exact match in public_suffix_name.dat, save TLD
		zcat $g_pslfile | grep -E "^$mcd_ctld$" >/dev/null 2>&1 && {
			mcd_ftld="$mcd_ctld"		# save found
			mcd_fdom="${1:-}"		# save domain next step might be invalid
			continue
		}
		# check if match any "*" in public_suffix_name.dat,
		zcat $g_pslfile | grep -E "^\*.$mcd_ctld$" >/dev/null 2>&1 && {
			[ -z "${1:-}" ] && break	# no more data break
			# check if next level TLD match excludes "!" in tld_names.dat
			if zcat $g_pslfile | grep -E "^!$1.$mcd_ctld$" >/dev/null 2>&1 ; then
				mcd_ftld="$mcd_ctld"	# Yes
			else
				mcd_ftld="$1.$mcd_ctld"
				shift
			fi
			mcd_fdom="$1"; shift
		}
		[ -n "$mcd_ftld" ] && break	# we have something valid, break
	done

	# the leftover parameters are the HOST/SUBDOMAIN
	while [ -n "${1:-}" ]; do
		mcd_fsub="${1}${mcd_fsub:+.$mcd_fsub}"	# remember we need to invert
		shift					# and insert dot if mcd_fsub not empty
	done

	# now validate found data
	[ -z "$mcd_ftld" ] && { printf "%s\\n" "mod_cloudflare_v1_domain() - no TLD not found in '$mcd_fqdn'" >&1; return 1; }
	[ -z "$mcd_fdom" ] && { printf "%s\\n" "mod_cloudflare_v1_domain() - no registrable Domain not found in '$mcd_fqdn'" >&1; return 1; }

	# return data
	printf "%s" "${mcd_fsub:+${mcd_fsub}@}${mcd_fdom}.${mcd_ftld}"
	return 0
}

# modify timer settings from interval and unit to dhms format
timer2dhms() {
# $1	Number and
# $2	Unit of time interval
	local t=0
	case $2 in
		days)		t=$(( $1 * 86400 ));;
		hours)		t=$(( $1 * 3600 ));;
		minutes)	t=$(( $1 * 60 ));;
		*)		t=$1;;
	esac

	local d=$(( $t / 86400 ))
	local h=$(( $t % 86400 / 3600 ))
	local m=$(( $t % 3600 / 60 ))
	local s=$(( $t % 60 ))
	if [ $d -gt 0 ]; then printf "%dd %02dh %02dm %02ds" "$d" "$h" "$m" "$s"
	elif [ $h -gt 0 ]; then printf "%dh %02dm %02ds" "$h" "$m" "$s"
	elif [ $m -gt 0 ]; then printf "%dm %02ds" "$m" "$s"
	else printf "%ds" "$s"; fi

	unset d h m s t
	return 0
}

# using function to not confuse function calls with existing ones inside /lib/functions.sh
update_config() {
	uc_uci="$(which uci) -q"	# ignore errors
	uc_cfg=""
	uc_name=""
	uc_var=""
	uc_val=""
	package() { return 0; }
	config () {
		uc_cfg="$1"
		uc_name="$2"

		# Type = ddns	Name = global
		if [ "$uc_cfg" = "$g_cfgfile" -a "$uc_name" = "global" ]; then
			option() {
				uc_var="$1"; shift
				uc_val="$*"
				case "$uc_var" in
					allow_local_ip)	$uc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_privateip";;
					date_format)	$uc_uci rename $g_cfgfile.$uc_name.$uc_var="ddns_dateformat";;
					log_lines)	$uc_uci rename $g_cfgfile.$uc_name.$uc_var="ddns_loglines";;
					log_dir)	$uc_uci rename $g_cfgfile.$uc_name.$uc_var="ddns_logdir";;
					run_dir)	$uc_uci rename $g_cfgfile.$uc_name.$uc_var="ddns_rundir";;
					# leave all other options currently unchanged
					*)	;;
				esac
			}

		# Type = service	Name = ???
		elif [ "$uc_cfg" = "service" ]; then
			option() {
				uc_var="$1"; shift
				uc_val="$*"
				case "$uc_var" in
					# fix some option service_name values
					# and some settings for specific providers
					service_name|upd_provider)
						case "$uc_val" in
							freedns\.afraid\.org|afraid\.org)
								$uc_uci set $g_cfgfile.$uc_name.$uc_var="afraid.org-keyauth";;
							Bind-nsupdate)
								$uc_uci set $g_cfgfile.$uc_name.$uc_var="bind-nsupdate";;
							CloudFlare|cloudflare\.com|cloudflare\.com-v1)
								# verify if lookup_host is set
								$uc_uci get $g_cfgfile.$uc_name.lookup_host >/dev/null 2>&1 || {
									ucv_domain=$($uc_uci get $g_cfgfile.$uc_name.domain 2>/dev/null)
									$uc_uci set $g_cfgfile.$uc_name.lookup_host="$ucv_domain"
								}
								if [ -f "$g_pslfile" ]; then
									# change value of domain/upd_object to new syntax
									# there is no sort order inside uci data so we need multiple checks
									ucv_domain=$($uc_uci get $g_cfgfile.$uc_name.domain 2>/dev/null)
									ucv_object=$($uc_uci get $g_cfgfile.$uc_name.upd_object 2>/dev/null)
									# still old option domain
									if [ -n "$ucv_domain" ]; then
										ucv_new=$(mod_cloudflare_v1_domain "$ucv_domain") || g_pslerr=1
										# no error save data save data
										[ $g_pslerr -eq 0 ] && \
											$uc_uci set $g_cfgfile.$uc_name.domain="$ucv_new"
									fi
									# already new option upd_object
									if [ -n "$ucv_object" ]; then
										ucv_new=$(mod_cloudflare_v1_domain "$ucv_object") || g_pslerr=1
										# no error save data save data
										[ $g_pslerr -eq 0 ] && \
											$uc_uci set $g_cfgfile.$uc_name.upd_object="$ucv_new"
									fi
								fi
								unset ucv_domain ucv_object ucv_new
								# set new option value
								$uc_uci set $g_cfgfile.$uc_name.$uc_var="cloudflare.com-v1"
								;;
							dyndns\.org|dyndns\.com)
								$uc_uci set $g_cfgfile.$uc_name.$uc_var="dyn.com";;
							free\.editdns\.net)
								$uc_uci set $g_cfgfile.$uc_name.$uc_var="editdns.net";;
							domains\.google\.com)
								$uc_uci set $g_cfgfile.$uc_name.$uc_var="google.com";;
							loopia\.com)
								$uc_uci set $g_cfgfile.$uc_name.$uc_var="loopia.se";;
							NoIP\.com|No-IP\.com)
								$uc_uci set $g_cfgfile.$uc_name.$uc_var="no-ip.com";;
							spdns\.de)
								$uc_uci set $g_cfgfile.$uc_name.$uc_var="spdyn.de";;
							strato\.de)
								$uc_uci set $g_cfgfile.$uc_name.$uc_var="strato.com";;
							*)
								# all others leave unchanged
								;;
						esac
						# rename option service_name to option upd_provider
#						$uc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_provider"
						;;
					domain|upd_object)
						# verify if lookup_host is set
						$uc_uci get $g_cfgfile.$uc_name.lookup_host >/dev/null 2>&1 || \
							$uc_uci set $g_cfgfile.$uc_name.lookup_host="$uc_val"
						if [ -f "$g_pslfile" ]; then
							# if service_name/upd_provider cloudflare_v1 then change domain/upd_object to new syntax
							# there is no sort order inside uci data so we need multiple checks
							uco_provider=$($uc_uci get $g_cfgfile.$uc_name.upd_provider 2>/dev/null) || \
								uco_provider=$($uc_uci get $g_cfgfile.$uc_name.service_name 2>/dev/null)
							if [ "$uco_provider" = "CloudFlare" \
							  -o "$uco_provider" = "cloudflare.com" \
							  -o "$uco_provider" = "cloudflare.com-v1" ]; then
								ucv_new=$(mod_cloudflare_v1_domain "$uc_val") || g_pslerr=1
								# no error save data save data
								[ $g_pslerr -eq 0 ] && \
									$uc_uci set $g_cfgfile.$uc_name.$uc_var="$ucv_new"
								unset ucv_new
							fi
							unset uco_provider
						fi
						# rename option domain to option upd_object
#						$uc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_object"
						;;
#					dns_server)
#						# if bind-nsupdate takeover old "dns_server" value as new "upd_nsupd_server" value
#						uco_provider=$($uc_uci get $g_cfgfile.$uc_name.upd_provider 2>/dev/null) || \
#							uco_provider=$($uc_uci get $g_cfgfile.$uc_name.service_name 2>/dev/null)
#						[ "$uco_provider" = "Bind-nsupdate" -o \
#						  "$uco_provider" = "bind-nsupdate" ] && \
#							$uc_uci set $g_cfgfile.$uc_name.upd_nsupd_server="$uc_val"
#						# rename option dns_server to new option global_dnssvr
#						$udc_uci rename $g_cfgfile.$uc_name.$uc_var="global_dnssvr"
#						;;
#					bind_network)
#						$udc_uci set $g_cfgfile.$uc_name.upd_url_bindnet="$uc_val"
#						$udc_uci rename $g_cfgfile.$uc_name.$uc_var="lip_url_bindnet"
#						;;
#					proxy)
#						# proxy value must include protocoll
#						$udc_uci set $g_cfgfile.$uc_name.$uc_var="http://$uc_val"
#						$udc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_url_proxy"
#						;;
#					use_ipv6)
#						$udc_uci set $g_cfgfile.$uc_name.$uc_var="$(( 4 + ( 2 * $uc_val ) ))"
#						$udc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_ipversion"
#				TODO	update_url)
#				TODO	update_script)
					# other renames
#				TODO	lookup_host)	-> rip_host
#					enabled)	$udc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_enabled";;
#					force_dnstcp)	$udc_uci rename $g_cfgfile.$uc_name.$uc_var="rip_host_dnstcp";;
#					is_glue)	$udc_uci rename $g_cfgfile.$uc_name.$uc_var="rip_host_isglue";;
#					ip_interface)	$udc_uci rename $g_cfgfile.$uc_name.$uc_var="lip_iface";;
#					ip_network)	$udc_uci rename $g_cfgfile.$uc_name.$uc_var="lip_net";;
#					use_https)	$udc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_url_secure";;
#					cacert)		$udc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_url_cacert";;
#					username)	$udc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_username";;
#					password)	$udc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_password";;
#					param_opt)	$udc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_paramopt";;
#					param_enc)	$udc_uci rename $g_cfgfile.$uc_name.$uc_var="upd_paramenc";;

					# leave all other options currently unchanged
					*)	;;
				esac
				return 0
			}
			return 0

		# ignore unknown
		else
			return 0
		fi
	}

	# read config file
	uc_data=$($uc_uci -S -n export "$g_cfgfile")
	uc_ret="$?"
	# Error then create config file
	[ $uc_ret -ne 0 ] && {
		touch /etc/config/$uc_cfgfile
		chmod 644 /etc/config/$uc_cfgfile
	}
	# No error and uc_data then execute (eval)
	# this will call functions defined above
	[ $uc_ret -eq 0 -a -n "$uc_data" ] && eval "$uc_data"

	# add config ddns "global" (ignore error if exists)
	$uc_uci set ddns.global="$g_cfgfile"

	# write changes to config file
	$uc_uci commit "$g_cfgfile"

	unset uc_uci uc_cfg uc_name uc_var uc_val uc_ret uc_data
	return 0
}

# clear LuCI indexcache
rm -f /tmp/luci-indexcache >/dev/null 2>&1

# do config update
update_config

#cleanup
[ $g_pslerr -ne 0 ] && {
	unset g_pslfile g_pslerr g_cfgfile
	return 1
}

[ -f "$g_pslfile" ] && rm -f "$g_pslfile"
unset g_pslfile g_pslerr g_cfgfile
return 0