#!/bin/sh /etc/rc.common

. /lib/functions/network.sh

START=90
STOP=10

USE_PROCD=1
PROG=/usr/bin/owsd
UPROXYD="/sbin/uproxyd"
CONFIGFILE="/etc/config/owsd"
JSONFILE="/tmp/owsd/owsd.json"
DHCP_DOMAINS=""

USERS=""
OWLNUM=0

owsd_acl() {
	local section=$1
	local username=$2
	local user=""

	acl_add_user() {
		local user=$1
		if [ "$user" == "$username" ]; then
			json_add_string "" $section
		fi
	}

	config_get user $section user
	if [ -n "$user" ]; then
		config_list_foreach "$section" "user" acl_add_user
	else
		for usr in $USERS; do
			acl_add_user $usr
		done
	fi

	OWLNUM=$((OWLNUM+1))
}

append_users() {
	local section=$1

	config_get username $section username
	USERS="$USERS $username"
}

rpcd_acl() {
	local section=$1
	local owl=""
	local _i=1

	config_get username $section username

	json_select $username
	uci -q delete rpcd.$section.owsd_listen
	while json_get_var owl $_i; do
		uci -q add_list rpcd.$section.owsd_listen="$owl"
		_i=$((_i+1))
	done

	[ $((OWLNUM+1)) -eq $_i ] && uci -q delete rpcd.$section.owsd_listen

	json_select ..
}

handle_owsd_acl() {
	[ -d /tmp/owsd ] || mkdir /tmp/owsd

	config_load rpcd
	config_foreach append_users "login"

	config_load owsd
	json_init
	for USR in $USERS; do
		json_add_array "$USR"
		OWLNUM=0
		config_foreach owsd_acl "owsd-listen" "$USR"
		json_close_array
	done
	json_dump > /tmp/owsd/owsd_acl

	json_load "$(cat /tmp/owsd/owsd_acl)"
	config_load rpcd
	config_foreach rpcd_acl "login"
	uci commit rpcd

	/etc/init.d/rpcd reload
}

load_dhcp_domains() {

	append_domain() {
		local domain=$1
		DHCP_DOMAINS="$DHCP_DOMAINS $domain"
	}

	dhcp_domain_section() {
		local section=$1
		local ip
		config_get ip "$section" ip
		[ -z "$ip" ] && config_list_foreach "$section" "name" append_domain
	}

	config_load dhcp # note: do not overload a config while parsing it
	config_foreach dhcp_domain_section "domain"
}

validate_owsd_ubusproxy() {
	uci_validate_section "owsd" "ubusproxy" "ubusproxy" \
		'enable:bool:1' \
		'peer:list(string)' \
		'object:list(string)' \
		'prefix:string' \
		'max_reconnect_time:string' \
		'client_backoff_timeout:string' \
		'reconnect_timeout:string' \
		'peer_key:file' \
		'peer_cert:file' \
		'peer_ca:file' \

}

validate_owsd_global() {
	uci_validate_section "owsd" "owsd" "global" \
		'enable:bool:1' \
		'sock:string' \
		'redirect:string' \
		'www:string' \
		'www_maxage:integer' \

}

append_str() {
	json_add_string "" "$1"
}

append_origin_parts() {
	local proto host port
	proto="$1"
	host="$2"
	port="$3"

	if [ "${proto}" = "https" -a "${port}" -eq 443 -o "${proto}" = "http" -a "${port}" -eq 80 ]; then
		append_str "${proto}://${host}"
	else
		append_str "${proto}://${host}:${port}"
	fi
}

validate_owsd_iface() {
	uci_validate_section "owsd" "owsd-listen" "$1" \
		'port:port' \
		'interface:network' \
		'origin:list(string)' \
		'whitelist_interface_as_origin:bool:1' \
		'whitelist_dhcp_domains:bool:0' \
		'whitelist_all_origins:bool:0' \
		'ipv6:bool:1' \
		'ipv6only:bool:0' \
		'linklocal:bool:0' \
		'cert:file' \
		'key:file' \
		'ca:file' \
		'restrict_to_user:list(string)' \
		'ubusx_acl:list(string)' \
		&&
	[ -n "${port}" ]
}

parse_owsd_iface() {
	local port interface whitelist_interface_as_origin whitelist_dhcp_domains whitelist_all_origins ipv6 ipv6only linklocal
	local cert key ca
	local restrict_to_user
	local ubusx_acl

	validate_owsd_iface "$1" || {
		echo "Validation failed"
		return 1
	}

	# utility function
	new_listen_socket() {
		json_add_int "port" ${port}

		[ -n "${cert}"      ] && json_add_string "cert" "${cert}"
		[ -n "${key}"       ] && json_add_string "key" "${key}"
		[ -n "${ca}"        ] && json_add_string "ca" "${ca}"

		[ -n "${restrict_to_user}" ] && {
			json_add_array "restrict_to_user"
			config_list_foreach "$1" "restrict_to_user" append_str
			json_close_array
		}
		[ -n "$2" ] && json_add_string "interface" "$2"

		[ -n "${ubusx_acl}" ] && json_add_string "ubusx_acl" "$ubusx_acl"

	}

	origin_whitelist_all() {
		[ "$1" = "*" ] && echo "true"
	}

	append_whitelists() {
		config_list_foreach "$1" "origin" append_str

		# uncomment code below when implementing one vhost per origin fix
		[ -n "$2" ] && whitelist_all_origins=1

		if [ "$whitelist_all_origins" -eq 1 ]; then
			whitelist_dhcp_domains=1
			whitelist_interface_as_origin=1
		fi

		if [ "$whitelist_dhcp_domains" -eq 1 ]; then
			for domain in $DHCP_DOMAINS; do
				append_origin_parts "${http}" "${domain}" "${port}"
			done
		fi

		if [ -n "${interface}" -a -n "${addr}" -a "${whitelist_interface_as_origin}" -eq 1 ]; then
			append_origin_parts "${http}" "${addr}" "${port}"
		fi
	}

	local http="http${cert:+s}"
	local ip4addrs ip6addrs linklocaladdrs

	# bind to some network
	if [ -n "${interface}" ]; then
		# 1 listen-socket (vhost) for each IP address on that network's iface

		whitelist=$(config_list_foreach "$1" "origin" origin_whitelist_all)

		# ipv4 addresses
		if [ "${ipv6only}" -eq 0 ]; then
			network_get_ipaddrs ip4addrs "${interface}";
		fi

		json_add_array "$1"
		for addr in ${ip4addrs}; do
			json_add_object "${1}"
			new_listen_socket "$1" "${addr}"
			[ -n "$whitelist" -o "$whitelist_all_origins" -eq 1 ] && json_add_boolean "origin_check" false
			json_add_array "origin"
			append_whitelists "$1" "$whitelist"
			json_close_array
			json_close_object
		done

		# ipv6 addresses
		if [ "${ipv6}" -eq 1 ]; then
			network_get_ipaddrs6 ip6addrs "${interface}"
			if [ "${linklocal}" -eq 1 ]; then
				network_get_device device "${interface}"
				[ -n "${device}" ] && linklocaladdrs=$(ifconfig $device | grep inet6 | grep Scope:Link | awk '{print$3}' | cut -d'/' -f1)
				ip6addrs="${ip6addrs} ${linklocaladdrs}"
			fi
		fi

		for addr in ${ip6addrs}; do
			json_add_object "${1}6"
			new_listen_socket "$1" "${addr}"
			addr="\\[${addr}]"
			[ -n "$whitelist" ] && json_add_boolean "origin_check" false
			json_add_array "origin"
			append_whitelists "$1" "$whitelist"
			json_close_array
			json_add_boolean "ipv6" 1
			json_close_object
		done
		json_close_array

	else
		new_listen_socket "$1"
		if [ "${ipv6}" -eq 1 ]; then json_add_boolean "ipv6only" 1; fi
		if [ "${ipv6}" -eq 1 -a "${ipv6only}" -eq 1 ]; then json_add_boolean "ipv6only" 1; fi

		append_whitelists "$1"
	fi
}

parser_timeout()
{
	local flag data
	local ret=0
#data format is  digital with time unit
#10s-10 seconds 10m-10 minutes 10h-10 hours 10d-10 days
	if [ -n "$1" ]; then
		flag=$(echo "$1"| tr -d '0-9' | tr 'A-Z' 'a-z')
		data=$(echo "$1"| tr -cd '0-9')

		case $flag in
			m)
			ret=$((data*1000*60))
			;;
			h)
			ret=$((data*1000*60*60))
			;;
			d)
			ret=$((data*1000*60*60*24))
			;;
			*)
			# assume seconds if no format given
			ret=$((data*1000))
			;;
		esac
		echo "$ret"
	else
		echo "0"
	fi
}

start_service() {
	# update rpcd config according to owsd acl
	handle_owsd_acl

	# preload dhcp domains list, in case any interface config requires it
	load_dhcp_domains

	config_load owsd # note: do not overload a config while parsing it

	config_get_bool owsd_enable global enable 1
	[ $owsd_enable -eq 1 ] || return 0

	procd_open_instance
	procd_set_param command $PROG

	local sock www redirect www_maxage
	local enable peer_cert peer_key peer_ca

	validate_owsd_global || {
		echo "Global validation failed"
		return 1
	}

	json_init
	json_add_object "global"
	[ -n "${sock}"       ] && json_add_string "socket" "$sock"
	[ -n "${www}"        ] && json_add_string "www" "$www"
	[ -n "${redirect}"   ] && json_add_string "redirect" "$redirect"
	[ -n "${www_maxage}" ] && json_add_int "www_maxage" "$www_maxage"
	json_close_object

	validate_owsd_ubusproxy && {
		if [ "${enable}" -eq 1 ]; then
			json_add_object "ubusproxy"

			[ -n "${peer_cert}" ] && json_add_string "peer_cert" "${peer_cert}"
			[ -n "${peer_key}"  ] && json_add_string "peer_key" "${peer_key}"
			[ -n "${peer_ca}"   ] && json_add_string "peer_ca" "${peer_ca}"

			append_peer () {
				[ -n "$1"   ] && json_add_string "peer" "$1"
			}

			append_object () {
				[ -n "$1"   ] && json_add_string "object" "$1"
			}

			json_add_array "peer"
			config_list_foreach "ubusproxy" "peer" append_peer
			json_close_array
			json_add_array "object"
			config_list_foreach "ubusproxy" "object" append_object
			json_close_array

			[ -n "${reconnect_timeout}"	] && json_add_int "reconnect_timeout" $(parser_timeout $reconnect_timeout)

			[ "${prefix}" == "mac" ] && json_add_string "prefix" "mac"
			json_close_object
		fi
	}

	json_add_object "owsd-listen"
	config_foreach parse_owsd_iface "owsd-listen"
	json_close_object

	[ ! -d "/tmp/owsd" ] && mkdir /tmp/owsd
	json_dump > "$JSONFILE"
	procd_append_param command -f "$JSONFILE"

#	procd_set_param stderr 1
	procd_set_param respawn

	procd_close_instance

	if [ "${enable}" -eq 1 ]; then
		# Start uproxyd
		procd_open_instance
		procd_set_param command $UPROXYD

		config_get ctimeout ubusproxy   client_backoff_timeout
		config_get reconn   ubusproxy   max_reconnect_time
		[ -n "${reconn}" ] &&  procd_append_param command -r "${reconn}"
		[ -n "${ctimeout}" ] && ctimeout=$(parser_timeout $ctimeout)
		[ -n "${ctimeout}" ] && procd_append_param command -t "${ctimeout}"

		procd_set_param respawn
		procd_close_instance
	fi

}

reload_service() {
        stop
        start
}

service_triggers() {
        procd_add_reload_trigger owsd
}
