#!/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
EXTRA_COMMANDS="up down"

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 -P /var/state get network.$v.ifname 2>&-)
				[ -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 disabled?
	section_enabled "$s" || {
		[ -f "$TMP_TINC/$n/hosts/$s" ] && rm "$TMP_TINC/$n/hosts/$s"
		return 1
	}

	[ ! -f "/etc/tinc/$n/hosts/$s" ] && {
		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
	}

	# append flags
	append_conf_bools "$s" "$TMP_TINC/$n/hosts/$s" \
		ClampMSS IndirectData PMTUDiscovery TCPOnly

	# append params
	append_conf_params "$s" "$TMP_TINC/$n/hosts/$s" \
		Address Cipher Compression Digest 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

        # rm old config
        rm -rf "$TMP_TINC/$s/"

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

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

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

	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"
	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
}