#!KAMAILIO
#
# This config file implements the basic P-CSCF functionality
#     - web: http://www.kamailio.org
#     - git: http://sip-router.org
#
# Refer to the Core CookBook at http://www.kamailio.org/dokuwiki/doku.php
# for an explanation of possible statements, functions and parameters.
#
# Direct your questions about this file to: <sr-users@lists.kamailio.org>.
#
# For more information about the various parameters, functions and statements
# try http://sip-router.org/wiki/ .
#

include_file "smsc.cfg"

####### Global Parameters #########
debug=2
log_stderror=no
sip_warning=no
children=4

user_agent_header="User-Agent: Kamailio SMSC"
server_header="Server: Kamailio SMSC"

/* comment the next line to enable the auto discovery of local aliases
   based on reverse DNS on IPs (default on) */
auto_aliases=no

check_via=no    # (cmd. line: -v)
dns=no          # (cmd. line: -r)
rev_dns=no      # (cmd. line: -R)
tcp_accept_no_cl=yes

#!define SMS_3GPP 1
#!define SMS_TEXT 2

alias=SERVER

# ------------------ module loading ----------------------------------
mpath="/root/kamailio-devel/lib64/kamailio/modules"
# (we try both the lib64 and the lib directory)

loadmodule "tm"
loadmodule "tmx"
loadmodule "smsops"
loadmodule "xlog"
loadmodule "maxfwd"
loadmodule "textops"
loadmodule "sl"
loadmodule "sanity"
loadmodule "siputils"
loadmodule "pv"
loadmodule "uac"
loadmodule "xhttp"
loadmodule "utils"
loadmodule "json"
loadmodule "enum"
loadmodule "db_mysql"
loadmodule "dialplan"
loadmodule "sqlops"
loadmodule "htable"
loadmodule "rtimer"
loadmodule "usrloc"
loadmodule "registrar"
loadmodule "pua"
loadmodule "pua_reginfo"

modparam("sqlops","sqlcon","sms=>mysql://root@localhost/smsc")
modparam("dialplan|pua", "db_url", "mysql://root@localhost/kamailio")
modparam("uac","restore_mode","none")
modparam("enum", "domain_suffix", "DOMAIN.")

modparam("htable", "htable", "publish_sent=>size=8;autoexpire=SUBSCRIBE_EXPIRE")

# time interval set to 3 seconds
modparam("rtimer", "timer", "name=sms;interval=3;mode=1;")
modparam("rtimer", "exec", "timer=sms;route=SMS_WORKER")

modparam("pua_reginfo", "server_address", "sip:reginfo@DOMAIN")
modparam("pua_reginfo", "publish_reginfo", 0)

####### Routing Logic ########
# Main SIP request routing logic
# - processing of any incoming SIP request starts with this route

route {
	xlog("L_DBG", "$rm ($fu ($si:$sp) to $tu, $ci)\n");

	# per request initial checks
	route(REQINIT);

	if (is_method("NOTIFY")) {
		route(NOTIFY);
		send_reply("202", "Accepted");
		exit;
	}

	if (!is_method("MESSAGE")) {
		append_to_reply("Allow: MESSAGE,NOTIFY\r\n");
		send_reply("405", "Method not allowed");
		exit;
	}

	if ($cT == "application/vnd.3gpp.sms") {
		route(SMS_FROM_3GPP);
	} else if ($cT == "text/plain") {
		route(SMS_FROM_SIP);
	} else {
		send_reply("488", "Content-Type not supported");
		exit;
	}
}

######################################################################
# Helper routes (Basic-Checks, NAT-Handling/RTP-Control, XML-RPC)
######################################################################
# Per SIP request initial checks
route[REQINIT] {
	# Trace this message

	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;
	}

	# Reply to OPTIONS:
	if (is_method("OPTIONS") && (uri==myself)) {
		options_reply();
		exit;
	}	

	# Ignore Re-Transmits:
	if (t_lookup_request()) {
		exit;
	}
}

######################################################################
# SMS from VoLTE Handsets
######################################################################
route[SMS_FROM_3GPP] {
#!ifdef WITH_DEBUG
	xlog("3GPP-SMS: $rm ($fu ($si:$sp) to $tu, $ci)\n");
	xlog("SMS for $tpdu(destination) \"$tpdu(payload)\" (Valid: $tpdu(validity) )\n");
#!endif
	send_reply("202", "Accepted");

	if (isRPDATA()) {
		$uac_req(method) = "MESSAGE";
		$uac_req(ruri) = $ai;
		$uac_req(furi) = $ai;
		$uac_req(turi) = $ai;
		$uac_req(hdrs) = "Content-Type: application/vnd.3gpp.sms\r\nRequest-Disposition: fork, parallel\r\nAccept-Contact: *;+g.3gpp.smsip;require;explicit\r\n";
		$uac_req(body) = $smsack;
		uac_req_send();


		$avp(from) = $(ai{uri.user});
		$avp(to) = $tpdu(destination);
		# Translate "To":
		dp_translate("1", "$avp(to)/$avp(to)");
			
		$avp(text) = $tpdu(payload);
#!ifdef WITH_DEBUG
		xlog("SMS from 3GPP/VoLTE\n");
		xlog("-------------------------------------\n");
		xlog("FROM $avp(from)\n");
		xlog("TO   $avp(to)\n");
		xlog("TEXT $avp(text)\n");
#!endif
		route(SMS);
	}

	exit;
}

######################################################################
# SMS from OTT Handsets
######################################################################
route[SMS_FROM_SIP] {
	send_reply("200", "OK");

	$avp(from) = $(ai{uri.user});
	# Translate "To":
	$avp(to) = $tU;
	dp_translate("1", "$avp(to)/$avp(to)");
	$avp(text) = $rb;
#!ifdef WITH_DEBUG
	xlog("SMS from SIP/OTT\n");
	xlog("-------------------------------------\n");
	xlog("FROM $avp(from)\n");
	xlog("TO   $avp(to)\n");
	xlog("TEXT $avp(text)\n");
#!endif
	route(SMS);

	exit;
}


######################################################################
# SMS from other networks
######################################################################
event_route[xhttp:request] {
	if ($(hu{url.querystring}{s.len}) > 0) {
		$avp(from) = $(hu{url.querystring}{param.value,msisdn,&});
		$avp(to) = $(hu{url.querystring}{param.value,to,&});
		$avp(text) = $(hu{url.querystring}{param.value,text,&}{s.replace,+,%20}{s.unescape.user});
		$avp(from_outbound) = 1;
#!ifdef WITH_DEBUG
		xlog("SMS from Outbound ($hu)\n");
		xlog("-------------------------------------\n");
		xlog("FROM $avp(from)\n");
		xlog("TO   $avp(to)\n");
		xlog("TEXT $avp(text)\n");
#!endif
	if ($avp(to) == "491771782261")
		$avp(to) = "494046895124"; 
	if ($avp(to) == "491771782319") 
		$avp(to) = "494034927220";

		route(SMS);
	}

	xhttp_reply("200", "OK", "text/html", "<html><body>OK - [$si:$sp]</body></html>");
}

######################################################################
# SMS to VoLTE Handsets
######################################################################
route[SMS_TO_3GPP] {
#!ifdef WITH_DEBUG
	xlog("SMS to 3GPP/VoLTE\n");
	xlog("-------------------------------------\n");
	xlog("FROM $avp(from)\n");
	xlog("TO   $avp(to)\n");
	xlog("TEXT $avp(text)\n");
#!endif

	// Construct a new SMS-Body:
	$rpdata(all) = $null;
	$rpdata(type) = 1; // RP-DATA: Network to UE
	$rpdata(reference) = $avp(id);
	$rpdata(originator) = $avp(from);
	$tpdu(type) = 4; // SMS-Deliver
	$tpdu(origen) = $avp(from); // The Destination becomes the originator of the SMS
	$tpdu(payload) = $avp(text);

	$uac_req(method) = "MESSAGE";
	$uac_req(ruri) = "sip:"+$avp(to)+"@"+DOMAIN;
	$uac_req(furi) = "sip:"+$avp(to)+"@"+DOMAIN;
	$uac_req(turi) = "sip:"+$avp(to)+"@"+DOMAIN;
	$uac_req(hdrs) = "Content-Type: application/vnd.3gpp.sms\r\nRequest-Disposition: fork, parallel\r\nAccept-Contact: *;+g.3gpp.smsip;require;explicit\r\nX-MSG-ID: "+$avp(id)+"\r\n";
	$uac_req(body) = $smsbody;
	$uac_req(evroute)=1;
	uac_req_send();
	sql_query("sms", "delete from messages where id=$avp(id);");
}

######################################################################
# SMS to OTT-Handsets
######################################################################
route[SMS_TO_SIP] {
#!ifdef WITH_DEBUG
	xlog("SMS to SIP/OTT\n");
	xlog("-------------------------------------\n");
	xlog("FROM $avp(from)\n");
	xlog("TO   $avp(to)\n");
	xlog("TEXT $avp(text)\n");
#!endif

	$uac_req(method) = "MESSAGE";
	$uac_req(ruri) = "sip:"+$avp(to)+"@"+DOMAIN;
	$uac_req(furi) = "sip:+"+$avp(from)+"@"+DOMAIN;
	$uac_req(turi) = "sip:"+$avp(to)+"@"+DOMAIN;
	$uac_req(hdrs) = "Content-Type: text/plain\r\nX-MSG-ID: "+$avp(id)+"\r\n";
	$uac_req(evroute)=1;
	$uac_req(body) = $avp(text);

	uac_req_send();
}

######################################################################
# SMS to Outbound
######################################################################
route[SMS_TO_OUTBOUND] {
#!ifdef WITH_DEBUG
	xlog("SMS to Outbound\n");
	xlog("-------------------------------------\n");
	xlog("FROM $avp(from)\n");
	xlog("TO   $avp(to)\n");
	xlog("TEXT $avp(text)\n");
#!endif
	if ($avp(from_outbound) == 1) {
		xlog("Not sending: FROM and TO Outbound!\n");
		return 1;
		exit;
	}
	if ($avp(from) == "494046895124")
		$avp(from) = "491771782261";
	if ($avp(from) == "494034927220")
		$avp(from) = "491771782319";

	http_query("https://rest.nexmo.com/sms/json?api_key=NEXMO_APIKEY&api_secret=NEXMO_APISECRET&from=$avp(from)&to=$avp(to)&text=$(avp(text){s.escape.user})", "$var(result)");
	if ($retcode != 200) return -1;
	json_get_field("$var(result)", "messages", "$var(messages)");
	json_get_field("$var(messages)", "status", "$var(status)");
	if ($var(status) != 0) return -1;	
	return 1;
}

######################################################################
# SMS Handling
######################################################################
route[SMS] {
#!ifdef WITH_DEBUG
	xlog("SMS-Task\n");
	xlog("-------------------------------------\n");
	xlog("FROM $avp(from)\n");
	xlog("TO   $avp(to)\n");
	xlog("TEXT $avp(text)\n");
#!endif

	# Query ENUM: Local number?
	$var(enum) = "+"+$avp(to);
	if (!enum_pv_query("$var(enum)")) {
		route(SMS_TO_OUTBOUND);
		return $retcode;
	}
	if (sql_query("sms", "insert into messages (caller, callee, text, valid) values ('$(avp(from){s.escape.common})', '$(avp(to){s.escape.common})', '$(avp(text){s.escape.common})', now());"))
		return 1;
	else
		return -1;
}

######################################################################
# SMS Handling
######################################################################
route[SMS_WORKER] {
	sql_query("sms", "select id, caller, callee, text from messages;", "q");
	if ($dbr(q=>rows) > 0) {
		$var(i) = 0;
		while ($var(i) < $dbr(q=>rows)) {
			$avp(id) = $dbr(q=>[$var(i),0]);
			$avp(from) = $dbr(q=>[$var(i),1]);
			$avp(to) = $dbr(q=>[$var(i),2]);
			$avp(text) = $dbr(q=>[$var(i),3]);

			route(SEND_SMS);
#!ifdef WITH_DEBUG
			xlog("ID   $avp(id)\n");
			xlog("FROM $avp(from)\n");
			xlog("TO   $avp(to)\n");
			xlog("TEXT $avp(text)\n");
#!endif
			$var(i) = $var(i) + 1;
		}
	}
	sql_result_free("q");
}

route[NOTIFY] {
	if (has_body("application/reginfo+xml")) {
		if (reginfo_handle_notify("location"))
			send_reply("202", "Accepted");
	} else {
		send_reply("503", "Invalid Content-Type");
	}
	exit;
}

route[SEND_SMS] {
	$var(uri) = "sip:"+$avp(to)+"@"+DOMAIN;
	
	if (reg_fetch_contacts("location", "$var(uri)", "caller")) {
		$var(j) = 0;
		$var(is3gpp) = 0;
		while($var(j) < $(ulc(caller=>count))) {
			$var(k) = 0;
			while($var(k) < $(ulc(caller=>addr)[$var(j)]{param.count})) {
				if ($(ulc(caller=>addr)[$var(j)]{param.name,$var(k)}) == "+g.3gpp.smsip")
					$var(is3gpp) = 1;
				$var(k) = $var(k) + 1;
			}
			if ($var(is3gpp) == 1)
				route(SMS_TO_3GPP);
			else
				route(SMS_TO_SIP);		
			$var(j) = $var(j) + 1;
		}
	} else {
		if ($sht(publish_sent=>$var(uri)) == $null) {
			reginfo_subscribe("$var(uri)", "SUBSCRIBE_EXPIRE");
			$sht(publish_sent=>$var(uri)) = 1;
		}
	}
}

event_route [tm:local-request] {
	if (is_method("SUBSCRIBE")) {
		append_hf("P-Asserted-Identity: $ru\r\n");
	}
}

event_route[uac:reply] {
	if (($uac_req(evtype) == 1) && ($uac_req(evcode) == 200)) {
		$var(msgid) = $(uac_req(hdrs){line.sw,X-MSG-ID:}{s.substr,10,0}{s.int});
		sql_query("sms", "delete from messages where id=$var(msgid);");
	}
}

