#!/bin/sh

alterator_api_version=1

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

. shell-config
. shell-quote

lease_conf="/var/lib/dhcp/dhcpd/state/dhcpd.leases"
general_conf="/etc/alterator/dhcp/general"
static_conf="/etc/alterator/dhcp/static"
dhcpd_conf="/etc/dhcp/dhcpd.conf"

static_list()
{
    cat "$static_conf"
}

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

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

static_add()
{
    printf '%s	%s\n' "$1" "$2" >>"$static_conf"
}

lease_list()
{
    /bin/awk \
	'BEGIN { OFS = "_"}

	match($0,/^[[:space:]]*lease[[:space:]]+([^[:space:]]+)[[:space:]]+\{[[:space:]]*$/,re) { r["ip"]=re[1]; }
	match($0,/^[[:space:]]*hardware ethernet[[:space:]]+([^[:space:]]+)[[:space:]]*;[[:space:]]*$/,re) { r["hw"] = re[1]; }
	match($0,/^[[:space:]]*ends[[:space:]]+[^[:space:]]+[[:space:]]+([^;]+);[[:space:]]*$/,re) { r["expired"]=re[1]; }
	match($0,/^[[:space:]]*client-hostname[[:space:]]+"([^;"]+)"[[:space:]]*;[[:space:]]*$/,re) { r["hostname"]=re[1]; }

	/^[[:space:]]*}[[:space:]]*$/	{ print r["ip"],r["hw"],r["expired"],r["hostname"]; for (i in r) delete r[i]; } ' \
	"$lease_conf"
}


write_date()
{
    local langlist="$(write_language "$in_language")"
    local firstlang="${langlist%%:*}"

    LC_TIME="$firstlang" date -d "$1 GMT"
}


check_dhcpd_conf()
{
    local iface="$(shell_config_get "$general_conf" iface)"
    local ip_start="$(shell_config_get "$general_conf" ip_start)"
    local ip_end="$(shell_config_get "$general_conf" 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
}



generate_dhcpd_conf()
{
    local iface="$(shell_config_get "$general_conf" iface)"
    local ip_start="$(shell_config_get "$general_conf" ip_start)"
    local ip_end="$(shell_config_get "$general_conf" ip_end)"

    printf '#auto generated by alterator-dhcp\n\n'
    printf 'ddns-update-style none;\n'
    printf 'authoritative;\n\n'

    while IFS="	" read mac ip; do
	printf 'host %s { hardware ethernet %s; fixed-address %s; }\n' "$ip" "$mac" "$ip"
    done <"$static_conf"

    printf '\n'

    local ip="$(read_iface_addr "/etc/net/ifaces/$iface")"

    local net="$(netname "$ip"|cut -f1)"
    local net_ip="${net%%/*}"
    local net_mask="$(maskname "${ip##*/}")"

    printf 'subnet %s netmask %s {\n' "$net_ip" "$net_mask"

	local client_gw="$(shell_config_get "$general_conf" client_gw)"
	local client_dns="$(shell_config_get "$general_conf" client_dns)"
	local client_search="$(shell_config_get "$general_conf" client_search)"
	local client_time="$(shell_config_get "$general_conf" client_time)"

	[ -z "$client_gw" ] || printf '\toption routers %s;\n' "$client_gw"
	[ -z "$client_dns" ] || printf '\toption domain-name-servers %s;\n' "$client_dns"
	[ -z "$client_search" ] || printf '\toption domain-name "%s";\n' "$client_search"
	[ -z "$client_time" ] || printf '\tdefault-lease-time %s;\n\tmax-lease-time %s;\n' "$client_time" "$client_time"

	printf '\trange %s %s;\n' "$ip_start" "$ip_end"

    printf '}\n'
}


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

do_lease_fix()
{
    local IFS=";"
    for i in $in_lease_name; do
	local mac="${i##*_}"
	local ip="${i%%_*}"

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

hex_re="[0-9a-fA-F]{2}"
mac_re="${hex_re}(:${hex_re}){5}"

on_message()
{
	case "$in_action" in
		constraints)
		    printf 'ip_start   (label "%s" ipv4-address #t)\n' "`_ "Starting IP address:"`" >&3
		    printf 'ip_end     (label "%s" ipv4-address #t)\n' "`_ "Ending IP address:"`" >&3
		    printf 'client_dns (label "%s" ipv4-address #t)\n' "`_ "DNS server:"`" >&3
		    printf 'client_gw (label "%s" ipv4-address #t)\n'  "`_ "Default gateway:"`" >&3
		    printf 'client_search (label "%s" hostname #t)\n' "`_ "Search domain:"`" >&3
		    printf 'new_static_ip (label "%s" hostname #t)\n' "`_ "Host address:"`" >&3
                    printf 'new_static_mac (label "%s" match ("%s" "%s"))\n' "`_ "MAC address:"`" "^${mac_re}$" "`_ "invalid MAC address"`" >&3
		    ;;
		list)
		    case "$in__objects" in
			avail_lease)
			    lease_list|
				while IFS='_' read -r ip mac expired hname; do
				    static_has "$mac" ||
					write_table_item \
					    lease_name "${ip}_${mac}" \
					    lease_ip "$ip" \
					    lease_mac "$mac" \
					    lease_hname "$hname" \
					    lease_expired "$(write_date "$expired")"
				done
			    ;;
			avail_static)
			    static_list|
			    while read mac ip; do
				write_table_item \
				    static_name "$mac" \
				    static_mac "$mac" \
				    static_ip "$ip"
			    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)
			local iface="$(shell_config_get "$general_conf" iface)"
			[ -n "$iface" ] || iiface="$(list_static_iface|head -n1)"
			
			local ip="$(read_iface_addr "/etc/net/ifaces/$iface")"
			netname "$ip"|
			    (
				read network_name network_begin network_end
				
				ip_start="$(shell_config_get "$general_conf" ip_start)"
				ip_end="$(shell_config_get "$general_conf" ip_end)"

				if ! netcheck "$network_name" "$ip_start" "$ip_end"; then
				    ip_start=
				    ip_end=
				fi

				write_string_param network "$network_name ($network_begin - $network_end)"
				write_string_param ip_start "$ip_start"
				write_string_param ip_end "$ip_end"
			    )
			write_string_param iface "$iface"
			write_string_param client_time "$(shell_config_get "$general_conf" client_time)"
			write_string_param client_dns "$(shell_config_get "$general_conf" client_dns)"
			write_string_param client_gw "$(shell_config_get "$general_conf" client_gw)"
			write_string_param client_search "$(shell_config_get "$general_conf" client_search)"
			;;
		write)
		    if [ -n "$in_general" -a -n "$in_iface" ];then
			local ip="$(read_iface_addr "/etc/net/ifaces/$in_iface")"
			netname "$ip"|
			    (
				read network_name network_begin network_end

				if [ -z "$in_ip_start" -o -z "$in_ip_end" ];then
				    write_error "`_ "You should define address range"`"
				    return 1
				elif ! netcheck "$network_name" "$in_ip_start" "$in_ip_end"; then
				    write_error "`_ "Invalid address range"`"
				    return 1
				else
				    shell_config_set "$general_conf" ip_start "$in_ip_start"
				    shell_config_set "$general_conf" ip_end "$in_ip_end"
				    return 0
				fi
			    ) || return


			shell_config_set "$general_conf" iface "$in_iface"
			shell_config_set "$general_conf" client_time "$in_client_time"
			shell_config_set "$general_conf" client_dns "$in_client_dns"
			shell_config_set "$general_conf" client_gw "$in_client_gw"
			shell_config_set "$general_conf" client_search "$in_client_search"


			shell_config_set "$general_conf" client_gw "$in_client_gw"
		    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
		    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
			else
			    static_add "$in_new_static_mac" "$in_new_static_ip"
			fi
		    elif [ -n "$in_lease_fix" -a -n "$in_lease_name" ]; then
			do_lease_fix
		    fi
		    generate_dhcpd_conf >"$dhcpd_conf"
		    ;;
	esac
}

message_loop
