#! /bin/sh
#
# (C) Copyright 2009, jw@suse.de, Novell Inc.
# distribute under GPLv2 or GPlv3, take care, have mercy.
#
# bluetooth-internet.sh 
#
# A dialup script connecting via bluetooth to
# my mobile phone.
#
# v1.0 2009-10-30, jw - initial draft, modelled after ~/HOWTO/bluetooth-internet
# v1.1 2009-11-02, jw - dns error checking added. bogus test calls removed
# v1.2 2009-11-03, jw - unchanged was not reset, when text changed.
# v1.3 2009-11-05, jw - enable /proc/acpi/ibm/bluetooth added. workaround bnc#552346
# v1.4 2010-03-05, jw - more sanity checks, resurrecting /dev/rfcomm? devices, if the connection closed.
#      2010-05-02, jw - added comments about dun and wvdial
# v1.5 2011-05-08, jw - sometimes it helps to disable/enable the bluetooth device, and 
#                       disconnect the dialup in the phone, then kill and restart bluetoth in the phone.
#
# This script attempts to make the creation of a network link as reliable as possible.
# Concepts that are helpful for the desired reliability include:
#  * break down the needed steps to atomic detail level
#   - look for required packages, install them if needed.
#   - look for required services, insserv and start them if needed.
#   - look for required config file contents, create/edit the files as needed.
#  * for each step, 
#   - check a precondition for skipping the step
#   - check a precondition for executing the step
#   - execute the step
#   - check the precondition for skipping again
#     - report an internal logic error if failed.
#  * allow a verbose option, that shows all the successful steps taken
#  * record all steps skipped or taken, with result and min/max/avg runtime/io/mem/cpu usage; 
#  * compare results or skips with previous runs, report any differences to the user.
#  * build a constant expression logic, ontop of variable results, so that we can
#    reduce false positives in the diffs report.
#  * measure runtime and resource utilization while a step is executing. 
#    - Report extraordinary slow or extraordinary fast runtime.
#    - Report extraordinary high/low cpu/io/memory usage.
#    - for slow steps show these reports already while, the step is executing, 
#    - give the user a way to halt, interrupt, continue a step.
#  * make the procedure reentrant or exclusive.
#    - find out and report multiple procedures running in parallel.
#

if [ $(whoami) != "root" ]; then
  echo sudo $0 "$@"
  exec sudo $0 "$@"
fi

novellvpn=1
requires="smpppd bluez"
## if you change device_addr here:
##  - also update /etc/bluetooth/rfcomm.conf, 
##  - rcbluetooth restart, is no longer available in 11.2 -> 
##    - sudo rfcomm release all
##    - sudo rmmod rfcomm
##    -> if this fails with ebusy, then reboot.

# device_addr=00:17:E5:E9:DF:F6
# device_name="Jw E61i"
# device_channel=2
# rfcomm_n=2

device_addr=00:26:69:58:F4:81
device_name="JW E71i"
# damned, this device moves the channel around, sometimes 4, sometimes 2.
device_channel=2
rfcomm_n=0

## FROM http://ivanz.com/2008/09/18/nokia-e71-as-a-usb-or-bluetooth-3g-data-modem-on-linux/
if [ $raw_wv_dial ]; then
 cat >> /etc/wvdial.conf << EOFwvdial
[Dialer nokia-usb]
Modem = /dev/ttyACM0
Baud = 3600000
Init1 = ATZ
Init2 = ATQ0 V1 E1 S0=0 &C1 &D2
Init3 =
Modem Type = USB Modem
Area Code =
Phone = *99#
Username = ppp
Password = ppp
Ask Password = 0
Dial Command = ATDT
Stupid Mode = 1
Compuserve = 0
Force Address =
Idle Seconds = 0
DialMessage1 =
DialMessage2 =
ISDN = 0
Auto DNS = 1
New PPPD = yes

[Dialer nokia-bluetooth]
Modem = /dev/rfcomm0
Baud = 3600000
Init1 = ATZ
Init2 = ATQ0 V1 E1 S0=0 &C1 &D2
Init3 =
Area Code =
Phone = *99#
Username = ppp
Password = ppp
Ask Password = 0
Dial Command = ATDT
Stupid Mode = 1
Compuserve = 0
Force Address =
Idle Seconds = 0
DialMessage1 =
DialMessage2 =
ISDN = 0
Auto DNS = 1
New PPPD = yes
EOFwvdial

  ## For USB when you connect the phone set it to “PC Suite” mode (else the modem won’t appear) 
  ## and then:
  #su -c "wvdial nokia-usb"

  # For Bluetooth, where $device_addr is the Bluetooth Address of you phone

  # device_channel=`sdptool search --bdaddr=$device_addr dun | grep -i Channel | cut -c14-14`
  # su -c "rfcomm release 0; rfcomm bind 0 $device_addr $device_channel && wvdial nokia-bluetooth"

  # Workaround for broken newer versions of NetworkManager where the DNS won’t be set properly 
  # can be found at 
  # http://seife.kernalert.de/blog/2008/12/11/using-dialup-with-111-if-networkmanager-does-not-handle-your-device/
fi


dns1=139.7.30.125
dns2=139.7.30.126

if [ $novellvpn ]; then
  requires="NetworkManager-novellvpn NetworkManager-novellvpn-gnome novell-ipsec-tools novell-nortelplugins turnpike $requires"
  for p in $requires; do
    rpm -q $p >/dev/null || zypper in $p
  done
  if [ ! -f /etc/init.d/racoond ]; then
    insserv racoond; rcracoond start
  fi
fi

if [ ! -f /etc/init.d/smpppd ]; then
  insserv smpppd; rcsmpppd start
fi
restart=0

if egrep -q '^status:.*disabled' /proc/acpi/ibm/bluetooth; then
  echo enable > /proc/acpi/ibm/bluetooth
  echo enable   /proc/acpi/ibm/bluetooth
  sleep 1
  if egrep -q '^status:.*disabled' /proc/acpi/ibm/bluetooth; then
    echo "failed to enable bluetooth"
  fi
fi

## is this okay? hcitool scan --class reports 0x50020c for my phone.
if egrep -q '^Class = 0x7e010c' /etc/bluetooth/main.conf; then : ; else
  sed -i -e 's@Class = .*@Class = 0x7e010c@' /etc/bluetooth/main.conf
  egrep '^Class = 0x7e010c' /etc/bluetooth/main.conf || echo "sed failed to edit /etc/bluetooth/main.conf"
  restart=1
fi

if egrep -q "^rfcomm$rfcomm_n" /etc/bluetooth/rfcomm.conf; then : ; else 

 echo "Below you should see 'BD Address: $device_addr, Device name: $device_name'"
 hcitool scan --class --info --refresh

 echo "The following line must read 'Channel: $device_channel'"
 sdptool browse | egrep -A8 '(Dial-Up|0x1103)' | grep Channel
 echo ""
 echo "... if not, try adjusting /etc/bluetooth/rfcomm.conf manually"


 cat >> /etc/bluetooth/rfcomm.conf << EOF0
rfcomm$rfcomm_n
{
  device $device_addr;
  comment "$device_name";
  channel $device_channel;
  bind yes;
}
EOF0
  restart=1
fi

if [ -c /dev/rfcomm$rfcomm_n ]; then : ; else
  if [ -f /etc/init.d/bluetooth ]; then
    insserv bluetooth; rcbluetooth restart;
    sleep 1	# the bind is asynchron, may take a few milliseconds
    test -c /dev/rfcomm$rfcomm_n || echo "'rcbluetooth start' did not create /dev/rfcomm$rfcomm_n"
  else
    # since openSUSE 11.2 we need to create this manualy
    echo rfcomm bind all
    rfcomm bind all
    sleep 1	# the bind is asynchron, may take a few milliseconds
    test -c /dev/rfcomm$rfcomm_n || echo "'rfcomm bind all' did not create /dev/rfcomm$rfcomm_n"
  fi 
  restart=1
fi

if [ -f /etc/sysconfig/network/ifcfg-modem$rfcomm_n ]; then : ; else
  cat <<EOF1 > /etc/sysconfig/network/ifcfg-modem$rfcomm_n
BOOTPROTO='none'
DIALCOMMAND='ATDT'
DIALPREFIX=''
DIALPREFIXREGEX=''
INIT1='ATZ'
INIT2='AT &FE1 +CMEE=1'
INIT3='AT +CGDCONT=1,"IP","web.vodafone.de"'
INIT8='ATM0'
INIT9='ATX3'
MODEM_DEVICE='/dev/rfcomm$rfcomm_n'
NAME='Bluetooth Modem $device_name'
PPPD_OPTIONS=''
PROVIDER='vodafone'
SPEED='921600'
STARTMODE='manual'
UNIQUE=''
USERCONTROL='yes'
EOF1
  restart=1
fi

if [ -f /etc/sysconfig/network/providers/vodafone ]; then : ; else
  cat << EOF2 > /etc/sysconfig/network/providers/vodafone
ASKPASSWORD='no'
AUTODNS='no'
AUTO_RECONNECT='yes'
DEMAND='no'
DSLSUPPORTED='no'
IDLETIME='600'
ISDNSUPPORTED='no'
MODEMSUPPORTED='yes'
MODIFYDNS='yes'
PASSWORD='internet'
PHONE='*99#'
PROVIDER='vodafone'
STUPIDMODE='yes'
USERNAME='vodafone'
DEFAULTROUTE='yes'
IPADDR=''
MODIFYIP='yes'
REMOTE_IPADDR=''
DNS1='$dns1'
DNS2='$dns2'
EOF2
  restart=1
fi


## DNS entries in /etc/sysconfig/network/providers/vodafone
## are ignored. test them, and if needed, make them brute force.
if egrep -q '^NETCONFIG_DNS_STATIC_SEARCHLIST="suse.de novell.com"' /etc/sysconfig/network/config; then : ; else
  sed -i -e 's@NETCONFIG_DNS_STATIC_SEARCHLIST.*@NETCONFIG_DNS_STATIC_SEARCHLIST="suse.de novell.com"@' /etc/sysconfig/network/config
  egrep -q '^NETCONFIG_DNS_STATIC_SEARCHLIST="suse.de novell.com"' /etc/sysconfig/network/config || echo "sed failed to edit /etc/sysconfig/network/config"
  restart=1
fi

dns_static="NETCONFIG_DNS_STATIC_SERVERS=\"$dns1 $dns2\""
if egrep -q "^$dns_static" /etc/sysconfig/network/config; then : ; else
  sed -i -e "s@NETCONFIG_DNS_STATIC_SERVERS.*@$dns_static@" /etc/sysconfig/network/config
  egrep -q "^$dns_static" /etc/sysconfig/network/config || echo "sed failed to edit /etc/sysconfig/network/config"
  restart=1
fi

## a recommendation found in 
## http://seife.kernalert.de/blog/2008/12/11/using-dialup-with-111-if-networkmanager-does-not-handle-your-device/
#sed -i -e 's@NETCONFIG_DNS_POLICY="auto"@NETCONFIG_DNS_POLICY="ppp* NetworkManager"@' /etc/sysconfig/network/config
sed -i -e 's@NETCONFIG_DNS_POLICY="auto"@NETCONFIG_DNS_POLICY="STATIC_FALLBACK * NetworkManager"@' /etc/sysconfig/network/config

# check if we are paired
r=$(l2ping -fc2 $device_addr 2>&1)
case $r in
  *\ 0%\ loss*)
    # ping test okay!
  ;;
  *)
    echo disable > /proc/acpi/ibm/bluetooth
    sleep 1
    echo enable > /proc/acpi/ibm/bluetooth
  ;;
esac

r=$(l2ping -fc2 $device_addr 2>&1)
case $r in
  *\ No\ route\ to\ host)
    echo "Bluetooth hardware may be off. Please check killswitch and/or try Fn-F5"
    echo $r
    sleep 3
  ;;
  *\ 0%\ loss*)
    # ping test okay!
  ;;
  *)
    echo "+ l2ping -fc4 $device_addr"
    echo "$device_name: $r"
    echo "+ hcitool lq $device_addr"
    hcitool lq $device_addr
    hcitool scan --class --info --refresh
    exit 0;
  ;;
esac

# if not, kbluetooth4 should be started, and played with until we have
# negotiated a PIN
# sending clipboard contents is most likely to provoke a pin dialog.
# it miht be easier to locate the PC from the phone, and enable
# automatich connection there. This also provokes a PIN dialog.
## TODO: find out, where the pin is stored in the system.

status=`rfcomm show $rfcomm_n`
echo $status
if [ -n "$status" ]; then
if [[ "$status" =~ "$device_addr" ]]; then : ; else 
  echo rfcomm$rfcomm_n does not point to $device_addr
  echo please check /etc/bluetooth/rfcomm.conf
  exit
fi
fi

if [[ "$status" =~ "closed" ]]; then
  rfcomm release $rfcomm_n
  rfcomm bind $rfcomm_n
  rfcomm show $rfcomm_n
fi

egrep -q "^nameserver $dns1" /etc/resolv.conf || restart=1

if [ $restart -ne 0 ]; then
  echo disable > /proc/acpi/ibm/bluetooth
  sleep 1
  echo enable > /proc/acpi/ibm/bluetooth
  test -f /etc/init.d/bluetooth && rcbluetooth restart
  test -f /etc/init.d/smpppd && rcsmpppd restart
  rcnetwork restart
  if egrep -q "^nameserver $dns1" /etc/resolv.conf; then
    echo "rcnetwork restart updated /etc/resolv.conf"
  else
    echo "rcnetwork restart did not add $dns1 to /etc/resolv.conf"
    echo "looking for commented out settings"
    sed -i -e "s@^# *nameserver $dns1@nameserver $dns1@" \
           -e "s@^# *nameserver $dns2@nameserver $dns2@" /etc/resolv.conf
    if egrep -q "^nameserver $dns1" /etc/resolv.conf; then
      echo "sed updated /etc/resolv.conf"
      cat /etc/resolv.conf
    else
      echo >> /etc/resolv.conf "nameserver $dns1"
      echo >> /etc/resolv.conf "nameserver $dns2"
      echo "appended nameservers to /etc/resolv.conf"
      cat /etc/resolv.conf
    fi
  fi
fi


connected=0
reconnect=0
unchanged=0
unchanged_lim=20
last=A_VERY_UNLIKELYL_STRING

while [ $connected -eq 0 ]; do
  t=$(cinternet -i modem$rfcomm_n -l 2>&1| tail -n 1)
  echo "|$t|"
  case $t in
    Status\ is:\ connected*)
      if ping -W 3 -c2 -n $dns1 >/dev/null; then
        echo "ping $dns1: okay"
        connected=1
        break
      else
        echo "... but cannot ping DNS1 $dns1"
        reconnect=1
      fi
      ;;
    error:\ connection\ to\ smpppd\ failed*)
      rcsmpppd restart
      reconnect=1
      ;;
    *Bad\ init\ string*)
      echo "This phone pretends not to be a modem device."
      echo "Check if SIM-Card access is allowed via Bluetooth."
      echo "Try a different channel."
      echo "And/or powercycle the phone."
      reconnect=1
      ;;
    '')
      echo "hmmm..."
      reconnect=1
      ;;

    pppd*\ died:*)
      echo "pppd died"
      if cinternet -i modem$rfcomm_n -l 2>&1| grep 'Failed to open /dev/rfcomm'; then
        if cinternet -i modem$rfcomm_n -l 2>&1| grep 'Failed to open /dev/rfcomm.: Operation already in progress'; then
	  sleep 5
	else
          echo ... restarting acpi/ibm/bluetooth
          echo disable > /proc/acpi/ibm/bluetooth
          sleep 1
          echo enable > /proc/acpi/ibm/bluetooth
          sleep 2
        fi
      fi
      reconnect=1
      ;;
  esac

  if [ "$t" = "$last" ]; then
    unchanged=$[$unchanged+1]
    echo "$unchanged/$unchanged_lim"
  else
    unchanged=0
  fi
  last=$t
  if [ $unchanged -ge $unchanged_lim ]; then
    unchanged=0
    reconnect=1
    echo "no progress."
  fi

  if [ $reconnect -gt 0 ]; then
    cinternet -i modem$rfcomm_n -l
    cinternet -i modem$rfcomm_n -O
    echo "... reconnecting"
    sleep 1

    cinternet -i modem$rfcomm_n -A
    reconnect=0
  fi
  sleep 2;
done

ping -c3 -n www.suse.de | head -n 4
