#!/bin/sh
# Sip / IAX extensions

# Add to internal extensions
add_extension() {
	logdebug 1 "Adding $1/$2 extension to $3"
	eval "local ext=\"\${dialplan_ext_$3}\""
	[ -z "${ext}" ] && append dialplan_exts "$3" " "
	local lower=`echo $1|tr [A-Z] [a-z]`
	eval "local ext=\"\${${lower}_last_extension}\""
	[ -z "${ext}" ] && eval "${lower}_last_extension=\"$3\""
	append dialplan_ext_$3 $1/${2} "&"
}

check_append_local() {
	local extension="${1}"
	logdebug 3 "added local context for ${1}"
	eval "local isadded=\"\${dialplan_add_local_${extension}-0}\""
	if [ "$isadded" != "1" ] ; then
		logdebug 3 "Not added before - adding"
		eval "dialplan_add_local_${extension}=1"
		append dialplan_locals "$extension"
		eval  "dialplan_local_${1}_context=\"${2}\""
		eval  "dialplan_local_${1}_selfmailbox=\"${3}\""
		eval  "dialplan_local_${1}_mailbox=\"${4}\""
		eval  "dialplan_local_${1}_displayname=\"${5}\""
		return 0
	else
		return 1
	fi
}
append_dialplan_locals(){
	for i in ${dialplan_locals} ; do
		local extension=$i
		for x in context selfmailbox mailbox displayname ; do
			eval "x_${x}=\${dialplan_local_${i}_${x}}"
		done
		local newcontext=local_${extension}

		if check_add_context ${newcontext} ; then
			# add_dialplan_voice ${newcontext} ${x_last_extension} ${x_last_mailbox}
			# Make sure as much is matched as possible
			#add_dialplan_goto ${newcontext} _[0-9#*+]. ${x_last_context}
			# add_dialplan_include ${newcontext}  ${x_last_context}

			append_dialplan_context ${newcontext} "exten => ${match_all},1,Set(CALLERID(num)=${extension})"
			local next=2
			if [ ! -z "${x_displayname}" ] ; then
				append_dialplan_context ${newcontext} "exten => ${match_all},2,Set(CALLERID(name)=${x_displayname})"
				local next=3
			fi
			if [ ! -z "${x_mailbox}" ] ; then
				[ "${x_selfmailbox}" = "yes" ] && append_dialplan_context ${newcontext} "exten => ${extension},${next},VoiceMailMain(${x_mailbox})"
				[ ! -z "${dialplan_voiceboxext}" ]  && append_dialplan_context ${newcontext} "exten => ${dialplan_voiceboxext},${next},VoiceMailMain(${x_mailbox})"
			fi
			append_dialplan_context ${newcontext} "exten => ${match_all},${next},Goto(${x_context},\${EXTEN},1)"
		fi
	done
}

# Sip

check_add_sipitems() {
	if [ "${sip_doregister}" == "1" ] ; then
		local line="register => ${sip_last_username}@${sip_last_fromdomain}:${sip_last_secret}:${sip_last_username}@${sip_sectionname}"
		case ${sip_last_registerextension}  in
			-) line="$line/${sip_last_username}" ;;
			.*) line="$line/${sip_last_registerextension}" ;;
		esac
		append sip_register "$line" "$N"
		sip_doregister=0
	fi
	do_check_add_items sip
}
check_add_iaxitems() {
	do_check_add_items iax
}

do_check_add_items(){

	for i in type last_host last_context selfmailbox last_extension last_mailbox last_displayname ; do
		eval "x_${i}=\"\${${1}_${i}-}\""
	done

	if [ ! -z "${x_last_context}" ] ; then
		if [ ! -z "${x_last_extension}" ] ; then
			[ "${x_last_context}" = "-" ] && eval "x_last_context=\"\${${1}_opt_context}\""
			check_append_local "${x_last_extension}" "${x_last_context}" "${x_selfmailbox}" "${x_last_mailbox}" "${x_last_displayname}"
			x_last_context=local_${x_last_extension}
		fi
		if [ "${x_last_context}" != "-" ] ; then
			append ${1}_sections "context=${x_last_context}" "$N"
		fi
		if [ "${x_type}" != "user"  -a  -z "${x_last_host}" ] ; then
			append ${1}_sections "host=dynamic" "$N"
		fi
	fi

	for i in last_username last_fromdomain last_secret last_username \
		sectionname last_fromuser last_context last_extension last_mailbox \
		last_type last_host last_displayname ; do
		eval unset $1_$i
	done

	eval ${1}_selfmailbox=no
	eval ${1}_last_registerextension=-
}

reload_sip() {
	astcmd "sip reload"
	return 1 # reboot
}
unload_sip() astcmd "unload chan_sip.so"

rtp_option_list="rtpstart rtpend rtpdtmftimeout rtcpinterval rtpchecksums"
# Validate RTP options
valid_rtp_option() {
	is_in_list $1 ${rtp_option_list}
}

# Validate sip options, depending on context.
valid_sipiax_option() {
	local use_glob=1
	local use_glob_iax=1
	local use_glob_sip=1
	local use_user=1
	local use_peer=1
	local use_user_sip=1
	local use_user_iax=1
	local use_peer_sip=1
	local use_peer_iax=1
	case "$1" in
		globalsip)
			use_glob_sip=0
			use_glob=0 ;;
		usersip)
			use_glob_sip=0
			use_glob=0
			use_user=0 ;;
		peersip|friendsip)
			use_glob_sip=0
			use_glob=0
			use_user=0
			use_peer=0
			use_user_sip=0
			use_peer_sip=0 ;;
		globaliax)
			use_glob_iax=0
			use_glob=0 ;;
		useriax)
			use_glob_iax=0
			use_glob=0
			use_user=0 ;;
		peeriax|friendiax)
			use_glob_iax=0
			use_glob=0
			use_user=0
			use_peer=0
			use_user_iax=0
			use_peer_iax=0 ;;
	esac

	case "$2" in
		writeprotect|static) return ${use_glob_iax} ;;
# Integer
		port|\
		maxexpirey|\
		rtptimeout|\
		rtpholdtimeout|\
		defaultexpirey|\
		registertimeout|\
		registerattempts|\
		call-limit) return ${use_glob_sip} ;;
# ip addr
		bindaddr|\
		externip)   return ${use_glob_sip} ;;
# net/mask
		localnet)   return ${use_glob_sip} ;;
		permit|\
		deny)  return ${use_user_sip} ;;
# Domain name
		realm|\
		domain) 				return ${use_glob_sip} ;;
# valid context
		context)    return ${use_glob} ;;
# Mime type
		notifymimetype) return ${use_glob_sip} ;;
# Yes/No
		canreinvite) 		return ${use_glob} ;;
		nat|allowoverlap|allowsubscribe|allowtransfer|\
		videosupport)  	return ${use_glob_sip} ;;
		pedantic|\
		trustrpid|\
		promiscredir|\
		useclientcode)  return ${use_user_sip} ;;
# Enums
		dtmfmode)  			return ${use_glob_sip} ;;
		type) 					return ${use_user} ;;
		insecure|callingpres|\
		progressinband)  return ${use_user_sip} ;;
# List 
		allow|\
		disallow)  return ${use_glob_sip} ;;
# Register string
		register) return ${use_glob_sip} ;;
# String
		username|secret|md5secret|host|\
		mailbox) return ${use_user} ;;
		auth) return ${use_user_iax} ;;
		callgroup|pickupgroup|language|accountcode|\
		setvar|callerid|amaflags|subscribecontext|\
		maxcallbitrate|rfc2833compensate|\
		mailbox) return ${use_user_sip};;
		template|fromdomain|regexten|fromuser|\
		qualify|defaultip|sendrpid|\
		outboundproxy) return ${use_peer_sip};;
		extension) return 0;;
		*) return 1;;
	esac
}

ast_add_conf sip
init_sipconf() {
	ast_add_reload sip
	ast_enable_type sipgeneral
	ast_enable_type sip
	ast_enable_type target

	sip_opt_port=5060
	sip_opt_bindaddr=0.0.0.0
	sip_opt_context=default
	sip_opt_maxexpirey=3600
	sip_opt_defaultexpirey=3600
	sip_opt_notifymimetype=text/plain
	sip_opt_rtptimeout=60
	sip_opt_rtpholdtimeout=300
	config_get WAN_IP wan ipaddr
	# TODO check why the above does not work all the time
	if [ -z "${WAN_IP}" ] ; then
		config_get WAN_IF wan ifname
		WAN_IP=$(ifconfig ${WAN_IF} | grep "inet addr:" | sed 's/^.*inet addr:\([^ ]*\) .*$/\1/')
	fi

	sip_opt_externip=${WAN_IP}

	sip_opt_realm=asterisk
	config_get LAN_MASK lan netmask
	config_get LAN_IP lan ipaddr
	local LAN_NET=$(/bin/ipcalc.sh $LAN_IP $LAN_MASK | grep NETWORK | cut -d= -f2)
	sip_opt_localnet_def=$LAN_NET/$LAN_MASK
	sip_opt_localip=${LAN_IP}

	# default to ulaw only
	sip_opt_allow=
	sip_opt_registertimeout=20
	sip_opt_registerattempts=10
	sip_opt_canreinvite=no

	sip_sections=
}

sip_list="port bindaddr context maxexpirey defaultexpirey notifymimetype \
rtptimeout rtpholdtimeout realm externip"

create_sipconf() {

	append_dialplan_locals

	file=${DEST_DIR}/sip.conf
	get_checksum sip_conf $file
	local isempty=1
	if [ -z "${sip_sections}" ] ; then
		rm -f $file
		isempty=2
	else
		[ -z "${sip_opt_domain}" ]  && sip_opt_domain="domain=${sip_opt_realm}"

		echo "${asteriskuci_gen}[general]" > $file
		echo "${sip_opt_domain}" >> "$file"
		echo "domain=${sip_opt_localip}" >> "$file"
		for i in ${sip_list} ; do
			eval value=\$sip_opt_$i
			[ ! -z "$value" ] && ( echo "$i=$value" >> $file )
		done
		[ -z "${sip_opt_localnet}" ] && sip_opt_localnet="localnet=${sip_opt_localnet_def}"
		echo "${sip_opt_localnet}" >> "${file}"
		echo "disallow=all" >> $file
		local rhs="${sip_opt_allow}"
		[ -z "$rhs" ] && rhs=ulaw
		while [ ! -z "$rhs" ] ; do
			cur=${rhs%%,*}
			nvar=${rhs#*,}
			enable_format ${cur}
			echo "allow=${cur}" >> $file
			[ "$nvar" == "$rhs" ] && break
			rhs=${nvar}
		done

		echo "${N}${sip_register}${N}${N}${sip_sections}" >> $file
		unset sip_register
		unset sip_sections
	fi
	check_checksum "$sip_conf" "$file" || ast_sip_restart=$isempty
}


handle_sipgeneral() {
	option_cb(){
		if [ "${1%_LENGTH}" = "${1}" ] ; then
			local opt=${1%_ITEM*}
			logdebug 4 "SipGeneral option ${opt} $2"
			if valid_sipiax_option globalsip ${opt} "$2" ; then
				case "${opt}" in
					host) sip_opt_host="$2"
						[ -z "${sip_opt_host}" ] && sip_opt_host=dynamic ;;
					allow)    append sip_opt_allow "$2" "," ;;
					domain)   split_append sip_opt_domain domain= "$2" "${N}"  ;;
					localnet) split_append sip_opt_localnet localnet= "$2" "${N}" ;;
					*) eval "sip_opt_$1=\"\$2\"" ;;
				esac
			elif valid_rtp_option $1 "$2" ; then
				eval "rtp_opt_$1=\"\$2\""
			else
				logerror "Invalid SIP global option: $1"
			fi
		fi
	}
}

handle_sip() {
	check_add sipitems
	append sip_sections [$1] "$N$N"
	enable_module chan_sip
	sip_sectionname=${1#sip_}
	sip_type=peer
	sip_doregister=0
	sip_last_context=-
	sip_last_doregister=-
	sip_selfmailbox=no
	option_cb() {
		logdebug 3 "SIP/${sip_sectionname}: '$1' '$2'"
		case $1 in
			type) sip_type=$2
				append sip_sections "$1=$2" "$N"
				;;
			register) [ "$2" == "yes" ] && sip_doregister=1 ;;
			registerextension|displayname) eval sip_last_$1="$2";;
			allow|allow_ITEM*) split_append sip_sections allow= "$2" "${N}" enable_format ;;
			extension|extension_ITEM*)    add_extension SIP ${sip_sectionname} "$2" ;;

			context)      sip_last_context="$2" ;;
			selfmailbox)  sip_selfmailbox="$2" ;;
			incoming|incoming_ITEM*)
				add_incoming SIP ${sip_sectionname} "$2" ;;
			timeout|prefix|internationalprefix|alwaysinternational|countrycode)
				eval "target_$1_SIP_${sectionname}=\"$2\""
			;;
			*_LENGTH) ;;
			_*) ;;
			*)
				eval sip_last_$1="$2"
				local opt=${1%_ITEM*}
				if valid_sipiax_option ${sip_type}sip ${opt} $2 ; then
					append sip_sections "${opt}=$2" "$N"
				else
					logerror "Invalid SIP option for ${sip_type}: ${opt}"
				fi
		esac
	}
}

# rtp.conf

ast_add_conf rtp
init_rtpconf() {
	ast_add_reload rtp
	rtp_opt_rtpstart=5000
	rtp_opt_rtpend=31000
	rtp_opt_rtpchecksums=
	rtp_opt_rtpdtmftimeout=
	rtp_opt_rtcpinterval=5000
}

create_rtpconf() {
	file=${DEST_DIR}/rtp.conf
	get_checksum rtp_conf $file
	local isempty=1
	if module_enabled chan_sip ; then
		echo "${asteriskuci_gen}[general]" > $file
		for i in $rtp_option_list ; do
			eval "local val=\"\$rtp_opt_$i\""
			if [ ! -z "$val" ] ; then
				lhs=$i
				case "$i" in
					rtpdtmftimeout) lhs=dtmftimeout
				esac
				echo "$lhs=$val" >> $file
			fi
		done
	else
		rm -f $file
		isempty=2
	fi

	check_checksum "$rtp_conf" "$file" || ast_rtp_restart=$isempty
}
reload_rtp() astcmd "rtp reload"
unload_rtp() astcmd "unload rtp"


# Iax

ast_add_conf iax

init_iaxconf() {
	ast_add_reload iax
	ast_enable_type iaxgeneral
	ast_enable_type iax

	return 0
}

create_iaxconf() {
	local file=$DEST_DIR/iax.conf
	get_checksum iax_conf $file
	local isempty=1
	if [ -z "${iax_sections}" ] ; then
		rm -f $file
		isempty=2
	else
		echo "${asteriskuci_gen}${iax_general}$N$N${iax_sections}" > $file
	fi
	check_checksum "$iax_conf" "$file"  || ast_iax_restart=${isempty}
}

handle_iaxgeneral() {
	iax_general="[general]"
	option_cb() {
		case $1 in
			allow_LENGTH) ;;
			allow|allow_ITEM*) split_append iax_general allow= "$2" "${N}" enable_format ;;
			*)
				if valid_sipiax_option globaliax $1 $2 ; then
					eval "iax_opt_$1=\"$2\""
					append iax_general "$1=$2" "$N"
				else
					logerror "Invalid IAX global option: $1"
				fi ;;
		esac
	}
}

handle_iax() {
	check_add iaxitems
	iax_type=peer
	iax_sectionname="${1#iax_}"
	append iax_sections "[${iax_sectionname}]" "$N$N"
	iax_last_context=-
	iax_selfmailbox=no
	enable_module chan_iax2
	option_cb() {
		case $1 in
			type)
				iax_type=$2
				append iax_sections "type=$2" "$N" ;;
			allow_LENGTH|incoming_LENGTH) ;;
			allow|allow_ITEM*)
				split_append iax_sections allow= "$2" "${N}" enable_format ;;
			extension_LENGTH) ;;
			extension|extension_ITEM*)    add_extension IAX ${iax_sectionname} "$2" ;;
			displayname) eval iax_last_$1="$2";;
			context)
				eval iax_last_context="$2" ;;
			selfmailbox)
				eval iax_selfmailbox="$2" ;;
			incoming|incoming_ITEM*)
				add_incoming IAX ${iax_sectionname} "$2" ;;
			timeout|prefix|internationalprefix|alwaysinternational|countrycode)
				eval "target_$1_IAX_${sectionname}=\"$2\"" ;;
			*)
				eval iax_last_$1="$2"
				if valid_sipiax_option ${iax_type}iax $1 $2 ; then
					append iax_sections "$1=$2" "$N"
				else
					logerror "Invalid IAX option for ${iax_type}: $1"
				fi
		esac
	}
}

reload_iax() {
	astcmd "iax2 reload"
	return 1
}
unload_iax() astcmd "unload chan_iax2.so"

handle_target() {
	# Target name
	if split_targettype targettype targetname "${1}" ; then
		logdebug 4 "Handle target ${targettype}/${targetname} - ${1}"
		handle_dialtarget "${targettype}" "${targetname}"
	else
		logerror "No target type specified for target $1"
	fi
}

# Set up options sip/iax targets for outgoing sip/iax
handle_dialtarget() {
	# Dialzone target option
	dt_areatype="$1"
	dt_areaname="$2"
	logdebug 1 "Dialzone Target for ${dt_areatype}/${dt_areaname}"
	option_cb(){
		logdebug 3 "Option $1='$2' for dialzone target"
		case $1 in
			timeout|prefix|internationalprefix|alwaysinternational|countrycode)
				eval "target_$1_${dt_areatype}_${dt_areaname}=\"$2\""
			;;
			*) 
				logerror "Invalid target for ${dt_areatype}/${dt_areaname}: ${1}"
		esac
	}
}

# vim: ts=2 sw=2 noet foldmethod=indent
