#!/bin/sh

alterator_api_version=1

. alterator-sh-functions
. alterator-net-functions
. alterator-dhcp-functions

. shell-quote

static_conf="/etc/alterator/dhcp/static"
ddns_add_tool="/usr/sbin/ddns-add-host"
ddns_del_tool="/usr/sbin/ddns-del-host"
ddns_check_tool="/usr/sbin/ddns-check-name"
hooks_dir=/usr/lib/alterator/hooks/dhcp.d

### ddns bindings
ddns_add()
{
    [ -x "$ddns_add_tool" ] || return
    "$ddns_add_tool" "$1" "$2"
}

ddns_del()
{
    [ -x "$ddns_del_tool" ] || return
    "$ddns_del_tool" "$1" "$2"
}

### static bindings

static_list()
{
    [ ! -s "$static_conf" ] || cat "$static_conf"
}

static_del()
{
    local IFS='	'
    grep "^$(quote_sed_regexp "$1")[[:space:]]" "$static_conf"|
	(read mac ip hname;
	    [ -z "$ip" -o -z "$hname" ] || ddns_del "$ip" "$hname")

    sed "/^$(quote_sed_regexp "$1")[[:space:]]/ d" -i "$static_conf"
}

static_has()
{
    grep -qs "^$(quote_sed_regexp "$1")[[:space:]]" "$static_conf"
}

static_check_name()
{
    [ -z "$1" ] ||
	[ ! -x "$ddns_check_tool" ] ||
	"$ddns_check_tool" "$1"
}

#TODO: check ip range
static_add()
{
    printf '%s	%s	%s\n' "$1" "$2" "$3">>"$static_conf"
    dhcp_lease_remove "$2"
    [ -z "$2" -o -z "$3" ] || ddns_add "$2" "$3"
}

static_rename()
{
    static_del "$1"
    static_add "$1" "$2" "$3"
}

### dynamic bindings
write_date()
{
    run_localized date -d "$1 GMT"
}

check_dhcpd_conf()
{
    local iface="$(dhcp_config_get iface)"
    local ip_start="$(dhcp_config_get ip_start)"
    local ip_end="$(dhcp_config_get ip_end)"

    if [ -z "$in_iface" ]; then
	write_error "`_ "You should define network interface"`"
	return 1
    elif [ -z "$in_ip_start" -o -z "$in_ip_end" ]; then
	write_error "`_ "You should define address range"`"
	return 1
    else
	return 0
    fi
}

do_static_del()
{
    local IFS=";"
    for i in $in_static_name; do static_del "$i" ; done
}

do_lease_fix()
{
    local IFS=";"
    local ip= mac= hname=

    for i in $in_lease_name; do
	ip="${i%%_*}"
	mac="${i##${ip}_}"
	mac="${mac%%_*}"
	hname="${i##${ip}_${mac}_}"

	static_check_name "$hname" || continue

	static_has "$mac" || static_add "$mac" "$ip" "$hname"
    done
}

check_range()
{
    local first="$1";shift
    local second="$1";shift
    local net="$1";shift

    [ -n "$first" ] &&
    [ -n "$second" ] &&
    ipv4addr_is_in_subnet "$first" "$net" &&
    ipv4addr_is_in_subnet "$second" "$net"
}

on_message()
{
	case "$in_action" in
		type)
		    write_type_item	ip_start		ipv4-address
		    write_type_item	ip_end			ipv4-address
		    write_type_item	client_dns		dhcp-dns-address
		    write_type_item	client_search		hostname
		    write_type_item	client_gw		ipv4-address
		    write_type_item	new_static_ip		ipv4-address
		    write_type_item	new_static_hname	system-computer-name
		    write_type_item	new_static_mac		dhcp-mac-address
		    ;;
		list)
		    case "$in__objects" in
			avail_lease)
			    dhcp_lease_list|
				while IFS='_' read -r ip mac expired hname; do
				    static_has "$mac" ||
					write_table_item \
					    name "${ip}_${mac}_${hname}" \
					    lease_ip "$ip" \
					    lease_mac "$mac" \
					    lease_hname "$hname" \
					    lease_expired "$(write_date "$expired")"
				done
			    ;;
			avail_static)
			    static_list|
			    while read mac ip hname; do
				write_table_item \
				    name "$mac" \
				    static_mac "$mac" \
				    static_ip "$ip" \
				    static_hname "$hname"
			    done
			    ;;
			avail_iface)
			    list_static_iface|
				while read name; do
				    local ip="$(read_iface_addr "/etc/net/ifaces/$name")"
				    printf "%s	%s\n" $name "$(netname "$ip")"
				done |
				while read name network_name ip_start ip_end; do
				    write_enum_item "$name" "$name ($ip_start - $ip_end)"
				done
			    ;;
			avail_time)
			    write_enum_item	3600		"`_ "1 hour"`"
			    write_enum_item	7200		"`_ "2 hours"`"
			    write_enum_item	14400		"`_ "4 hours"`"
			    write_enum_item	28800		"`_ "8 hours"`"
			    write_enum_item	43200		"`_ "12 hours"`"
			    write_enum_item	86400		"`_ "1 day"`"
			    write_enum_item	604800		"`_ "1 week"`"
			    write_enum_item	1209600		"`_ "2 weeks"`"
			    write_enum_item	18748800	"`_ "1 month"`"
			    ;;
		    esac
		    ;;
		read)
		    case "$in__objects" in
			/)
			    ! dhcp_daemon_status
			    write_bool_param daemon "$?"

			    local iface="$(dhcp_config_get iface)"
			    [ -n "$iface" ] || iface="$(list_static_iface|head -n1)"
			
			    local ip="$(read_iface_addr "/etc/net/ifaces/$iface")"
			    local ip_start="$(dhcp_config_get ip_start)"
			    local ip_end="$(dhcp_config_get ip_end)"

			    if [ -z "$ip" ] ||
			       ! check_range "$ip_start" "$ip_end" "$ip";then
				ip_start=
				ip_end=
			    fi

			    write_string_param ip_start "$ip_start"
			    write_string_param ip_end "$ip_end"

			    write_string_param iface "$iface"
			    write_string_param client_time "$(dhcp_config_get client_time)"
			    write_string_param client_dns "$(dhcp_config_get client_dns)"
			    write_string_param client_gw "$(dhcp_config_get client_gw)"
			    write_string_param client_search "$(dhcp_config_get client_search)"

			    role=$(shell_config_get /etc/sysconfig/system SERVER_ROLE)
			    [ ! -x "$ddns_check_tool" ] || [  "$role" != 'master' ]
			    write_bool_param has_ddns "$?"
			    ;;
			static)
			    grep "^$in_mac[[:space:]]" "$static_conf"|
				(read mac ip hname;
				    write_string_param mac "$mac"
				    write_string_param new_static_ip "$ip"
				    write_string_param new_static_hname "$hname")
			;;
		    esac
		    ;;
		write)
		    case "$in__objects" in
		    /)
			if [ -n "$in_general" -a -n "$in_iface" ];then
			    local old_iface="$(dhcp_config_get iface)" # previous value of iface
			    local new_iface="$in_iface" # new value of iface
			    local ip="$(read_iface_addr "/etc/net/ifaces/$new_iface")"

			    if ! check_range "$in_ip_start" "$in_ip_end" "$ip";then
				write_error "`_ "Invalid address range"`"
				return 1
			    fi

			    dhcp_config_set ip_start "$in_ip_start"
			    dhcp_config_set ip_end "$in_ip_end"

			    dhcp_config_set iface "$new_iface"
			    dhcp_config_set client_time "$in_client_time"
			    dhcp_config_set client_dns "$in_client_dns"
			    dhcp_config_set client_gw "$in_client_gw"
			    dhcp_config_set client_search "$in_client_search"

			    dhcp_config_set client_gw "$in_client_gw"

			    #note: update config before daemon start to create initial config
			    dhcp_update_config
			    if test_bool "$in_daemon"; then
				dhcp_daemon_on
			    else
				dhcp_daemon_off
			    fi

			    #postinstall hook
			    if [ "$old_iface" != "$new_iface" ];then
				export old_iface new_iface
				run-parts "$hooks_dir"
			    fi
			elif ! check_dhcpd_conf; then #check general conf before any other actions
			    return
			elif [ -n "$in_static_del" -a -n "$in_static_name" ];then
			    do_static_del
			    dhcp_update_config
			elif [ -n "$in_static_add" -a -n "$in_new_static_ip" -a -n "$in_new_static_mac" ]; then
			    if static_has "$in_new_static_mac"; then
				write_error "`_ "Same entry already exists"`"
				return
			    elif ! static_check_name "$in_new_static_hname";then
				write_error "`_ "This computer name is registered for internal purposes"`"
				return
			    else
				static_add "$in_new_static_mac" "$in_new_static_ip" "$in_new_static_hname"
			    fi
			    dhcp_update_config
			elif [ -n "$in_lease_fix" -a -n "$in_lease_name" ]; then
			    do_lease_fix
			    dhcp_update_config
			fi
			;;
		    static)
			[ -n "$in_mac" ] || return
			if ! static_check_name "$in_new_static_hname"; then
			    write_error "`_ "This computer name is registered for internal purposes"`"
			    return
			else
			    [ -z "$in_new_static_ip" ] ||
				static_rename "$in_mac" "$in_new_static_ip" "$in_new_static_hname"
			fi

			dhcp_update_config
			;;
		    esac
		    ;;
	esac
}

message_loop
