#!/bin/sh

PATH="/usr/lib/alterator-net-common:$PATH"

BR='	'

#########

exit_handler()
{
    local rc=$?
    trap - EXIT
    [ -n "$WATCH_PID" ] && kill "$WATCH_PID" 2>/dev/null
    exit $rc
}

trap exit_handler HUP INT QUIT TERM EXIT

#########

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

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
	printf %s\\n "$name$value" >> "$file"
}

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

        [ -f "$file" ] &&
        grep -qs "^$name=" "$file" &&
        grep "^$name=" "$file"|sed "s,^$name=,,"
}

#########

supplicant_driver()
{
    local drv="$(ifdriver "$1")"
    case "${drv##*/}" in
	ath_pci)
	    echo "madwifi"
	    ;;
	ipw2100)
	    echo "ipw"
	    ;;
	*)
	    echo "wext"
	    ;;
    esac
}

supplicant_sure()
{

    local name="$1";shift

    echo "try to start supplicant for $name..." >&2

    local ifacedir="/etc/net/ifaces/$name"
    local options="$ifacedir/options"
    local supp_conf="$ifacedir/wpa_supplicant.conf"
    
    #prepare interface
    [ -d "$ifacedir" ] ||
	mkdir "$ifacedir"
	 
    if [ ! -s "$options" ]; then
	shell_add_or_subst "TYPE=" "eth" "$options" #wi-fi devices has strange names
	shell_add_or_subst "BOOTPROTO=" "static" "$options" #protection from DHCP timeout
        shell_add_or_subst "WPA_DRIVER=" "$(supplicant_driver "$name")" "$options"
    fi

    [ -s "$supp_conf" ] ||
	printf 'ctrl_interface=/var/run/wpa_supplicant\nupdate_config=1\n'>"$supp_conf"

    ifdown "$name"
    ifup "$name"
    /sbin/ip link set up dev "$name" #hardware up: for some ugly wifi cards
}

supplicant_request()
{
    local iface="$1"; shift

    if ! /usr/sbin/wpa_cli -i"$iface" "$@";then
	supplicant_sure "$iface"
	/usr/sbin/wpa_cli -i"$iface" "$@"
    fi
    
    
    local supp_conf="/etc/net/ifaces/$iface/wpa_supplicant.conf"

    [ -s "$supp_conf" ] && chmod 600 "$supp_conf"
}

supplicant_watch()
{
    while true;do
	alterator-mailbox-send 'wpa-supplicant'
	sleep 3
    done
}

#########

list_scan_results()
{
    #bssid / frequency / signal level / flags / ssid
    supplicant_request "$1" scan_results|
	sed '1d'|
	awk -F"$BR" '{printf "(\"%s\" bssid \"%s\" flags \"%s\")\n",$5,$1,$4}'
}

list_networks()
{
    #network id / ssid / bssid / flags
    supplicant_request "$1" list_networks|
	sed '1d'|
	awk -F"$BR" '{printf "(\"%s\" essid \"%s\" bssid \"%s\" flags \"%s\")\n",$1,$2,$3,$4}'
}


list_actions()
{
    printf '("enable" label "%s")' "`_ "enable"`"
    printf '("disable" label "%s")' "`_ "disable"`"
    printf '("select" label "%s")' "`_ "select as current"`"
    printf '("remove" label "%s")' "`_ "remove from list"`"
}

list_auth_types()
{
    #WPA-PSK WPA-EAP IEEE8021X WPA-NONE NONE
    for i in $(supplicant_request "$1" get_capability key_mgmt);do
	case "$i" in
	    NONE)
	        printf '("nopasswd" label "%s")' "`_ "No password"`"
	        printf '("wep" label "%s")' "`_ "WEP Password"`"
		;;
	    WPA-PSK)
	        printf '("wpa-psk" label "%s")' "`_ "WPA Personal (WPA-PSK)"`"
		printf '("wpa2-psk" label "%s")' "`_ "WPA2 Personal (WPA-PSK)"`"
		;;
#	    WPA-EAP)
#		printf '("wpa-eap" label "%s")' "`_ "WPA Enterprise (WPA-EAP)"`"
#		printf '("wpa2-eap" label "%s")' "`_ "WPA2 Enterprise (WPA-EAP)"`"
#		;;
	esac
    done
}

list_eap_methods()
{
    for i in $(supplicant_request "$1" get_capability eap);do
	printf '("%s")' "$i"
    done
}

list_pairwise_types()
{
    for i in $(supplicant_request "$1" get_capability pairwise);do
	printf '("%s")' "$i"
    done
}

#connection status
read_status()
{
    printf 'name "%s"\n' "$1"
    supplicant_request "$1" status|
	while read line;do
	    case "$line" in
		bssid=*)
		    printf 'bssid "%s"\n' "${line##bssid=}"
		    ;;
		wpa_state=*)
		    printf 'status "%s"\n' "${line##wpa_state=}"
		    ;;
	    esac
	done
}

#network information
read_network()
{
    local iface="$1";shift
    local id="$1";shift
    local value=

    value="$(supplicant_request "$iface" get_network "$id" "ssid")"
    [ "$value" != "FAIL" ] && printf 'essid %s' "$value"

    value="$(supplicant_request "$iface" get_network "$id" "pairwise")"
    [ "$value" != "FAIL" ] && printf 'pairwise "%s"' "$value"

    local key_mgmt="$(supplicant_request "$iface" get_network "$id" "key_mgmt")"
    local proto="$(supplicant_request "$iface" get_network "$id" "proto")"

    case "$key_mgmt" in
	NONE)
	    value="$(supplicant_request "$iface" get_network "$id" "wep_key0")"

	    if [ "$value" = "FAIL" -o -z "$value" ];then
		echo 'auth_type "nopasswd"'
	    else
		echo 'auth_type "wep"'
	    fi
	    ;;
	WPA-PSK)
	    if [ "$proto" = "RSN" -o "$proto" = "WPA2" ];then
		echo 'auth_type "wpa2-psk"'
	    else
		echo 'auth_type "wpa-psk"'
	    fi
	    ;;
	WPA-EAP)
	    if [ "$proto" = "RSN" -o "$proto" = "WPA2" ];then
		echo 'auth_type "wpa2-eap"'
	    else
		echo 'auth_type "wpa-eap"'
	    fi
	    ;;
    esac
}

write_network()
{
    local iface="$1";shift
    local id="$1";shift

    supplicant_request "$iface" set_network "$id" ssid "\"$in_essid\"" >/dev/null

    case "$in_auth_type" in
	nopasswd)
	    supplicant_request "$iface" set_network "$id" key_mgmt "NONE" >/dev/null
	    supplicant_request "$iface" set_network "$id" wep_key0 "\"\"" >/dev/null
	    ;;
        wep)
	    supplicant_request "$iface" set_network "$id" key_mgmt "NONE" >/dev/null
	    [ -n "$in_key" ] &&
		if [ "${in_key#0x}" != "$in_key" ];then
		    supplicant_request "$iface" set_network "$id" wep_key0 "${in_key#0x}" >/dev/null
		else
		    supplicant_request "$iface" set_network "$id" wep_key0 "\"$in_key\"" >/dev/null
		fi
	    ;;
	wpa-psk|wpa2-psk)
	    supplicant_request "$iface" set_network "$id" key_mgmt "WPA-PSK" >/dev/null
	    supplicant_request "$iface" set_network "$id" pairwise "$in_pairwise" >/dev/null
	    [ -n "$in_key" ] &&
		if [ "${in_key#0x}" != "$in_key" ];then
		    supplicant_request "$iface" set_network "$id" psk "${in_key#0x}" >/dev/null
		else
		    supplicant_request "$iface" set_network "$id" psk "\"$in_key\"" >/dev/null
		fi

	    if [ "$in_auth_type" = "wpa-psk" ];then
		    supplicant_request "$iface" set_network "$id" proto "WPA" >/dev/null
	    else
		    supplicant_request "$iface" set_network "$id" proto "WPA2" >/dev/null
	    fi
	    ;;
    esac
}

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

    for i in $@;do
	printf ' exclude ("%s" %s) ' "$name" "$i"
    done
}

#turn off auto expansion
set -f

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

on_message()
{
	case "$in_action" in
		constraints)
			local is_new="$([ "$in_orig_action" = "new" ] && echo "#t" || echo "#f")"

			echo '('
			printf 'essid (label "%s" required %s)\n' "`_ "Network name"`" "$is_new"
			printf 'bssid (label "%s")\n' "`_ "Access point"`"
			printf 'status (label "%s")\n' "`_ "Status"`"
			printf 'auth_type (label "%s" %s %s %s %s)\n' \
			    "`_ "Security"`" \
			    "$(print_excludes "nopasswd" key user pairwise eap)" \
			    "$(print_excludes "wep" user pairwise eap)" \
			    "$(print_excludes "wpa-psk" user eap)" \
			    "$(print_excludes "wpa2-psk" user eap)"
			printf 'user (label "%s")\n' "`_ "User name"`"
			printf 'key (label "%s")\n' "`_ "Network key (use 0x for hex)"`"
			printf 'pairwise (label "%s")\n' "`_ "Encryption"`"
			printf 'eap (label "%s")\n' "`_ "EAP method"`"
			echo ')'
			;;
		monitor)
			if [ -z "$WATCH_PID" -a "$in_mode" = "on" ];then
			    supplicant_watch&
			    WATCH_PID=$!
			else
			    [ -n "$WATCH_PID" ] && kill "$WATCH_PID"
			    WATCH_PID=
			fi

			echo '()'
			;;
		list)
			echo '('
			case "${in__objects##*/}" in
			    scan_results)
				list_scan_results "${in__objects%%/*}"
				;;
			    networks)
				list_networks "${in__objects%%/*}"
				;;
			    network_actions)
				list_actions
				;;
			    auth_types)
				list_auth_types "${in__objects%%/*}"
				;;
			    pairwise_types)
				list_pairwise_types "${in__objects%%/*}"
				;;
			    eap_methods)
				list_eap_methods "${in__objects%%/*}"
				;;
			esac
			echo ')'
			;;
		read)
			local iface="${in__objects%%/*}"

			echo '('
			case "${in__objects#*/}" in
			    networks/*)
				read_network "$iface" "${in__objects##*/}"
				;;
			    *)
				if ifcheckwireless "$iface" ;then
				    read_status "$iface"
				else
				    printf 'error "%s"' "`_ "Not a wireless interface"`"
				fi
				;;
			esac
			echo ')'
			;;
		write)
			local iface="${in__objects%%/*}"

			case "${in__objects#*/}" in
			    networks/*)
				write_network "$iface" "${in__objects##*/}"
				supplicant_request "$iface" save_config >/dev/null
				;;
			    *)
				[ -n "$in_reconfigure" ] && supplicant_request "$iface" reconfigure >/dev/null
				[ -n "$in_scan" ] && supplicant_request "$iface" scan >/dev/null
				;;
			esac
			echo '()'
			;;
		enable|disable|remove|select)
			if [ "${in__objects%%/networks/*}" != "$in__objects" ];then
			    local iface="${in__objects%%/networks/*}"
			    local id="${in__objects##*/}"

			    supplicant_request "$iface" "${in_action}_network" "$id" >/dev/null
			    supplicant_request "$iface" save_config >/dev/null
			fi
			echo '()'
			;;
		new)
			local iface="$in__objects"
			local id="$(supplicant_request "$iface" add_network)"

			if [ "$id" = "FAIL" ];then
			    printf '(error "%s")' "`_ "Unable to create network"`"
			else
			    write_network "$iface" "$id"
			    supplicant_request "$iface" enable_network "$id" >/dev/null
			    supplicant_request "$iface" save_config >/dev/null
			    echo '()'
			fi
			;;
		*)
			echo '#f'
			;;
	esac
}

message_loop
