#!/bin/sh -ef

PATH=/usr/lib/alterator-net-common:$PATH
chapfile=/etc/ppp/chap-secrets
retries=3

################### shell file helpers

shell_add_or_subst()
{
	local name="$1" && shift
	local value="$1" && shift
	local file="$1" && shift

	[ -f "$file" ] || touch "$file"
	if grep -qs "^$name" "$file"; then
		/bin/sed -r -i "s,^$name.*,$name$value," -- "$file"
		return 0
	fi
	echo "$name$value" >> "$file"
}

shell_swap_or_add()
{
	local del="$1" && shift
	local add="$1" && shift
	local file="$1" && shift

	[ -f "$file" ] || touch "$file"
	/bin/sed -i "/^$add$/d" "$file"
	if grep -qs "^$del" "$file"; then
		/bin/sed -i "s,^$del$,$add," -- "$file"
		return 0
	fi
	echo "$add" >> "$file"
}

################### debug

debug()
{
	[ -n "$DEBUG" ] && echo $* >&2
}

################### interface helpers

# sleep order is on purpose: dhcp runs in background
start_iface()
{
	[ -n "$1" ] || return
	debug -n "start_iface[$1]: "
	for i in `seq 1 $retries`; do 
		ifcheckup "$1" && break
		ifup "$1"
		sleep 1
	debug -n "$i "
	done
}

# if it's up, ensure it's down first, then back up
stop_iface()
{
	[ -n "$1" ] || return
	debug -n "stop_iface[$1]: "
	for i in `seq 1 $retries`; do 
	debug -n "$i "
		ifcheckup "$1" || break
		ifdown "$1"
		sleep 1
	done
}

# do our best so that $1 is working
# FIXME: this should become redundant when etcnet gets fixed
# (https://bugzilla.altlinux.org/show_bug.cgi?id=14021)
bring_up()
{
	[ -n "$1" ] || return
	debug "bring_up[$1]"

        # ensure underlying iface is up
        start_iface "$in_iface"

	# workaround: etcnet might try to bring up ppp too early
	# when underlying eth is configured via dhcp and is down
	sleep 1

        # try to wait until ppp0 might go down (otherwise 
        # additional static route might not appear)
        stop_iface "$1"
        start_iface "$1"
}

################### interface modificators

first_available()
{
	local path="/etc/net/ifaces/ppp"
	local i=0
	while true; do
		[ -d "$path$i" ] || { echo "ppp$i" && break; }
		i=$(($i + 1))
	done
}

list_iface()
{
	echo '('
		for i in `find /etc/net/ifaces -type d -name 'ppp*'`
		do
			[ -f $i/options ] &&
			grep -qs 'PPPTYPE=pptp' $i/options &&
			echo "(\"`basename $i`\")"
		done
	echo ')'
}

list_eth()
{
	echo '('
	iflist|
	    while read name mac;do
		printf '("%s")' "$name"
	    done
	echo ')'
}

read_iface()
{
	local name="/etc/net/ifaces/$1"

	# mppe is off by default with pppd
	local MPPE="#f"
	grep -q '^require-mppe' "$name/pppoptions" && MPPE="#t"

	# we prefer it to work by default, at least when already configured
	# and for just created interfaces
	local SAVE_GW="#t"
	[ -x "$name/ifup-pre" -a -x "$name/ifdown-post" ] || SAVE_GW="#f"
	
	# usually default route needs to be replaced
	# /etc/ppp/options should hold "replacedeaultroute",
	# $name/pppoptions should hold "defaultroute" then; see pppd(8)
	local DEF_VIA_VPN="#t"
	grep -q '^nodefaultroute' "$name/pppoptions" && DEF_VIA_VPN="#f"

	local PERSIST="#f"
	grep -q '^persist' "$name/pppoptions" && PERSIST="#t"

	echo '('

	local info=
	ifcheckup "$1" && info="`_ "connection established"`" || info="`_ "connection down"`"
	printf 'info "%s"' "$info"
	printf 'def_via_vpn %s' "$DEF_VIA_VPN"
	printf 'save_route %s' "$SAVE_GW"
	printf 'mppe %s' "$MPPE"
	printf 'persist %s' "$PERSIST"
	
	( PPTP_SERVER=
	  REQUIRES=
	  . $name/options

	printf 'server "%s"' "$PPTP_SERVER"
	printf 'iface "%s"' "$REQUIRES"
	printf 'login "%s"' "$(grep '^user' "$name/pppoptions"|sed -r 's,user[[:space:]]*,,')"
	printf 'onboot %s' "$ONBOOT"	# FIXME: "no" problem?
	
	)

	echo ')'
}

write_iface()
{
	[ "$1" = "/" ] && return
	local name="/etc/net/ifaces/$1"
	local prev_login="$(grep '^user' "$name/pppoptions"|sed -r 's,user[[:space:]]*,,')"
	[ "$in_onboot" = "#t" ] && in_onboot="yes" || in_onboot="no"
	[ -d "$name" ] || mkdir "$name"
	[ -n "$in_server" ] && shell_add_or_subst "PPTP_SERVER=" "$in_server" "$name/options"
	[ -n "$in_iface" ] && shell_add_or_subst "REQUIRES=" "$in_iface" "$name/options"
	[ -n "$in_iface" ] && shell_add_or_subst "ONBOOT=" "$in_onboot" "$name/options"
	
	if [ -n "$in_login" ]; then
		sed -r "s,^user[[:space:]].*,user $in_login," -i "$name/pppoptions"
		# FIXME: login per IP
		#sed -r "s,^$prev_login[[:space:]]\([[:space:]]*$in_server\)$,$in_login	\1," -i "$chapfile"
		sed -r "s,^$prev_login[[:space:]]*,$in_login	," -i "$chapfile"
		prev_login="$in_login"
	fi
	if [ -n "$in_password" ]; then
		in_password=$(echo "$in_password"|sed -e 's/["]/\\\\&/g')
		sed -r "s,^$prev_login[[:space:]].*,$prev_login	*	\"$in_password\"	*," -i "$chapfile"
	fi

	set_mppe
	save_default_gw
	replace_defroute
	set_persist

	bring_up "$1"
}

new_iface()
{
	# FIXME: login per IP
	#grep -qs "^$in_login[[:space:]].*[[:space:]]$in_server$" "$chapfile" && return 1
	grep -qs "^$in_login[[:space:]]" "$chapfile" && return 1

	local name="/etc/net/ifaces/$1"
	in_password=$(echo "$in_password"|sed -e 's/["]/\\\\&/g')
	[ "$in_onboot" = "#t" ] && in_onboot="yes" || in_onboot="no"

	[ -d "$name" ] || mkdir "$name"
	shell_add_or_subst "ONBOOT=" "$in_onboot" "$name/options"
	shell_add_or_subst "PPPTYPE=" "pptp" "$name/options"
	shell_add_or_subst "PPTP_SERVER=" "$in_server" "$name/options"
	shell_add_or_subst "REQUIRES=" "$in_iface" "$name/options"

	shell_add_or_subst "user " "$in_login" "$name/pppoptions"
	#shell_add_or_subst "defaultroute" "" "$name/pppoptions"

	# FIXME: login per IP
	#echo "$in_login		$in_server	\"$in_password\"" >>"$chapfile"
	echo "$in_login		*	\"$in_password\"			*" >>"$chapfile"

	set_mppe
	save_default_gw
	replace_defroute
	set_persist

	bring_up "$1"
	return 0
}


delete_iface()
{
	[ "$1" = "/" ] && return
	local name="/etc/net/ifaces/$1"
	local prev_login="$(grep '^user' "$name/pppoptions"|sed -r 's,user[[:space:]]*,,')"

	# FIXME: login per IP
	#sed -r "/^$in_login[[:space:]].*[[:space:]]$in_server$/ d" -i "$chapfile"
	sed -r "/^$prev_login[[:space:]]/ d" -i "$chapfile"

	ifdown "$1"
	rm -rf -- "$name"
}

set_mppe()
{
	# no mppe by default
	if [ "$in_mppe" = "#t" ]; then
		shell_swap_or_add "nomppe" "require-mppe" "$name/pppoptions"
	else
		shell_swap_or_add "require-mppe" "nomppe" "$name/pppoptions"
	fi
}

save_default_gw()
{
	[ "$in_save_route" = "#f" ] && {
		[ -x "$name/ifdown-post" ] && "$name/ifdown-post"
		rm -f "$name/ifup-pre" "$name/ifdown-post"
		return
	}
	DEF_GW="`/sbin/ip ro | awk '/^default via/ { print $3; exit; }'`"
	[ -n "$DEF_GW" ] || return
	[ "$DEF_GW" != "$in_server" ] || return
	echo -e "#!/bin/sh
ip route add $in_server via $DEF_GW" > "$name/ifup-pre"
	echo -e "#!/bin/sh
ip route del $in_server via $DEF_GW" > "$name/ifdown-post"
	chmod +x "$name/ifup-pre" "$name/ifdown-post"
}

replace_defroute()
{
	# replace by default
	if [ "$in_def_via_vpn" = "#f" ]; then
		shell_swap_or_add "defaultroute" "nodefaultroute" "$name/pppoptions"
	else
		shell_swap_or_add "nodefaultroute" "defaultroute" "$name/pppoptions"
	fi
}

set_persist()
{
	# nopersist by default
	if [ "$in_persist" = "#t" ]; then
		shell_swap_or_add "nopersist" "persist" "$name/pppoptions"
	else
		shell_swap_or_add "persist" "nopersist" "$name/pppoptions"
	fi
}

start_connection()
{
	debug "start_connection: [$1]"
	[ -z "$1" -o "$1" = "/" ] && return
	bring_up "$1"
	debug "start_connection: $?"
}

stop_connection()
{
	debug "stop_connection: [$1]"
	[ -z "$1" -o "$1" = "/" ] && return
	stop_iface "$1"
	debug "stop_connection: $?"
}

_()
{
LANG=${in_language%%;*}.utf8 gettext "alterator-net-pptp" "$1"
}

. /usr/share/alterator/build/backend3.sh

on_message()
{
	case "$in_action" in
		##information
		constraints)
			local required="$([ "$in_orig_action" = "new" ] && echo "#t" || echo "#f")"
			echo '('
			printf 'server (required #t match ("^[[:alnum:].]+$" "%s") label "%s")' \
				 "$(_ "invalid server name")" "$(_ "Server")"
			printf 'iface (required #t label "%s")' \
				"$(_ "Walk throw interface")"
			printf 'login (required #t label "%s")' \
				"`_ "Login"`"
			printf 'name  (required %s match ("^ppp[0-9]+$"  "%s")  default "%s" label "%s")' \
					"$required" \
					"`_ "should be ppp[number]"`" \
					"$(first_available)" \
					"`_ "Connection"`"
			printf 'password  (required %s label "%s")' \
				"$required" \
				"`_ "Password"`"
			echo ')'
			;;
		##actions
		list)
			if [ "$in__objects" = "/" ]; then
				list_iface
			else
				list_eth
			fi
			;;
		read) 
			if [ "$in__objects" = "/" ]; then
				echo '()'
			else
				read_iface "$in__objects"
			fi
			;;
		write)
			case "$in_what" in
			start)
				start_connection "$in_name";;
			stop)
				stop_connection "$in_name";;
			*)
				write_iface "$in__objects";;
			esac
			echo '()'
			;;
		new) 
			if new_iface "$in_name"; then
				echo '()'
			else
				printf '(error "%s")' "`_ "same login name already in use"`"
			fi
			;;
		delete)
			delete_iface "$in__objects"
			echo '()'
			;;
		*)
			echo '#f'
			;;
	esac
}

message_loop

