#!/bin/bash
#
# open-wifi-auto-connect.sh: Auto connect open WIFI(s).
# This script scanns for WIFIs and connects to the first open WIFI it can find
# and which is really online, e. g. can send a ping to a root nameserver.
# If no open and online WIFI could be connected or after a disconnect it 
# restarts with a new and fast scan for WIFIs, to make the PC nearly always 
# online.
# To avoid problems with duplicate ESSIDs and hidden ESSIDs this script uses
# MACs instead of ESSIDs.
# 

# ----------------------------------------------------------------------------
# "THE BEERWARE LICENSE" (Revision 44):
# Dr. Rolf Freitag (rolf dot freitag at email dot de) wrote this file.
# As long as you retain this notice you can do whatever
# the GPL (GNU Public License version 3) allows with this stuff.
# If you think this stuff is worth it, you can send me money via
# paypal, and get a contribution receipt if you wish, or if we met some day
# you can buy me a beer in return.
# ----------------------------------------------------------------------------
#
# Version 2010-06-20
#

# TODOs: 
#  * sort the list of open WIFIs due to their quality to connect the best
#  * set a lockfile with the device name in the file name

# set -u : Stop the script when a variable isn't set (add -x for debugging)
set -u 

if [ $# -ne 1 ]
then
 echo "Error: Not exact one parameter (the WIFI device); exiting."
 exit 1
fi

# device used for wifi (usually wlan0): first argument
DEVICE="$1"

# Make sure the script is run as root.
#if [ $EUID -ne 0 ]; then
if [ $GROUPS -ne 0 ]; then
   echo "Error: This script must be run from group root, but your group is $GROUPS; exiting."
   exit 1
fi

# kill other stuff which uses the device
/etc/init.d/network-manager stop 2>&1 >/dev/null

# if necessary bring the device up
ifconfig "$DEVICE" 2>&1 > /dev/null
if [ $? -ne 0 ]
then
  ifconfig "$DEVICE" up
fi

# check if the device can now be used
ifconfig "$DEVICE" 2>&1 > /dev/null
if [ $? -ne 0 ]; then
  echo "Error: Device $DEVICE is not ready (ifconfig $DEVICE returned not zero); exiting."
  exit 1
fi

# set the device offline for configuration
ifconfig "$DEVICE" down

# disable  the  ESSID  checking (ESSID promiscuous), no key
iwconfig "$DEVICE" channel auto
iwconfig "$DEVICE" essid any
iwconfig "$DEVICE" key off
# set auto parameters with maximum speed of 1 Mbit/s (half duplex)
# for maximum sensitivity, maximum range and maximum immunity to interference
# because SNR and bit rate are complementary.
iwconfig "$DEVICE" txpower auto
#iwconfig "$DEVICE" commit
# Set a trasmission power of 500 mW, which is ok e. g. in the USA and works 
# with many cards. The value set here should be (smaller than or) equal to the 
# maximum of your card.
iwconfig "$DEVICE" txpower 500mW
iwconfig "$DEVICE" power on
iwconfig "$DEVICE" rate 1M auto
iwconfig "$DEVICE" frag auto
# set client mode
iwconfig "$DEVICE" mode managed
#iwconfig "$DEVICE" commit

# create temporary working directory and change into that directory
#TMPDIR0="/tmp/.$0.$$.$RANDOM.DIR1.if.txt"
#mkdir "$TMPDIR0"
TMPDIR0=`mktemp -d`
cd "$TMPDIR0"

# bash trap function for cleanup at exit (executed when CTRL-C is pressed)
# Signals: 1/HUP, 2/INT, 3/QUIT, 9/KILL, 15/TERM, ERR
trap bashtrap 2 9 15
bashtrap()
{
  cd -
  rm -rf "$TMPDIR0"
  exit 0
}

# create temporary files
#TMPFILE0="$TMPDIR0/.$0.$$.$RANDOM.0.if.txt"
#TMPFILE0=`tempfile -d "$TMPDIR0"` # tempfile is not part of the coreutils
TMPFILE0=`mktemp --tmpdir="$TMPDIR0"`
TMPFILE1=`mktemp --tmpdir="$TMPDIR0"`

# variables for open WIFI count (0...), ...
typeset -i OPENCOUNT=0
typeset -i CLOSEDCOUNT=0
typeset -i CELLCOUNT=0
typeset -i i=0
declare -a APMAC
declare -a OPENCELLNUMBER
declare -a CHANNEL
declare -a ESSID
typeset -i deadline_counter=0
typeset -i loop_counter=0
typeset -i connected=0
typeset -i flag=0
typeset -i SCANNUMBER=0

# start of the endless loop with scanning, looking for open WIFIs, testing/using
while [ 1 = 1 ]
do
  OPENCOUNT=0

  # Change the MAC before every scan to a random and valid MAC by limiting the second byte to 5.
  # Because modern WIFI cards usually have higher bytes, this ensures another and random MAC.
  ran=$(head /dev/urandom | md5sum)
  MAC=00:0$[$RANDOM%6]:${ran:0:2}:${ran:3:2}:${ran:5:2}:${ran:7:2}
  echo "switching MAC to $MAC"
  ifconfig "$DEVICE" down
  ifconfig "$DEVICE" promisc
  ifconfig "$DEVICE" hw ether $MAC
  # clear the arp cache
  #ip neigh flush all 2>/dev/null
  ifconfig "$DEVICE" up

  # scan and get a list of (open) wifi points
  echo "Scan number $SCANNUMBER, scanning ..."
  SCANNUMBER=$[$SCANNUMBER +1]
  iwlist "$DEVICE" scanning > "$TMPFILE0" 2>/dev/null

  # remove the first line of the scan output (with "Scan completed")
  i=`wc -l < "$TMPFILE0"`
  i=$[$i -1]
  tail -n $i "$TMPFILE0" > "$TMPFILE1"
  
  # get the number of open WIFIs
  cat "$TMPFILE1" | grep "Encryption key:off" > "$TMPFILE0"
  OPENCOUNT=`wc -l < "$TMPFILE0"`

  # get the number of closed WIFIs
  cat "$TMPFILE1" | grep "Encryption key:on" > "$TMPFILE0"
  CLOSEDCOUNT=`wc -l < "$TMPFILE0"`

  echo "Found $OPENCOUNT open WIFI(s) and $CLOSEDCOUNT closed WIFI(s)."

  # get the total number of WIFIs (Cells)
  cat "$TMPFILE1" | grep "Encryption key:" > "$TMPFILE0"
  CELLCOUNT=`wc -l < "$TMPFILE0"`

  # Split the scan output into one file per cell; the files are xx1, xx2, ...
  csplit --digits=1 -k "$TMPFILE1" '/Cell/' {99} 2> /dev/null > /dev/null

  # print the ESSID(s)
  echo "List of WIFI(s) with Channel, Encryption, Quality, Signal Level, MAC, ESSID:"
  i=1 # number for the first open WIFI MAC
  for loop_counter in $(seq 1 $CELLCOUNT)
  do
      echo -n "`cat xx$loop_counter | awk '/Channel:/{ print $1 }'`"
      echo -n -e "\t`cat xx$loop_counter | awk '/Encryption/{ print $2 }'`"
      echo -n -e "\t`cat xx$loop_counter | awk '/Quality/{ print $1}'`"
      echo -n "  `cat xx$loop_counter | awk '/Quality/{ print $3}'`"
      echo -n "  `cat xx$loop_counter | awk '/Address/{ print $5 }'`"
      echo "  `cat xx$loop_counter | awk '/ESSID/{ print $1 }'`"
  done

  # if no open WIFI found: continue (make a new scan)
  if [ $OPENCOUNT -eq 0 ]
  then
    continue
  fi
  # now we have at minimum one open WIFI

  # put the MACs of the open WIFIs into the array (APMAC)
  i=1 # number for the first open WIFI MAC
  for loop_counter in $(seq 1 $CELLCOUNT)
  do
    cat xx$loop_counter | grep "Encryption key:off" 2>&1 >/dev/null
    if [ $? -eq 0 ]; then
      APMAC[$i]=`cat xx$loop_counter | awk '/Address/{ print $5 }'`
      CHANNEL[$i]=`cat xx$loop_counter | awk '/Channel:/{ print $1 }' | cut -d ":" -f 2`
      foo=`cat xx$loop_counter | awk '/ESSID:/{ print $1 }' | cut -d ":" -f 2 | cut -c 2-128`
      ESSID[$i]=${foo%?}
      OPENCELLNUMBER[$i]=$loop_counter
      i=$[$i +1]
    fi
  done
  # now the MACs of the open WIFIs are at APMAC[1]...APMAC[$OPENCOUNT]

  # Check/use the list of open WIFI(s)
  for loop_counter in $(seq 1 $OPENCOUNT)
  do
    deadline_counter=0
    connected=0

    echo "Checking the open WIFI with MAC ${APMAC[$loop_counter]}, Channel ${CHANNEL[$loop_counter]}, ESSID ${ESSID[$loop_counter]}"
    while [ $deadline_counter -lt 2 ] # give a connection try at minimum two chances
    do
      if [ $connected -eq 0 ]; then # if not connected
        # kill the now outdated dhcpcd
        if [ -f /var/run/dhcpcd-"$DEVICE".pid ]
        then
          kill `cat /var/run/dhcpcd-"$DEVICE".pid`
          rm -f /var/run/dhcpcd-"$DEVICE".pid
        fi
        killall dhclient 2>&1 >/dev/null
        rm -f /var/run/dhclient.pid

        # Connect
        iwconfig "$DEVICE" mode managed ap "${APMAC[$loop_counter]}" channel "${CHANNEL[$loop_counter]}" essid ${ESSID[$loop_counter]}
        # iwconfig commit

        # DHCP configuration: use dhcpcd if availible, dhclient else
	# if command -s -v dhcpcd 
        type -P dhcpcd
        if [ $? -eq 0 ]
        then
          dhcpcd "$DEVICE"
        else
          dhclient "$DEVICE"
        fi

        connected=1 # we should now be connected to the access point
      fi
      echo "Connectet"

      # check the internet connection (online test) by pinging
      # TODO: Add a httping test (with checking the content).
      flag=0;
      for HOST in 8.8.8.8 4.2.2.2 192.5.5.241 184.73.110.30 173.222.58.135; do
        { ping -M do -Q 0x02 -q -w 5 -W 5 -c 1 -s 24 -p 0f1e2d3c4b5a6978 -n $HOST &> /dev/null ; }&& flag=1 && break
      done

      # if no internet connection: increase deadline counter 
      if [ $flag -eq 0 ]; then
        deadline_counter=$[$deadline_counter +1]
      else # we are connected; wait before the next check
        echo "Online!"
        sleep 10 
      fi
    done
    # the last open WIFI failed the online test two times; loop to the next
    deadline_counter=0
    connected=0
    echo "Not connectet"
  done # go to the next open WIFI (or make a new scan)

done # while [ 1 = 1 ],  end of the endless loop

exit 0




