#
# $Id$
#
# control tool for maintaining Kamailio
#
#===================================================================

##### ----------------------------------------------- #####
### path to useful tools
locate_tool() {
	TOOLPATH=""
	while [ -n "$1" ]
	do
		if [ -x /usr/bin/which ] ; then
			TOOLPATH=`which $1`
			if [ -n "$TOOLPATH" ]; then
				return
			fi
		fi
		# look in common locations
		if [ -x "/usr/bin/$1" ] ; then
			TOOLPATH="/usr/bin/$1"
			return
		fi
		if [ -x "/bin/$1" ] ; then
			TOOLPATH="/bin/$1"
			return
		fi
		if [ -x "/usr/local/bin/$1" ] ; then
			TOOLPATH="/usr/local/bin/$1"
			return
		fi
		shift
	done
	return
}

if [ -z "$EGREP" ] ; then
	locate_tool egrep
	if [ -z "$TOOLPATH" ] ; then
		# now error, but we can look for alternative names if it is the case
		echo "error: 'egrep' tool not found: set EGREP variable to correct tool path"
		exit
	fi
	EGREP="$TOOLPATH"
fi
if [ -z "$AWK" ] ; then
	locate_tool awk
	if [ -z "$TOOLPATH" ] ; then
		# now error, but we can look for alternative names if it is the case
		echo "error: 'awk' tool not found: set AWK variable to correct tool path"
		exit
	fi
	AWK="$TOOLPATH"
fi
if [ -z "$MD5" ]; then
	locate_tool md5sum md5
	if [ -z "$TOOLPATH" ] ; then
		# now error, but we can look for alternative names if it is the case
		echo "error: 'md5sum' or 'md5' tool not found: set MD5 variable to correct tool path"
		exit
	fi
	MD5="$TOOLPATH"
fi
if [ -z "$LAST_LINE" ] ; then
	locate_tool tail
	if [ -z "$TOOLPATH" ] ; then
		# now error, but we can look for alternative names if it is the case
		echo "error: 'tail' tool not found: set LAST_LINE variable to correct tool path"
		exit
	fi
	LAST_LINE="$TOOLPATH -n 1"
fi
if [ -z "$EXPR" ] ; then
	locate_tool expr
	if [ -z "$TOOLPATH" ] ; then
		# now error, but we can look for alternative names if it is the case
		echo "error: 'expr' tool not found: set EXPR variable to correct tool path"
		exit
	fi
	EXPR="$TOOLPATH"
fi

##### ------------------------------------------------ #####
### configuration for starting/stopping kamailio
if [ -z "$PID_FILE" ] ; then
	PID_FILE=/var/run/kamailio.pid
fi
if [ -z "$SYSLOG" ] ; then
	SYSLOG=1 # 0=output to console, 1=output to syslog
fi
if [ -z "$STARTOPTIONS" ] ; then
	STARTOPTIONS= # for example -dddd
fi
if [ -z "$DIR" ] ; then
	DIR=`dirname $0`
fi
if [ -z "$OSERBIN" ] ; then
	OSERBIN=$DIR/kamailio
fi

##### ------------------------------------------------ #####
### aliases configuration
#
ENABLE_ALIASES=0
if [ "$ALIASES_TYPE" = "UL" ] ; then
	ENABLE_ALIASES=1
else
	if [ "$ALIASES_TYPE" = "DB" ] ; then
		ENABLE_ALIASES=2
	fi
fi

##### ------------------------------------------------ #####
### ACL name verification
if [ -z "$VERIFY_ACL" ] ; then
	VERIFY_ACL=1
fi
if [ -z "$ACL_GROUPS" ] ; then
	ACL_GROUPS="local ld int voicemail free-pstn"
fi

##### ----------------------------------------------- #####
#### Defined values
ALL_METHODS=4294967295
USERNAME_RE="[-a-zA-Z0-9&=\+\$,;\?/_\.\!~\*'\(\)]+"

##### ----------------------------------------------- #####
#### database tables for SQL databases and DBTEXT

SRDB_LOAD_SER=$((1 << 0))       # The row should be loaded by SER
SRDB_DISABLED=$((1 << 1))       # The row is disabled
SRDB_CANON=$((1 << 2))          # Canonical entry (domain or uri)
SRDB_IS_TO=$((1 << 3))          # The URI can be used in To
SRDB_IS_FROM=$((1 << 4))        # The URI can be used in From
SRDB_FOR_SERWEB=$((1 << 5))     # Credentials instance can be used by serweb
SRDB_PENDING=$((1 << 6)) 
SRDB_DELETED=$((1 << 7)) 
SRDB_CALLER_DELETED=$((1 << 8)) # Accounting table
SRDB_CALLEE_DELETED=$((1 << 9)) # Accounting table
SRDB_MULTIVALUE=$((1 << 10))    # Attr_types table
SRDB_FILL_ON_REG=$((1 << 11))   # Attr_types table
SRDB_REQUIRED=$((1 << 12))      # Attr_types table
SRDB_DIR=$((1 << 13))           # Domain_settings table

# UsrLoc Table
if [ -z "$UL_TABLE" ] ; then
	UL_TABLE=location
fi
USER_COLUMN=username
DOMAIN_COLUMN=domain
CALLID_COLUMN=callid

# subscriber table
if [ -z "$SUB_TABLE" ] ; then
	SUB_TABLE=subscriber
fi
REALM_COLUMN=domain
HA1_COLUMN=ha1
HA1B_COLUMN=ha1b
PASSWORD_COLUMN=password
RPID_COLUMN=rpid
SUBSCRIBER_COLUMN='username'
PHP_LIB_COLUMN=phplib_id

if [ -z "$STORE_PLAINTEXT_PW" ] ; then
	STORE_PLAINTEXT_PW=1
fi

# acl table
if [ -z "$ACL_TABLE" ] ; then
	ACL_TABLE=grp
fi
ACL_USER_COLUMN=username
ACL_DOMAIN_COLUMN=domain
ACL_GROUP_COLUMN=grp
ACL_MODIFIED_COLUMN=last_modified

# aliases table
if [ -z "$ALS_TABLE" ] ; then
	ALS_TABLE=aliases
fi
A_USER_COLUMN=username
A_CONTACT_COLUMN=contact
A_EXPIRES_COLUMN=expires
A_Q_COLUMN=q
A_CALLID_COLUMN=callid
A_CSEQ_COLUMN=cseq
A_LAST_MODIFIED_COLUMN=last_modified

# domain table
if [ -z "$DOMAIN_TABLE" ] ; then
	DOMAIN_TABLE=domain
fi
DO_DOMAIN_COLUMN=domain
DO_LAST_MODIFIED_COLUMN=last_modified

# uid_domain table
if [ -z "$UID_DOMAIN_TABLE" ] ; then
	UID_DOMAIN_TABLE=uid_domain
fi
UID_DO_DOMAIN_COLUMN=domain
UID_DO_DID_COLUMN=did
UID_DO_FLAGS_COLUMN=flags

# lcr tables
if [ -z "$LCR_TABLE" ] ; then
	LCR_TABLE=lcr
fi
LCR_ID_COLUMN=lcr_id
LCR_PREFIX_COLUMN=prefix
LCR_GRPID_COLUMN=grp_id

# gw table
if [ -z "$GW_TABLE" ] ; then
	GW_TABLE=gw
fi

# carrier_name table
if [ -z "$CARRIER_NAME_TABLE" ] ; then
	CARRIER_NAME_TABLE=carrier_name
fi
CARRIERROUTE_CARRIER_NAME_ID_COLUMN=id
CARRIERROUTE_CARRIER_NAME_CARRIER_COLUMN=carrier

# domain_name table
if [ -z "$DOMAIN_NAME_TABLE" ] ; then
	DOMAIN_NAME_TABLE=domain_name
fi
CARRIERROUTE_DOMAIN_NAME_ID_COLUMN=id
CARRIERROUTE_DOMAIN_NAME_DOMAIN_COLUMN=domain

# carrierroute table
if [ -z "$CARRIERROUTE_TABLE" ] ; then
	CARRIERROUTE_TABLE=carrierroute
fi
CARRIERROUTE_CARRIERROUTE_PREFIX_COLUMN=id
CARRIERROUTE_CARRIERROUTE_CARRIER_COLUMN=carrier
CARRIERROUTE_CARRIERROUTE_SCAN_PREFIX_COLUMN=scan_prefix
CARRIERROUTE_CARRIERROUTE_DOMAIN_COLUMN=domain
CARRIERROUTE_CARRIERROUTE_PROB_COLUMN=prob
CARRIERROUTE_CARRIERROUTE_STRIP_COLUMN=strip
CARRIERROUTE_CARRIERROUTE_REWRITE_HOST_COLUMN=rewrite_host
CARRIERROUTE_CARRIERROUTE_REWRITE_PREFIX_COLUMN=rewrite_prefix
CARRIERROUTE_CARRIERROUTE_REWRITE_SUFFIX_COLUMN=rewrite_suffix
CARRIERROUTE_CARRIERROUTE_COMMENT_COLUMN=description
CARRIERROUTE_CARRIERROUTE_FLAGS_COLUMN=flags
CARRIERROUTE_CARRIERROUTE_MASK_COLUMN=mask

# URI table
if [ -z "$URI_TABLE" ] ; then
	URI_TABLE=uri
fi
URIUSER_COLUMN=uri_user
MODIFIED_COLUMN=last_modified

# dbaliases table
if [ -z "$DA_TABLE" ] ; then
	DA_TABLE=dbaliases
fi
DA_USER_COLUMN=username
DA_DOMAIN_COLUMN=domain
DA_ALIAS_USER_COLUMN=alias_username
DA_ALIAS_DOMAIN_COLUMN=alias_domain

# speeddial table
if [ -z "$SD_TABLE" ] ; then
	SD_TABLE=speed_dial
fi
SD_USER_COLUMN=username
SD_DOMAIN_COLUMN=domain
SD_SD_USER_COLUMN=sd_username
SD_SD_DOMAIN_COLUMN=sd_domain
SD_NEW_URI_COLUMN=new_uri
SD_DESC_COLUMN=description

# avp table
if [ -z "$AVP_TABLE" ] ; then
	AVP_TABLE=usr_preferences
fi

AVP_UUID_COLUMN=uuid
AVP_USER_COLUMN=username
AVP_DOMAIN_COLUMN=domain
AVP_ATTRIBUTE_COLUMN=attribute
AVP_VALUE_COLUMN=value
AVP_TYPE_COLUMN=type
AVP_MODIFIED_COLUMN=last_modified

# trusted table
if [ -z "$TRUSTED_TABLE" ] ; then
	TRUSTED_TABLE=trusted
fi

TRUSTED_SRC_IP_COLUMN=src_ip
TRUSTED_PROTO_COLUMN=proto
TRUSTED_FROM_PATTERN_COLUMN=from_pattern
TRUSTED_TAG_COLUMN=tag

# address table
if [ -z "$ADDRESS_TABLE" ] ; then
	ADDRESS_TABLE=address
fi

# dispatcher tables  
if [ -z "$DISPATCHER_TABLE" ] ; then
	DISPATCHER_TABLE=dispatcher
fi
DISPATCHER_ID_COLUMN=id
DISPATCHER_SETID_COLUMN=setid
DISPATCHER_DESTINATION_COLUMN=destination
DISPATCHER_FLAGS_COLUMN=flags
DISPATCHER_PRIORITY_COLUMN=priority
DISPATCHER_ATTRS_COLUMN=attrs
DISPATCHER_DESCRIPTION_COLUMN=description

# dialog tables
if [ -z "$DIALOG_TABLE" ] ; then
	DIALOG_TABLE=dialog
fi

# dialplan tables
if [ -z "$DIALPLAN_TABLE" ] ; then
	DIALPLAN_TABLE=dialplan
fi
DIALPLAN_ID_COLUMN=id
DIALPLAN_DPID_COLUMN=dpid
DIALPLAN_PR_COLUMN=pr
DIALPLAN_MATCH_OP_COLUMN=match_op
DIALPLAN_MATCH_EXP_COLUMN=match_exp
DIALPLAN_MATCH_LEN_COLUMN=match_len
DIALPLAN_SUBST_EXP_COLUMN=subst_exp
DIALPLAN_REPL_EXP_COLUMN=repl_exp
DIALPLAN_ATTRS_COLUMN=attrs

#
##### ------------------------------------------------ #####
### usage functions
#

usage_base() {
	echo
	mecho " -- command 'start|stop|restart'"
	echo
cat <<EOF
 restart ............................ restart Kamailio
 start .............................. start Kamailio
 stop ............................... stop Kamailio
EOF
}

usage_tls() {
	echo
	mecho " -- command 'tls'"
	echo
cat <<EOF
 tls rootCA [<etcdir>] .......... creates new rootCA
 tls userCERT <user> [<etcdir>] ... creates user certificate
 default <etcdir> is $ETCDIR/tls
EOF
}

USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_base"

usage_acl() {
	echo
	mecho " -- command 'acl' - manage access control lists (acl)"
	echo
cat <<EOF
 acl show [<username>] .............. show user membership
 acl grant <username> <group> ....... grant user membership (*)
 acl revoke <username> [<group>] .... grant user membership(s) (*)
EOF
}
USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_acl"

usage_lcr() {
	echo
	mecho " -- command 'lcr' - manage least cost routes (lcr)"
	echo
cat <<EOF
 lcr show_gws....... show database gateways
 lcr show_routes.... show database routes
 lcr dump_gws....... show in memory gateways
 lcr dump_routes.... show in memory routes
 lcr reload ........ reload lcr gateways and routes
 lcr eval_weights .. evaluates probability for given GW's weights
EOF
}
USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_lcr"

usage_cr() {
	echo
	mecho " -- command 'cr' - manage carrierroute tables"
	echo
cat <<EOF
 cr show ....................................................... show tables
 cr reload ..................................................... reload tables
 cr dump ....................................................... show in memory tables
 cr addcn <carrier id> <carrier name> .......................... add a carrier name
 cr rmcn  <carrier id> ......................................... rm a carrier name
 cr adddn <domain id> <domain name> ............................ add a domain name
 cr rmdn  <domain id> .......................................... rm a domain name
 cr addcarrier <carrier> <scan_prefix> <domain> <rewrite_host> ................
               <prob> <strip> <rewrite_prefix> <rewrite_suffix> ...............
               <flags> <mask> <comment> .........................add a carrier
               (prob, strip, rewrite_prefix, rewrite_suffix,...................
                flags, mask and comment are optional arguments) ...............
 cr rmcarrier  <carrier> <scan_prefix> <domain> ................ rm a carrier
EOF
}
USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_cr"

usage_rpid() {
	echo
	mecho " -- command 'rpid' - manage Remote-Party-ID (RPID)"
	echo
cat <<EOF
 rpid add <username> <rpid> ......... add rpid for a user (*)
 rpid rm <username> ................. set rpid to NULL for a user (*)
 rpid show <username> ............... show rpid of a user
EOF
}
USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_rpid"

usage_subscriber() {
	echo
	mecho " -- command 'add|passwd|rm' - manage subscribers"
	echo
cat <<EOF
 add <username> <password> .......... add a new subscriber (*)
 show <username> .................... show subscriber attributes (*)
 passwd <username> <passwd> ......... change user's password (*)
 rm <username> ...................... delete a user (*)
 sets <username> <attr> <val> ....... set string attribute (column value)
 setn <username> <attr> <val> ....... set numeric attribute (column value)
EOF
}
USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_subscriber"

usage_trusted() {
	echo
	mecho " -- command 'add|dump|reload|rm|show' - manage trusted"
	echo
cat <<EOF
 trusted show ...................... show db content
 trusted dump ...................... show cache content
 trusted reload .................... reload db table into cache
 trusted add <src_ip> <proto> <from_pattern> <tag>
             ....................... add a new entry
	     ....................... (from_pattern and tag are optional arguments)
 trusted rm <src_ip> ............... remove all entries for the given src_ip
EOF
}
USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_trusted"

usage_address() {
	echo
	mecho " -- command 'add|dump|reload|rm|show' - manage address"
	echo
cat <<EOF
 address show ...................... show db content
 address dump ...................... show cache content
 address reload .................... reload db table into cache
 address add <grp> <ipaddr> <mask> <port> <tag>
             ....................... add a new entry
	     ....................... (mask, port and tag are optional arguments)
 address rm <grp> <ipaddr> ......... remove entries for given grp and ipaddr
EOF
}
USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_address"

usage_dispatcher() {
	echo
	mecho " -- command 'dispatcher' - manage dispatcher"
	echo
cat <<EOF
   * Examples: dispatcher add 1 sip:1.2.3.1:5050 1 5 'prefix=123' 'gw one'
   *           dispatcher add 2 sip:1.2.3.4:5050 3 0
   *           dispatcher rm 4
 dispatcher show ..................... show dispatcher gateways
 dispatcher reload ................... reload dispatcher gateways
 dispatcher dump ..................... show in memory dispatcher gateways
 dispatcher add <setid> <destination> [flags] [priority] [attrs] [description]
            .......................... add gateway
 dispatcher rm <id> .................. delete gateway
EOF
}
USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_dispatcher"

usage_dialog() {
	echo
	mecho " -- command 'dialog' - manage dialog records"
	echo
cat <<EOF
   * Examples: dialog show
   *           dialog showdb
 dialog show ..................... show in-memory dialog records
 dialog showdb ................... show database dialog records
EOF
}
USAGE_FUNCTIONS="$USAGE_FUNCTIONS usage_dialog"

usage_dialplan() {
	echo
	mecho " -- command 'dialplan' - manage dialplans"
	echo
cat <<EOF
 dialplan show <dpid> .............. show dialplan tables
 dialplan reload ................... reload dialplan tables
 dialplan addrule <dpid> <prio> <match_op> <match_exp>
 		<match_len> <subst_exp> <repl_exp> <attrs>
		.................... add a rule
 dialplan rm ....................... removes the entire dialplan table
 dialplan rmdpid <dpid> ............ removes all the gived dpid entries
 dialplan rmrule <dpid> <prio> ..... removes all the gived dpid/prio entries
EOF
}

##### ----------------------------------------------- #####
#### Common functions


mdbg() {
	if [ "0$VERBOSE" -ne 0 ] ; then
		if [ -t 1 -a -z "$NOHLPRINT" ] ; then
			echo -e "\033[1m$1\033[0m"
		else
			echo "$1"
		fi
	fi
}

mwarn() {
	if [ -t 1 -a -z "$NOHLPRINT" ] ; then
		echo -e '\E[37;32m'"\033[1mWARNING: $1\033[0m"
	else
		echo "** WARNING: $1"
	fi
}

minfo() {
	if [ -t 1 -a -z "$NOHLPRINT" ] ; then
		echo -e '\E[37;33m'"\033[1mINFO: $1\033[0m"
	else
		echo "** INFO: $1"
	fi
}

mecho() {
	if [ -t 1 -a -z "$NOHLPRINT" ] ; then
		echo -e "\033[1m$1\033[0m"
	else
		echo "$1"
	fi
}

merr() {
	if [ -t 1 -a -z "$NOHLPRINT" ] ; then
		echo -e '\E[37;31m'"\033[1mERROR: $1\033[0m"
	else
		echo "** ERROR: $1"
	fi
}


# determine host name, typically for use in printing UAC
# messages; we use today a simplistic but portable uname -n way --
# no domain name is displayed ; fifo_uac expands !! to host
# address only for optional header fields; uname output without
# domain is sufficient for informational header fields such as
# From
#
get_my_host() {
	if [ -z "$SIP_DOMAIN" ]; then
		uname -n
	else
		echo "$SIP_DOMAIN"
	fi
}

# calculate name and domain of current user
set_user() {

	OSERUSER=`echo $1|$AWK -F@ '{print $1}'`
	OSERDOMAIN=`echo $1|$AWK -F@ '{print $2}'`

	if [ -z "$OSERDOMAIN" ] ; then
		OSERDOMAIN="$SIP_DOMAIN"
	fi

	if [ -z "$OSERDOMAIN" ] ; then
		merr "domain unknown: use usernames with domain or set default domain \
in SIP_DOMAIN"
		exit 1
	fi
}

# check the parameter if it is a valid address of record (user@domain)
check_aor() {
	echo "$1" | $EGREP "^$USERNAME_RE@.*\..*" >/dev/null
	if [ $? -ne 0 ] ; then 
		echo "error: invalid AoR: $1" > /dev/stderr
		exit 1
	fi
}

# check the parameter if it is a valid address of record (user@domain)
is_aor() {
	echo "$1" | $EGREP "^$USERNAME_RE@.*\..*" >/dev/null
	if [ $? -ne 0 ] ; then 
		false
	else
		true
	fi
}

# check the parameter if it is a valid SIP address of record (sip:user@domain)
check_sipaor() {
	echo "$1" | $EGREP "^sip(s)?:$USERNAME_RE@.*\..*" >/dev/null
	if [ $? -ne 0 ] ; then 
		echo "error: invalid SIP AoR: $1" > /dev/stderr
		exit 1
	fi
}

# check the parameter if it is a valid SIP URI
# quite simplified now -- it captures just very basic
# errors
check_uri() {
	echo "$1" | $EGREP "^sip(s)?:($USERNAME_RE@)?.*\..*"  > /dev/null
	if [ $? -ne 0 ] ; then 
		echo "error: invalid SIP URI: $1" > /dev/stderr
		exit 1
	fi
}

print_status() {
	echo $1 | $EGREP "^[1-6][0-9][0-9]" > /dev/null
	if [ "$?" -eq 0 ] ; then 
		echo $1
	else
		echo "200 OK"
	fi
}

# process output from FIFO/Unixsock server; if everything is ok
# skip the first "ok" line and proceed to returned 
# parameters
filter_fl()
{
#	tail +2
	
	$AWK 'BEGIN {line=0;IGNORECASE=1;}
		{line++}
		NR == 1 && /^200 OK/ { next }
		/^$/ { next }
		{ print }'
}

# params: user, realm, password
# output: HA1
_gen_ha1()
{
	HA1=`echo -n "$1:$2:$3" | $MD5 | $AWK '{ print $1 }'`
	if [ $? -ne 0 ] ; then
		echo "HA1 calculation failed"
		exit 1
	fi
}

# params: user, realm, password
# output: HA1B
_gen_ha1b()
{
	HA1B=`echo -n "$1@$2:$2:$3" | $MD5 | $AWK '{ print $1 }'`
	if [ $? -ne 0 ] ; then
		echo "HA1B calculation failed"
		exit 1
	fi
}

# params: user, realm, password
# output: PHPLIB_ID
_gen_phplib_id()
{
	NOW=`date`;
	PHPLIB_ID=`echo -n "$1$2:$3:$NOW" | $MD5 | $AWK '{ print $1 }'`
}

# params: user, password
# output: HA1, HA1B
credentials()
{
	set_user $1
	_gen_ha1 "$OSERUSER" "$OSERDOMAIN" "$2"
	_gen_ha1b "$OSERUSER" "$OSERDOMAIN" "$2"
}
