#!/bin/ash

#
# App : AutoDisconnect
# Url : https://garage.maemo.org/projects/autodisconnect/
# Version: 0.4
# Author: Aymeric Brisse <aymeric.brisse@gmail.com>
# License: GNU General Public License
#

# ---------------------------------------------------------------------------
# FUNCTIONS
# ---------------------------------------------------------------------------

GC_ROOT="/apps/autodisconnect"

# ---------------------------------------------------------------------------
getvalue()
# ---------------------------------------------------------------------------
# Get Conf value
#
{
    echo "`gconftool-2 -g $GC_ROOT/param_$1`"
}

# ---------------------------------------------------------------------------
read_parameters()
# ---------------------------------------------------------------------------
# Read general parameters
#
{
    # Enable logging
    g_logging=true

    # Notifications
    g_notifications=`getvalue "notifications"`

    # Exceptions (SSH)
    g_exceptions=""
    [[ `getvalue "exception_ssh"` = true ]] && g_exceptions="ssh $g_exceptions"

    # Exceptions (OpenVPN)
    g_exception_openvpn=`getvalue "exception_openvpn"`

    # Exceptions (VoIP)
    g_exception_voip=`getvalue "exception_voip"`

    # Exceptions (IM Accounts)
    g_exception_im_accounts=`getvalue "exception_im_accounts"`

    # Network Interfaces to check
    g_enable=""
    [[ `getvalue "connection_wlan"` = true ]] && g_enable="wlan $g_enable"
    [[ `getvalue "connection_gprs"` = true ]] && g_enable="gprs $g_enable"

    # Auto 2G Mode
    g_use_2g=`getvalue "use_2g"`

    # Where to store the logfile. This is cleared when it goes over 50k.
    logfile=/var/log/autodisconnect.log
}

# ---------------------------------------------------------------------------
read_connection_parameters()
# ---------------------------------------------------------------------------
# Read connection specific parameters
#
{
    # How often to test for inactivity (min)
    g_samplerate=`getvalue $short_interface"_interval"`

    # Number of bytes allowed to be received without disconnect
    g_dontcountbytespermin=`getvalue $short_interface"_traffic"`
    
    # Number of bytes to ignore for each time period
    g_dontcountbytes=$((g_dontcountbytespermin*g_samplerate))

    # Convert min interval to sec interval
    g_samplerate=$((g_samplerate*60))
}

# ---------------------------------------------------------------------------
killsiblings()
# ---------------------------------------------------------------------------
# Kill other instances of the process
#
{
    TmpFile=/tmp/$(basename $0).tmp

    `ps | egrep 'S[ ]*/bin/ash[ ]*/opt/autodisconnect/autodisconnect-network' | sed 's/^[ ]*\([0-9]*\) .*$/\1/g' > $TmpFile`

    while read Pid
    do
        [[ -n "$Pid" -a "$Pid" -ne "$$" ]] && {
            logentry "<Kill $Pid>"
            kill -KILL $Pid 2>/dev/null
        }
    done < $TmpFile
}

# ---------------------------------------------------------------------------
init()
# ---------------------------------------------------------------------------
{
    # Log
    logentry "<Start>"  

    # Kill every instances already running
    killsiblings

    # Allow dbus to work
    [[ -e /tmp/dbus-info ]] && eval `cat /tmp/dbus-info`

    export DBUS_SESSION_BUS_ADDRESS \
           DBUS_SESSION_BUS_PID \
           DBUS_SESSION_BUS_WINDOWID

    # Don't let log file get too big
    if [[ -w "$logfile" ]]; then
        size=`stat $logfile | grep Size | awk '{ print $2; }'`
        [[ "$size" -gt 50000 ]] && :>$logfile
    fi

    # Waiting for any if-post-down.d script if reconnection
    sleep 5

    # Register
    set_current_instance

    # Waiting for possible closing connections
    # Waiting for initial traffic to be made -> then the next check will close the connection if idle
    sleep 20
}

# ---------------------------------------------------------------------------
logentry()
# ---------------------------------------------------------------------------
# Send a string to this function to append it to the log file
#
# Arg 1 - The text to log
{
    [[ "$g_logging" = true ]] && {
        echo -e "[`date +%T`][$$][Network] $1" >> $logfile
    }
}

# ---------------------------------------------------------------------------
osnotify()
# ---------------------------------------------------------------------------
# Send a string to this function to generate a system popup
#
# Arg 1 - The text to display
{
    [[ "$g_notifications" = true ]] && {
        /usr/bin/dbus-send --type=method_call \
            --dest=org.freedesktop.Notifications \
            /org/freedesktop/Notifications \
            org.freedesktop.Notifications.SystemNoteInfoprint \
            string:"$1"
    }
}

# ---------------------------------------------------------------------------
disconnect()
# ---------------------------------------------------------------------------
# Disconnect current connection
{   
    dbus-send --system --dest=com.nokia.icd \
        /com/nokia/icd_ui \
        com.nokia.icd_ui.disconnect \
        boolean:true

    # Waiting for any if-post-down.d to run
    sleep 2
}

# ---------------------------------------------------------------------------
isrunning()
# ---------------------------------------------------------------------------
# Check if a process is running
#
# Arg 1 - the service
{
	test -n "`ps | grep "$1" | grep -v grep`"
}

# ---------------------------------------------------------------------------
set_current_instance()
# ---------------------------------------------------------------------------
# Register the current PID
#
{
    gconftool-2 --set --type int /apps/autodisconnect/current_instance $$
}

# ---------------------------------------------------------------------------
disable_quit()
# ---------------------------------------------------------------------------
# Disabled for some interfaces
#
{
    connection_check=`echo "$g_enable" | grep "$short_interface" 2>/dev/null`
    
    if [[ -n "$short_interface" -a -n "$connection_check" ]]; then
        osnotify "AutoDisconnect is now running"
        logentry "$interface started"
        false
    else
        logentry "$interface ignored or gprs has been quickly closed"
        true
    fi
}

# ---------------------------------------------------------------------------
switch_2G()
# ---------------------------------------------------------------------------
# Switch network mode to 2G
#
# No args
{
    if [[ "`dbus-send --system --type=method_call --print-reply --dest=com.nokia.phone.net /com/nokia/phone/net Phone.Net.get_selected_radio_access_technology | awk '/b/ {print $2}'`" -ne 1 ]]; then

        # Check if any call is active
        if [[ -z "`dbus-send --type=method_call --print-reply --dest=org.freedesktop.Telepathy.ConnectionManager.ring /org/freedesktop/Telepathy/Connection/ring/tel/ring org.freedesktop.DBus.Properties.Get string:org.freedesktop.Telepathy.Connection.Interface.Requests string:Channels | grep /org/freedesktop/Telepathy/Connection/ring/tel/ring/`" ]]; then

            dbus-send --system --type=method_call \
                --dest=com.nokia.phone.net /com/nokia/phone/net \
                Phone.Net.set_selected_radio_access_technology byte:1

            logentry "switched to 2G"

        fi

    fi
}

# ---------------------------------------------------------------------------
connection_is_closed()
# ---------------------------------------------------------------------------
# Check if the connection has been closed
#
# No args
{
    interface_check="`grep $interface /proc/net/route | tail -1 | cut -f1`"

    if [[ -z "$interface_check" ]]; then
        logentry "$interface closed (externally)"
        true
    else
        false
    fi
}

# ---------------------------------------------------------------------------
main()
# ---------------------------------------------------------------------------
# Main entry point
#
{
    # Read general parameters
    read_parameters

    # Init -> Kill other instances & dbus & log & sleep
    init

    # Get interface
        
    interface=$IFACE
    
    if [[ -z "$interface" ]]; then
        # gprs0 generally. See https://bugs.maemo.org/show_bug.cgi?id=7733
        interface="`grep gprs0 /proc/net/route | tail -1 | cut -f1`"
    fi

    short_interface=`echo "$interface" | tr -d 0-9`

    # Switch network mode back to 2G if wlan
    [[ "$short_interface" = "wlan" -a $g_use_2g = true ]] && {
        switch_2G
    }

    # Quit if the interface doesn't have to be checked
    # Quit if the interface has been closed
    if `connection_is_closed` || `disable_quit`; then
        return 0
    fi

    # Read connection specific parameters
    read_connection_parameters
    
    packets_before="`grep $interface /proc/net/dev | awk -F":" '{print $2}' | awk '{print $1}'`"
    
    # MONITORING LOOP
    while true
    do
        sleep $g_samplerate

        # Check if connection is still alive
        if `connection_is_closed`; then
            break
        fi

        exception_apply=""

        # Check if the phone is charging
        if [[ "$g_running_options" = 2 -a "`lshal -u '/org/freedesktop/Hal/devices/bme' | grep battery.rechargeable.is_charging | awk '{print $3}'`" = true ]]; then
            exception_apply="phone_charging"
        fi

        # Check if no exception service is running
        if [[ -z "$exception_apply" -a -n "$g_exceptions" ]]; then
            my_ip="`ifconfig "$interface" | grep "inet addr" | sed 's/^[ ]*inet addr:\([0-9\.]*\) .*$/\1/g'`"
            for exception in $g_exceptions ; do
                [[ -n "`netstat | grep :$exception | grep ESTABLISHED | grep $my_ip`" ]] && {
                    exception_apply="$exception"
                    break
                }
            done
        fi

        # Check if OpenVPN is not active
        if [[ -z "$exception_apply" -a "$g_exception_openvpn" = true -a -n "`grep -E \"tun|tap\" /proc/net/route`" ]]; then
            exception_apply="openvpn"
        fi

        # Check if VoIP is not active

        if [[ -z "$exception_apply" -a "$g_exception_voip" = true ]] && `isrunning "/usr/lib/telepathy/telepathy-sofiasip"`; then
            exception_apply="voip"
        fi

        # Check if an IM Account is active (XMMP or Skype)

        if [[ -z "$exception_apply" -a "$g_exception_im_accounts" = true ]] && \
        (`isrunning "/usr/lib/telepathy/telepathy-gabble"` || `isrunning "/usr/lib/telepathy/telepathy-spirit"`); then
            exception_apply="im_account"
        fi

        # Log exception

        if [[ -n "$exception_apply" ]]; then
            logentry "$interface checked ($exception_apply)"
            continue
        fi
        
        # Check the traffic volume
        packets_after="`grep $interface /proc/net/dev | awk -F":" '{print $2}' | awk '{print $1}'`"

        if [[ "$packets_after" -lt "$((packets_before+g_dontcountbytes))" ]]; then
            osnotify "AutoDisconnect has closed the inactive connection"
            logentry "$interface closed (inactive) +$((packets_after-packets_before))"            
            disconnect            
            break
        else            
            logentry "$interface checked (+$((packets_after-packets_before)))"
            packets_before=$packets_after   
        fi

    done
    
}

# ---------------------------------------------------------------------------
# RUNTIME
# ---------------------------------------------------------------------------

# Start AutoDisconnect

g_running_options=`getvalue "running_options"`

if [[ "$g_running_options" != 0 ]]; then
    main
    logentry "<Stop>"
fi

exit 0

