#!/bin/sh /etc/rc.common
# Copyright (C) 2011 OpenWrt.org
# Copyright (C) 2011 Linus Lüssing
#  Based on Jo-Philipp Wich's OpenVPN init script
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.

START=42

SERVICE_USE_PID=1

BIN=/usr/sbin/tincd
if ( type extra_command >/dev/null 2>&1 ); then
        extra_command "up" "<instance> Setting instance up"
        extra_command "down" "<instance> Setting instance down"
else
	EXTRA_COMMANDS="up down"
fi

LIST_SEP="
"
TMP_TINC="/tmp/tinc"

append_param() {
	local v="$1"
	case "$v" in
		*_*_*_*) v=${v%%_*}-${v#*_}; v=${v%%_*}-${v#*_}; v=${v%%_*}-${v#*_} ;;
		*_*_*)   v=${v%%_*}-${v#*_}; v=${v%%_*}-${v#*_} ;;
		*_*)     v=${v%%_*}-${v#*_} ;;
	esac
	ARGS="$ARGS --$v"
	return 0
}

append_conf_bools() {
	local p; local v; local s="$1"; local f="$2"; shift; shift
	for p in $*; do
		config_get_bool v "$s" "$p"
		[ "$v" == 1 ] && echo "$p = yes" >> "$f"
		[ "$v" == 0 ] && echo "$p = no" >> "$f"
	done
}

append_params() {
	local p; local v; local s="$1"; shift
	for p in $*; do
		config_get v "$s" "$p"
		IFS="$LIST_SEP"
		for v in $v; do
			[ -n "$v" ] && append_param "$p" && ARGS="$ARGS=$v"
		done
		unset IFS
	done
}

append_conf_params() {
	local p; local v; local s="$1"; local f="$2"; shift; shift
	for p in $*; do
		config_get v "$s" "$p"
		IFS="$LIST_SEP"
		for v in $v; do
			# Look up OpenWRT interface names
			[ "$p" = "BindToInterface" ] && {
				local ifname=$(uci_get_state network "$v" ifname "")
				[ -n "$ifname" ] && v="$ifname"
			}

			[ -n "$v" ] && echo "$p = $v" >> "$f"
		done
		unset IFS
	done
}

section_enabled() {
	config_get_bool enabled "$1" 'enabled' 0
	[ $enabled -gt 0 ]
}

prepare_host() {
	local s="$1"
	local n

	# net disabled?
	config_get n "$s" net
	section_enabled "$n" || return 1

	if [ "$#" = "2" ]; then
		[ "$2" != "$n" ] && return 1
	fi

	HOST_CONF_FILE="$TMP_TINC/$n/hosts/$s"
	MANDATORY_PARAM_IN_UCI=0
	[ ! -f "/etc/tinc/$n/hosts/$s" ] && {
		config_get pk_i "$s" "PublicKey"
		config_get pk_f "$s" "PublicKeyFile"
		config_get pked_i "$s" "Ed25519PublicKey"
		config_get pked_f "$s" "Ed25519PublicKeyFile"
		config_get na "$s" "Name"
		if [ -n "$na" ] ; then
			HOST_CONF_FILE="$TMP_TINC/$n/hosts/$na"
		fi
		if [ -n "$pk_i$pk_f$pked_i$pked_f" ] ; then
			MANDATORY_PARAM_IN_UCI=1
		fi
	}

	# host disabled?
	section_enabled "$s" || {
		[ -f "$HOST_CONF_FILE" ] && rm "$HOST_CONF_FILE"
		return 1
	}

	[ ! -f "/etc/tinc/$n/hosts/$s" ] && {
		if [ "$MANDATORY_PARAM_IN_UCI" -eq 1 ] ; then
			touch "$HOST_CONF_FILE"
		else
			echo -n "tinc: Warning, public key for $s for network $n "
			echo -n "missing in /etc/tinc/$n/hosts/$s, "
			echo "skipping configuration of $s"
			return 1
		fi
	}

	# append flags
	append_conf_bools "$s" "$HOST_CONF_FILE" \
		ClampMSS \
		IndirectData \
		PMTUDiscovery \
		TCPOnly

	# append params
	append_conf_params "$s" "$HOST_CONF_FILE" \
		Address \
		Cipher \
		Compression \
		Digest \
		Ed25519PublicKey \
		Ed25519PublicKeyFile \
		MACLength \
		PMTU \
		Port \
		PublicKey \
		PublicKeyFile \
		Subnet
}

check_gen_own_key() {
	local s="$1"; local n; local k

	config_get n "$s" Name
	config_get_bool k "$s" generate_keys 0
	[ "$k" == 0 ] && return 0

	([ -z "$n" ] || [ -f "$TMP_TINC/$s/hosts/$n" ] || [ -f "$TMP_TINC/$s/rsa_key.priv" ]) && \
		return 0
	[ ! -d "$TMP_TINC/$s/hosts" ] && mkdir -p "$TMP_TINC/$s/hosts"

	config_get k "$s" key_size
	if [ -z "$k" ]; then
		$BIN -c "$TMP_TINC/$s" generate-keys </dev/null
	else
		$BIN -c "$TMP_TINC/$s" generate-keys "$k" </dev/null
	fi

	[ ! -d "/etc/tinc/$s/hosts" ] && mkdir -p "/etc/tinc/$s/hosts"
	cp "$TMP_TINC/$s/rsa_key.priv" "/etc/tinc/$s/"
	[ -n "$n" ] && cp "$TMP_TINC/$s/hosts/$n" "/etc/tinc/$s/hosts/"
}

prepare_net() {
	local s="$1"
	local n

	section_enabled "$s" || return 1

	[ -d "$TMP_TINC/$s" ] && rm -rf "$TMP_TINC/$s/"
	mkdir -p "$TMP_TINC/$s/hosts"
	[ -d "/etc/tinc/$s" ] && cp -r "/etc/tinc/$s" "$TMP_TINC/"

	# append flags
	append_conf_bools "$s" "$TMP_TINC/$s/tinc.conf" \
		AutoConnect \
		DecrementTTL \
		DeviceStandby \
		DirectOnly \
		ExperimentalProtocol \
		Hostnames \
		LocalDiscovery \
		PriorityInheritance \
		StrictSubnets \
		TunnelServer \
		ClampMSS \
		IndirectData \
		PMTUDiscovery \
		TCPOnly

	# append params
	append_conf_params "$s" "$TMP_TINC/$s/tinc.conf" \
		AddressFamily \
		BindToAddress \
		BindToInterface \
		Broadcast \
		BroadcastSubnet \
		ConnectTo \
		Device \
		DeviceType \
		Ed25519PrivateKeyFile \
		Forwarding \
		Interface \
		ListenAddress \
		LocalDiscoveryAddress \
		Mode \
		KeyExpire \
		MACExpire \
		MaxConnectionBurst \
		Name \
		PingInterval \
		PingTimeout \
		PrivateKey \
		PrivateKeyFile \
		ProcessPriority \
		Proxy \
		ReplayWindow \
		UDPRcvBuf \
		UDPSndBuf \
		Address \
		Cipher \
		Compression \
		Digest \
		MACLength \
		PMTU \
		Port \
		PublicKey \
		PublicKeyFile \
		Subnet \
		Weight

	check_gen_own_key "$s" && return 0
}

start_instance() {
	local s="$1"

	section_enabled "$s" || return 1

	ARGS=""

	# append params
	append_params "$s" logfile debug

	SERVICE_PID_FILE="/var/run/tinc.$s.pid"
	# consequences of option -n "%s"
	# tinc will display this warning Both netname and configuration directory given, using the latter...
	# BUT the exported variable NETNAME will be set with the right value when run tinc-up script
	service_start $BIN -c "$TMP_TINC/$s" -n "$s" $ARGS --pidfile="$SERVICE_PID_FILE"
}

stop_instance() {
	local s="$1"

	section_enabled "$s" || return 1

	SERVICE_PID_FILE="/var/run/tinc.$s.pid"
	service_stop $BIN
	# rm old config
	rm -rf "$TMP_TINC/$s/"
}

reload_instance() {
	local s="$1"

	section_enabled "$s" || return 1

	SERVICE_PID_FILE="/var/run/tinc.$s.pid"
	service_reload $BIN
}

start() {
	config_load 'tinc'

	config_foreach prepare_net 'tinc-net'
	config_foreach prepare_host 'tinc-host'

	config_foreach start_instance 'tinc-net'
}

stop() {
	config_load 'tinc'
	config_foreach stop_instance 'tinc-net'
}

reload() {
	config_load 'tinc'
	config_foreach reload_instance 'tinc-net'
}

up() {
	local exists
	local instance
	config_load 'tinc'
	for instance in "$@"; do
		config_get exists "$instance" 'TYPE'
		if [ "$exists" == "tinc-net" ]; then
			prepare_net "$instance"
			config_foreach prepare_host 'tinc-host' "$instance"
			start_instance "$instance"
		fi
	done
}

down() {
	local exists
	local instance
	config_load 'tinc'
	for instance in "$@"; do
		config_get exists "$instance" 'TYPE'
		if [ "$exists" == "tinc-net" ]; then
			stop_instance "$instance"
		fi
	done
}
