#!/bin/sh
#
#
# wifiprobe ver 0.2
#
# Copyright (c) 2007, anonymous donor to the public domain
#
# BSD license and disclaimers apply.
#
# Do not change to zsh;  it will break. There are subtle differences.
#
# This script should be installed with execute permissions, and
# be invoked by name.
#
# Developed under and for OpenBSD 4.2  11/2007
#
#-------------------------------------------------------------------------
#
#  helper functions
#
#-------------------------------------------------------------------------

function parseit 
{
   local therest

   shift 1
   beamname=$1
   shift 1
   therest=$*
   IFS=" "
   set $therest
   shift 4
   sigstrength=$1
   sigstrength=${sigstrength:%dB}
# needed in 4.2?
   sigstrength=${sigstrength:%%%}
   shift 1
}


function readprobe
{
#
# this was not as easy to write as it looks
#
   local Foo

   while read Foo
   do
      IFS=" "
      if  echo $Foo | grep -q \"  ; then
         IFS="\""
      fi
      parseit $Foo
      beam[nbeam]=${beamname}
      strength[nbeam]=${sigstrength%dB}
#      printf "%4d\t%-32s\t%s\t%s\n" $nbeam "${beam[$nbeam]}" $sigstrength ${strength[nbeam]}
      nbeam=$(($nbeam+1))
   done 
}

function sortandprint
{
   typeset tempnm
   typeset tempst
   typeset -i i
   typeset -i j
   typeset -i inc
   typeset -i n
   typeset -i s

   s=$1
   n=$2
# s is offset in arrays where sort starts
# n is number of items to sort
# In other words, sort elements $s to $s + $n
#
# Implement a Shell sort.  In ksh.  Painful.  All the write-only jive-notation
# of perl, none of the functionality.
#

   if [ $n -eq 0 ]; then
      echo $MSG9
      return
   fi

   inc=$(($n/2))
   while [ $inc -gt 0 ]
   do
      i=$inc
      while [ $i -lt $n ] 
      do
         j=$i
         tempst=${strength[$(($i+$s))]}
         tempnm=${beam[$(($i+$s))]}
#
# to change the sense of the sort, change the second test.
# use -lt for biggest first, -gt for smallest first.
#
         while [[ $j -ge $inc  && ${strength[$(($j+$s-$inc))]} -lt $tempst ]]
         do
            strength[$(($j+$s))]=${strength[$(($j+$s-$inc))]}
            beam[$(($j+$s))]=${beam[$(($j+$s-$inc))]}
            j=$(($j-$inc))
         done
         strength[$(($j+$s))]=$tempst
         beam[$(($j+$s))]=$tempnm
         i=$(($i+1))
      done

      if [ $inc -eq 2 ]; then
         inc=1
      else
         inc=$(($inc/2))
      fi
   done

   i=$s
   while [ $i -lt $(($n+$s)) ]
   do
      printf "%4d   %-32s\t%3d\n" $(($i+1)) "${beam[$i]}" ${strength[$i]}
      i=$(($i+1))
   done
}


cleanup ()
{
   if [ -t 0 -a -t 1 ]; then
      stty sane
   fi
   rm -f ${TMPPROBE}
   exit
}

#-------------------------------------------------------------------------
#
# "main" part of the script
#
#-------------------------------------------------------------------------

LANG="english"
progname=`basename $0`

if [ X$LANG = X"english" ]; then

   MSG1="$progname:  Wireless access selection for device:"
   MSG2="Available public beams . . . . . . . . score"
   MSG3="Available secured beams"
   MSG4="$progname: no wireless beams found"
   MSG5="choice out of range"
   MSG6="try again"
   MSG7="public access beam selected"
   MSG8="$progname: not interactive and no public beams"
   MSG9="none probed"
   MSG10="usage: $progname [interface_name]"
   CHOOSEPROMPT="Select beam> "
   PASSPROMPT="Password for"

elif [ X$LANG = X"catala" ] ; then

   MSG1="$progname: Dispositu de xarxa sense fil:"
   MSG2="Xarxes obertes disponibles . . . . . . Punts"
   MSG3="Xarxes tancades detectades"
   MSG4="$progname: No hi ha cap xarxa disponible"
   MSG5="La xarxa que has triat no es troba en la llista"
   MSG6="mira de fer-ho una altra vegada..."
   MSG7="Has triat una xarxa oberta"
   MSG8="$progname: No interactiu, i no hi ha d'obertes"
   MSG9="no n'has avaluat cap"
   MSG10="Com fer-ho servir: $progname [nom_dispositiu]"
   CHOOSEPROMPT="Tria la xarxa> "
   PASSPROMPT="Mot de pas per a"
fi

IFACE=ath0
if [ $# -gt 0 ]; then
   IFACE=$1
   shift 1
   if [ $# -gt 0 ]; then
      echo Extra arguments: $@ 1>&2
      echo $MSG10 1>&2
      exit 1
   fi
fi

TMPPROBE=`mktemp -t ${IFACE}.XXXXXXXX`

# only half-hearted efforts to rm this file are made.


testing=false
#testing=true
if [ X$testing = Xtrue ]; then
# hacks for development use by author
   exho="echo dummy command: "
   PROBECMD="cat /home/mass/play/weasel"
   SUDO=""
   SUDO="Pseudo-SUDO"
else
   exho=""
   SUDO="sudo"
   PROBECMD="${SUDO} ifconfig -M ${IFACE}"
fi

trap cleanup EXIT INT PIPE

typeset -i nbeam=0
typeset -i private=0

# one can get more clever than this, one might involve PAGER
# one might involve LESS
# not this one, though

if [ -t 0 -a -t 1 ]; then
   mypager=more
else
   mypager=cat
fi

# generate a menu 
# Do this even for batch use, to log results during, say, boot/rc
#
# if this script is used in another script and output is not
# wanted, then invoke it with >/dev/null and perhaps 2>/dev/null
# That is the True Path
#
# The parsing done by the sed/grep pipe is not robust at all.
# It is possible that a recipe for bouillabaise might pass muster
# as output sufficiently parseable to attempt to configure a device
# later on.  The "correct" way to do this job is with a compiled
# program, modelled on ifconfig, that gathers this information from
# ioctl(2) calls on the device.  (see routines getinfo() et al in
# the sources for ifconfig.)  Parsing output not intended for
# human reading is a path to later madness.  This script is twice
# as long as it might be, if the need to parse and later accomodate 
# device "nwids" with embedded blanks did not exist.
#

echo $MSG1 ${IFACE}
echo
echo $MSG2
echo "-----------------------------------------------------------------"
eval $PROBECMD | sed "s/^[[:space:]]*//g" | grep ^nwid | grep -v privacy > ${TMPPROBE}
readprobe < ${TMPPROBE}

sortandprint 0 $nbeam  > ${TMPPROBE}
eval ${mypager} ${TMPPROBE}

# for obscure reasons of scoping, do not "simplify" the preceding
# two lines by discarding the temp file and putting readprobe on the
# end of the pipe.
#
# Put no function in a pipe (wrecks scope)
#
# the dividing point between public and private:

private=${nbeam}

echo
echo $MSG3
echo "-----------------------------------------------------------------"
eval $PROBECMD | sed "s/^[[:space:]]*//g" | grep ^nwid | grep privacy >${TMPPROBE}
readprobe < ${TMPPROBE}

sortandprint $private $(($nbeam-$private)) > ${TMPPROBE}
eval ${mypager} ${TMPPROBE}

# the "if" establishes whether stdin and stdout are ttys.
# This is not flawless, of course, but tends to guess whether
# an operator is present.
# It would be better if a variable asserting batchness were present
# on the command line or in the environment.

typeset -i choice=-1

if [ $nbeam -eq 0 ] ; then
   echo $MSG4 1>&2
   cleanup
fi

if [ -t 0 -a -t 1 ]; then
   while [ 0 ]
   do
      read choice?"$CHOOSEPROMPT"
      if [ $choice -eq 0 ] ; then
         break
      elif [ $choice -gt $nbeam ] ; then
         echo $MSG5 $choice
         echo $MSG6
      else
         break
      fi
   done

   if [ $choice -eq 0 ] ; then
      cleanup
   fi

#
# decrement choice to accommodate zero-based arrays
#
   choice=$(($choice-1))
   if [ $choice -ge $private ] ; then
      stty -echo
      read pass?"$PASSPROMPT ${beam[$choice]}> "
      stty echo
      echo
#        this leaks the password to stdout when exho=echo.  Big deal?
      eval ${exho} ${SUDO} ifconfig ${IFACE} nwid \"${beam[$choice]}\" \
    nwkey \"${pass}\"
   else
      echo $MSG7 ${beam[$choice]}
      eval ${exho} ${SUDO} ifconfig ${IFACE} nwid \"${beam[$choice]}\" \
    -nwkey down
   fi
else   # non-interactive: configure the first (strongest) public beam
   if [ $private -eq 0 ] ; then
      echo $MSG8 1>&2
      cleanup
   else 
      choice=1
      eval ${exho} ${SUDO} ifconfig ${IFACE} nwid \"${beam[$choice]}\" \
    -nwkey down 1>&2
   fi
fi

eval ${exho} ${SUDO} dhclient ${IFACE}

cleanup

