#!KAMAILIO
#
# example kamailio.cfg with Shared Call Appearances (SCA)

#!define WITH_AUTH
#!define WITH_MYSQL
#!define WITH_SCA

####### Defined Values #########

#!ifdef WITH_MYSQL
# - database URL - used to connect to database server by modules such
#       as: auth_db, acc, usrloc, a.s.o.
#!ifndef DBURL
#!define DBURL "mysql://kamailio:kamailiorw@localhost/kamailio"
#!endif
#!endif

####### Global Parameters #########

#!ifdef WITH_DEBUG
debug=4
log_stderror=yes
#!else
debug=2
log_stderror=no
#!endif

memdbg=5
memlog=5

log_facility=LOG_LOCAL0

fork=yes
children=4

listen=udp:10.0.0.10:5060
port=5060

####### Modules Section ########

# set paths to location of modules (to sources or installation folders)
#!ifdef WITH_SRCPATH
mpath="modules_k:modules"
#!else
mpath="/usr/local/kamailio/lib64/kamailio/modules_k/:/usr/local/kamailio/lib64/kamailio/modules/"
#!endif

#!ifdef WITH_MYSQL
loadmodule "db_mysql.so"
#!endif

loadmodule "tm.so"
loadmodule "sl.so"
loadmodule "rr.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "usrloc.so"
loadmodule "registrar.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "ctl.so"
loadmodule "cfg_rpc.so"

#!ifdef WITH_AUTH
loadmodule "auth.so"
loadmodule "auth_db.so"
#!ifdef WITH_IPAUTH
loadmodule "permissions.so"
#!endif
#!endif

#!ifdef WITH_SCA
loadmodule "sca.so"
#!endif


# ----------------- setting module-specific parameters ---------------


# ----- tm params -----
# auto-discard branches from previous serial forking leg
modparam("tm", "failure_reply_mode", 3)
# default retransmission timeout: 30sec
modparam("tm", "fr_timer", 30000)
# default invite retransmission timeout after 1xx: 120sec
modparam("tm", "fr_inv_timer", 120000)


# ----- rr params -----
# add value to ;lr param to cope with most of the UAs
modparam("rr", "enable_full_lr", 1)
# do not append from tag to the RR (no need for this script)
modparam("rr", "append_fromtag", 0)


# ----- registrar params -----
modparam("registrar", "method_filtering", 1)
/* uncomment the next line to disable parallel forking via location */
# modparam("registrar", "append_branches", 0)
/* uncomment the next line not to allow more than 10 contacts per AOR */
#modparam("registrar", "max_contacts", 10)
# max value for expires of registrations
modparam("registrar", "max_expires", 3600)
# set it to 1 to enable GRUU
modparam("registrar", "gruu_enabled", 0)


# ----- usrloc params -----
/* enable DB persistency for location entries */
#!ifdef WITH_USRLOCDB
modparam("usrloc", "db_url", DBURL)
modparam("usrloc", "db_mode", 2)
modparam("usrloc", "use_domain", 0)
#!endif


# ----- auth_db params -----
#!ifdef WITH_AUTH
modparam("auth_db", "db_url", DBURL)
modparam("auth_db", "calculate_ha1", yes)
modparam("auth_db", "password_column", "password")
modparam("auth_db", "load_credentials", "")

# ----- permissions params -----
#!ifdef WITH_IPAUTH
modparam("permissions", "db_url", DBURL)
modparam("permissions", "db_mode", 1)
#!endif

#!endif

# ----- sca params -----
#!ifdef WITH_SCA
modparam("sca", "call_info_max_expires", 300)
modparam("sca", "db_url", DBURL)
#!endif


####### Routing Logic ########

# Main SIP request routing logic
# - processing of any incoming SIP request starts with this route
# - note: this is the same as route { ... }
request_route {

	# per request initial checks
	route(REQINIT);

	# CANCEL processing
	if (is_method("CANCEL"))
	{
		if (t_check_trans()) {
			route(SCA);
			t_relay();
		}
		exit;
	}

	# handle requests within SIP dialogs
	route(WITHINDLG);

	### only initial requests (no To tag)

	t_check_trans();

	# authentication
	route(AUTH);

	# record routing for dialog forming requests (in case they are routed)
	# - remove preloaded route headers
	remove_hf("Route");
	if (is_method("INVITE|SUBSCRIBE"))
		record_route();

	# dispatch requests to foreign domains
	route(SIPOUT);

	# handle registrations
	route(REGISTRAR);

	if ($rU==$null)
	{
		# request with no Username in RURI
		sl_send_reply("484","Address Incomplete");
		exit;
	}

	# user location service
	route(LOCATION);

	route(RELAY);
}


route[RELAY] {

	# enable additional event routes for forwarded requests
	if (is_method("INVITE|BYE|SUBSCRIBE|PRACK|REFER|UPDATE")) {
		if(!t_is_set("onreply_route")) t_on_reply("MANAGE_REPLY");
	}
	if (is_method("INVITE")) {
		if(!t_is_set("failure_route")) t_on_failure("MANAGE_FAILURE");
	}

	route(SCA);

	if (!t_relay()) {
		sl_reply_error();
	}
	exit;
}

# Per SIP request initial checks
route[REQINIT] {
	if (!mf_process_maxfwd_header("10")) {
		sl_send_reply("483","Too Many Hops");
		exit;
	}

	if(!sanity_check("1511", "7"))
	{
		xlog("Malformed SIP message from $si:$sp\n");
		exit;
	}
}

# Handle requests within SIP dialogs
route[WITHINDLG] {
	if (has_totag()) {
		# sequential request withing a dialog should
		# take the path determined by record-routing
		if (loose_route()) {
			if ( is_method("NOTIFY") ) {
				# Add Record-Route for in-dialog NOTIFY as per RFC 6665.
				record_route();
			}
			route(RELAY);
		} else {
			if (is_method("SUBSCRIBE") && uri == myself) {
				# in-dialog subscribe requests
				route(SCA);
				exit;
			}
			if ( is_method("ACK") ) {
				if ( t_check_trans() ) {
					# no loose-route, but stateful ACK;
					# must be an ACK after a 487
					# or e.g. 404 from upstream server
					t_relay();
					exit;
				} else {
					# ACK without matching transaction ... ignore and discard
					exit;
				}
			}
			sl_send_reply("404","Not here");
		}
		exit;
	}
}

# Handle SIP registrations
route[REGISTRAR] {
	if (is_method("REGISTER"))
	{
		if (!save("location"))
			sl_reply_error();

		exit;
	}
}

# USER location service
route[LOCATION] {
	$avp(oexten) = $rU;
	if (!lookup("location")) {
		$var(rc) = $rc;
		t_newtran();
		switch ($var(rc)) {
			case -1:
			case -3:
				send_reply("404", "Not Found");
				exit;
			case -2:
				send_reply("405", "Method Not Allowed");
				exit;
		}
	}
}

# Authentication route
route[AUTH] {
#!ifdef WITH_AUTH

#!ifdef WITH_IPAUTH
	if((!is_method("REGISTER")) && allow_source_address())
	{
		# source IP allowed
		return;
	}
#!endif

	if (is_method("REGISTER") || from_uri==myself)
	{
		# authenticate requests
		if (!auth_check("$fd", "subscriber", "1")) {
			auth_challenge("$fd", "0");
			exit;
		}
		# user authenticated - remove auth header
		if(!is_method("REGISTER|PUBLISH"))
			consume_credentials();
	}
	# if caller is not local subscriber, then check if it calls
	# a local destination, otherwise deny, not an open relay here
	if (from_uri!=myself && uri!=myself)
	{
		sl_send_reply("403","Not relaying");
		exit;
	}

#!endif
	return;
}

# Shared Call Appearances handling
route[SCA] {
#!ifdef WITH_SCA
	if(is_method("SUBSCRIBE")) {
		if ($hdr(Event) == "call-info" || $hdr(Event) == "line-seize") {
			xdbg("SCA: $hdr(Event) SUBSCRIBE $ru from $si:$sp");
			sca_handle_subscribe();
			exit;
		}

		return;
	}

	if (!is_method("BYE|CANCEL|INVITE|PRACK|REFER")) {
		return;
	}

	# this updates appearance state and NOTIFYs SCA subscribers as
	# necessary. it also removes the Call-Info header, if found.
	sca_call_info_update();
#!endif

	return;
}

# Routing to foreign domains
route[SIPOUT] {
	if (!uri==myself)
	{
		append_hf("P-hint: outbound\r\n");
		route(RELAY);
	}
}

# XMLRPC routing
#!ifdef WITH_XMLRPC
route[XMLRPC] {
	# allow XMLRPC from localhost
	if ((method=="POST" || method=="GET")
			&& (src_ip==127.0.0.1)) {
		# close connection only for xmlrpclib user agents (there is a bug in
		# xmlrpclib: it waits for EOF before interpreting the response).
		if ($hdr(User-Agent) =~ "xmlrpclib")
			set_reply_close();
		set_reply_no_connect();
		dispatch_rpc();
		exit;
	}
	send_reply("403", "Forbidden");
	exit;
}
#!endif

# manage incoming replies
onreply_route[MANAGE_REPLY] {
	xdbg("incoming reply\n");

	if (status =~ "[456][0-9][0-9]") {
		# don't update SCA state here, since there may be
		# failure route processing (e.g., call forwarding).
		# update state in failure route instead.
	    return;
	}

	route(SCA);
}

# manage failure routing cases
failure_route[MANAGE_FAILURE] {
	if (t_is_canceled()) {
		exit;
	}

	route(SCA);
}