#!/bin/sh
max_hostname_length=253

alterator_api_version=1

. alterator-sh-functions
. alterator-hw-functions
. shell-config
. shell-ip-address

resolv_default_options="edns0 trust-ad"

INTERFACES_FILE=/etc/network/interfaces
HOSTNAME_FILE=/etc/hostname
RESOLVCONF_CONF_FILE=/etc/resolvconf.conf
HOSTS_FILE=/etc/hosts

### Helper functions (taken from alterator-net-functions)
list_eth()
{
    netdev_list|
	while read iface; do
	    netdev_is_eth "$iface" || continue
	    netdev_is_real "$iface" || continue

	    echo "$iface" 2>/dev/null
	done
}

# Heavy based on valid_ipv4() from libshell.
# We can't use valid_ipv4() because it
# don't treat 127.* and 224.* addresses as valid addresses.
valid_ipv4addr()
{
	local ipaddr="$1"
	local i=0 byte

	byte="${ipaddr##*.}"
	ipaddr="${ipaddr%.$byte}"

	[ "$byte" -ge 0 -a "$byte" -le 255 ] 2>/dev/null ||
		return 1

	while [ $i -lt 3 ]; do
		byte="${ipaddr##*.}"

		[ "$byte" != "$ipaddr" ] ||
			break

		ipaddr="${ipaddr%.$byte}"

		[ "$byte" -ge 0 -a "$byte" -le 255 ] 2>/dev/null ||
			return 1

		i=$(($i+1))
	done

	[ $i -eq 2  -a \
		"$byte" -gt 0 -a "$byte" -lt 255 ] 2>/dev/null ||
		return 1
}

###
is_defined()
{
    set|grep -qs "^$(quote_sed_regexp "$1")="
}

### hostname
check_hostname()
{
    local hn="$1"
    local length=

	if [ -z "$hn" ]; then
		write_error "`_ "Host name should be provided"`"
		return 1
	fi

    length=${#hn}
    if [ $length -gt $max_hostname_length ]; then
        write_error "`_ "Host name is too long"`"
        return 1
    fi
	
	fqdn_regex='^(([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+([A-Za-z]{2,}))$'
	if [[ "$hn" == *.example.invalid || ! "$hn" =~ $fqdn_regex  ]]; then
  		write_error "`_ "Host name is not valid"`"
  		return  1
	fi

    return 0
}

### interface work
list_ifaces()
{
	list_eth
}

list_mask()
{
	local ipv="${1:-4}"
	if [ "$ipv" = 4 ]; then
		for i in `seq 32 -1 0`; do
			write_enum_item "$i" "/$i ($(ipv4_prefix2mask "$i"))"
		done
	else
		for i in `seq 128 -1 0`; do
			write_enum_item "$i" "/$i"
		done
	fi
}

read_info()
{
	local name="$1";shift
	local info=
	local tmp=
	local nl='
'

    [ -n "$name" ] || return 0

	info="`_ "Network adaptor:"`"
	info="$info $(netdev_read_info "$name")"

	if ! netdev_is_wireless "$name"; then
		netdev_is_plugged "$name" && tmp="`_ "plugged"`" || tmp="`_ "unplugged"`"
		info="$info$nl$tmp"
	fi

	# MAC
	tmp="$(netdev_read_mac "$name")"
	if [ -n "$tmp" ]; then
		info="$info$nl`_ "MAC:"` $tmp"
	fi

	# Status
	netdev_is_up "$name" && tmp="`_ "UP"`" || tmp="`_ "DOWN"`"
	if [ -n "$tmp" ]; then
		info="$info$nl`_ "Interface is "`$tmp"
	fi

	echo "$info"
}

read_iface()
{
	local name="$1"; shift

	#collect general information

	write_string_param iface_info "$(read_info "$name")"
}

check_dns()
{
	local ns_list="$1"; shift

	for ns in $ns_list; do
		if check_ip "$ns" 4; then
			continue
		fi
		
		return 1
	done

	return 0
}

check_default_gateway()
{
	if ! is_defined "in_default" || ! [ -n "$in_default" ] || ! check_ip "$in_default" "4" ; then
		write_error "`_ "Invalid default gateway"`"
		return 1
	fi

	return 0
}

get_resolvconf_lines()
{
	local dns="$1"
	local search="$2"

	if [[ $dns = *[![:space:]]* ]]; then
		echo -n "	dns-nameservers "
		for addr in $dns; do
			echo -n "$addr "
		done
		echo
	fi
	
	if [[ $search = *[![:space:]]* ]]; then
		echo -n "	dns-search "
		for domain in $search; do
			echo -n "$domain "
		done
		echo
	fi
}

create_network_interfaces()
{
	local iface_name="$1"
	local gateway="$2"
	local address="$3"
	local vlan_id="$4"

	local dns="$5"
	local search="$6"

	# Put resolv.conf entries to ifupdown2 config
	# So it will be catched by ifupdown2 resolvconf hooks
	local resolvconf_spec=$(get_resolvconf_lines "$dns" "$search")

	cat << EOF > $INTERFACES_FILE
# Generated by Alterator

auto lo
iface lo inet loopback

iface $iface_name inet manual
$(
if [ -n "$vlan_id" ]; then
# Setup VLAN on the Host
	echo "
iface $iface_name.$vlan_id inet manual

auto vmbr0v$vlan_id
iface vmbr0v$vlan_id inet static
	address $address
	gateway $gateway
	bridge-ports $iface_name.$vlan_id
	bridge-stp off
	bridge-fd 0
$resolvconf_spec

auto vmbr0
iface vmbr0 inet manual
	bridge-ports $iface_name
	bridge-stp off
	bridge-fd 0
"
else
# No VLAN needed
	echo "
auto vmbr0
iface vmbr0 inet static
	address $address
	gateway $gateway
	bridge-ports $iface_name
	bridge-stp off
	bridge-fd 0
$resolvconf_spec
"
fi
)


source /etc/network/interfaces.d/*

EOF
}

create_hostname_file()
{
	local fqdn="$1"

	short=${fqdn%%.*}

	cat << EOF > $HOSTNAME_FILE
# Generated by Alterator

$short
		
EOF
}



add_default_resolv_opts()
{
	# Edits resolvconf.conf to contain default options

	touch "$RESOLVCONF_CONF_FILE"
	
	# Edit existing line
	sed -i '/^resolv_conf_options=/c resolv_conf_options="'"$resolv_default_options"'"' "$RESOLVCONF_CONF_FILE"

	if ! grep -q '^resolv_conf_options=' "$RESOLVCONF_CONF_FILE"; then
		# Append new line, if none was found
  		printf "\nresolv_conf_options=\"$resolv_default_options\"\n" | tee -a "$RESOLVCONF_CONF_FILE" >/dev/null
	fi
}

create_hosts_file()
{
	local fqdn="$1"
	local ip_address="$2"

	short=${fqdn%%.*}
	if [ "$short" = "$fqdn" ]; then
		short=""
  	fi

	cat << EOF > $HOSTS_FILE
# Generated by Alterator

127.0.0.1       localhost.localdomain localhost
$ip_address       $fqdn $short

EOF

}

write_iface()
{
	local name="$1"; shift


	if ! check_hostname "$in_computer_name"; then 
		return
	fi

	if ! check_default_gateway ; then
		return
	fi

	if ! check_iface_address "$in_name" "${in_addip}" "${in_addmask}" "4"; then
		return
	fi

	if is_defined "in_dns" && [ -n "$in_dns" ] && ! check_dns "$in_dns"; then
		write_error "`_ "Invalid DNS list"`"
		return 1
	fi

	if is_defined "in_vlan_id" && [ -n "$in_vlan_id" ] && ! check_vlan_id "$in_vlan_id"; then
		return
	fi

	mkdir -p "/etc/network/"
	create_network_interfaces "$name" "$in_default" "${in_addip}/${in_addmask}" "$in_vlan_id" "$in_dns" "$in_search"
	create_hostname_file "$in_computer_name"
	add_default_resolv_opts
	create_hosts_file "$in_computer_name" "$in_addip"

	/sbin/update_chrooted conf >&2 || :
}

check_ip()
{
	local ip="$1"
	local ipv="${2:-4}"

	if [ "$ipv" = 4 ]; then
		valid_ipv4addr "$ip"
	else
		return 1
	fi
}

check_prefix()
{
	local prefix="$1"
	local ipv="${2:-4}"
	local max=

	[ "$ipv" = 4 ] &&
		max=32 ||
		max=128

	[ -n "$prefix" -a $prefix -ge 0 -a $prefix -le $max ]
}

check_iface_address()
{
	local iface="$1"
	local addr="$2"
	local mask="$3"
	local v="$4"


	if ! check_ip "$addr" "$v" || ! check_prefix "$mask" "$v"; then
		write_error "`_ "Invalid IP address:"` $addr/$mask"
		return 1
	fi

	if ! [ -n "$iface" -a -n "$addr" -a -n "$mask" ] ; then
		write_error "`_ "Invalid IP address:"` $addr/$mask"
		return 1
	fi

	return 0
}

check_vlan_id()
{
	local vlan_id="$1"
	if ! [[ $vlan_id =~ ^[0-9]+$ ]]; then
		write_error "`_ "VLAN ID should be a number:"` \"$vlan_id\""
		return 1
	fi
	
	if [ "$vlan_id" -lt 1 ] || [ "$vlan_id" -gt 4094 ]; then
		write_error "`_ "VLAN ID should between 1 and 4094:"` $vlan_id"
		return 1
	fi

	return 0
}

#initial actions
# iface_up lo

# alterator_export_var \
#     computer_name	hostname \
#     search		hostname-list \
#     dns			ip-address-list
#    default		ipv4-address \
#    addresses 		ipv4-addrwmask-list \


on_message()
{
	case "$in_action" in
		list)
			case "${in__objects##*/}" in
			    avail_masks) list_mask "$in_ipv";;
          # avail_ifaces
			    *) list_ifaces|write_enum;;
			esac
			;;
		read)
			local name="${in_name}"
			[ -n "$name" ] || name="$(list_ifaces|head -n1)"
			case "$in__objects" in
			    /)
				[ -n "$name" ] && read_iface "$name"

				write_string_param name		"$name"
				;;
			esac
			;;
		write)
			if  [ -n "$in_reset" ]; then
			    return
			fi
			local name="${in_name}"
			[ -n "$name" ] || name="$(list_ifaces|head -n1)"

			[ -n "$name" ] && write_iface "$name"

			;;
	esac
}

message_loop
# vim: autoindent tabstop=2 shiftwidth=2 expandtab softtabstop=2 filetype=sh
