#!/bin/sh


alterator_api_version=1
. alterator-sh-functions
. alterator-net-functions
. /etc/net/scripts/functions-ipv4

list_file="$(mktemp /var/cache/alterator/net_routing_XXXXXX)"
exit_handler() {
  rm -f -- "$list_file"
}
trap "local rc=$?; trap - EXIT; exit_handler; exit $rc;"\
  HUP PIPE INT QUIT TERM EXIT

list_ifaces()
{
    local TYPE=
    local HOST=
    local NM_CONTROLLED=
    local ignore=`echo -e "default\nunknown\nlo"`
    ls -1 /etc/net/ifaces/*/options 2>/dev/null | \
    while read iface_opts; do
	TYPE=
	HOST=
	NM_CONTROLLED=
	. $iface_opts
	iface_name=${iface_opts%/*}; iface_name=${iface_name##*/};
	# ignore bridge hosts
	if [ "$TYPE" == "bri" -a -n "$HOST" ]; then
	    ignore+=`echo -e "\n$HOST"`
	fi
	# ignore networkmanager interfaces
	if [ "$NM_CONTROLLED" == "true" -o "$NM_CONTROLLED" == "yes" -o "$NM_CONTROLLED" == "1" ]; then
	    ignore+=`echo -e "\n$iface_name"`
	fi
	if echo "$ignore" | grep -qe "^$iface_name$"; then
	    continue
	fi
	echo "$iface_name"
    done
}

init_cache()
{
    > "$list_file"
    list_ifaces | \
    while read iface_name; do
	route_file="/etc/net/ifaces/$iface_name/ipv4route"
	cat "$route_file" | sed "s|\(.*\)|$iface_name \1|" >> "$list_file"
    done
}

do_reset(){
    init_cache
}

view_system_route(){
    ip route show
}

read_iface_route()
{
    cat "$list_file"
}

make_route_line() {
    local iface="$1"
    local src_net="$2"
    local src_mask="$3"
    local dst_iface="$4"
    local dst_ip="$5"
    local metric="$6"

    local str_src=
    if [ -z "$src_net" ]; then
	write_error "`_ "Invalid source network address:"`"
	return
    elif [ "$src_net" == "default" ]; then
	str_src="default"
	src_mask=
    else
	if ! echo "$src_net" | grep -q -E '^[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}$'; then
	    write_error "`_ "Invalid source network address:"` $src_net"
	    return
	fi
    fi

    if [ "$src_net" != "default" ]; then
	[ -n "$src_mask" ] \
	    || src_mask=24
	if ((src_mask<0 || src_mask>32)); then
	    write_error "`_ "Invalid source network mask:"` $src_mask"
	    return
	fi
    fi

    [ -z "$src_net" -o -z "$src_mask" ] || str_src="${src_net}/${src_mask}"

    local str_via_host="$dst_ip"
    if [ -n "$dst_ip" ]; then
	if ! echo "$dst_ip" | grep -q -E '^[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}$'; then
	    write_error "`_ "Invalid destination address:"` $dst_ip"
	    return
	fi
    fi
    [ -z "$str_via_host" ] || str_via_host="via $str_via_host"

    local str_dev_iface="$dst_iface"
    [ -z "$str_dev_iface" ] || str_dev_iface="dev $dst_iface"
    [ "$dst_iface" == "$iface" -a -n "$str_via_host" ] && str_dev_iface="" ||:

    [ -n "$metric" ] || metric=0
    if ! ((metric>=0 && metric<=255)) || ! echo "$metric"| grep -qe '^[[:digit:]][[:digit:]]*$'; then
	write_error "`_ "Invalid metric:"` $metric"
	return
    fi
    [ "$metric" != "0" ] || metric=
    [ -z "$metric" ] || metric="metric $metric"

    if [ -n "$iface" -a -n "$str_src" -a -n "$str_dev_iface$str_via_host" ]; then
	echo "$iface $str_src $str_via_host $str_dev_iface $metric"
    fi
}

add_route()
{
    local routeline=$(make_route_line "$1" "$2" "$3" "$4" "$5" "$6")
    [ -z "$routeline" ] \
	|| echo "$routeline" >> "$list_file"
}

do_apply(){
    local iface=
    local routestr=
    # clear routes
    list_ifaces | \
    while read iface
    do
	if [ -d "/etc/net/ifaces/$iface" ]; then
	    alterator-net-routing $iface del
	    > "/etc/net/ifaces/$iface/ipv4route"
	fi
    done
    # write config
    while read routeline
    do
	iface=`echo "$routeline" | sed 's|[[:space:]].*||'`
	routestr=`echo "$routeline" | sed 's|^[[:alnum:]]*[[:space:]]*||'`
	if [ -f "/etc/net/ifaces/$iface/ipv4route" ]; then
	    echo "$routestr" >> "/etc/net/ifaces/$iface/ipv4route"
	fi
    done < "$list_file"
    # up routes
    list_ifaces | \
    while read routeline
    do
	iface=`echo "$routeline" | sed 's|[[:space:]].*||'`
	alterator-net-routing $iface add
    done
}

select_route() {
    num="$1"
    [ -n "$num" ] || num=1

    strline=`head -n $num "$list_file" | tail -n 1`

    str="$(echo "$strline"| sed 's|[[:space:]].*||')"
    [ "$str" != "$strline" ] || str=
    write_string_param "interface" "$str"

    str="$(echo "$strline"| sed 's|.*[[:space:]]\([[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\)/[[:digit:]]*[[:space:]].*|\1|')"
    [ "$str" != "$strline" ] || str="default"
    write_string_param "src_net" "$str"

    str="$(echo "$strline"| sed 's|.*[[:space:]][[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*/\([[:digit:]]*\)[[:space:]].*|\1|')"
    [ "$str" != "$strline" ] || str=
    write_string_param "src_mask" "$str"

    str="$(echo "$strline"| sed 's|.*[[:space:]]dev[[:space:]][[:space:]]*\([[:alnum:]][[:alnum:]]*\)[[:space:]]*.*|\1|')"
    [ "$str" != "$strline" ] || str=
    write_string_param "dst_interface" "$str"

    str="$(echo "$strline"| sed 's|.*[[:space:]]via[[:space:]][[:space:]]*\([[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\)[[:space:]]*.*|\1|')"
    [ "$str" != "$strline" ] || str=
    write_string_param "dst_ip" "$str"

    str="$(echo "$strline"| sed 's|.*[[:space:]]metric[[:space:]][[:space:]]*\([[:digit:]][[:digit:]]*\)[[:space:]]*.*|\1|')"
    [ "$str" != "$strline" ] || str="0"
    write_string_param "metric" "$str"
}

change_route() {
    local routeline=$(make_route_line "$1" "$2" "$3" "$4" "$5" "$6")
    local setlist="$7"
    [ -n "$setlist" ] || setlist=1

    if [ -n "$routeline" ]; then
	sed -i "${setlist} i\\${routeline}" "$list_file"
	((setlist++))
	sed -i "${setlist}d" "$list_file"
    fi
}

# initialize
do_reset

on_message(){
  case "$in_action" in
  delete)
    sed -i "${in_setlist}d" "$list_file" ;;
  add)
    add_route "$in_interface" "$in_src_net" "$in_src_mask" "$in_dst_interface" "$in_dst_ip" "$in_metric" ;;
  select)
    select_route "${in_setlist}" ;;
  change)
    change_route "$in_interface" "$in_src_net" "$in_src_mask" "$in_dst_interface" "$in_dst_ip" "$in_metric" "$in_setlist";;
  apply)
    do_apply ;;
  reset)
    do_reset ;;
  routes)
    read_iface_route | nl | write_enum ;;
  list)
	case "${in__objects##*/}" in
	    *) list_ifaces | write_enum ;;
	esac
	;;
  read)
	write_string_param view "$(view_system_route)" ;;
  esac
}

message_loop
