#!/bin/sh

#turn off auto expansion
set -f

alterator_api_version=1
. alterator-sh-functions
. alterator-hw-functions

. shell-config

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


### supplicant utilities

supplicant_request(){
    local iface="$1"; shift
    wpa_cli -i "$iface" "$@"
}

supplicant_read_status(){
  local iface="$1";shift
  local status=

  if ! status="$(supplicant_request "$iface" status)";then
    echo "`_ "connection is not configured"`"
    return 0
  fi

  echo "$status" |
  {
    local SSID=
    local BSSID=
    local STATE=
    local IP=
    while read line; do
	case "$line" in
	    ssid=*)
    		SSID="${line##ssid=}"
		;;
	    bssid=*)
        	BSSID="${line##bssid=}"
		;;
	    wpa_state=*)
        	STATE="${line##wpa_state=}"
		;;
	    ip_address=*)
        	IP="${line##ip_address=}"
		;;
	esac
    done
    case "$STATE" in
	COMPLETED)
	    printf "`_ "connected to network \"%s\"(%s)\n"`" "$SSID" "$BSSID"
	    ;;
	SCANNING)
	    echo "`_ "scanning for available networks..."`"
	    ;;
	ASSOCIATING|ASSOCIATED|4WAY_HANDSHAKE|GROUP_HANDSHAKE)
	    echo "`_ "connecting to network..."`"
	    ;;
	DISCONNECTED|INACTIVE)
	    echo "`_ "disconnected"`"
	    ;;
	*)
	    echo "$STATE"
    esac
  }
}


#########


configure_interface()
{
  local iface="$1"

  echo "configuring interface $iface..." >&2

  local ifacedir="/etc/net/ifaces/$iface"
  local options="$ifacedir/options"
  local supp_conf="$ifacedir/wpa_supplicant.conf"

  #prepare interface
  [ -d "$ifacedir" ] || mkdir "$ifacedir"

  shell_config_set "$options" "DISABLED" "no"
  shell_config_set "$options" "USE_IFPLUGD" "no"
  shell_config_set "$options" "NM_CONTROLLED" "no"
  shell_config_set "$options" "TYPE" "eth"         #wi-fi devices has strange names
  shell_config_set "$options" "WPA_DRIVER" "wext"

  shell_config_set "$supp_conf" "ctrl_interface" "/var/run/wpa_supplicant"
  shell_config_set "$supp_conf" "update_config"  "1"
  chmod 600 "$supp_conf"

  wpa_cli ping -i"$iface" > /dev/null ||
    wpa_supplicant -B -Dwext -i"$iface" -c"$supp_conf"
}

MBSENT="/usr/bin/alterator-mailbox-send"
supplicant_watch()
{
    [ -x "$MBSENT" ] || return
    while true;do
	$MBSENT 'wpa-supplicant'
	sleep 3
    done
}

make_psk(){
  wpa_passphrase "$1" "$2" | awk -F'=' '/^[[:space:]]*psk=/{print $2}'
}

#########

scan_results=""

get_scan_results(){
    scan_results="$(supplicant_request "$1" scan_results | sed '1d')"
}

list_scan_results()
{
    #bssid / frequency / signal level / flags / ssid
    [ -n "$scan_results" ] || get_scan_results "$1" > /dev/null

    echo "$scan_results" |\
      while read bssid frequency level flags ssid x; do
        echo "$ssid $ssid ($bssid)"
      done
}

list_networks()
{
    #network id / ssid / bssid / flags
    # ssid can be empty! Use "-"
    supplicant_request "$1" list_networks | sed '1d;s/\t\t/\t-\t/' |
      while read id ssid bssid flags; do
        [ "$ssid" == "-" ] && ssid=""
        local label=
        if [ "$flags" = "[DISABLED]" ]; then
          label="-- $ssid"
        else
          label="+ $ssid"
        fi
        if [ "$flags" = "[CURRENT]" ]; then
          label="* $ssid"
        fi
        echo "$id" "$label"
      done
}

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)
	        echo "nopasswd `_ "No password"`"
	        echo "wep `_ "WEP Password"`"
		;;
	    WPA-PSK)
	        echo "wpa-psk `_ "WPA Personal (WPA-PSK)"`"
		echo "wpa2-psk `_ "WPA2 Personal (WPA-PSK)"`"
		;;
#	    WPA-EAP)
#		echo "wpa-eap `_ "WPA Enterprise (WPA-EAP)"`"
#		echo "wpa2-eap `_ "WPA2 Enterprise (WPA-EAP)"`"
#		;;
	esac
    done
}

list_eap_methods(){
    supplicant_request "$1" get_capability eap | tr -s ' ' '\n'
}

list_pairwise_types(){
    supplicant_request "$1" get_capability pairwise | tr -s ' ' '\n'
}

#connection status


new_name=

#network information
read_network()
{
    local iface="$1";shift
    local id="${new_name:-${1:-0}}"; shift # use new_name if exists
    [ "$id" != "-1" ] || id="0"
    new_name=
    local value=

    write_string_param "name" "$id"

    # ssid can be empty! Use "-"
    supplicant_request "$iface" list_networks | sed '1d;s/\t\t/\t-\t/' |
      while read net_id ssid bssid flags; do
        [ "$net_id" = "$id" ] || continue

        [ "$ssid" != "-" ] || ssid=""

        write_string_param "bssid" "$bssid"

        [ "$flags" = "[DISABLED]" ] &&\
          write_bool_param "enabled" "no" ||\
          write_bool_param "enabled" "yes"

        [ "$flags" = "[CURRENT]" ] &&\
          write_string_param "current" "`_ "Current network"`" ||\
          write_string_param "current" ""
        break
      done

    value="$(supplicant_request "$iface" get_network "$id" ssid | tr -d '"')"
    [ "$value" = "FAIL" ] || write_string_param "essid" "$value"

    value="$(supplicant_request "$iface" get_network "$id" pairwise)"
    [ "$value" = "FAIL" ] || write_string_param "pairwise" "$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
		write_string_param "auth_type" "nopasswd"
	    else
	        write_string_param "auth_type" "wep"
	    fi
	    ;;
	WPA-PSK)
	    if [ "$proto" = "RSN" -o "$proto" = "WPA2" ]; then
		write_string_param "auth_type" "wpa2-psk"
	    else
		write_string_param "auth_type" "wpa-psk"
	    fi
	    ;;
	WPA-EAP)
	    if [ "$proto" = "RSN" -o "$proto" = "WPA2" ]; then
		write_string_param "auth_type" "wpa2-eap"
	    else
		write_string_param "auth_type" "wpa-eap"
	    fi
	    ;;
    esac
}

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

    if test_bool "$in_enabled"; then
      supplicant_request "$iface" enable "$in_name" > /dev/null
    else
      supplicant_request "$iface" disable "$in_name" > /dev/null
    fi

    supplicant_request "$iface" set_network "$in_name" ssid "\"$in_essid\"" >/dev/null
    supplicant_request "$iface" set_network "$in_name" scan_ssid 1 >/dev/null

    local passwd_error=0;

    case "$in_auth_type" in
	nopasswd)
	    supplicant_request "$iface" set_network "$in_name" key_mgmt "NONE" >/dev/null
	    supplicant_request "$iface" set_network "$in_name" wep_key0 "\"\"" >/dev/null
	    ;;
        wep)
	    supplicant_request "$iface" set_network "$in_name" key_mgmt "NONE" >/dev/null
	    [ -n "$in_key" ] &&
	      supplicant_request "$iface" set_network "$in_name" wep_key0 "$in_key" >/dev/null
	    ;;
	wpa-psk|wpa2-psk)
	    supplicant_request "$iface" set_network "$in_name" key_mgmt "WPA-PSK" >/dev/null
	    supplicant_request "$iface" set_network "$in_name" pairwise "$in_pairwise" >/dev/null

	    # Меняем пароль, только если он не пуст и wpa_passphrase не 
	    # отдает пустой hash (короткий пароль)
	    # Такие пароли supplicant может записывать в конф.файл, но читать такой файл 
	    # уже не сможет!!!
	    # Кроме того, нельзя допускать, чтобы при записи новой сети с wpa-psk пароль оставался
	    # пустым!!!

	    # Установим пароль, если он не пуст
	    if [ -n "$in_key" ]; then
	      local hash=$(make_psk "$in_essid" "$in_key")
	      if [ -n "$hash" ]; then 
	        supplicant_request "$iface" set_network "$in_name" psk "$hash" >/dev/null
	      else 
	        # короткий пароль -- надо ругаться!
	        passwd_error=1
	      fi
	    fi

	    # Если пароля так и нет
	    local passwd=$(supplicant_request "$iface" get_network "$in_name" psk)
	    if [ "$passwd" != "*" ]; then
	      # Принимаем крайне меры, а то все упадет...
	      supplicant_request "$iface" set_network "$in_name" psk "\"password\"" >/dev/null
	    fi

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

    supplicant_request "$iface" save_config >/dev/null
    return $passwd_error
}

configured_iface=

on_message()
{
  if [ "$in_action" = "read" -o "$in_action" = "write" -o "$in_action" = "list" ]; then
    if [ -z "$in_iface" ] || ! netdev_is_wireless "$in_iface"; then
      write_error "`_ "Not a wireless interface"`"
      return
    fi
  fi

  if [ -n "$in_iface" -a "$in_iface" != "$configured_iface" ]; then
    configure_interface "$in_iface"
    configured_iface="$in_iface"
  fi

  case "$in_action" in

    monitor)
      if [ -z "$WATCH_PID" -a "$in_mode" = "on" ]; then
        supplicant_watch&
        WATCH_PID=$!
      else
        [ -n "$WATCH_PID" ] && kill "$WATCH_PID"
        WATCH_PID=
      fi
    ;;

    list)
      case "${in__objects}" in
        scan_results)
          list_scan_results "$in_iface"   | write_enum
          write_enum_item "" "`_ "Other network"`"
        ;;
        networks)       list_networks "$in_iface"       | write_enum ;;
        auth_types)     list_auth_types "$in_iface"     | write_enum ;;
        pairwise_types) list_pairwise_types "$in_iface" | write_enum ;;
        eap_methods)    list_eap_methods "$in_iface"    | write_enum ;;
      esac
    ;;

    read)
	  write_string_param iface "$in_iface"
          write_string_param iface_status "$(supplicant_read_status "$in_iface")"
          read_network "$in_iface" "$in_name"
    ;;

    write)
      [ -n "$in_select" ] &&\
        read_network "$in_iface" "$in_name"

      if [ -n "$in_btn_apply" ]; then
        if ! write_network "$in_iface"; then
          write_error "`_ "Password is too short"`"
          return
        fi
      elif [ -n "$in_btn_delete" ]; then
        supplicant_request "$in_iface" disable "$in_name" > /dev/null
        supplicant_request "$in_iface" remove_network "$in_name" >/dev/null
        supplicant_request "$in_iface" save_config >/dev/null
        new_name=$((in_name-1))
        [ $(( $new_name < 0 )) == "1" ] && new_name=0
      elif [ -n "$in_btn_reconfigure" ]; then
        supplicant_request "$in_iface" reconfigure >/dev/null
      elif [ -n "$in_btn_rescan" ]; then
        supplicant_request "$in_iface" scan >/dev/null
        get_scan_results "$in_iface" >/dev/null
      elif [ -n "$in_btn_new" ]; then
        local id="$(supplicant_request "$in_iface" add_network)"
        if [ "$id" = "FAIL" ]; then
          write_error "`_ "Unable to create network"`"
          return
        fi
        new_name="$id"
        in_name="$id"
        in_essid="$in_scanned_net"
        local flags="$(echo "$scan_results" | awk -F"$BR" "\"$in_scanned_net\" == \$5 {print \$4}")"
        in_enabled="#t"

        in_auth_type="nopasswd"
        if [ "${flags/WEP}"      != "$flags" ]; then in_auth_type="wep";fi
        if [ "${flags/WPA-PSK}"  != "$flags" ]; then in_auth_type="wpa-psk";fi
        if [ "${flags/WPA2-PSK}" != "$flags" ]; then in_auth_type="wpa2-psk";fi
        if [ "$in_auth_type" == "wpa-psk" -o "$in_auth_type" == "wpa2-psk" ]; then
          in_pairwise="TKIP"
          if [ "${flags/CCMP}" != "$flags" ]; then in_pairwise="CCMP"; fi
        fi
        write_network "$in_iface"
      fi
    ;;

  esac
}

message_loop
