#!KAMAILIO
#
# This config file implements an Online-Charging-Server
#     - 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/ .
#

import_file "ocs.cfg"

#!ifndef GRANT
#!define GRANT 600
#!endif
#!ifndef MULTIPLIER
#!define MULTIPLIER 10000
#!endif

##!define WITH_DEBUG

#!ifdef WITH_XMLRPC
listen=tcp:127.0.0.1:5080
#!endif


####### Defined Values #########
# *** Value defines - IDs used later in config

# - flags
#	FLT_ - per transaction (message) flags
#	FLB_ - per branch flags


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

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

memdbg=5
memlog=5

log_stderror=no
sip_warning=no

rundir="/run/kamailio_ocs"

user_agent_header="User-Agent: TelcoSuite OCS"
server_header="Server: TelcoSuite OCS"

/* 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)

# Do SRV-Loadbalancing:
dns_srv_lb=no
# Always: Also try IPv6:
dns_try_ipv6=no
# Always prefer IPv6:
dns_cache_flags=6
# DNS-Based failover
use_dns_failover = on
# Query NAPTR-Records as well:
dns_try_naptr=no

#!ifdef WITH_XMLRPC
tcp_accept_no_cl=yes
tcp_rd_buf_size=16384
tcp_children=3
#!else
disable_tcp=yes
#!endif

children=3
log_name="[Charging]"

system.shutdownmode = 0 desc "System shutdown mode"
system.service = "Online-Charging-Server" desc "Function of this server"

# ------------------ module loading ----------------------------------
mpath="/usr/lib64/kamailio/modules_k/:/usr/lib64/kamailio/modules/:/usr/local/lib/kamailio/modules/"
# (we try both the lib64 and the lib directory)

loadmodule "cdp"
loadmodule "cdp_avp"
loadmodule "ims_ocs"
loadmodule "xlog"
loadmodule "pv"
loadmodule "sqlops"
loadmodule "cfgutils"
loadmodule "db_mysql"
loadmodule "textops"
loadmodule "ctl"
loadmodule "sl"
loadmodule "tm"
loadmodule "kex"
loadmodule "corex"
loadmodule "cfg_rpc"
loadmodule "mi_rpc"

# ----- db_cluster params -----

# ----- ctl params -----
modparam("ctl", "binrpc", "unix:/run/kamailio_ocs/kamailio_ctl")

#!ifdef DB_URL2
loadmodule "db_cluster"
# ----- db_cluster params -----
modparam("db_cluster", "connection", DB_URL)
modparam("db_cluster", "connection", DB_URL2)
modparam("db_cluster", "cluster", "cluster1=>con1=2s2s;con2=1s1s")
#!endif

#!ifdef WITH_DEBUG
loadmodule "debugger"
modparam("debugger", "mod_hash_size", 5)
modparam("debugger", "mod_level_mode", 1)
modparam("debugger", "mod_level", "ims_ocs=3")
#!endif

#!ifdef WITH_DMQ
loadmodule "dmq"
# ----- dmq params -----
modparam("dmq", "server_address", DMQ_SERVER_ADDRESS)
modparam("dmq", "notification_address", DMQ_NOTIFICATION_ADDRESS)
#!endif

loadmodule "htable"
# ----- htable params -----

#!ifdef WITH_DMQ
modparam("htable", "enable_dmq", 1)
# Per User:
modparam("htable", "htable", "user=>size=16;autoexpire=14400;dmqreplicate=1")
# Per Call:
modparam("htable", "htable", "call=>size=16;autoexpire=14400;dmqreplicate=1")
# Association: Calls/User
modparam("htable", "htable", "cid=>size=8;autoexpire=14400;dmqreplicate=1")
#!else
modparam("htable", "enable_dmq", 0)
# Per User:
modparam("htable", "htable", "user=>size=16;autoexpire=14400")
# Per Call:
modparam("htable", "htable", "call=>size=16;autoexpire=14400")
# Association: Calls/User
modparam("htable", "htable", "cid=>size=8;autoexpire=14400")
#!endif

# ----- cdp params -----
modparam("cdp","config_file","/etc/kamailio_ocs/ocs.xml")

# ----- sqlops params -----
#!ifdef DB_URL2
modparam("sqlops","sqlcon", "hss_db=>cluster://cluster1")
#!else
modparam("sqlops","sqlcon", DB_URL)
#!endif

#!ifdef WITH_XMLRPC
loadmodule "xmlrpc"
# ----- xmlrpc params -----
modparam("xmlrpc", "route", "XMLRPC");
modparam("xmlrpc", "url_match", "^/RPC")
#!endif

route {
#!ifdef WITH_DMQ
	if(is_method("KDMQ")) {
                dmq_handle_message();
        }
#!endif
	drop();
	exit;
}

# XMLRPC routing
#!ifdef WITH_XMLRPC
route[XMLRPC] {
	# allow XMLRPC from localhost
	if ((method=="POST" || method=="GET") && ((src_ip==127.0.0.1)
#!ifdef XMLRPC_WHITELIST_1
 || (src_ip == XMLRPC_WHITELIST_1)
#!endif
#!ifdef XMLRPC_WHITELIST_2
 || (src_ip == XMLRPC_WHITELIST_2)
#!endif
#!ifdef XMLRPC_WHITELIST_3
 || (src_ip == XMLRPC_WHITELIST_3)
#!endif
        )) {
		# 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

event_route[ocs:ccr-orig] {
#!ifdef WITH_DEBUG
	xlog("ORIG: $rm $fu => $ru\n\n");
#!endif
	$var(result) = 5012;
	$var(granted) = 0;
	$var(final) = 0;
	$var(multiplier) = MULTIPLIER;
	$var(id) = 0;
	
	if (is_method("INVITE")) {
		if ($fU =~ "^\+[1-9][0-9]+$")
			$var(from) = $(fU{s.substr,1,0});
		else
			$var(from) = $fU;

		sql_pvquery("hss_db", "SELECT impi.id AS impi_id, imsu.id, CAST(imsu.credit*$var(multiplier) as UNSIGNED), imsu.rate_plan_id, imsu.payment_method, imsu.max_concurrent_calls FROM impu LEFT JOIN impi_impu ON impu.id = impi_impu.id_impu LEFT JOIN impi ON impi.id = impi_impu.id_impi LEFT JOIN imsu ON impi.id_imsu = imsu.id WHERE ((impu.type = 0 AND impu.identity='sip:$var(from)@$fd') OR (impu.type = 0 AND impu.identity='sip:+$var(from)@$fd') OR (impu.type = 0 AND impu.identity='tel:$var(from)') OR (impu.type = 0 AND impu.identity='tel:+$var(from)') OR (impu.type = 0 AND impu.identity='tel:+$var(from)') OR (impu.type = 3 AND 'sip:$var(from)@$fd' REGEXP impu.identity) OR (impu.type = 3 AND 'tel:$var(from)' REGEXP impu.identity) OR (impu.type = 3 AND 'tel:+$var(from)' REGEXP impu.identity)) limit 1;", "$var(impi_id),$var(id),$var(credit),$var(ratecard),$var(payment),$var(concurrent_calls)");
		if ($retcode != 1) {
			# User not found, send DIAMETER_USER_UNKNOWN
			$var(result) = 5030;
		} else {
			if (($var(impi_id) == 0) || ($var(id) == 0)) {
				# User not found, send DIAMETER_USER_UNKNOWN
				$var(result) = 5030;
			} else {
				# Remove "+"
				if ($rU =~ "^\+[1-9][0-9]+$")
					strip(1);

				$var(destination_impu_id) = 0;
				sql_pvquery("hss_db", "SELECT id FROM impu WHERE ((type = 0 AND identity='$ru') OR (type = 0 AND identity='tel:$rU') OR (type = 3 AND '$ru' REGEXP identity) OR (type = 3 AND 'tel:$rU' REGEXP identity)) limit 1;", "$var(destination_impu_id)");
				if ($retcode != 1) {
					$var(destination_impu_id) = 0;
				}


				sht_lock("user=>$var(id)::current_calls");
				if ($sht(user=>$var(id)::current_calls) != $null)
					$sht(user=>$var(id)::current_calls) = $(sht(user=>$var(id)::current_calls){s.int});
				sht_unlock("user=>$var(id)::current_calls");

				sht_lock("user=>$var(id)::reserved_credit");
				if ($sht(user=>$var(id)::reserved_credit) != $null)
					$sht(user=>$var(id)::reserved_credit) = $(sht(user=>$var(id)::reserved_credit){s.int});
				sht_unlock("user=>$var(id)::reserved_credit");


#!ifdef WITH_DEBUG
				xlog("IMPI:             $fu ($var(impi_id))\n");
				xlog("IMSU-ID:          $var(id)\n");
				xlog("Credit:           $var(credit) (Reserved: $sht(user=>$var(id)::reserved_credit))\n");
				xlog("Rate-Card:        $var(ratecard)\n");
				xlog("Payment:          $var(payment)\n");
				xlog("Concurrent-Calls: $var(concurrent_calls), currently ($sht(user=>$var(id)::current_calls))\n");
				if ($var(destination_impu_id) > 0) {
					xlog(">>>>>>>>>>>>>>>>>>>>>> DESTINATION ($ru) IS ONNET ($var(destination_impu_id))!\n");
				}
#!endif
				if (($sht(user=>$var(id)::current_calls) != $null) && ($var(concurrent_calls) >= 0)) {
					if ($sht(user=>$var(id)::current_calls) >= $var(concurrent_calls)) {
						$var(result) = 5006; # DIAMETER_RESOURCES_EXCEEDED
					} else {
						$sht(cid=>$ci) = $var(id);
						$var(result) = 2001;
					}
				} else {
					sht_lock("user=>$var(id)::current_calls");
					if ($sht(user=>$var(id)::current_calls) == $null) {
						$sht(user=>$var(id)::current_calls) = 0;
					}
					sht_unlock("user=>$var(id)::current_calls");
					$sht(cid=>$ci) = $var(id);
					$var(result) = 2001;
				}
			}
			if ($var(result) == "2001") {
				$var(result) = 5031; # DIAMETER_RATING_FAILED
				if ($var(destination_impu_id) > 0) {
					sql_pvquery("hss_db", "SELECT id, description, country_name, iso3166, type, CAST(rate_init*$var(multiplier) as UNSIGNED), rate_init_interval, CAST(rate_follow*$var(multiplier) as UNSIGNED), rate_follow_interval, blocked, max_call_duration FROM call_rates WHERE rate_plan_id = $var(ratecard) AND destination_prefix = 'ONNET' limit 1;", "$var(rate_id),$var(rate_name),$var(country),$var(iso),$var(type),$var(rate_init),$var(rate_init_interval),$var(rate_follow),$var(rate_follow_interval),$var(blocked),$var(maxduration)");
					if ($retcode == 1) {
						$var(result) = 2001;
					}
				}
				if ($var(result) == 5031) {
					if ($rU =~ "^[1-9][0-9]+$") {
						sql_pvquery("hss_db", "SELECT id, description, country_name, iso3166, type, CAST(rate_init*$var(multiplier) as UNSIGNED), rate_init_interval, CAST(rate_follow*$var(multiplier) as UNSIGNED), rate_follow_interval, blocked, max_call_duration FROM call_rates WHERE rate_plan_id = $var(ratecard) AND destination_prefix = SUBSTRING('$rU', 1, LENGTH(destination_prefix)) ORDER BY LENGTH(destination_prefix) DESC limit 1;", "$var(rate_id),$var(rate_name),$var(country),$var(iso),$var(type),$var(rate_init),$var(rate_init_interval),$var(rate_follow),$var(rate_follow_interval),$var(blocked),$var(maxduration)");
						if ($retcode == 1) {
							$var(result) = 2001;
						} else {
							$var(result) = 5031; # DIAMETER_RATING_FAILED
						}
					}
				}
				if ($var(result) == "2001") {
#!ifdef WITH_DEBUG
					xlog("Rate-ID:      $var(rate_id)\n"); 
					xlog("Country:      $var(iso): $var(country) ($var(type))\n"); 
					xlog("Destination:  $var(rate_name) ($rU)\n"); 
					xlog("Rate:         $var(rate_init)/$var(rate_init_interval), $var(rate_follow)/$var(rate_follow_interval)\n"); 
					xlog("Blocked:      $var(blocked)\n");
					xlog("Max-Duration: $var(maxduration)\n");
#!endif
					if ($var(blocked) == 1) {
						$var(result) = 4010; # DIAMETER_END_USER_SERVICE_DENIED
					} else {
						$var(used_credit) = 0;
						if ($var(payment) == 1) {
							# Prepaid
							if ($sht(user=>$var(id)::reserved_credit) != $null)
								$var(current_credit) = $var(credit) - $sht(user=>$var(id)::reserved_credit);
							else
								$var(current_credit) = $var(credit);
							if ($var(rate_init) > $var(current_credit)) {
								$var(result) = 4012; # DIAMETER_CREDIT_LIMIT_REACHED
								$var(granted) = 0;
								$var(used_credit) = 0;
							} else {
								$var(used_credit) = $var(rate_init);
								$var(granted) = $var(rate_init_interval);
								if (($var(rate_follow) == 0) || ($var(rate_follow_interval) <= 0)) {
									$var(granted) = GRANT;
								} else {
									$var(used_credit) = $var(rate_init);
									while ($var(granted) < GRANT) {
										if ($var(rate_follow) > ($var(current_credit) - $var(used_credit))) {
											$var(final) = 1;
											break;
										}
										$var(granted) = $var(granted) + $var(rate_follow_interval);
										$var(used_credit) = $var(used_credit) + $var(rate_follow);
									}
									# If it's not sufficient for the next unit, it's the final unit:
									if ($var(rate_follow) > ($var(current_credit) - $var(used_credit))) {
										$var(final) = 1;
									}
								}
							}
						} else {
							# Postpaid
							$var(granted) = $var(rate_init_interval);
							$var(used_credit) = $var(rate_init);
							if (($var(rate_follow) == 0) || ($var(rate_follow_interval) <= 0)) {
								$var(granted) = GRANT;
							} else {
								while ($var(granted) < GRANT) {
									$var(granted) = $var(granted) + $var(rate_follow_interval);
									$var(used_credit) = $var(used_credit) + $var(rate_follow);
								}
							}
						}
						if ($var(result) == "2001") {
							$sht(call=>$ci::follow_rate) = $(var(rate_follow){s.int});
							$sht(call=>$ci::follow_unit) = $(var(rate_follow_interval){s.int});
							$sht(call=>$ci::initial_rate) = $(var(rate_init){s.int});
							$sht(call=>$ci::initial_unit) = $(var(rate_init_interval){s.int});

							$sht(call=>$ci::reserved_credit) = $(var(used_credit){s.int});
							$sht(call=>$ci::used_unit) = 0;
							$sht(call=>$ci::granted_unit) = $var(granted);

							sht_lock("user=>$var(id)::credit");
							if ($var(payment) == 1)
								$sht(user=>$var(id)::credit) = $var(credit);
							else
								$sht(user=>$var(id)::credit) = -1;
							sht_unlock("user=>$var(id)::credit");

							sht_lock("user=>$var(id)::reserved_credit");
							if ($sht(user=>$var(id)::reserved_credit) == $null) {
								$sht(user=>$var(id)::reserved_credit) = $(var(used_credit){s.int});						
							} else {
								$sht(user=>$var(id)::reserved_credit) = $sht(user=>$var(id)::reserved_credit) + $(var(used_credit){s.int});						
							}
							sht_unlock("user=>$var(id)::reserved_credit");

							$sht(call=>$ci::max_units) = $null;
							if ($var(maxduration) > 0) {
								$sht(call=>$ci::max_units) = $var(maxduration);
								if ($var(maxduration) < $var(granted)) {
									$var(granted) = $var(maxduration);
									$var(final) = 1;
								}
							}
						}
#!ifdef WITH_DEBUG
						xlog("Granted $var(granted)s, reserved new credit $var(used_credit)\n");
#!endif
					}
				} else {
					$var(result) = 5031; # DIAMETER_RATING_FAILED
				}
			}
		}
		if ($var(result) == "2001") {
			sht_lock("user=>$var(id)::current_calls");
			$sht(user=>$var(id)::current_calls) = $sht(user=>$var(id)::current_calls) + 1;
			sht_unlock("user=>$var(id)::current_calls");
#!ifdef WITH_DEBUG
			xlog("User has now $sht(user=>$var(id)::current_calls) Calls, allowed is $var(concurrent_calls)\n");
			xlog("INSERT INTO tb_processed_cdrs (imsu_id, impi_id, dest_e164, callid, start, call_rate) VALUES ($var(id), $var(impi_id), '$fU', '$rU', '$ci', now(), $var(id), $var(rate_id))\n");
#!endif
			sql_query("hss_db", "INSERT INTO tb_processed_cdrs (imsu_id, impi_id, src_e164, dest_e164, callid, start, call_rate) VALUES ($var(id), $var(impi_id), '$fU', '$rU', '$ci', now(), $var(rate_id));");
			if ($retcode == -1) {
				$var(result) = 5012;
			}
		}
	} else if (is_method("UPDATE")) {
		$var(id) = $sht(cid=>$ci);
		$var(used_credit) = 0;
		$var(granted) = 0;
		$var(credit) = $sht(user=>$var(id)::credit);
		if ($var(credit) >= 0) {
			# Prepaid
			if (($sht(call=>$ci::follow_rate) == 0) || ($sht(call=>$ci::follow_unit) <= 0)) {
				$var(granted) = GRANT;
			} else {
				$var(current_credit) = $sht(user=>$var(id)::credit) - $sht(user=>$var(id)::reserved_credit);
				while ($var(granted) < GRANT) {
					if ($sht(call=>$ci::follow_rate) > ($var(current_credit) - $var(used_credit))) {
						$var(final) = 1;
						break;
					}
					$var(granted) = $var(granted) + $sht(call=>$ci::follow_unit);
					$var(used_credit) = $var(used_credit) + $sht(call=>$ci::follow_rate);
				}
				# If it's not sufficient for the next unit, it's the final unit:
				if ($sht(call=>$ci::follow_rate) > ($var(current_credit) - $var(used_credit))) {
					$var(final) = 1;
				}
			}
		} else {
			# Postpaid
			if (($sht(call=>$ci::follow_rate) == 0) || ($sht(call=>$ci::follow_unit) <= 0)) {
				$var(granted) = GRANT;
			} else {
				while ($var(granted) < GRANT) {
					$var(granted) = $var(granted) + $sht(call=>$ci::follow_unit);
					$var(used_credit) = $var(used_credit) + $sht(call=>$ci::follow_rate);
				}
			}
		}
		$sht(call=>$ci::used_unit) = $sht(call=>$ci::used_unit) + $(hdr(P-Used-Units){s.int});
		if ($sht(call=>$ci::max_units) > 0) {
			$var(cur_granted) = $sht(call=>$ci::used_unit) + $var(granted);
			$var(max_granted) = $sht(call=>$ci::max_units);
			if ($var(max_granted) > $var(cur_granted)) {
				$var(granted) = $sht(call=>$ci::max_units) - $sht(call=>$ci::used_unit);
				$var(final) = 1;
			}
		}
		if ($var(granted) <= 0) {
			$var(result) = 4012; # DIAMETER_CREDIT_LIMIT_REACHED
			$var(granted) = 0;		
		} else {
			$var(granted) = $var(granted) + ($sht(call=>$ci::granted_unit) - $(hdr(P-Used-Units){s.int}));
			$sht(call=>$ci::granted_unit) = $var(granted);
			$var(result) = 2001;
		}

		sht_lock("user=>$var(id)::reserved_credit");
		$sht(user=>$var(id)::reserved_credit) = $sht(user=>$var(id)::reserved_credit) + $var(used_credit);
		sht_unlock("user=>$var(id)::reserved_credit");

		$sht(call=>$ci::reserved_credit) = $sht(call=>$ci::reserved_credit) + $var(used_credit);
#!ifdef WITH_DEBUG
		xlog("$var(result): Granted $var(granted)s (used $sht(call=>$ci::used_unit)), total granted $sht(call=>$ci::granted_unit), reserved credit $sht(user=>$var(id)::reserved_credit), new $var(used_credit)\n");
#!endif
	} else if (is_method("BYE")) {
		$var(id) = $sht(cid=>$ci);
#!ifdef WITH_DEBUG
		xlog("$ci: User $var(id) has $sht(user=>$var(id)::current_calls) Calls, decrementing\n");
#!endif
		if ($var(id) != $null) {
			sht_lock("user=>$var(id)::current_calls");
			if ($sht(user=>$var(id)::current_calls) <= 0) {
				$sht(user=>$var(id)::current_calls) = $null;
			} else {
				$sht(user=>$var(id)::current_calls) = $sht(user=>$var(id)::current_calls) - 1;
			}
			sht_unlock("user=>$var(id)::current_calls");
		}
		$var(used) = $sht(call=>$ci::used_unit) + $(hdr(P-Used-Units){s.int});
		$var(credit) = $sht(user=>$var(id)::credit);
		if ($var(used) > 0) {
			$var(used_credit) = $sht(call=>$ci::initial_rate);
			$var(used) = $var(used) - $sht(call=>$ci::initial_unit);
			if (($sht(call=>$ci::follow_rate) != 0) && ($sht(call=>$ci::follow_unit) != 0) && ($var(used) > 0)) {
				while ($var(used) > 0) {
					$var(used) = $var(used) - $sht(call=>$ci::follow_unit);
					$var(used_credit) = $var(used_credit) + $sht(call=>$ci::follow_rate);
				}
			} else {
				$var(used) = $sht(call=>$ci::used_unit);
			}
		} else {
			$var(used_credit) = 0;
			$var(used) = 0;
		}
		if (($var(used_credit) > 0) && ($var(credit) > 0)) {
			sht_lock("user=>$var(id)::credit");
			$sht(user=>$var(id)::credit) = $sht(user=>$var(id)::credit) - $var(used_credit);
			sht_unlock("user=>$var(id)::credit");
			sql_query("hss_db", "UPDATE imsu SET credit=credit - ($var(used_credit)/$var(multiplier)) WHERE id=$var(id);");
			if ($retcode == -1) {
				$var(result) = 5012;
			}
		}
		$var(duration) = $sht(call=>$ci::used_unit) + $(hdr(P-Used-Units){s.int});
#!ifdef WITH_DEBUG
		xlog("Call-Cost: $var(used_credit), Duration $var(duration)\n");
		xlog("Credit: $sht(user=>$var(id)::credit)\n");
#!endif
		sht_lock("user=>$var(id)::reserved_credit");
		$sht(user=>$var(id)::reserved_credit) = $sht(user=>$var(id)::reserved_credit) - $sht(call=>$ci::reserved_credit);
		sht_unlock("user=>$var(id)::reserved_credit");

		sql_query("hss_db", "UPDATE tb_processed_cdrs SET connect=DATE_SUB(now(), interval $var(duration) second), stop=now(), duration=$var(duration), price=($var(used_credit)/$var(multiplier)) WHERE callid='$ci';");
		if ($retcode == -1) {
			$var(result) = 5012;
		}

		$var(result) = "2001";
	}

	ccr_result("$var(result)", "$var(granted)", "$var(final)");
}

event_route[ocs:ccr-term] {
	# For the terminating case, we only check the user existence and Line-Limit
	if ($fU =~ "^\+[1-9][0-9]+$")
		$var(from) = $(fU{s.substr,1,0});
	else
		$var(from) = $fU;

#!ifdef WITH_DEBUG
	xlog("TERM: $rm $var(from) => $ru\n\n");
#!endif
	$var(result) = "5012";
	$var(granted) = "0";
	$var(final) = "0";

	if (is_method("INVITE")) {
		sql_pvquery("hss_db", "SELECT impi.id AS impi_id, imsu.id, imsu.max_concurrent_calls FROM impu LEFT JOIN impi_impu ON impu.id = impi_impu.id_impu LEFT JOIN impi ON impi.id = impi_impu.id_impi LEFT JOIN imsu ON impi.id_imsu = imsu.id WHERE ((impu.type = 0 AND impu.identity='sip:$var(from)@$fd') OR (impu.type = 0 AND impu.identity='sip:+$var(from)@$fd') OR (impu.type = 0 AND impu.identity='tel:$var(from)') OR (impu.type = 0 AND impu.identity='tel:+$var(from)') OR (impu.type = 3 AND 'sip:$var(from)@$fd' REGEXP impu.identity) OR (impu.type = 3 AND 'sip:+$var(from)@$fd' REGEXP impu.identity) OR (impu.type = 3 AND 'tel:$var(from)' REGEXP impu.identity) OR (impu.type = 3 AND 'tel:+$var(from)' REGEXP impu.identity)) limit 1;", "$var(impi_id),$var(id),$var(concurrent_calls)");
		if ($retcode != 1) {
			# User not found, send DIAMETER_USER_UNKNOWN
			$var(result) = 5030;
		} else {
#!ifdef WITH_DEBUG
			xlog("IMPI:             $fu ($var(impi_id))\n");
			xlog("IMSU-ID:          $var(id)\n");
			xlog("Concurrent-Calls: $var(concurrent_calls), currently ($sht(user=>$var(id)::current_calls))\n");
#!endif

			if (($sht(user=>$var(id)::current_calls) != $null) && ($var(concurrent_calls) >= 0)) {
				if ($sht(user=>$var(id)::current_calls) >= $var(concurrent_calls)) {
					$var(result) = 5006; # DIAMETER_RESOURCES_EXCEEDED
				} else {
					$sht(cid=>$ci) = $var(id);
					sht_lock("user=>$var(id)::current_calls");
					$sht(user=>$var(id)::current_calls) = $sht(user=>$var(id)::current_calls) + 1;
					sht_unlock("user=>$var(id)::current_calls");
					$var(result) = 2001;
					$var(granted) = GRANT;
				}
			} else {
				sht_lock("user=>$var(id)::current_calls");
				if ($sht(user=>$var(id)::current_calls) == $null) {
					$sht(user=>$var(id)::current_calls) = 1;
				} else {
					$sht(user=>$var(id)::current_calls) = $sht(user=>$var(id)::current_calls) + 1;
				}
				sht_unlock("user=>$var(id)::current_calls");
				$sht(cid=>$ci) = $var(id);
				$var(result) = 2001;
				$var(granted) = GRANT;
			}
		}
	} else if (is_method("UPDATE")) {
		$var(result) = 2001;
		$var(granted) = GRANT;
	} else if (is_method("BYE")) {
		$var(id) = $sht(cid=>$ci);
#!ifdef WITH_DEBUG
		xlog("$ci: User $var(id) has $sht(user=>$var(id)::current_calls) Calls, decrementing\n");
#!endif
		if ($var(id) != $null) {
			sht_lock("user=>$var(id)::current_calls");
			if ($sht(user=>$var(id)::current_calls) <= 0) {
				$sht(user=>$var(id)::current_calls) = $null;
			} else {
				$sht(user=>$var(id)::current_calls) = $sht(user=>$var(id)::current_calls) - 1;
			}
			sht_unlock("user=>$var(id)::current_calls");
		}
		$var(result) = 2001;
	}
	// xlog("$rm: $var(result): $var(granted) units...\n");

	ccr_result("$var(result)", "$var(granted)", "$var(final)");
}
