#!/bin/sh
# /usr/lib/dynamic_dns/dynamic_dns_updater.sh
#
# Written by Eric Paul Bishop, Janary 2008
# Distributed under the terms of the GNU General Public License (GPL) version 2.0
#
# This script is (loosely) based on the one posted by exobyte in the forums here:
# http://forum.openwrt.org/viewtopic.php?id=14040
#

. /usr/lib/ddns/dynamic_dns_functions.sh


service_id=$1
if [ -z "$service_id" ]
then
	echo "ERRROR: You must specify a service id (the section name in the /etc/config/ddns file) to initialize dynamic DNS."
	return 1
fi

#default mode is verbose_mode, but easily turned off with second parameter
verbose_mode="1"
if [ -n "$2" ]
then
	verbose_mode="$2"
fi

###############################################################
# Leave this comment here, to clearly document variable names
# that are expected/possible
#
# Now use load_all_config_options to load config
# options, which is a much more flexible solution.
#
#
#config_load "ddns"
#
#config_get enabled $service_id enabled
#config_get service_name $service_id service_name
#config_get update_url $service_id update_url
#
#
#config_get username $service_id username
#config_get password $service_id password
#config_get domain $service_id domain
#
#
#config_get ip_source $service_id ip_source
#config_get ip_interface $service_id ip_interface
#config_get ip_network $service_id ip_network
#config_get ip_url $service_id ip_url
#
#config_get force_interval $service_id force_interval
#config_get force_unit $service_id force_unit
#
#config_get check_interval $service_id check_interval
#config_get check_unit $service_id check_unit
#########################################################
load_all_config_options "ddns" "$service_id"


#some defaults
if [ -z "$check_interval" ]
then
	check_interval=600
fi

if [ -z "$check_unit" ]
then
	check_unit="seconds"
fi


if [ -z "$force_interval" ]
then
	force_interval=72
fi

if [ -z "$force_unit" ]
then
	force_unit="hours"
fi



#some constants

retrieve_prog="/usr/bin/wget -O - ";
service_file="/usr/lib/ddns/services"

ip_regex="[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}"

NEWLINE_IFS='
'


#determine what update url we're using if the service_name is supplied
if [ -n "$service_name" ]
then
	#remove any lines not containing data, and then make sure fields are enclosed in double quotes
	quoted_services=$(cat $service_file |  grep "^[\t ]*[^#]" |  awk ' gsub("\x27", "\"") { if ($1~/^[^\"]*$/) $1="\""$1"\"" }; { if ( $NF~/^[^\"]*$/) $NF="\""$NF"\""  }; { print $0 }' )


	#echo "quoted_services = $quoted_services"
	OLD_IFS=$IFS
	IFS=$NEWLINE_IFS
	for service_line in $quoted_services
	do
		#grep out proper parts of data and use echo to remove quotes
		next_name=$(echo $service_line | grep -o "^[\t ]*\"[^\"]*\"" | xargs -r -n1 echo)
		next_url=$(echo $service_line | grep -o "\"[^\"]*\"[\t ]*$" | xargs -r -n1 echo)

		if [ "$next_name" = "$service_name" ]
		then
			update_url=$next_url
		fi
	done
	IFS=$OLD_IFS
fi



verbose_echo "update_url=$update_url"



#if this service isn't enabled then quit
if [ "$enabled" != "1" ] 
then
	return 0
fi





#compute update interval in seconds
case "$force_unit" in
	"days" )
		force_interval_seconds=$(($force_interval*60*60*24))
		;;
	"hours" )
		force_interval_seconds=$(($force_interval*60*60))
		;;
	"minutes" )
		force_interval_seconds=$(($force_interval*60))
		;;
	"seconds" )
		force_interval_seconds=$force_interval
		;;
	* )
		#default is hours
		force_interval_seconds=$(($force_interval*60*60))
		;;
esac



#compute check interval in seconds
case "$check_unit" in
	"days" )
		check_interval_seconds=$(($check_interval*60*60*24))
		;;
	"hours" )
		check_interval_seconds=$(($check_interval*60*60))
		;;
	"minutes" )
		check_interval_seconds=$(($check_interval*60))
		;;
	"seconds" )
		check_interval_seconds=$check_interval
		;;
	* )
		#default is seconds
		check_interval_seconds=$check_interval
		;;
esac



verbose_echo "force seconds = $force_interval_seconds"
verbose_echo "check seconds = $check_interval_seconds"

#kill old process if it exists & set new pid file
if [ -d /var/run/dynamic_dns ]
then
	#if process is already running, stop it
	if [ -e "/var/run/dynamic_dns/$service_id.pid" ]
	then
		old_pid=$(cat /var/run/dynamic_dns/$service_id.pid)
		test_match=$(ps | grep "^[\t ]*$old_pid")
		verbose_echo "old process id (if it exists) = \"$test_match\""
		if [ -n  "$test_match" ]
		then
			kill $old_pid
		fi
	fi

else
	#make dir since it doesn't exist
	mkdir /var/run/dynamic_dns
fi
echo $$ > /var/run/dynamic_dns/$service_id.pid




#determine when the last update was
current_time=$(date +%s)
last_update=$(( $current_time - (2*$force_interval_seconds) ))
if [ -e "/var/run/dynamic_dns/$service_id.update" ]
then
	last_update=$(cat /var/run/dynamic_dns/$service_id.update)
fi
time_since_update=$(($current_time - $last_update))


human_time_since_update=$(( $time_since_update / ( 60 * 60 ) ))
verbose_echo "time_since_update = $human_time_since_update hours"




registered_ip=$(echo $(nslookup "$domain" 2>/dev/null) |  grep -o "Name:.*" | grep -o "$ip_regex")


#do update and then loop endlessly, checking ip every check_interval and forcing an updating once every force_interval

while [ true ]
do
	current_ip=$(get_current_ip)
	ddnslogtime = $(date +%c)

	current_time=$(date +%s)
	time_since_update=$(($current_time - $last_update))


	verbose_echo "Running IP check..."
	ddnslog -P "`date +%X-%x` Running IP check...##"
	
	verbose_echo "current system ip = $current_ip"
	ddnslog -P "`date +%X-%x` Current system ip = $current_ip##"

	verbose_echo "registered domain ip = $registered_ip"
	ddnslog -P "`date +%X-%x` Registered domain ip = $registered_ip##"

	if [ "$current_ip" != "$registered_ip" ]  || [ $force_interval_seconds -lt $time_since_update ]
	then
		verbose_echo "update necessary, performing update ..."
		ddnslog -P "`date +%X-%x` $service_name Update necessary, performing update ...##"
		#do replacement
		final_url=$update_url
		final_host=$(echo $final_url | awk -F"?" '{print $1}')
		final_uri=$(echo $final_url | awk -F"?" '{print $2}')
		for option_var in $ALL_OPTION_VARIABLES
		do
			if [ "$option_var" != "update_url" ]
			then
				replace_name=$(echo "\[$option_var\]" | tr 'a-z' 'A-Z')
				replace_value=$(eval echo "\$$option_var")
				replace_value=$(echo $replace_value | sed s/"\\\\"/"\\\\\\\\"/g)
				replace_value=$(echo $replace_value | sed s/"\^"/"\\\^"/g)
				replace_value=$(echo $replace_value | sed s/"\&"/"\\\\\&"/g)
				replace_value_esc=$(echo $replace_value | sed -f /usr/lib/ddns/url_escape.sed)
				final_host=$(echo $final_host | sed s^"$replace_name"^"$replace_value"^g )
				final_uri=$(echo $final_uri | sed s^"$replace_name"^"$replace_value_esc"^g )
				#final_url=$(echo $final_url | sed s^"$replace_name"^"$replace_value"^g )
			fi
		done
		final_url=$(echo "$final_host?$final_uri")
		final_url=$(echo $final_url | sed s/"\[IP\]"/"$current_ip"/g )


		verbose_echo "updating with url=\"$final_url\""
		ddnslog -P "`date +%X-%x` >>> Updating... <<<##"
		sleep 10	# WAN connection may not stable, sleep 10 before sending request
		logger "[DDNS]DDNS update: current system ip = $current_ip"

		count=0
		MAXTRY=3
		while [ "$count" -le "$MAXTRY" ]
		do
			#here we actually connect, and perform the update
			update_output=$( $retrieve_prog "$final_url" )
			#ddnslog -P " update_output = $update_output ##"
			echo $update_output > /tmp/ddnstmp
			#cat /tmp/ddnstmp
			if [ $? -eq 0 ]; then
				res=$( grep -c "\<200 Successful\>" /tmp/ddnstmp )
				if [ $res -gt 0 ]; then
					ddnslog -P "`date +%X-%x` Update Successful... ##"
				fi

				res=$( grep -c "\<Failed Update\>" /tmp/ddnstmp )
				if [ $res -gt 0 ]; then
					ddnslog -P "`date +%X-%x` Update Successful... ##"
				fi
				
				res=$( grep -c "\<good\>" /tmp/ddnstmp )
				if [ $res -gt 0 ]; then
					ddnslog -P "`date +%X-%x` Update Successful...##"
				fi

				res=$( grep -c "\<badpass\>" /tmp/ddnstmp )
				if [ $res -gt 0 ]; then
					ddnslog -P "`date +%X-%x` Update Failed... Please Check Password ##"
				fi

				res=$( grep -c "\<nouser\>" /tmp/ddnstmp )
				if [ $res -gt 0 ]; then
					ddnslog -P "`date +%X-%x` Update Failed... No User ##"
				fi

				res=$( grep -c "\<success\>" /tmp/ddnstmp )
				if [ $res -gt 0 ]; then
					ddnslog -P "`date +%X-%x` Update Successful... ##"
				fi


				res=$( grep -c "\<SUCCESS\>" /tmp/ddnstmp )
				if [ $res -gt 0 ]; then
					ddnslog -P "`date +%X-%x` Update Successful... ##"
				fi
				
				res=$( grep -c "\<nohost\>" /tmp/ddnstmp )
				if [ $res -gt 0 ]; then
					ddnslog -P "`date +%X-%x` Update Failed... No Host ##"
				fi

				res=$( grep -c "\<ERROR\>" /tmp/ddnstmp )
				if [ $res -gt 0 ]; then
					ddnslog -P "`date +%X-%x` Update Failed... ##"
				fi

				verbose_echo "wget success..."
				logger "[DDNS]DDNS response: $update_output"
				#ddnslog -P "`date +%X-%x` Update Done... ##"
				break
			fi

			# wget fail....
			count=$(($count+1))
			verbose_echo "wget fail...retry: $count"
			ddnslog -P "`date +%X-%x` Update fail...##"
			sleep 10
		done

		verbose_echo "Update Output:"
		verbose_echo "$update_output"
		verbose_echo ""

		#save the time of the update
		current_time=$(date +%s)
		last_update=$current_time
		time_since_update='0'
		registered_ip=$current_ip

		human_time=$(date)
		verbose_echo "update complete, time is: $human_time"
		ddnslog -P "`date +%X-%x` Update complete, if there is no successful info, please check your setting, time is: $human_time##"
		echo "$last_update" > "/var/run/dynamic_dns/$service_id.update"
	else
		human_time=$(date)
		human_time_since_update=$(( $time_since_update / ( 60 * 60 ) ))
		verbose_echo "update unnecessary"
		ddnslog -P "`date +%X-%x` Update unnecessary...##"
		verbose_echo "time since last update = $human_time_since_update hours"
		ddnslog -P "`date +%X-%x` Time since last update = $human_time_since_update hours##"
		verbose_echo "the time is now $human_time"
	fi

	#sleep for 10 minutes, then re-check ip && time since last update
	sleep $check_interval_seconds
done

#should never get here since we're a daemon, but I'll throw it in anyway
return 0




