#!/bin/sh
set -eu
. /usr/libexec/haproxy-manager/common.sh
umask 077

CHECK_ONLY=0
FORCE_DISABLED=0
case "${1:-}" in
	'') ;;
	--check) CHECK_ONLY=1 ;;
	--disable) FORCE_DISABLED=1 ;;
	*) echo "Usage: firewall-sync [--check|--disable]" >&2; exit 1 ;;
esac

ZONE="$(uci_get main firewall_zone wan)"
MODE="$(uci_get main firewall_conflict_mode warn)"
MANAGED="$(uci_get main manage_firewall 0)"
[ "$FORCE_DISABLED" != 1 ] || MANAGED=0
PLAN=/tmp/haproxy-manager-firewall-plan.$$
/usr/libexec/haproxy-manager/firewall-plan > "$PLAN"
trap 'rm -f "$PLAN"' EXIT

CONFLICTS="$(sed -n 's/^conflicts[[:space:]]*//p' "$PLAN")"
if [ "$MANAGED" = 1 ] && [ "${CONFLICTS:-0}" -gt 0 ] && [ "$MODE" != disable ]; then
	echo "Enabled firewall port forwards conflict with HAProxy listeners:" >&2
	awk -F '\t' '$1 == "conflict" { printf "- %s (WAN %s -> %s:%s)\n", $3, $4, $5, $6 }' "$PLAN" >&2
	echo "Choose automatic conflict resolution in Settings or disable the redirects manually." >&2
	exit 3
fi

[ "$CHECK_ONLY" != 1 ] || { cat "$PLAN"; exit 0; }

PORTS=""
[ "$MANAGED" != 1 ] || PORTS="$(sed -n 's/^port[[:space:]]*//p' "$PLAN")"
CHANGED=0

for redirect in $(uci -q show firewall | sed -n 's/^firewall\.\(@redirect\[[0-9][0-9]*\]\)=redirect/\1/p'); do
	name="$(uci -q get "firewall.$redirect.name" 2>/dev/null || true)"
	case "$name" in
		"$FIREWALL_DISABLED_PREFIX"*) managed_disabled=1 ;;
		*) managed_disabled=0 ;;
	esac
	[ "$managed_disabled" = 1 ] || continue
	spec="$(uci -q get "firewall.$redirect.src_dport" 2>/dev/null || true)"
	still_needed=0
	for port in $PORTS; do
		port_spec_contains "$spec" "$port" || continue
		still_needed=1
		break
	done
	[ "$still_needed" = 1 ] && continue
	uci set "firewall.$redirect.enabled=1"
	CHANGED=1
	name="${name#"$FIREWALL_DISABLED_PREFIX"}"
	if [ -n "$name" ]; then
		uci set "firewall.$redirect.name=$name"
	else
		uci -q delete "firewall.$redirect.name" || true
	fi
done

if [ "$MANAGED" = 1 ] && [ "$MODE" = disable ]; then
	for redirect in $(awk -F '\t' '$1 == "conflict" { print $2 }' "$PLAN"); do
		[ -n "$redirect" ] || continue
		name="$(uci -q get "firewall.$redirect.name" 2>/dev/null || true)"
		case "$name" in
			"$FIREWALL_DISABLED_PREFIX"*) ;;
			*) uci set "firewall.$redirect.name=$FIREWALL_DISABLED_PREFIX$name" ;;
		esac
		uci set "firewall.$redirect.enabled=0"
		CHANGED=1
	done
fi

for index in $(uci -q show firewall | sed -n 's/^firewall\.@rule\[\([0-9][0-9]*\)\]=rule/\1/p' | sort -rn); do
	rule="@rule[$index]"
	name="$(uci -q get "firewall.$rule.name" 2>/dev/null || true)"
	if [ "$name" = "$FIREWALL_RULE_NAME" ]; then
		uci -q delete "firewall.$rule" || true
		CHANGED=1
	fi
done

if [ -n "$PORTS" ]; then
	rule="$(uci add firewall rule)"
	uci set "firewall.$rule.name=$FIREWALL_RULE_NAME"
	uci set "firewall.$rule.src=$ZONE"
	uci set "firewall.$rule.proto=tcp"
	uci set "firewall.$rule.target=ACCEPT"
	uci set "firewall.$rule.family=ipv4"
	for port in $PORTS; do
		uci add_list "firewall.$rule.dest_port=$port"
	done
	CHANGED=1
fi

if [ "$CHANGED" = 1 ]; then
	uci commit firewall
	/etc/init.d/firewall reload
fi

if [ "$MANAGED" = 1 ]; then
	echo "Firewall rules synchronized"
else
	echo "Firewall automation disabled; managed rules removed"
fi
