#!/bin/bash
#Todo: IPv6
#Todo: Other service types than gnudip (generic update URL)
#Todo: GET WAN IP from Router via UPnP if supported

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

#static values
WGET=$(which wget)
WGETOPTIONS="-4 -o /dev/null -t 3 -T 3"
EMPTYSTRING="none"
NOIP="0.0.0.0"
#how often do we poll for IP changes if we are behind a NAT?
UPDATEMINUTES=5
#if we do not have a IP check URL, how often should we do a "blind" update
UPDATEMINUTESUNKNOWN=3600
TOOLNAME=ez-ipupdate
UPDATE_TOOL=$(which ${TOOLNAME})
DISABLED_STRING='disabled'
ENABLED_STRING='enabled'

#Dirs and filenames
CFGDIR="/etc/${TOOLNAME}/"
CFG="${CFGDIR}${TOOLNAME}.conf"
CFG_disabled="${CFGDIR}${TOOLNAME}.inactive"
IPFILE="${CFGDIR}${TOOLNAME}.currentIP"
STATUSFILE="${CFGDIR}${TOOLNAME}.status"
LASTUPDATE="${CFGDIR}/last-update"
HELPERCFG="${CFGDIR}${TOOLNAME}-plinth.cfg"
CRONJOB="/etc/cron.d/${TOOLNAME}"
PIDFILE="/var/run/ez-ipupdate.pid"

doGetOpt()
{
	basicauth=0
	ignoreCertError=0
	
    while getopts ":s:d:u:p:I:U:c:b:" opt; do
        case ${opt} in
            s)
                server=${OPTARG}
            ;;
            d)
                host=${OPTARG}
            ;;
            u)
                user=${OPTARG}
            ;;
            p)
                pass=${OPTARG}
            ;;
            I)
                if [ "${OPTARG}" != "${EMPTYSTRING}" ];then
                    ipurl=${OPTARG}
                else
                    ipurl=""
                fi
            ;;
            U)
                if [ "${OPTARG}" != "${EMPTYSTRING}" ];then
                    updateurl=${OPTARG}
                else
                    updateurl=""
                fi
            ;;
            b)
                basicauth=${OPTARG}
            ;;
            c)
                ignoreCertError=${OPTARG}
            ;;            
            \?)
                echo "Invalid option: -${OPTARG}" >&2
                exit 1
            ;;
        esac
    done
}

doWriteCFG()
{
    mkdir ${CFGDIR} 2> /dev/null
    #always write to the inactive config - needs to be enabled via "start" command later
    local out_file=${CFG_disabled}

    #reset the last update time
    echo 0 > ${LASTUPDATE}

    #reset the last updated IP
    echo "0.0.0.0" > ${IPFILE}

    #reset last update (if there is one)
    rm ${STATUSFILE} 2> /dev/null

    #find the interface (always the default gateway interface)
    default_interface=$(ip route |grep default |awk '{print $5}')

    #store the given options in ez-ipupdate compatible config file
    echo "host=${host}" > ${out_file}
    echo "server=${server}" >> ${out_file}
    echo "user=${user}:${pass}" >> ${out_file}
    echo "service-type=gnudip" >> ${out_file}
    echo "retrys=3" >> ${out_file}
    echo "wildcard" >> ${out_file}
    
    #store UPDATE URL params
    echo "POSTURL ${updateurl}" > ${HELPERCFG}
    echo "POSTAUTH ${basicauth}" >> ${HELPERCFG}
    echo "POSTSSLIGNORE ${ignoreCertError}" >> ${HELPERCFG}
    
    #check if we are behind a NAT Router
    echo "IPURL ${ipurl}" >> ${HELPERCFG}
    if [ -z ${ipurl} ];then
        echo "NAT unknown" >> ${HELPERCFG}
    else 
        doGetWANIP
        ISGLOBAL=$(ip addr ls ${default_interface} | grep ${wanip})
        if [ -z ${ISGLOBAL} ];then
            #we are behind NAT
            echo "NAT yes" >> ${HELPERCFG}
        else
            #we are directly connected
            echo "NAT no" >> ${HELPERCFG}
            #if this file is added ez-ipupdate will take ip form this interface
            echo "interface=${default_interface}" >> ${out_file}
            #if this line is added to config file, ez-ipupdate will be launched on startup via init.d
            echo "daemon" >> ${out_file}
            echo "execute=${0} success" >> ${out_file}
        fi
    fi
}

doReadCFG()
{ 
    host=""
    server=""
    user=""
    pass=""
    ipurl=""

    if [ ! -z ${cfgfile} ];then
        host=$(cat ${cfgfile} 2> /dev/null |grep host |cut -d = -f 2)
        server=$(cat ${cfgfile} 2> /dev/null |grep server |cut -d = -f 2 |grep -v ^\'\')
        user=$(cat ${cfgfile} 2> /dev/null |grep user |cut -d = -f 2 |cut -d : -f 1 )
        pass=$(cat ${cfgfile} 2> /dev/null |grep user |cut -d = -f 2 |cut -d : -f 2)
    fi

    if [ ! -z ${HELPERCFG} ];then
        ipurl=$(cat ${HELPERCFG} 2> /dev/null |grep ^IPURL |awk '{print $2}' |grep -v ^\'\')
        updateurl=$(cat ${HELPERCFG} 2> /dev/null |grep POSTURL |awk '{print $2}' |grep -v ^\'\')
        basicauth=$(cat ${HELPERCFG} 2> /dev/null |grep POSTAUTH |awk '{print $2}' |grep -v ^\'\')
        ignoreCertError=$(cat ${HELPERCFG} 2> /dev/null |grep POSTSSLIGNORE |awk '{print $2}' |grep -v ^\'\')
    fi  
}

doReplaceVars()
{
    local url=`echo ${updateurl} | sed "s/<Ip>/${wanip}/g"`
    url=`echo ${url} | sed "s/<Domain>/${host}/g"`
	url=`echo ${url} | sed "s/<User>/${user}/g"`
    url=`echo ${url} | sed "s/<Pass>/${pass}/g"`
    url=`echo ${url} | sed "s/'//g"`
	updateurl=$url
}

doStatus()
{
	PROC=$(pgrep ${TOOLNAME})
	if [ -f ${CRONJOB} ];then
	     echo $ENABLED_STRING 
	elif [ ! -z ${PROC} ];then
	    echo $ENABLED_STRING
	else
	    echo $DISABLED_STRING
	fi
		if [ ! -z ${server} ];then
			echo ${server}
		else
			echo $DISABLED_STRING
		fi
	if [ ! -z ${host}  ];then
		echo ${host}
		else
			echo $DISABLED_STRING
		fi
		if [ ! -z ${user} ];then
			echo ${user}
		else
			echo $DISABLED_STRING
		fi
	if [ ! -z ${pass}  ];then
		echo ${pass}
		else
			echo $DISABLED_STRING
		fi        
		if [ ! -z ${ipurl} ];then
		echo ${ipurl}
		else
			echo $DISABLED_STRING
		fi
	if [ ! -z ${updateurl}  ];then
		echo ${updateurl}
		else
			echo $DISABLED_STRING
		fi  
	if [ ! -z ${basicauth} ];then
		echo ${basicauth}
	else
			echo $DISABLED_STRING
	fi		
	if [ ! -z ${ignoreCertError} ];then
		echo ${ignoreCertError}
	else
		echo $DISABLED_STRING
	fi	
}

doGetWANIP()
{
    if [ ! -z ${ipurl} ];then
        outfile=$(mktemp)
		${WGET} ${WGETOPTIONS} -O ${outfile} ${ipurl}
        wanip=$(cat ${outfile})
		rm ${outfile}
    [ -z ${wanip} ] && wanip=${NOIP}
    else
        #no WAN IP found because of missing check URL
        wanip=${NOIP}
    fi
}

doUpdate()
{
	if [ ! -z ${server} ];then
		start-stop-daemon -S -x ${UPDATE_TOOL} -m -p ${PIDFILE} -- -c ${cfgfile}
	fi
	
	if [ ! -z ${updateurl} ];then
		doReplaceVars

		if [ "${basicauth}" = "enabled" ];then
			wgetoptions=" --user $user --password $pass "
		fi
        if [ "${ignoreCertError}" = "enabled" ];then
			wgetoptions=" --no-check-certificate "
		fi

		$WGET ${wgetoptions} ${updateurl}
	fi
}

cmd=${1}
shift
cfgfile="/tmp/none"
[ -f ${CFG_disabled} ] && cfgfile=${CFG_disabled}
[ -f ${CFG} ] && cfgfile=${CFG}
case ${cmd} in
    configure)
        doGetOpt ${@}
        doWriteCFG
    ;;
    start)
		doGetWANIP
        if [ "$(cat $HELPERCFG |grep ^NAT | awk '{print $2}')" = "no" ];then
			#if we are not behind a NAT device and we use gnudip, start the daemon tool
			if [ -f ${CFG} -a ! -z $(cat ${cfgfile} 2> /dev/null |grep server |cut -d = -f 2 |grep -v ^\'\')];then
				mv ${CFG_disabled} ${CFG}
            	/etc/init.d/${TOOLNAME} start        		
			fi
			#if we are not behind a NAT device and we use update-URL, add a cronjob 
			#(daemon tool does not support update-URL feature)
			if [ ! -z $(cat $HELPERCFG |grep ^POSTURL | awk '{print $2}') ];then
				echo "*/${UPDATEMINUTES} * * * * root $0 update" > ${CRONJOB}
            	$0 update
			fi
        else
			#if we are behind a NAT device, add a cronjob (daemon tool cannot monitor WAN IP changes)
            echo "*/${UPDATEMINUTES} * * * * root $0 update" > $CRONJOB
            $0 update
        fi
    ;;
    get-nat)
        NAT=$(cat $HELPERCFG 2> /dev/null |grep ^NAT | awk '{print $2}')
        [ -z ${NAT} ] && NAT="unknown"
        echo ${NAT}
    ;;
    update)
        doReadCFG
        oldip=$(cat ${IPFILE})
        doGetWANIP
        echo ${wanip} > ${IPFILE}
        cat ${cfgfile} |grep -v execute > ${cfgfile}.tmp
        mv ${cfgfile}.tmp ${cfgfile}
        echo "execute=${0} success ${wanip}" >> ${cfgfile}
        #if we know our WAN IP, only update if IP changes
        if [ "${oldip}" != "${wanip}" -a "${wanip}" != ${NOIP} ];then
            doUpdate
        fi
        #if we don't know our WAN IP do a blind update once a hour
        if [ "${wanip}" = ${NOIP} ];then
            uptime=$(cat /proc/uptime |cut -d . -f 1)
            LAST=0
            [ -f ${LASTUPDATE} ] && LAST=$(cat ${LASTUPDATE})
            diff=$(expr ${uptime} - ${LAST})
            if [ ${diff} -gt ${UPDATEMINUTESUNKNOWN} ];then
                doUpdate
            fi
        fi
    ;;
    stop)
        rm ${CRONJOB} 2> /dev/null
        /etc/init.d/${TOOLNAME} stop
        kill $(cat ${PIDFILE}) 2> /dev/null
        mv ${CFG} ${CFG_disabled}
    ;;
    success)
        date=$(date)
        echo "last update done (${date})" > ${STATUSFILE}
        cat /proc/uptime |awk '{print $1}' |cut -d . -f 1 > ${LASTUPDATE}
        #if called from cronjob, the current IP is given as parameter
        if [ $# -eq 1 ];then
			echo ${1} > ${IPFILE}
        else
			#if called from ez-ipupdate daemon, no WAN IP is given as parameter
			doGetWANIP
			echo ${wanip} > ${IPFILE}
        fi
    ;;
    failed)
        date=$(date)
        echo "last update failed (${date})" > ${STATUSFILE}
    ;;    
    get-last-success)
        if [ -f ${STATUSFILE} ];then
            cat ${STATUSFILE}
        else
            echo "no successful update recorded since last config change"
        fi
    ;;
    status)
        doReadCFG
		doStatus			
    ;;
    get-timer)
		echo ${UPDATEMINUTES}
    ;;
    clean)
        rm ${CFGDIR}/*
        rm ${CRONJOB}
    ;;
    *)
        echo "usage: status|configure <options>|start|stop|update|get-nat|clean|success [updated IP]|failed"
        echo ""
        echo "options are:"
        echo "-s <server>             Gnudip Server address"
        echo "-d <domain>             Domain to be updated"
        echo "-u <user>               Account username"
        echo "-p <password>           Account Password"
        echo "-I <IP check URL>       A URL which returns the IP of the client who is requesting"
        echo "-U <update URL>         The update URL (a HTTP GET on this URL will be done)"
        echo "-c <1|0>                disable SSL check on Update URL"
        echo "-b <1|0>                use HTTP basic auth on Update URL"
        echo ""
        echo "update                  do a one time update"
        echo "clean                   delete configuration"
        echo "success                 store update success and optional the updated IP"
        echo "failed                  store update failure"
        echo "get-nat                 return the detected nat type"
    ;;
esac
exit 0
