#!/bin/sh

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

RPC_SCRIPTS=/usr/libexec/libreswan/rpc

[ -d $RPC_SCRIPTS ] && include $RPC_SCRIPTS

IPSEC_TRAFFIC_STATES="/tmp/ipsec_traffic.$$"
IPSEC_TUNNEL_STATUS="/tmp/ipsec_status.$$"

__function__() {
    type "$1" > /dev/null 2>&1
}

foreach_extra() {
	local file obj

	[ ! -d $RPC_SCRIPTS ] && return

	for file in $RPC_SCRIPTS/*; do
		obj="${file##*/}"
		$1 "${obj%%.*}"
	done
}

get_index() {
	[ $# -lt 2 ] && return 1

	local var=$1
	local str=$2
	local ele
	local i=1

	eval "val=\"\${$var}\""

	for ele in ${val}; do
		if [[ "$ele" = "$str" ]]; then
			echo "$i"
			return 0
		fi
		i="$((i+1))"
	done

	return 1
}

phase1_established() {
	grep -q "\"${1%/*}\/.*(IKE SA established)\|\"${1%/*}\/.*(established IKE SA)" "$IPSEC_TUNNEL_STATUS"
}

phase2_established() {
	grep -q "\"$1\".*(IPsec SA established)\|\"$1\".*(established Child SA)" "$IPSEC_TUNNEL_STATUS"
}

add_tunnel_object() {
	local id="$1"
	local leftsubnets rightsubnets right ctime active_right
	local phase1=0 phase2=0 add_time inBytes outBytes

	config_get right "$id" right
	config_get leftsubnets "$id" leftsubnets
	config_get rightsubnets "$id" rightsubnets

	if [ -z "$right" ] || [ "$right" = "%any" ] || [ "$right" == "0.0.0.0" ]; then
		active_right=$(awk -F'[: ]' '{ if ( $4 ~ "'"$id/"'") {print $5; exit 0};}' "$IPSEC_TUNNEL_STATUS")
	fi

	for lsubnet in $leftsubnets; do
		lidx=$(get_index leftsubnets $lsubnet)
		for rsubnet in $rightsubnets; do
			ridx=$(get_index rightsubnets $rsubnet)
			tid="${id}/${lidx}x${ridx}"

			eval $(awk -F, '{if ($1 ~ "'"$tid"'" ) {printf("%s %s %s", $3, $4, $5)};}' "$IPSEC_TRAFFIC_STATES")
			json_add_object tunnels
			json_add_string name "$id"
			json_add_string right "$right${active_right:+ (${active_right})}"
			json_add_string leftsubnet "$lsubnet"
			json_add_string rightsubnet "$rsubnet"
			json_add_int tx "$outBytes"
			json_add_int rx "$inBytes"

			phase1_established "$tid" && phase1=1
			phase2_established "$tid" && phase2=1

			json_add_boolean phase1 "$phase1"
			json_add_boolean phase2 "$phase2"

			if [ "$phase1" = "1" ] && [ "$phase2" = "1" ]; then
				ctime="$(date +%s)"
				json_add_boolean connected 1
				json_add_int uptime "$((ctime - add_time))"
			else
				json_add_boolean connected 0
				json_add_int uptime 0
			fi

			json_close_object
		done
	done
}

generate_libreswan_states() {
	ipsec trafficstatus  > "$IPSEC_TRAFFIC_STATES"
	ipsec status > "$IPSEC_TUNNEL_STATUS"
}

clean_libreswan_states() {
	return
	rm -f "$IPSEC_TRAFFIC_STATES" "$IPSEC_TUNNEL_STATUS"
}

libreswan_status() {
	config_load libreswan

	generate_libreswan_states

	json_init
	json_add_array tunnels
	config_foreach add_tunnel_object tunnel
	json_close_array
	json_dump

	clean_libreswan_states
}

call_extra() {
	if __function__ "$1"; then
		$1
	else
		json_init
		json_add_string error "invalid call $1"
		json_dump
	fi
}

call_method() {
	case "$1" in
		status)
			libreswan_status
			;;
		*)
			call_extra $1
			;;
	esac
}

list_extra() {
	if __function__ "${1}_help"; then
		${1}_help
	else
		json_add_object "$1"
		json_close_object
	fi
}

list_methods() {
	local file

	json_init

	json_add_object status
	json_close_object

	foreach_extra list_extra ${1}

	json_dump
}

main () {
	case "$1" in
		list)
			list_methods
			;;
		call)
			call_method $2
			;;
	esac
}

main "$@"
