瀏覽代碼

Merge remote branch 'origin/master' into tmp/hpw_curl_improvements

Hugh Waite 9 年之前
父節點
當前提交
5ff788247f
共有 100 個文件被更改,包括 5618 次插入752 次删除
  1. 2 2
      Makefile.defs
  2. 2 2
      Makefile.groups
  3. 15 1
      dprint.c
  4. 2 0
      dprint.h
  5. 39 0
      events.c
  6. 6 0
      events.h
  7. 20 2
      forward.h
  8. 5 0
      ip_addr.h
  9. 15 1
      lib/srdb1/schema/uacreg.xml
  10. 2 0
      modules/auth_radius/authrad_mod.c
  11. 1 0
      modules/auth_radius/authrad_mod.h
  12. 5 0
      modules/auth_radius/doc/auth_radius.xml
  13. 17 0
      modules/auth_radius/doc/auth_radius_admin.xml
  14. 1 1
      modules/auth_radius/sterman.c
  15. 9 1
      modules/carrierroute/cr_fifo.c
  16. 2 0
      modules/cdp/routing.c
  17. 17 0
      modules/cfgt/Makefile
  18. 159 0
      modules/cfgt/README
  19. 45 0
      modules/cfgt/cfgt.c
  20. 40 0
      modules/cfgt/cfgt.h
  21. 797 0
      modules/cfgt/cfgt_int.c
  22. 67 0
      modules/cfgt/cfgt_int.h
  23. 389 0
      modules/cfgt/cfgt_json.c
  24. 38 0
      modules/cfgt/cfgt_json.h
  25. 112 0
      modules/cfgt/cfgt_mod.c
  26. 29 0
      modules/cfgt/cfgt_mod.h
  27. 4 0
      modules/cfgt/doc/Makefile
  28. 36 0
      modules/cfgt/doc/cfgt.xml
  29. 183 0
      modules/cfgt/doc/cfgt_admin.xml
  30. 0 24
      modules/cnxcc/cnxcc.c
  31. 24 3
      modules/cnxcc/cnxcc.h
  32. 37 0
      modules/crypto/Makefile
  33. 106 0
      modules/crypto/README
  34. 383 0
      modules/crypto/crypto_mod.c
  35. 4 0
      modules/crypto/doc/Makefile
  36. 37 0
      modules/crypto/doc/crypto.xml
  37. 107 0
      modules/crypto/doc/crypto_admin.xml
  38. 4 0
      modules/curl/TODO.txt
  39. 28 1
      modules/curl/curl_api.h
  40. 7 3
      modules/db_mysql/km_dbase.c
  41. 129 0
      modules/db_text/dbt_base.c
  42. 2 2
      modules/db_text/dbt_lib.c
  43. 1 1
      modules/db_text/dbt_raw_util.c
  44. 2 1
      modules/db_text/dbtext.c
  45. 6 0
      modules/db_text/dbtext.h
  46. 1 0
      modules/debugger/Makefile
  47. 31 14
      modules/debugger/README
  48. 148 12
      modules/debugger/debugger_api.c
  49. 1 0
      modules/debugger/debugger_api.h
  50. 472 154
      modules/debugger/debugger_mod.c
  51. 181 0
      modules/debugger/doc/debugger_admin.xml
  52. 1 0
      modules/dispatcher/README
  53. 13 0
      modules/dispatcher/dispatch.c
  54. 1 0
      modules/dispatcher/doc/dispatcher.cfg
  55. 7 4
      modules/dmq_usrloc/usrloc_sync.c
  56. 1 1
      modules/htable/htable.c
  57. 3 1
      modules/ims_auth/cxdx_mar.c
  58. 37 99
      modules/ims_charging/dialog.c
  59. 1 3
      modules/ims_charging/dialog.h
  60. 69 5
      modules/ims_charging/ims_ro.c
  61. 6 1
      modules/ims_charging/ims_ro.h
  62. 101 24
      modules/ims_charging/mod.c
  63. 1 1
      modules/ims_charging/ro_session_hash.c
  64. 8 1
      modules/ims_charging/ro_session_hash.h
  65. 21 0
      modules/ims_charging/ro_timer.c
  66. 1 1
      modules/ims_qos/rx_str.c
  67. 50 68
      modules/ims_registrar_scscf/registrar_notify.c
  68. 6 4
      modules/ims_registrar_scscf/registrar_notify.h
  69. 10 3
      modules/ims_usrloc_scscf/udomain.c
  70. 1 1
      modules/ims_usrloc_scscf/udomain.h
  71. 1 1
      modules/ims_usrloc_scscf/usrloc.h
  72. 3 3
      modules/matrix/README
  73. 6 6
      modules/matrix/doc/matrix_admin.xml
  74. 34 38
      modules/nat_traversal/nat_traversal.c
  75. 1 1
      modules/ndb_mongodb/README
  76. 1 1
      modules/ndb_mongodb/doc/ndb_mongodb_admin.xml
  77. 36 21
      modules/permissions/README
  78. 20 0
      modules/permissions/doc/permissions_admin.xml
  79. 3 0
      modules/permissions/hash.c
  80. 0 2
      modules/permissions/hash.h
  81. 3 0
      modules/permissions/permissions.c
  82. 17 5
      modules/presence/notify.c
  83. 1 1
      modules/presence/publish.c
  84. 1 0
      modules/presence_dialoginfo/add_events.c
  85. 36 2
      modules/presence_dialoginfo/notify_body.c
  86. 33 10
      modules/presence_xml/notify_body.c
  87. 6 1
      modules/registrar/reg_mod.c
  88. 122 3
      modules/rtpengine/doc/rtpengine_admin.xml
  89. 250 48
      modules/rtpengine/rtpengine.c
  90. 0 1
      modules/rtpengine/rtpengine.h
  91. 544 0
      modules/rtpengine/rtpengine_hash.c
  92. 40 0
      modules/rtpengine/rtpengine_hash.h
  93. 27 7
      modules/siptrace/README
  94. 24 0
      modules/siptrace/doc/siptrace_admin.xml
  95. 225 128
      modules/siptrace/siptrace.c
  96. 1 1
      modules/smsops/doc/smsops_admin.xml
  97. 19 19
      modules/statsd/lib_statsd.c
  98. 9 11
      modules/statsd/lib_statsd.h
  99. 45 0
      modules/tcpops/tcpops.c
  100. 2 0
      modules/tcpops/tcpops.h

+ 2 - 2
Makefile.defs

@@ -97,7 +97,7 @@ INSTALL_FLAVOUR=$(FLAVOUR)
 VERSION = 4
 VERSION = 4
 PATCHLEVEL = 4
 PATCHLEVEL = 4
 SUBLEVEL =  0
 SUBLEVEL =  0
-EXTRAVERSION = -dev7
+EXTRAVERSION = -dev8
 
 
 # memory manager switcher
 # memory manager switcher
 # 0 - f_malloc (fast malloc)
 # 0 - f_malloc (fast malloc)
@@ -1785,7 +1785,7 @@ ifeq ($(CC_NAME), suncc)
 endif
 endif
 	OLD_SOLARIS= $(shell echo "$(OSREL)" | \
 	OLD_SOLARIS= $(shell echo "$(OSREL)" | \
 				sed -e 's/^5\.[0-6][^0-9]*$$/yes/' )
 				sed -e 's/^5\.[0-6][^0-9]*$$/yes/' )
-	LIBS+= -L$(LOCALBASE)/lib -lxnet -lnsl 
+	LIBS+= -L$(LOCALBASE)/lib -lxnet -lsocket -lnsl 
 ifeq	($(OLD_SOLARIS), yes)
 ifeq	($(OLD_SOLARIS), yes)
 		LIBS+=-lposix4
 		LIBS+=-lposix4
 else
 else

+ 2 - 2
Makefile.groups

@@ -15,7 +15,7 @@ mod_list_basic=async auth benchmark blst cfg_rpc cfgutils corex counters \
 				   nat_traversal nathelper path pike pv ratelimit rr rtimer \
 				   nat_traversal nathelper path pike pv ratelimit rr rtimer \
 				   rtpproxy sanity sdpops siputils sl statistics textops \
 				   rtpproxy sanity sdpops siputils sl statistics textops \
 				   textopsx tm tmx topoh xlog rtpengine stun sipt tcpops \
 				   textopsx tm tmx topoh xlog rtpengine stun sipt tcpops \
-				   auth_xkeys smsops tsilo
+				   auth_xkeys smsops tsilo cfgt
 
 
 # - extra used modules, with no extra dependency
 # - extra used modules, with no extra dependency
 mod_list_extra=avp auth_diameter call_control dmq domainpolicy msrp pdb \
 mod_list_extra=avp auth_diameter call_control dmq domainpolicy msrp pdb \
@@ -90,7 +90,7 @@ mod_list_purple=purple
 mod_list_memcached=memcached
 mod_list_memcached=memcached
 
 
 # - modules depending on openssl library
 # - modules depending on openssl library
-mod_list_tlsdeps=auth_identity tls
+mod_list_tlsdeps=auth_identity crypto tls
 
 
 # - modules depending on openssl library
 # - modules depending on openssl library
 mod_list_outbound=outbound
 mod_list_outbound=outbound

+ 15 - 1
dprint.c

@@ -90,13 +90,27 @@ int str2facility(char *s)
 {
 {
 	int i;
 	int i;
 
 
-	for( i=0; str_fac[i] ; i++) {
+	for (i=0; str_fac[i]; i++) {
 		if (!strcasecmp(s,str_fac[i]))
 		if (!strcasecmp(s,str_fac[i]))
 			return int_fac[i];
 			return int_fac[i];
 	}
 	}
 	return -1;
 	return -1;
 }
 }
 
 
+char* facility2str(int fl, int *len)
+{
+	int i;
+
+	for (i=0; str_fac[i]; i++) {
+		if (fl == int_fac[i]) {
+			*len = strlen(str_fac[i]);
+			return str_fac[i];
+		}
+	}
+
+	return NULL;
+}
+
 /* fixup function for log_facility cfg parameter */
 /* fixup function for log_facility cfg parameter */
 int log_facility_fixup(void *handle, str *gname, str *name, void **val)
 int log_facility_fixup(void *handle, str *gname, str *name, void **val)
 {
 {

+ 2 - 0
dprint.h

@@ -160,6 +160,8 @@ extern volatile int dprint_crit;
 #endif
 #endif
 
 
 int str2facility(char *s);
 int str2facility(char *s);
+char* facility2str(int fl, int *len);
+
 int log_facility_fixup(void *handle, str *gname, str *name, void **val);
 int log_facility_fixup(void *handle, str *gname, str *name, void **val);
 
 
 void dprint_color(int level);
 void dprint_color(int level);

+ 39 - 0
events.c

@@ -165,6 +165,21 @@ int sr_event_register_cb(int type, sr_event_cb_f f)
 					_sr_events_list.rcv_nosip = f;
 					_sr_events_list.rcv_nosip = f;
 				else return -1;
 				else return -1;
 			break;
 			break;
+		case SREV_TCP_CLOSED:
+				if(_sr_events_list.tcp_closed==0)
+					_sr_events_list.tcp_closed = f;
+				else return -1;
+			break;
+		case SREV_NET_DATA_RECV:
+				if(_sr_events_list.net_data_recv==0)
+					_sr_events_list.net_data_recv = f;
+				else return -1;
+			break;
+		case SREV_NET_DATA_SEND:
+				if(_sr_events_list.net_data_send==0)
+					_sr_events_list.net_data_send = f;
+				else return -1;
+			break;
 		default:
 		default:
 			return -1;
 			return -1;
 	}
 	}
@@ -284,6 +299,24 @@ int sr_event_exec(int type, void *data)
 					ret = _sr_events_list.rcv_nosip(data);
 					ret = _sr_events_list.rcv_nosip(data);
 					return ret;
 					return ret;
 				} else return 1;
 				} else return 1;
+		case SREV_TCP_CLOSED:
+				if(unlikely(_sr_events_list.tcp_closed!=0))
+				{
+					ret = _sr_events_list.tcp_closed(data);
+					return ret;
+				} else return 1;
+		case SREV_NET_DATA_RECV:
+				if(unlikely(_sr_events_list.net_data_recv!=0))
+				{
+					ret = _sr_events_list.net_data_recv(data);
+					return ret;
+				} else return 1;
+		case SREV_NET_DATA_SEND:
+				if(unlikely(_sr_events_list.net_data_send!=0))
+				{
+					ret = _sr_events_list.net_data_send(data);
+					return ret;
+				} else return 1;
 		default:
 		default:
 			return -1;
 			return -1;
 	}
 	}
@@ -319,6 +352,12 @@ int sr_event_enabled(int type)
 				return (_sr_events_list.stun_in!=0)?1:0;
 				return (_sr_events_list.stun_in!=0)?1:0;
 		case SREV_RCV_NOSIP:
 		case SREV_RCV_NOSIP:
 				return (_sr_events_list.rcv_nosip!=0)?1:0;
 				return (_sr_events_list.rcv_nosip!=0)?1:0;
+		case SREV_TCP_CLOSED:
+				return (_sr_events_list.tcp_closed!=0)?1:0;
+		case SREV_NET_DATA_RECV:
+				return (_sr_events_list.net_data_recv!=0)?1:0;
+		case SREV_NET_DATA_SEND:
+				return (_sr_events_list.net_data_send!=0)?1:0;
 	}
 	}
 	return 0;
 	return 0;
 }
 }

+ 6 - 0
events.h

@@ -34,6 +34,9 @@
 #define SREV_TCP_WS_FRAME_IN		10
 #define SREV_TCP_WS_FRAME_IN		10
 #define SREV_TCP_WS_FRAME_OUT		11
 #define SREV_TCP_WS_FRAME_OUT		11
 #define SREV_STUN_IN			12
 #define SREV_STUN_IN			12
+#define SREV_TCP_CLOSED			13
+#define SREV_NET_DATA_RECV		14
+#define SREV_NET_DATA_SEND		15
 
 
 #define SREV_CB_LIST_SIZE	3
 #define SREV_CB_LIST_SIZE	3
 
 
@@ -52,6 +55,9 @@ typedef struct sr_event_cb {
 	sr_event_cb_f tcp_ws_frame_out;
 	sr_event_cb_f tcp_ws_frame_out;
 	sr_event_cb_f stun_in;
 	sr_event_cb_f stun_in;
 	sr_event_cb_f rcv_nosip;
 	sr_event_cb_f rcv_nosip;
+	sr_event_cb_f tcp_closed;
+	sr_event_cb_f net_data_recv;
+	sr_event_cb_f net_data_send;
 } sr_event_cb_t;
 } sr_event_cb_t;
 
 
 void sr_event_cb_init(void);
 void sr_event_cb_init(void);

+ 20 - 2
forward.h

@@ -56,6 +56,7 @@ enum ss_mismatch {
 	SS_MISMATCH_MCAST  /* mcast forced send socket */
 	SS_MISMATCH_MCAST  /* mcast forced send socket */
 };
 };
 
 
+
 struct socket_info* get_send_socket2(struct socket_info* force_send_socket,
 struct socket_info* get_send_socket2(struct socket_info* force_send_socket,
 									union sockaddr_union* su, int proto,
 									union sockaddr_union* su, int proto,
 									enum ss_mismatch* mismatch);
 									enum ss_mismatch* mismatch);
@@ -97,6 +98,7 @@ void forward_set_send_info(int v);
 
 
 int is_check_self_func_list_set(void);
 int is_check_self_func_list_set(void);
 
 
+#define msg_send(_dst, _buf, _len) msg_send_buffer((_dst), (_buf), (_len), 0)
 
 
 /* params:
 /* params:
  * dst = struct dest_info containing:
  * dst = struct dest_info containing:
@@ -109,12 +111,17 @@ int is_check_self_func_list_set(void);
  *        (useful for sending replies on  the same connection as the request
  *        (useful for sending replies on  the same connection as the request
  *         that generated them; use 0 if you don't want this)
  *         that generated them; use 0 if you don't want this)
  * buf, len = buffer
  * buf, len = buffer
+ * flags = control internal behavior
+ *    * 1 - skip executing event SREV_NET_DATA_OUT
  * returns: 0 if ok, -1 on error*/
  * returns: 0 if ok, -1 on error*/
 
 
-static inline int msg_send(struct dest_info* dst, char* buf, int len)
+static inline int msg_send_buffer(struct dest_info* dst, char* buf, int len,
+		int flags)
 {
 {
 	struct dest_info new_dst;
 	struct dest_info new_dst;
 	str outb;
 	str outb;
+	sr_net_info_t netinfo;
+
 #ifdef USE_TCP 
 #ifdef USE_TCP 
 	int port;
 	int port;
 	struct ip_addr ip;
 	struct ip_addr ip;
@@ -127,7 +134,9 @@ static inline int msg_send(struct dest_info* dst, char* buf, int len)
 	
 	
 	outb.s = buf;
 	outb.s = buf;
 	outb.len = len;
 	outb.len = len;
-	sr_event_exec(SREV_NET_DATA_OUT, (void*)&outb);
+	if(!(flags&1)) {
+		sr_event_exec(SREV_NET_DATA_OUT, (void*)&outb);
+	}
 
 
 	if(outb.s==NULL) {
 	if(outb.s==NULL) {
 		LM_ERR("failed to update outgoing buffer\n");
 		LM_ERR("failed to update outgoing buffer\n");
@@ -265,6 +274,15 @@ static inline int msg_send(struct dest_info* dst, char* buf, int len)
 	}
 	}
 	ret = 0;
 	ret = 0;
 done:
 done:
+
+	if(!(flags&1)) {
+		memset(&netinfo, 0, sizeof(sr_net_info_t));
+		netinfo.data.s = outb.s;
+		netinfo.data.len = outb.len;
+		netinfo.dst = dst;
+		sr_event_exec(SREV_NET_DATA_SEND, (void*)&netinfo);
+	}
+
 	if(outb.s != buf)
 	if(outb.s != buf)
 		pkg_free(outb.s);
 		pkg_free(outb.s);
 	return ret;
 	return ret;

+ 5 - 0
ip_addr.h

@@ -136,6 +136,11 @@ struct receive_info{
 	/* no need for dst_su yet */
 	/* no need for dst_su yet */
 };
 };
 
 
+typedef struct sr_net_info {
+	str data;
+	struct dest_info* dst;
+	struct receive_info* rcv;
+} sr_net_info_t;
 
 
 /* send flags */
 /* send flags */
 #define SND_F_FORCE_CON_REUSE	1 /* reuse an existing connection or fail */
 #define SND_F_FORCE_CON_REUSE	1 /* reuse an existing connection or fail */

+ 15 - 1
lib/srdb1/schema/uacreg.xml

@@ -9,7 +9,7 @@
 
 
 <table id="uacreg" xmlns:db="http://docbook.org/ns/docbook">
 <table id="uacreg" xmlns:db="http://docbook.org/ns/docbook">
     <name>uacreg</name>
     <name>uacreg</name>
-    <version>1</version>
+    <version>2</version>
     <type db="mysql">&MYSQL_TABLE_TYPE;</type>
     <type db="mysql">&MYSQL_TABLE_TYPE;</type>
     <description>
     <description>
         <db:para>This table is used by theuac module to load user details for remote server registration: &KAMAILIO_MOD_DOC;uac.html
         <db:para>This table is used by theuac module to load user details for remote server registration: &KAMAILIO_MOD_DOC;uac.html
@@ -105,6 +105,20 @@
         <description>Expiration time (in seconds, 0 means disabled)</description>
         <description>Expiration time (in seconds, 0 means disabled)</description>
     </column>
     </column>
 
 
+    <column id="flags">
+        <name>flags</name>
+        <type>int</type>
+        <default>0</default>
+        <description>Flags to control the behavior</description>
+    </column>
+
+    <column id="reg_delay">
+        <name>reg_delay</name>
+        <type>int</type>
+        <default>0</default>
+        <description>Initial registration delay</description>
+    </column>
+
 	<index>
 	<index>
         <name>l_uuid_idx</name>
         <name>l_uuid_idx</name>
         <colref linkend="l_uuid"/>
         <colref linkend="l_uuid"/>

+ 2 - 0
modules/auth_radius/authrad_mod.c

@@ -66,6 +66,7 @@ static int service_type = -1;
 
 
 int use_ruri_flag = -1;
 int use_ruri_flag = -1;
 int ar_radius_avps_mode = 0;
 int ar_radius_avps_mode = 0;
+int append_realm_to_username = 1;
 
 
 static char *auth_extra_str = 0;
 static char *auth_extra_str = 0;
 struct extra_attr *auth_extra = 0;
 struct extra_attr *auth_extra = 0;
@@ -95,6 +96,7 @@ static param_export_t params[] = {
 	{"use_ruri_flag",    INT_PARAM, &use_ruri_flag	},
 	{"use_ruri_flag",    INT_PARAM, &use_ruri_flag	},
 	{"auth_extra",       PARAM_STRING, &auth_extra_str	},
 	{"auth_extra",       PARAM_STRING, &auth_extra_str	},
 	{"radius_avps_mode",	 INT_PARAM, &ar_radius_avps_mode	},
 	{"radius_avps_mode",	 INT_PARAM, &ar_radius_avps_mode	},
+	{"append_realm_to_username", INT_PARAM, &append_realm_to_username       },
 	{0, 0, 0}
 	{0, 0, 0}
 };
 };
 
 

+ 1 - 0
modules/auth_radius/authrad_mod.h

@@ -41,6 +41,7 @@ extern struct extra_attr *auth_extra;
 
 
 extern int use_ruri_flag;
 extern int use_ruri_flag;
 extern int ar_radius_avps_mode;
 extern int ar_radius_avps_mode;
+extern int append_realm_to_username;
 
 
 extern auth_api_s_t auth_api;
 extern auth_api_s_t auth_api;
 
 

+ 5 - 0
modules/auth_radius/doc/auth_radius.xml

@@ -38,6 +38,11 @@
 		<surname>Janak</surname>
 		<surname>Janak</surname>
 		<email>[email protected]</email>
 		<email>[email protected]</email>
 		</editor>
 		</editor>
+		<editor>
+		<firstname>Phil</firstname>
+		<surname>Lavin</surname>
+		<email>[email protected]</email>
+		</editor>
 	</authorgroup>
 	</authorgroup>
 	<copyright>
 	<copyright>
 		<year>2002</year>
 		<year>2002</year>

+ 17 - 0
modules/auth_radius/doc/auth_radius_admin.xml

@@ -206,6 +206,23 @@ modparam("auth_radius", "radius_avps_mode", 1)
 		</programlisting>
 		</programlisting>
 		</example>
 		</example>
 	</section>
 	</section>
+	<section id="auth_radius.p.append_realm_to_username">
+		<title><varname>append_realm_to_username</varname> (integer)</title>
+		<para>
+			If set to 1, the username passed to the RADIUS server will have the
+			digest realm appended to it, if no domain is provided in the digest
+			username.
+		</para>
+		<para>
+			Default value is 1.
+		</para>
+		<example>
+		<title><varname>append_realm_to_username</varname> parameter usage</title>
+		<programlisting format="linespecific">
+modparam("auth_radius", "append_realm_to_username", 0)
+		</programlisting>
+		</example>
+	</section>
 	</section>
 	</section>
 
 
 	<section>
 	<section>

+ 1 - 1
modules/auth_radius/sterman.c

@@ -243,7 +243,7 @@ int radius_authorize_sterman(struct sip_msg* _msg, dig_cred_t* _cred, str* _meth
 	 * Add all the user digest parameters according to the qop defined.
 	 * Add all the user digest parameters according to the qop defined.
 	 * Most devices tested only offer support for the simplest digest.
 	 * Most devices tested only offer support for the simplest digest.
 	 */
 	 */
-	if (_cred->username.domain.len) {
+	if (_cred->username.domain.len || !append_realm_to_username) {
 		if (!rc_avpair_add(rh, &send, attrs[A_USER_NAME].v, _cred->username.whole.s, _cred->username.whole.len, 0)) {
 		if (!rc_avpair_add(rh, &send, attrs[A_USER_NAME].v, _cred->username.whole.s, _cred->username.whole.len, 0)) {
 			LM_ERR("unable to add User-Name attribute\n");
 			LM_ERR("unable to add User-Name attribute\n");
 			goto err;
 			goto err;

+ 9 - 1
modules/carrierroute/cr_fifo.c

@@ -490,7 +490,15 @@ static int get_fifo_opts(str * buf, fifo_opt_t * opts, unsigned int opt_set[]) {
 		LM_DBG("token %.*s", opt_argv[i].len, opt_argv[i].s);
 		LM_DBG("token %.*s", opt_argv[i].len, opt_argv[i].s);
 		if (opt_argv[i].len >= 1) {
 		if (opt_argv[i].len >= 1) {
 			switch(*opt_argv[i].s) {
 			switch(*opt_argv[i].s) {
-					case '-': switch(opt_argv[i].s[1]) {
+					case '-': 
+						  /* -{OPTION}{PARAMETER} is not allowed */
+                                                  if (opt_argv[i].len != 2) {
+                                                        FIFO_ERR(E_WRONGOPT);
+                                                        LM_DBG("Unknown option: %.*s\n", opt_argv[i].len, opt_argv[i].s);
+                                                        return -1;
+                                                  }
+
+						  switch(opt_argv[i].s[1]) {
 							case OPT_DOMAIN_CHR:
 							case OPT_DOMAIN_CHR:
 							op = OPT_DOMAIN;
 							op = OPT_DOMAIN;
 							used_opts |= O_DOMAIN;
 							used_opts |= O_DOMAIN;

+ 2 - 0
modules/cdp/routing.c

@@ -89,7 +89,9 @@ peer* get_first_connected_route(cdp_session_t* cdp_session, routing_entry *r, in
         /*try and find an already used peer for this session - sticky*/
         /*try and find an already used peer for this session - sticky*/
         if ((cdp_session->sticky_peer_fqdn.len > 0) && cdp_session->sticky_peer_fqdn.s) {
         if ((cdp_session->sticky_peer_fqdn.len > 0) && cdp_session->sticky_peer_fqdn.s) {
             //we have an old sticky peer. let's make sure it's up and connected before we use it.
             //we have an old sticky peer. let's make sure it's up and connected before we use it.
+            AAASessionsUnlock(cdp_session->hash); /*V1.1 - Don't attempt to hold two locks at same time */
             p = get_peer_by_fqdn(&cdp_session->sticky_peer_fqdn);
             p = get_peer_by_fqdn(&cdp_session->sticky_peer_fqdn);
+            AAASessionsLock(cdp_session->hash); /*V1.1 - As we were...no call seems to pass cdp_session unlocked */  
             if (p && !p->disabled && (p->state == I_Open || p->state == R_Open) && peer_handles_application(p, app_id, vendor_id)) {
             if (p && !p->disabled && (p->state == I_Open || p->state == R_Open) && peer_handles_application(p, app_id, vendor_id)) {
                 p->last_selected = time(NULL);
                 p->last_selected = time(NULL);
                 LM_DBG("Found a sticky peer [%.*s] for this session - re-using\n", p->fqdn.len, p->fqdn.s);
                 LM_DBG("Found a sticky peer [%.*s] for this session - re-using\n", p->fqdn.len, p->fqdn.s);

+ 17 - 0
modules/cfgt/Makefile

@@ -0,0 +1,17 @@
+#
+# cfgt module makefile
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=cfgt.so
+LIBS=
+
+DEFS+=-DKAMAILIO_MOD_INTERFACE
+
+SERLIBPATH=../../lib
+SER_LIBS+=$(SERLIBPATH)/kcore/kcore
+SER_LIBS+=$(SERLIBPATH)/srutils/srutils
+
+include ../../Makefile.modules

+ 159 - 0
modules/cfgt/README

@@ -0,0 +1,159 @@
+cfgt Module
+
+Victor Seva
+
+   sipwise.com
+
+Edited by
+
+Victor Seva
+
+   <[email protected]>
+
+   Copyright © 2015 Victor Seva (sipwise.com)
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Parameters
+
+              3.1. basedir (string)
+              3.2. mask (int)
+              3.3. callid_prefix (string)
+
+        4. Usage
+
+   List of Examples
+
+   1.1. Set cfgtrace parameter
+   1.2. Set mask parameter
+   1.3. Set callid_prefix parameter
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Parameters
+
+        3.1. basedir (string)
+        3.2. mask (int)
+        3.3. callid_prefix (string)
+
+   4. Usage
+
+1. Overview
+
+   This module provides a report of the way Kamailio SIP Server Platform
+   configuration has been executed as part of a unit test for different
+   SIP scenarios.
+
+   In order to identify different scenarios a prefix string should be used
+   inside the Call-ID header.
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * None.
+
+2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * None.
+
+3. Parameters
+
+   3.1. basedir (string)
+   3.2. mask (int)
+   3.3. callid_prefix (string)
+
+3.1. basedir (string)
+
+   Control where the config reports should be stored. The dir must exist
+   and Kamailio SIP Server Platform must have permissions to write on it.
+
+   Default value is “/tmp”.
+
+   Example 1.1. Set cfgtrace parameter
+...
+modparam("cfgt", "basedir", "/var/run/kamailio/cfgtest")
+...
+
+3.2. mask (int)
+
+   mask - Control the type of vars it should display in the report:
+     * 1 - dump null values
+     * 2 - dump avp vars
+     * 4 - dump script vars
+     * 8 - dump xavp vars
+     * 16 - dump DP_OTHER vars
+     * 32 - dump ALL vars
+
+   Default value is “32” (ALL).
+
+   Example 1.2. Set mask parameter
+...
+# dump xavp(8) and avp(4) vars
+modparam("cfgt", "mask", 12)
+...
+
+3.3. callid_prefix (string)
+
+   Prefix used to identify test scenario messages. Last char of the string
+   will be used as delimiter for the scenario ID. With parameter set to
+   “NGCP%” and Call-ID “NGCP%123%456” the scenario identified will be
+   “123”.
+
+   Default value is “NGCP%” (using “%” as delimiter).
+
+   Example 1.3. Set callid_prefix parameter
+...
+# using '%' as delimiter
+modparam("cfgt", "callid_prefix", "TEST-ID%")
+...
+
+4. Usage
+
+   This module is used by the debugger module, so it must be loaded first.
+   To enable the generation of the reports, the debugger module must be
+   configured by setting the cfgtest parameter to "1".
+
+   Example of configuration:
+...
+#!ifdef WITH_DEBUG
+loadmodule "cfgt.so"
+loadmodule "debugger.so"
+#!endif
+...
+#!ifdef WITH_DEBUG
+# ----- cfgt params -----
+modparam("cfgt", "basedir", "/tmp/kamailio/cfgtest")
+modparam("cfgt", "callid_prefix", "TRACE-ID%")
+modparam("cfgt", "mask", 32)
+
+# ----- debugger params -----
+modparam("debugger", "cfgtrace", 1)
+modparam("debugger", "log_level_name", "exec")
+
+modparam("debugger", "cfgtest", 1)
+#!endif
+...

+ 45 - 0
modules/cfgt/cfgt.c

@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 Victor Seva (sipwise.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *
+ */
+#include "cfgt_mod.h"
+#include "cfgt.h"
+#include "cfgt_int.h"
+
+/*!
+ * \brief cfgt module API export bind function
+ * \param api cfgt API
+ * \return 0 on success, -1 on failure
+ */
+int bind_cfgt(cfgt_api_t* api)
+{
+	if (!api) {
+		LM_ERR("invalid parameter value\n");
+		return -1;
+	}
+	if (init_flag==0) {
+		LM_ERR("configuration error - trying to bind to cfgt module"
+				" before being initialized\n");
+		return -1;
+	}
+
+	api->cfgt_process_route   = cfgt_process_route;
+	return 0;
+}

+ 40 - 0
modules/cfgt/cfgt.h

@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 Victor Seva (sipwise.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *
+ */
+
+#ifndef _CFGT_BIND_H
+#define _CFGT_BIND_H
+
+#include "../../sr_module.h"
+
+/* export not usable from scripts */
+#define NO_SCRIPT	-1
+
+typedef int (*cfgt_process_route_f)(struct sip_msg *msg, struct action *a);
+
+typedef struct cfgt_api {
+	cfgt_process_route_f cfgt_process_route;
+} cfgt_api_t;
+
+/*! cfgt API export bind function */
+typedef int (*bind_cfgt_t)(cfgt_api_t* api);
+
+#endif

+ 797 - 0
modules/cfgt/cfgt_int.c

@@ -0,0 +1,797 @@
+/**
+ *
+ * Copyright (C) 2015 Victor Seva (sipwise.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include <stdio.h>
+#include <sys/stat.h>
+
+#include "../../events.h"
+#include "../../lib/kcore/cmpapi.h"
+#include "../../pvar.h"
+#include "../../rpc.h"
+#include "../../rpc_lookup.h"
+
+#include "cfgt_int.h"
+#include "cfgt_json.h"
+
+static str _cfgt_route_prefix[] = {
+	str_init("start|"),
+	str_init("exit|"),
+	str_init("drop|"),
+	str_init("return|"),
+	{0, 0}
+};
+cfgt_node_p _cfgt_node = NULL;
+cfgt_hash_p _cfgt_uuid = NULL;
+str cfgt_hdr_prefix = {"NGCP%", 5};
+str cfgt_basedir = {"/tmp", 4};
+int cfgt_mask = CFGT_DP_ALL;
+
+static int shm_str_hash_alloc(struct str_hash_table *ht, int size)
+{
+	ht->table = shm_malloc(sizeof(struct str_hash_head) * size);
+
+	if (!ht->table)
+		return -1;
+
+	ht->size = size;
+	return 0;
+}
+
+static int _cfgt_init_hashtable(struct str_hash_table *ht)
+{
+	if (shm_str_hash_alloc(ht, CFGT_HASH_SIZE) != 0)
+	{
+		LM_ERR("Error allocating shared memory hashtable\n");
+		return -1;
+	}
+
+	str_hash_init(ht);
+
+	return 0;
+}
+
+int _cfgt_pv_parse(str *param, pv_elem_p *elem)
+{
+	if (param->s && param->len > 0)
+	{
+		if (pv_parse_format(param, elem)<0)
+		{
+			LM_ERR("malformed or non AVP %.*s AVP definition\n",
+					param->len, param->s);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+void _cfgt_remove_uuid(const str *uuid)
+{
+	struct str_hash_head *head;
+	struct str_hash_entry *entry, *back;
+	int i;
+
+	if(_cfgt_uuid==NULL) return;
+	if(uuid)
+	{
+		lock_get(&_cfgt_uuid->lock);
+		entry = str_hash_get(&_cfgt_uuid->hash, uuid->s, uuid->len);
+		if(entry)
+		{
+			str_hash_del(entry);
+			shm_free(entry->key.s);
+			shm_free(entry);
+			LM_DBG("uuid[%.*s] removed from hash\n", uuid->len, uuid->s);
+		}
+		else LM_DBG("uuid[%.*s] not found in hash\n", uuid->len, uuid->s);
+		lock_release(&_cfgt_uuid->lock);
+	}
+	else
+	{
+		lock_get(&_cfgt_uuid->lock);
+		for(i=0; i<CFGT_HASH_SIZE; i++)
+		{
+			head = _cfgt_uuid->hash.table+i;
+			clist_foreach_safe(head, entry, back, next)
+			{
+				LM_DBG("uuid[%.*s] removed from hash\n",
+					entry->key.len, entry->key.s);
+				str_hash_del(entry);
+				shm_free(entry->key.s);
+				shm_free(entry);
+			}
+			lock_release(&_cfgt_uuid->lock);
+		}
+		LM_DBG("remove all uuids. done\n");
+	}
+}
+
+int _cfgt_get_uuid_id(cfgt_node_p node)
+{
+	struct str_hash_entry *entry;
+
+	if(_cfgt_uuid==NULL || node==NULL || node->uuid.len == 0) return -1;
+	lock_get(&_cfgt_uuid->lock);
+	entry = str_hash_get(&_cfgt_uuid->hash, node->uuid.s, node->uuid.len);
+	if(entry)
+	{
+		entry->u.n = entry->u.n + 1;
+		node->msgid = entry->u.n;
+	}
+	else
+	{
+		entry = shm_malloc(sizeof(struct str_hash_entry));
+		if(entry==NULL)
+		{
+			lock_release(&_cfgt_uuid->lock);
+			LM_ERR("No shared memory left\n");
+			return -1;
+		}
+		if (shm_str_dup(&entry->key, &node->uuid) != 0)
+		{
+			lock_release(&_cfgt_uuid->lock);
+			shm_free(entry);
+			LM_ERR("No shared memory left\n");
+			return -1;
+		}
+		entry->u.n = 1;
+		node->msgid = 1;
+		LM_DBG("Add new entry[%.*s]\n", node->uuid.len, node->uuid.s);
+		str_hash_add(&_cfgt_uuid->hash, entry);
+	}
+	lock_release(&_cfgt_uuid->lock);
+	LM_DBG("msgid:[%d]\n", node->msgid);
+	return 1;
+}
+
+int _cfgt_get_hdr_helper(struct sip_msg *msg, str *res, int mode)
+{
+	struct hdr_field *hf;
+	char *delimiter, *end;
+	str tmp = STR_NULL;
+
+	if(msg==NULL || (mode==0 && res==NULL))
+		return -1;
+
+	/* we need to be sure we have parsed all headers */
+	if(parse_headers(msg, HDR_EOH_F, 0)<0)
+	{
+		LM_ERR("error parsing headers\n");
+		return -1;
+	}
+
+	hf = msg->callid;
+	if(!hf) return 1;
+
+	if(strncmp(hf->body.s, cfgt_hdr_prefix.s, cfgt_hdr_prefix.len)==0)
+	{
+		tmp.s = hf->body.s+cfgt_hdr_prefix.len;
+		delimiter = tmp.s-1;
+		LM_DBG("Prefix detected. delimiter[%c]\n", *delimiter);
+		if(mode==0)
+		{
+			end = strchr(tmp.s, *delimiter);
+			if(end)
+			{
+				tmp.len = end-tmp.s;
+				if(pkg_str_dup(res, &tmp)<0)
+				{
+					LM_ERR("error copying header\n");
+					return -1;
+				}
+				LM_DBG("cfgtest uuid:[%.*s]\n", res->len, res->s);
+				return 0;
+			}
+		}
+		else
+		{
+			tmp.len = res->len;
+			LM_DBG("tmp[%.*s] res[%.*s]\n", tmp.len, tmp.s, res->len, res->s);
+			return STR_EQ(tmp, *res);
+		}
+	}
+	return 1; /* not found */
+}
+
+int _cfgt_get_hdr(struct sip_msg *msg, str *res)
+{
+	return _cfgt_get_hdr_helper(msg, res, 0);
+}
+
+int _cfgt_cmp_hdr(struct sip_msg *msg, str *res)
+{
+	return _cfgt_get_hdr_helper(msg, res, 1);
+}
+
+cfgt_node_p cfgt_create_node(struct sip_msg *msg)
+{
+	cfgt_node_p node;
+
+	node = (cfgt_node_p) pkg_malloc(sizeof(cfgt_node_t));
+	if(node==NULL)
+	{
+		LM_ERR("cannot allocate cfgtest msgnode\n");
+		return node;
+	}
+	memset(node, 0, sizeof(cfgt_node_t));
+	srjson_InitDoc(&node->jdoc, NULL);
+	if (msg)
+	{
+		node->msgid = msg->id;
+		LM_DBG("msgid:%d\n", node->msgid);
+		if(_cfgt_get_hdr(msg, &node->uuid)!=0 || node->uuid.len==0)
+		{
+			LM_ERR("cannot get value of cfgtest uuid header!!\n");
+			goto error;
+		}
+	}
+	node->jdoc.root = srjson_CreateObject(&node->jdoc);
+	if(node->jdoc.root==NULL)
+	{
+		LM_ERR("cannot create json root\n");
+		goto error;
+	}
+	node->flow = srjson_CreateArray(&node->jdoc);
+	if(node->flow==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		goto error;
+	}
+	srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "flow\0", node->flow);
+	node->in = srjson_CreateArray(&node->jdoc);
+	if(node->in==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		goto error;
+	}
+	srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "sip_in\0", node->in);
+	node->out = srjson_CreateArray(&node->jdoc);
+	if(node->out==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		goto error;
+	}
+	srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "sip_out\0", node->out);
+	LM_DBG("node created\n");
+	return node;
+
+error:
+	srjson_DestroyDoc(&node->jdoc);
+	pkg_free(node);
+	return NULL;
+}
+
+void _cfgt_remove_node(cfgt_node_p node)
+{
+	if(!node) return;
+	srjson_DestroyDoc(&node->jdoc);
+	if(node->uuid.s) pkg_free(node->uuid.s);
+	while(node->flow_head)
+	{
+		node->route = node->flow_head;
+		node->flow_head = node->route->next;
+		pkg_free(node->route);
+		node->route = NULL;
+	}
+	pkg_free(node);
+}
+
+int _cfgt_get_filename(int msgid, str uuid, str *dest, int *dir)
+{
+	int i, lid;
+	char buff_id[INT2STR_MAX_LEN];
+	char *sid;
+	if(dest==NULL || uuid.len == 0) return -1;
+
+	dest->len = cfgt_basedir.len + uuid.len;
+	if(cfgt_basedir.s[cfgt_basedir.len-1]!='/')
+		dest->len = dest->len + 1;
+	sid = sint2strbuf(msgid, buff_id, INT2STR_MAX_LEN, &lid);
+	dest->len += lid + 6;
+	dest->s = (char *) pkg_malloc((dest->len*sizeof(char)+1));
+	if(dest->s==NULL)
+	{
+		LM_ERR("no more memory.\n");
+		return -1;
+	}
+	strncpy(dest->s, cfgt_basedir.s, cfgt_basedir.len);
+	i = cfgt_basedir.len;
+	if(cfgt_basedir.s[cfgt_basedir.len-1]!='/')
+	{
+		strncpy(dest->s+i, "/", 1);
+		i = i + 1;
+	}
+	strncpy(dest->s+i, uuid.s, uuid.len);
+	i = i + uuid.len; (*dir) = i;
+	strncpy(dest->s+i, "\0", 1);
+	i = i + 1;
+	strncpy(dest->s+i, sid, lid);
+	i = i + lid;
+	strncpy(dest->s+i, ".json\0", 6);
+	return 0;
+}
+
+int _cfgt_node2json(cfgt_node_p node)
+{
+       srjson_t *jobj;
+
+       if(!node) return -1;
+       jobj = srjson_CreateStr(&node->jdoc, node->uuid.s, node->uuid.len);
+       if(jobj==NULL)
+       {
+               LM_ERR("cannot create json object\n");
+               return -1;
+       }
+       srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "uuid\0", jobj);
+
+       jobj = srjson_CreateNumber(&node->jdoc, (double)node->msgid);
+       if(jobj==NULL)
+       {
+               LM_ERR("cannot create json object\n");
+               return -1;
+       }
+       srjson_AddItemToObject(&node->jdoc, node->jdoc.root, "msgid\0", jobj);
+       return 0;
+}
+
+void cfgt_save_node(cfgt_node_p node)
+{
+	FILE *fp;
+	str dest = STR_NULL;
+	int dir = 0;
+	if(_cfgt_get_filename(node->msgid, node->uuid, &dest, &dir)<0)
+	{
+		LM_ERR("can't build filename\n");
+		return;
+	}
+	LM_DBG("dir [%s]\n", dest.s);
+	mkdir(dest.s, S_IRWXO|S_IXGRP|S_IRWXU);
+	dest.s[dir] = '/';
+	fp = fopen(dest.s, "w");
+	LM_DBG("file [%s]\n", dest.s);
+	if(fp) {
+		pkg_free(dest.s);
+		dest.s = srjson_Print(&node->jdoc, node->jdoc.root);
+	    if(dest.s==NULL)
+        {
+           LM_ERR("Cannot get the json string\n");
+           fclose(fp);
+           return;
+        }
+        if(fputs(dest.s, fp)<0){
+        	LM_ERR("failed writing to file\n");
+        }
+        fclose(fp);
+        node->jdoc.free_fn(dest.s);
+	}
+	else {
+		LM_ERR("Can't open file [%s] to write\n", dest.s);
+		pkg_free(dest.s);
+	}
+}
+
+void _cfgt_print_node(cfgt_node_p node, int json)
+{
+	char *buf = NULL;
+	cfgt_str_list_p route;
+
+	if(!node) return;
+	if(node->flow_head)
+	{
+		route = node->flow_head;
+		while(route)
+		{
+			if(route == node->route)
+				LM_DBG("[--[%.*s][%d]--]\n",
+					route->s.len, route->s.s, route->type);
+			else LM_DBG("[%.*s][%d]\n",
+					route->s.len, route->s.s, route->type);
+			route = route->next;
+		}
+	}
+	else LM_DBG("flow:empty\n");
+	if(json) {
+		buf = srjson_PrintUnformatted(&node->jdoc, node->jdoc.root);
+		if(buf==NULL)
+		{
+			LM_ERR("Cannot get the json string\n");
+			return;
+		}
+		LM_DBG("node[%p]: id:[%d] uuid:[%.*s] info:[%s]\n",
+			node, node->msgid, node->uuid.len, node->uuid.s, buf);
+		node->jdoc.free_fn(buf);
+	}
+}
+
+int _cfgt_set_dump(struct sip_msg *msg, cfgt_node_p node, str *flow)
+{
+	srjson_t *f, *vars;
+
+	if(node==NULL || flow == NULL) return -1;
+	vars = srjson_CreateObject(&node->jdoc);
+	if(vars==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		return -1;
+	}
+	if(cfgt_get_json(msg, 30, &node->jdoc, vars)<0)
+	{
+		LM_ERR("cannot get var info\n");
+		return -1;
+	}
+	f = srjson_CreateObject(&node->jdoc);
+	if(f==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		srjson_Delete(&node->jdoc, vars);
+		return -1;
+	}
+	srjson_AddStrItemToObject(&node->jdoc, f,
+		flow->s, flow->len, vars);
+	srjson_AddItemToArray(&node->jdoc, node->flow, f);
+	LM_DBG("node[%.*s] flow created\n", flow->len, flow->s);
+	return 0;
+}
+
+void _cfgt_set_type(cfgt_str_list_p route, struct action *a)
+{
+	switch(a->type)
+	{
+		case DROP_T:
+			if(a->val[1].u.number&DROP_R_F) {
+				route->type = CFGT_DROP_D;
+				LM_DBG("set[%.*s]->CFGT_DROP_D\n", route->s.len, route->s.s);
+			}
+			if(a->val[1].u.number&RETURN_R_F){
+				route->type = CFGT_DROP_R;
+				LM_DBG("set[%.*s]->CFGT_DROP_R\n", route->s.len, route->s.s);
+			}
+			else {
+				route->type = CFGT_DROP_E;
+				LM_DBG("set[%.*s]->CFGT_DROP_E\n", route->s.len, route->s.s);
+			}
+			break;
+		case ROUTE_T:
+			route->type = CFGT_ROUTE;
+			LM_DBG("set[%.*s]->CFGT_ROUTE\n", route->s.len, route->s.s);
+			break;
+		default:
+			if(route->type!=CFGT_DROP_E)
+			{
+				route->type = CFGT_DROP_R;
+				LM_DBG("[%.*s] no relevant action: CFGT_DROP_R[%d]\n",
+					route->s.len, route->s.s, a->type);
+			}
+			else
+			{
+				LM_DBG("[%.*s] already set to CFGT_DROP_E[%d]\n",
+					route->s.len, route->s.s, a->type);
+			}
+			break;
+	}
+}
+
+int _cfgt_add_routename(cfgt_node_p node, struct action *a,
+		str *routename)
+{
+	cfgt_str_list_p route;
+	int ret = 0;
+
+	if(!node->route) /* initial */
+	{
+		node->route = pkg_malloc(sizeof(cfgt_str_list_t));
+		if(!node->route)
+		{
+			LM_ERR("No more pkg mem\n");
+			return -1;
+		}
+		memset(node->route, 0, sizeof(cfgt_str_list_t));
+		node->flow_head = node->route;
+		node->route->type = CFGT_ROUTE;
+		ret = 1;
+	}
+	else
+	{
+		LM_DBG("actual routename:[%.*s][%d]\n", node->route->s.len,
+			node->route->s.s, node->route->type);
+		if(node->route->prev)
+			LM_DBG("prev routename:[%.*s][%d]\n", node->route->prev->s.len,
+				node->route->prev->s.s,	node->route->prev->type);
+		if(node->route->next)
+			LM_DBG("next routename:[%.*s][%d]\n", node->route->next->s.len,
+				node->route->next->s.s,	node->route->next->type);
+		if(STR_EQ(*routename, node->route->s))
+		{
+			LM_DBG("same route\n");
+			_cfgt_set_type(node->route, a);
+			return 2;
+		}
+		else if(node->route->prev &&
+				STR_EQ(*routename, node->route->prev->s))
+		{
+			LM_DBG("back to route[%.*s]\n", node->route->prev->s.len,
+				node->route->prev->s.s);
+			_cfgt_set_type(node->route->prev, a);
+			return 3;
+		}
+		route = pkg_malloc(sizeof(cfgt_str_list_t));
+		if(!route)
+		{
+			LM_ERR("No more pkg mem\n");
+			return -1;
+		}
+		memset(route, 0, sizeof(cfgt_str_list_t));
+		route->prev = node->route;
+		node->route->next = route;
+		node->route = route;
+		_cfgt_set_type(node->route, a);
+	}
+	node->route->s.s = routename->s;
+	node->route->s.len = routename->len;
+	LM_DBG("add[%d] route:[%.*s]\n", ret, node->route->s.len, node->route->s.s);
+	_cfgt_print_node(node, 0);
+	return ret;
+}
+
+void _cfgt_del_routename(cfgt_node_p node)
+{
+	if(node->route->next!=NULL) {
+		LM_ERR("wtf!! route->next[%p] not null!!\n", node->route->next);
+		_cfgt_print_node(node, 0);
+	}
+	LM_DBG("del route[%.*s]\n", node->route->s.len, node->route->s.s);
+	node->route = node->route->prev;
+	pkg_free(node->route->next);
+	node->route->next = NULL;
+}
+/* dest has to be freed */
+int _cfgt_node_get_flowname(cfgt_str_list_p route, int *indx, str *dest)
+{
+	int i;
+	if(route==NULL) return -1;
+	LM_DBG("routename:[%.*s][%d]\n", route->s.len, route->s.s,
+		route->type);
+	if(indx) i = *indx;
+	else i = route->type-1;
+	if(str_append(&_cfgt_route_prefix[i],
+		&route->s, dest)<0)
+	{
+		LM_ERR("Cannot create route name\n");
+		return -1;
+	}
+	return 0;
+}
+
+int cfgt_process_route(struct sip_msg *msg, struct action *a)
+{
+	str routename;
+	int ret = -1;
+	int indx = 0;
+	str flowname = STR_NULL;
+	if(!_cfgt_node) {
+		LM_ERR("node empty\n");
+		return -1;
+	}
+	if (a->rname==NULL) {
+		LM_DBG("no routename. type:%d\n", a->type);
+		return 0;
+	}
+	LM_DBG("route from action:[%s]\n", a->rname);
+	routename.s = a->rname; routename.len = strlen(a->rname);
+	switch(_cfgt_add_routename(_cfgt_node, a, &routename))
+	{
+		case 2: /* same name */
+			return 0;
+		case 1: /* initial */
+			LM_DBG("Initial route[%.*s]. dump vars\n",
+				_cfgt_node->route->s.len, _cfgt_node->route->s.s);
+			if(_cfgt_node_get_flowname(_cfgt_node->route, &indx, &flowname)<0)
+			{
+				LM_ERR("cannot create flowname\n");
+				return -1;
+			}
+			ret = _cfgt_set_dump(msg, _cfgt_node, &flowname);
+			break;
+		case 0: /* new */
+			LM_DBG("Change from[%.*s] route to route[%.*s]. dump vars\n",
+				_cfgt_node->route->prev->s.len, _cfgt_node->route->prev->s.s,
+				_cfgt_node->route->s.len, _cfgt_node->route->s.s);
+			if(_cfgt_node_get_flowname(_cfgt_node->route, &indx, &flowname)<0)
+			{
+				LM_ERR("cannot create flowname\n");
+				return -1;
+			}
+			ret = _cfgt_set_dump(msg, _cfgt_node, &flowname);
+			break;
+		case 3: /* back to previous */
+			if(_cfgt_node_get_flowname(_cfgt_node->route, 0, &flowname)<0)
+			{
+				LM_ERR("cannot create flowname\n");
+				return -1;
+			}
+			ret = _cfgt_set_dump(msg, _cfgt_node, &flowname);
+			_cfgt_del_routename(_cfgt_node);
+			break;
+		default:
+			return -1;
+	}
+	if(flowname.s) pkg_free(flowname.s);
+	return ret;
+}
+
+/*
+TODO:
+- parse first line, check if is SIP
+- parse for header cfgtest
+*/
+int cfgt_msgin(void *data)
+{
+	srjson_t *jobj;
+	str *buf = (str *) data;
+	if(buf==NULL) return 0;
+	if(_cfgt_node) {
+		cfgt_save_node(_cfgt_node);
+		_cfgt_remove_node(_cfgt_node);
+		LM_DBG("node removed\n");
+		_cfgt_node = NULL;
+	}
+	LM_DBG("msg in:{%.*s}\n", buf->len, buf->s);
+	_cfgt_node = cfgt_create_node(NULL);
+	if(_cfgt_node)
+	{
+		jobj = srjson_CreateStr(&_cfgt_node->jdoc, buf->s, buf->len);
+		if(jobj==NULL)
+		{
+			LM_ERR("cannot create json object\n");
+			return -1;
+		}
+		srjson_AddItemToArray(&_cfgt_node->jdoc, _cfgt_node->in, jobj);
+		return 0;
+	}
+	LM_ERR("_cfgt_node empty\n");
+	return -1;
+}
+
+int cfgt_pre(struct sip_msg *msg, unsigned int flags, void *bar) {
+	str unknown = {"unknown", 7};
+
+	if(_cfgt_node)
+	{
+		if (_cfgt_node->msgid == 0)
+		{
+			LM_DBG("new node\n");
+			if(_cfgt_get_hdr(msg, &_cfgt_node->uuid)!=0 ||
+				_cfgt_node->uuid.len==0)
+			{
+				LM_ERR("cannot get value of cfgtest uuid header."
+					" Using unknown\n");
+				pkg_str_dup(&_cfgt_node->uuid, &unknown);
+			}
+			return _cfgt_get_uuid_id(_cfgt_node);
+		}
+		else
+		{
+			LM_DBG("_cfgt_node->uuid:[%.*s]\n", _cfgt_node->uuid.len,
+				_cfgt_node->uuid.s);
+			if(_cfgt_cmp_hdr(msg, &_cfgt_node->uuid))
+			{
+				LM_DBG("same uuid\n");
+				return 1;
+			}
+			else {
+				LM_DBG("different uuid\n");
+			}
+		}
+	}
+	else { LM_ERR("node empty??\n"); }
+	_cfgt_node = cfgt_create_node(msg);
+	if(_cfgt_node) {
+		LM_DBG("node created\n");
+		return 1;
+	}
+	return -1;
+}
+int cfgt_post(struct sip_msg *msg, unsigned int flags, void *bar) {
+	str flowname = STR_NULL;
+
+	if(_cfgt_node) {
+		LM_DBG("dump last flow\n");
+		if(_cfgt_node_get_flowname(_cfgt_node->route, 0, &flowname)<0)
+			LM_ERR("cannot create flowname\n");
+		else _cfgt_set_dump(msg, _cfgt_node, &flowname);
+		if(flowname.s) pkg_free(flowname.s);
+		cfgt_save_node(_cfgt_node);
+	}
+	return 1;
+}
+
+int cfgt_msgout(void *data)
+{
+	srjson_t *jobj;
+	str *buf = (str *) data;
+	if(buf==NULL) return 0;
+	LM_DBG("msg out:{%.*s}\n", buf->len, buf->s);
+
+	if(_cfgt_node)
+	{
+		jobj = srjson_CreateStr(&_cfgt_node->jdoc, buf->s, buf->len);
+		if(jobj==NULL)
+		{
+			LM_ERR("cannot create json object\n");
+			return -1;
+		}
+		srjson_AddItemToArray(&_cfgt_node->jdoc, _cfgt_node->out, jobj);
+		return 0;
+	}
+	LM_ERR("node empty\n");
+	return -1;
+}
+
+/**
+ *
+ */
+static const char* cfgt_rpc_mask_doc[2] = {
+	"Specify module mask",
+	0
+};
+
+static void cfgt_rpc_mask(rpc_t* rpc, void* ctx){
+	int mask = CFGT_DP_ALL;
+
+	if (rpc->scan(ctx, "*d", &mask) != 1)
+	{
+		rpc->fault(ctx, 500, "invalid parameters");
+		return;
+	}
+	cfgt_mask = mask;
+	rpc->add(ctx, "s", "200 ok");
+}
+
+rpc_export_t cfgt_rpc[] = {
+	{"dbg.mask", cfgt_rpc_mask, cfgt_rpc_mask_doc, 0},
+	{0, 0, 0, 0}
+};
+
+int cfgt_init(void)
+{
+	if (rpc_register_array(cfgt_rpc)!=0)
+	{
+		LM_ERR("failed to register RPC commands\n");
+		return -1;
+	}
+	_cfgt_uuid = shm_malloc(sizeof(cfgt_hash_t));
+	if(_cfgt_uuid==NULL)
+	{
+		LM_ERR("Cannot allocate shared memory\n");
+		return -1;
+	}
+	if(!lock_init(&_cfgt_uuid->lock))
+	{
+		LM_ERR("cannot init the lock\n");
+		shm_free(_cfgt_uuid);
+		_cfgt_uuid = NULL;
+		return -1;
+	}
+	if(_cfgt_init_hashtable(&_cfgt_uuid->hash)<0)
+		return -1;
+	sr_event_register_cb(SREV_NET_DATA_IN, cfgt_msgin);
+	sr_event_register_cb(SREV_NET_DATA_OUT, cfgt_msgout);
+	return 0;
+}

+ 67 - 0
modules/cfgt/cfgt_int.h

@@ -0,0 +1,67 @@
+/**
+ *
+ * Copyright (C) 2015 Victor Seva (sipwise.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#ifndef _CFGT_INT_H_
+#define _CFGT_INT_H_
+
+#include "../../lib/srutils/srjson.h"
+#include "../../locking.h"
+#include "../../route_struct.h"
+#include "../../str_hash.h"
+
+#define CFGT_HASH_SIZE 32
+
+enum _cfgt_action_type {
+	CFGT_ROUTE=1,
+	CFGT_DROP_E, CFGT_DROP_D, CFGT_DROP_R
+};
+
+typedef struct _cfgt_hash
+{
+	gen_lock_t lock;
+	struct str_hash_table hash;
+	str save_uuid; /* uuid to be save */
+} cfgt_hash_t, *cfgt_hash_p;
+
+typedef struct _cfgt_str_list
+{
+	str s;
+	enum _cfgt_action_type type;
+	struct _cfgt_str_list *next, *prev;
+} cfgt_str_list_t, *cfgt_str_list_p;
+
+typedef struct _cfgt_node
+{
+	srjson_doc_t jdoc;
+	str uuid;
+	int msgid;
+	cfgt_str_list_p flow_head;
+	cfgt_str_list_p route;
+	srjson_t *in, *out, *flow;
+	struct _cfgt_node *next, *prev;
+} cfgt_node_t, *cfgt_node_p;
+
+int cfgt_init(void);
+cfgt_node_p cfgt_create_node(struct sip_msg *msg);
+int cfgt_process_route(struct sip_msg *msg, struct action *a);
+int cfgt_pre(struct sip_msg *msg, unsigned int flags, void *bar);
+int cfgt_post(struct sip_msg *msg, unsigned int flags, void *bar);
+#endif

+ 389 - 0
modules/cfgt/cfgt_json.c

@@ -0,0 +1,389 @@
+/**
+ *
+ * Copyright (C) 2013-2015 Victor Seva (sipwise.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include <stdio.h>
+
+#include "../../pvar.h"
+#include "../../mem/shm_mem.h"
+#include "../../xavp.h"
+#include "../pv/pv_xavp.h"
+
+#include "cfgt_json.h"
+
+int _cfgt_get_array_avp_vals(struct sip_msg *msg,
+		pv_param_t *param, srjson_doc_t *jdoc, srjson_t **jobj,
+		str *item_name)
+{
+	struct usr_avp *avp;
+	unsigned short name_type;
+	int_str avp_name;
+	int_str avp_value;
+	struct search_state state;
+	srjson_t *jobjt;
+	memset(&state, 0, sizeof(struct search_state));
+
+	if(pv_get_avp_name(msg, param, &avp_name, &name_type)!=0)
+	{
+		LM_ERR("invalid name\n");
+		return -1;
+	}
+	*jobj = srjson_CreateArray(jdoc);
+	if(*jobj==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		return -1;
+	}
+	if ((avp=search_first_avp(name_type, avp_name, &avp_value, &state))==0)
+	{
+		goto ok;
+	}
+	do
+	{
+		if(avp->flags & AVP_VAL_STR)
+		{
+			jobjt = srjson_CreateStr(jdoc, avp_value.s.s, avp_value.s.len);
+			if(jobjt==NULL)
+			{
+				LM_ERR("cannot create json object\n");
+				return -1;
+			}
+		} else {
+			jobjt = srjson_CreateNumber(jdoc, avp_value.n);
+			if(jobjt==NULL)
+			{
+				LM_ERR("cannot create json object\n");
+				return -1;
+			}
+		}
+		srjson_AddItemToArray(jdoc, *jobj, jobjt);
+	} while ((avp=search_next_avp(&state, &avp_value))!=0);
+ok:
+	item_name->s = avp_name.s.s;
+	item_name->len = avp_name.s.len;
+	return 0;
+}
+#define CFGT_XAVP_DUMP_SIZE 32
+static str* _cfgt_xavp_dump[CFGT_XAVP_DUMP_SIZE];
+int _cfgt_xavp_dump_lookup(pv_param_t *param)
+{
+	unsigned int i = 0;
+	pv_xavp_name_t *xname;
+
+	if(param==NULL)
+		return -1;
+
+	xname = (pv_xavp_name_t*)param->pvn.u.dname;
+
+	while(_cfgt_xavp_dump[i]!=NULL&&i<CFGT_XAVP_DUMP_SIZE)
+	{
+		if(_cfgt_xavp_dump[i]->len==xname->name.len)
+		{
+			if(strncmp(_cfgt_xavp_dump[i]->s, xname->name.s, xname->name.len)==0)
+				return 1; /* already dump before */
+		}
+		i++;
+	}
+	if(i==CFGT_XAVP_DUMP_SIZE)
+	{
+		LM_WARN("full _cfgt_xavp_dump cache array\n");
+		return 0; /* end cache names */
+	}
+	_cfgt_xavp_dump[i] = &xname->name;
+	return 0;
+}
+
+void _cfgt_get_obj_xavp_val(sr_xavp_t *avp, srjson_doc_t *jdoc, srjson_t **jobj)
+{
+	static char _pv_xavp_buf[128];
+	int result = 0;
+
+	switch(avp->val.type) {
+		case SR_XTYPE_NULL:
+			*jobj = srjson_CreateNull(jdoc);
+		break;
+		case SR_XTYPE_INT:
+			*jobj = srjson_CreateNumber(jdoc, avp->val.v.i);
+		break;
+		case SR_XTYPE_STR:
+			*jobj = srjson_CreateStr(jdoc, avp->val.v.s.s, avp->val.v.s.len);
+		break;
+		case SR_XTYPE_TIME:
+			result = snprintf(_pv_xavp_buf, 128, "%lu", (long unsigned)avp->val.v.t);
+		break;
+		case SR_XTYPE_LONG:
+			result = snprintf(_pv_xavp_buf, 128, "%ld", (long unsigned)avp->val.v.l);
+		break;
+		case SR_XTYPE_LLONG:
+			result = snprintf(_pv_xavp_buf, 128, "%lld", avp->val.v.ll);
+		break;
+		case SR_XTYPE_XAVP:
+			result = snprintf(_pv_xavp_buf, 128, "<<xavp:%p>>", avp->val.v.xavp);
+		break;
+		case SR_XTYPE_DATA:
+			result = snprintf(_pv_xavp_buf, 128, "<<data:%p>>", avp->val.v.data);
+		break;
+		default:
+			LM_WARN("unknown data type\n");
+			*jobj = srjson_CreateNull(jdoc);
+	}
+	if(result<0)
+	{
+		LM_ERR("cannot convert to str\n");
+		*jobj = srjson_CreateNull(jdoc);
+	}
+	else if(*jobj==NULL)
+	{
+		*jobj = srjson_CreateStr(jdoc, _pv_xavp_buf, 128);
+	}
+}
+
+int _cfgt_get_obj_avp_vals(str name, sr_xavp_t *xavp, srjson_doc_t *jdoc, srjson_t **jobj)
+{
+	sr_xavp_t *avp = NULL;
+	srjson_t *jobjt = NULL;
+
+	*jobj = srjson_CreateArray(jdoc);
+	if(*jobj==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		return -1;
+	}
+	avp = xavp;
+	while(avp!=NULL&&!STR_EQ(avp->name,name))
+	{
+		avp = avp->next;
+	}
+	while(avp!=NULL)
+	{
+		_cfgt_get_obj_xavp_val(avp, jdoc, &jobjt);
+		srjson_AddItemToArray(jdoc, *jobj, jobjt);
+		jobjt = NULL;
+		avp = xavp_get_next(avp);
+	}
+
+	return 0;
+}
+
+int _cfgt_get_obj_xavp_vals(struct sip_msg *msg,
+		pv_param_t *param, srjson_doc_t *jdoc, srjson_t **jobjr,
+		str *item_name)
+{
+	pv_xavp_name_t *xname = (pv_xavp_name_t*)param->pvn.u.dname;
+	sr_xavp_t *xavp = NULL;
+	sr_xavp_t *avp = NULL;
+	srjson_t *jobj = NULL;
+	srjson_t *jobjt = NULL;
+	struct str_list *keys;
+	struct str_list *k;
+
+	*jobjr = srjson_CreateArray(jdoc);
+	if(*jobjr==NULL)
+	{
+		LM_ERR("cannot create json object\n");
+		return -1;
+	}
+
+	item_name->s = xname->name.s;
+	item_name->len = xname->name.len;
+	xavp = xavp_get_by_index(&xname->name, 0, NULL);
+	if(xavp==NULL)
+	{
+		return 0; /* empty */
+	}
+
+	do
+	{
+		if(xavp->val.type==SR_XTYPE_XAVP)
+		{
+			avp = xavp->val.v.xavp;
+			jobj = srjson_CreateObject(jdoc);
+			if(jobj==NULL)
+			{
+				LM_ERR("cannot create json object\n");
+				return -1;
+			}
+			keys = xavp_get_list_key_names(xavp);
+			if(keys!=NULL)
+			{
+				do
+				{
+					_cfgt_get_obj_avp_vals(keys->s, avp, jdoc, &jobjt);
+					srjson_AddStrItemToObject(jdoc, jobj, keys->s.s,
+						keys->s.len, jobjt);
+					k = keys;
+					keys = keys->next;
+					pkg_free(k);
+					jobjt = NULL;
+				}while(keys!=NULL);
+			}
+		}
+		if(jobj!=NULL)
+		{
+			srjson_AddItemToArray(jdoc, *jobjr, jobj);
+			jobj = NULL;
+		}
+	}while((xavp = xavp_get_next(xavp))!=0);
+
+	return 0;
+}
+
+int cfgt_get_json(struct sip_msg* msg, unsigned int mask, srjson_doc_t *jdoc,
+	srjson_t *head)
+{
+	int i;
+	pv_value_t value;
+	pv_cache_t **_pv_cache = pv_cache_get_table();
+	pv_cache_t *el = NULL;
+	srjson_t *jobj = NULL;
+	str item_name = STR_NULL;
+	static char iname[128];
+
+	if(_pv_cache==NULL)
+	{
+		LM_ERR("cannot access pv_cache\n");
+		return -1;
+	}
+	if(jdoc==NULL){
+		LM_ERR("jdoc is null\n");
+		return -1;
+	}
+	if(head==NULL){
+		LM_ERR("head is null\n");
+		return -1;
+	}
+
+	memset(_cfgt_xavp_dump, 0, sizeof(str*)*CFGT_XAVP_DUMP_SIZE);
+	for(i=0;i<PV_CACHE_SIZE;i++)
+	{
+		el = _pv_cache[i];
+		while(el)
+		{
+			if(!(el->spec.type==PVT_AVP||
+				el->spec.type==PVT_SCRIPTVAR||
+				el->spec.type==PVT_XAVP||
+				el->spec.type==PVT_OTHER)||
+				!((el->spec.type==PVT_AVP&&mask&CFGT_DP_AVP)||
+				(el->spec.type==PVT_XAVP&&mask&CFGT_DP_XAVP)||
+				(el->spec.type==PVT_SCRIPTVAR&&mask&CFGT_DP_SCRIPTVAR)||
+				(el->spec.type==PVT_OTHER&&mask&CFGT_DP_OTHER))||
+				(el->spec.trans!=NULL))
+			{
+				el = el->next;
+				continue;
+			}
+			jobj = NULL;
+			item_name.len = 0;
+			item_name.s = 0;
+			iname[0] = '\0';
+			if(el->spec.type==PVT_AVP)
+			{
+				if(el->spec.pvp.pvi.type==PV_IDX_ALL||
+					(el->spec.pvp.pvi.type==PV_IDX_INT&&el->spec.pvp.pvi.u.ival!=0))
+				{
+					el = el->next;
+					continue;
+				}
+				else
+				{
+					if(_cfgt_get_array_avp_vals(msg, &el->spec.pvp, jdoc, &jobj, &item_name)!=0)
+					{
+						LM_WARN("can't get value[%.*s]\n", el->pvname.len, el->pvname.s);
+						el = el->next;
+						continue;
+					}
+					if(srjson_GetArraySize(jdoc, jobj)==0 && !(mask&CFGT_DP_NULL))
+					{
+						el = el->next;
+						continue;
+					}
+					snprintf(iname, 128, "$avp(%.*s)", item_name.len, item_name.s);
+				}
+			}
+			else if(el->spec.type==PVT_XAVP)
+			{
+				if(_cfgt_xavp_dump_lookup(&el->spec.pvp)!=0)
+				{
+					el = el->next;
+					continue;
+				}
+				if(_cfgt_get_obj_xavp_vals(msg, &el->spec.pvp, jdoc, &jobj, &item_name)!=0)
+				{
+					LM_WARN("can't get value[%.*s]\n", el->pvname.len, el->pvname.s);
+					el = el->next;
+					continue;
+				}
+				if(srjson_GetArraySize(jdoc, jobj)==0 && !(mask&CFGT_DP_NULL))
+				{
+					el = el->next;
+					continue;
+				}
+				snprintf(iname, 128, "$xavp(%.*s)", item_name.len, item_name.s);
+			}
+			else
+			{
+				if(pv_get_spec_value(msg, &el->spec, &value)!=0)
+				{
+					LM_WARN("can't get value[%.*s]\n", el->pvname.len, el->pvname.s);
+					el = el->next;
+					continue;
+				}
+				if(value.flags&(PV_VAL_NULL|PV_VAL_EMPTY|PV_VAL_NONE))
+				{
+					if(mask&CFGT_DP_NULL)
+					{
+						jobj = srjson_CreateNull(jdoc);
+					}
+					else
+					{
+						el = el->next;
+						continue;
+					}
+				}else if(value.flags&(PV_VAL_INT)){
+					jobj = srjson_CreateNumber(jdoc, value.ri);
+				}else if(value.flags&(PV_VAL_STR)){
+					jobj = srjson_CreateStr(jdoc, value.rs.s, value.rs.len);
+				}else {
+					LM_WARN("el->pvname[%.*s] value[%d] unhandled\n", el->pvname.len, el->pvname.s,
+						value.flags);
+					el = el->next;
+					continue;
+				}
+				if(jobj==NULL)
+				{
+					LM_ERR("el->pvname[%.*s] empty json object\n", el->pvname.len,
+						el->pvname.s);
+					goto error;
+				}
+				snprintf(iname, 128, "%.*s", el->pvname.len, el->pvname.s);
+			}
+			if(jobj!=NULL)
+			{
+				srjson_AddItemToObject(jdoc, head, iname, jobj);
+			}
+			el = el->next;
+		}
+	}
+	return 0;
+
+error:
+	srjson_Delete(jdoc, head);
+	return -1;
+}

+ 38 - 0
modules/cfgt/cfgt_json.h

@@ -0,0 +1,38 @@
+/**
+ *
+ * Copyright (C) 2013-2015 Victor Seva (sipwise.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef _CFGT_JSON_H
+#define _CFGT_JSON_H
+
+#include "../../lib/srutils/srjson.h"
+#include "../../route_struct.h"
+
+#define CFGT_DP_NULL       1
+#define CFGT_DP_AVP        2
+#define CFGT_DP_SCRIPTVAR  4
+#define CFGT_DP_XAVP       8
+#define CFGT_DP_OTHER     16
+#define CFGT_DP_ALL       31
+
+int cfgt_get_json(struct sip_msg* msg, unsigned int mask, srjson_doc_t *jdoc,
+	srjson_t *head);
+#endif

+ 112 - 0
modules/cfgt/cfgt_mod.c

@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015 Victor Seva (sipwise.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *
+ */
+
+#include "../../events.h"
+#include "../../script_cb.h"
+#include "../../sr_module.h"
+
+#include "cfgt_mod.h"
+#include "cfgt_int.h"
+#include "cfgt_json.h"
+#include "cfgt.h"
+
+MODULE_VERSION
+
+static int mod_init(void);        /*!< Module initialization function */
+static void destroy(void);        /*!< Module destroy function */
+static int child_init(int rank);  /*!< Per-child init function */
+
+extern int bind_cfgt(cfgt_api_t* api);
+
+/*! flag to protect against wrong initialization */
+unsigned int init_flag = 0;
+extern int cfgt_mask;
+extern str cfgt_basedir;
+extern str cfgt_hdr_prefix;
+
+/*! \brief
+ * Exported functions
+ */
+static cmd_export_t cmds[] = {
+	{"cfgt_bind_cfgt", (cmd_function)bind_cfgt, 1, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0}
+};
+
+/*! \brief
+ * Exported parameters
+ */
+static param_export_t params[] = {
+	{"basedir",  PARAM_STR, &cfgt_basedir},
+	{"mask", INT_PARAM, &cfgt_mask },
+	{"callid_prefix", PARAM_STR, &cfgt_hdr_prefix },
+	{0, 0, 0}
+};
+
+struct module_exports exports = {
+	"cfgt",
+	DEFAULT_DLFLAGS, /*!< dlopen flags */
+	cmds,       /*!< Exported functions */
+	params,     /*!< Export parameters */
+	0,          /*!< exported statistics */
+	0,          /*!< exported MI functions */
+	0,          /*!< exported pseudo-variables */
+	0,          /*!< extra processes */
+	mod_init,   /*!< Module initialization function */
+	0,          /*!< Response function */
+	destroy,    /*!< Destroy function */
+	child_init  /*!< Child initialization function */
+};
+
+/*! \brief
+ * Module initialization function
+ */
+static int mod_init(void)
+{
+	unsigned int ALL = REQUEST_CB+FAILURE_CB+ONREPLY_CB
+		+BRANCH_CB+ONSEND_CB+ERROR_CB+LOCAL_CB+EVENT_CB+BRANCH_FAILURE_CB;
+	if(cfgt_init()<0) return -1;
+	if (register_script_cb(cfgt_pre, PRE_SCRIPT_CB|ALL, 0) != 0)
+	{
+		LM_ERR("could not insert PRE_SCRIPT callback");
+		return -1;
+	}
+	if (register_script_cb(cfgt_post, POST_SCRIPT_CB|ALL, 0) != 0)
+	{
+		LM_ERR("could not insert POST_SCRIPT callback");
+		return -1;
+	}
+
+	init_flag = 1;
+	return 0;
+}
+
+static int child_init(int _rank)
+{
+	return 0;
+}
+
+/*! \brief
+ * Module destroy function
+ */
+static void destroy(void)
+{
+}

+ 29 - 0
modules/cfgt/cfgt_mod.h

@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 Victor Seva (sipwise.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *
+ */
+
+#ifndef _CFGT_MOD_H
+#define _CFGT_MOD_H
+
+/*! flag to protect against wrong initialization */
+extern unsigned int init_flag;
+
+#endif

+ 4 - 0
modules/cfgt/doc/Makefile

@@ -0,0 +1,4 @@
+docs = cfgt.xml
+
+docbook_dir = ../../../docbook
+include $(docbook_dir)/Makefile.module

+ 36 - 0
modules/cfgt/doc/cfgt.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+	<title>cfgt Module</title>
+	<productname class="trade">&kamailioname;</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Victor</firstname>
+		<surname>Seva</surname>
+		<affiliation><orgname>sipwise.com</orgname></affiliation>
+	    </author>
+	    <editor>
+		<firstname>Victor</firstname>
+		<surname>Seva</surname>
+		    <email>[email protected]</email>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2015</year>
+	    <holder>Victor Seva (sipwise.com)</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+
+	<xi:include href="cfgt_admin.xml"/>
+
+</book>

+ 183 - 0
modules/cfgt/doc/cfgt_admin.xml

@@ -0,0 +1,183 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<!-- Module User's Guide -->
+
+<chapter>
+
+	<title>&adminguide;</title>
+
+	<section>
+	<title>Overview</title>
+	<para>
+		This module provides a report of the way &kamailioname; configuration
+		has been executed as part of a unit test for different
+		SIP scenarios.
+	</para>
+	<para>
+		In order to identify different scenarios a prefix string should be
+		used inside the Call-ID header.
+	</para>
+	</section>
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>None</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>None</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+	<section>
+	<title>Parameters</title>
+	<section id="cfg.p.basedir">
+		<title><varname>basedir</varname> (string)</title>
+		<para>
+			Control where the config reports should be stored. The dir must
+			exist and &kamailioname; must have permissions to write on it.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>/tmp</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>cfgtrace</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("cfgt", "basedir", "/var/run/kamailio/cfgtest")
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section id="cfgt.p.mask">
+		<title><varname>mask</varname> (int)</title>
+		<itemizedlist>
+			<para><emphasis>mask</emphasis> - Control the type of vars it should
+			display in the report:
+			</para>
+			<itemizedlist>
+			<listitem><para>
+			  1 - dump null values
+			</para></listitem>
+			<listitem><para>
+			  2 - dump avp vars
+			</para></listitem>
+			<listitem><para>
+			  4 - dump script vars
+			</para></listitem>
+			<listitem><para>
+			  8 - dump xavp vars
+			</para></listitem>
+			<listitem><para>
+			  16 - dump DP_OTHER vars
+			</para></listitem>
+			<listitem><para>
+			  32 - dump ALL vars
+			</para></listitem>
+			</itemizedlist>
+		</itemizedlist>
+		<para>
+		<emphasis>
+			Default value is <quote>32</quote> (ALL).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>mask</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+# dump xavp(8) and avp(4) vars
+modparam("cfgt", "mask", 12)
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section id="cfgt.p.callid_prefix">
+		<title><varname>callid_prefix</varname> (string)</title>
+		<para>
+			Prefix used to identify test scenario messages. Last char of the
+			string will be used as delimiter for the scenario ID. With parameter
+			set to <quote>NGCP%</quote> and Call-ID <quote>NGCP%123%456</quote> 
+			the scenario identified will be <quote>123</quote>.
+		</para>
+		<para>
+		<emphasis>
+			Default value is <quote>NGCP%</quote>
+			(using <quote>%</quote> as delimiter).
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>callid_prefix</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+# using '%' as delimiter
+modparam("cfgt", "callid_prefix", "TEST-ID%")
+...
+</programlisting>
+		</example>
+	</section>
+
+	</section>
+	<section>
+		<title>Usage</title>
+		<para>
+		This module is used by the <emphasis>debugger</emphasis> module, so it must be loaded
+		first. To enable the generation of the reports, the <emphasis>debugger</emphasis> module
+		must be configured by setting the <emphasis>cfgtest</emphasis> parameter to "1".
+		</para>
+		<para>
+		Example of configuration:
+		</para>
+<programlisting  format="linespecific">
+...
+#!ifdef WITH_DEBUG
+loadmodule "cfgt.so"
+loadmodule "debugger.so"
+#!endif
+...
+#!ifdef WITH_DEBUG
+# ----- cfgt params -----
+modparam("cfgt", "basedir", "/tmp/kamailio/cfgtest")
+modparam("cfgt", "callid_prefix", "TRACE-ID%")
+modparam("cfgt", "mask", 32)
+
+# ----- debugger params -----
+modparam("debugger", "cfgtrace", 1)
+modparam("debugger", "log_level_name", "exec")
+
+modparam("debugger", "cfgtest", 1)
+#!endif
+...
+</programlisting>
+    </section>
+</chapter>

+ 0 - 24
modules/cnxcc/cnxcc.c

@@ -28,30 +28,6 @@
 
 
 #include "cnxcc.h"
 #include "cnxcc.h"
 
 
-inline void get_datetime(str *dest)
-{
-	timestamp2isodt(dest, get_current_timestamp());
-}
-
-inline unsigned int get_current_timestamp()
-{
-	return time(NULL);
-}
-
-inline int timestamp2isodt(str *dest, unsigned int timestamp)
-{
-	time_t  		tim;
-	struct tm 		*tmPtr;
-
-	tim 		= timestamp;
-	tmPtr 		= localtime(&tim);
-
-	strftime( dest->s, DATETIME_SIZE, "%Y-%m-%d %H:%M:%S", tmPtr);
-	dest->len	= DATETIME_LENGTH;
-
-	return 0;
-}
-
 double str2double(str *string)
 double str2double(str *string)
 {
 {
 	char buffer[string->len + 1];
 	char buffer[string->len + 1];

+ 24 - 3
modules/cnxcc/cnxcc.h

@@ -31,9 +31,30 @@
 #define DATETIME_LENGTH		DATETIME_SIZE - 1
 #define DATETIME_LENGTH		DATETIME_SIZE - 1
 
 
 
 
-inline void get_datetime(str *dest);
-inline unsigned int get_current_timestamp();
-inline int timestamp2isodt(str *dest, unsigned int timestamp);
+static inline unsigned int get_current_timestamp()
+{
+	return time(NULL);
+}
+
+static inline int timestamp2isodt(str *dest, unsigned int timestamp)
+{
+	time_t  		tim;
+	struct tm 		*tmPtr;
+
+	tim 		= timestamp;
+	tmPtr 		= localtime(&tim);
+
+	strftime( dest->s, DATETIME_SIZE, "%Y-%m-%d %H:%M:%S", tmPtr);
+	dest->len	= DATETIME_LENGTH;
+
+	return 0;
+}
+
+static inline void get_datetime(str *dest)
+{
+	timestamp2isodt(dest, get_current_timestamp());
+}
+
 double str2double(str *string);
 double str2double(str *string);
 
 
 #endif /* _CNXCC_H */
 #endif /* _CNXCC_H */

+ 37 - 0
modules/crypto/Makefile

@@ -0,0 +1,37 @@
+#
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=crypto.so
+
+ifeq ($(CROSS_COMPILE),)
+SSL_BUILDER=$(shell \
+	if pkg-config --exists libssl; then \
+		echo 'pkg-config libssl'; \
+	fi)
+
+ifneq ($(SSL_BUILDER),)
+SSL_BUILDER+=$(shell \
+	if pkg-config --exists libcrypto; then \
+		echo 'libcrypto'; \
+	fi)
+endif
+
+endif
+
+ifneq ($(SSL_BUILDER),)
+	DEFS += $(shell $(SSL_BUILDER) --cflags)
+	LIBS += $(shell $(SSL_BUILDER) --libs)
+else
+	DEFS += -I$(LOCALBASE)/ssl/include
+	LIBS += -L$(LOCALBASE)/lib -L$(LOCALBASE)/ssl/lib \
+			-L$(LOCALBASE)/lib64 -L$(LOCALBASE)/ssl/lib64 \
+			-lssl -lcrypto
+endif
+
+DEFS+=-DKAMAILIO_MOD_INTERFACE
+
+include ../../Makefile.modules
+

+ 106 - 0
modules/crypto/README

@@ -0,0 +1,106 @@
+CRYPTO Module
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+Edited by
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+   Copyright © 2016 asipto.com
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Functions
+
+              3.1. crypto_aes_encrypt(text, key, res)
+              3.2. crypto_aes_decrypt(text, key, res)
+
+   List of Examples
+
+   1.1. crypto_aes_encrypt usage
+   1.2. crypto_aes_decrypt usage
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Functions
+
+        3.1. crypto_aes_encrypt(text, key, res)
+        3.2. crypto_aes_decrypt(text, key, res)
+
+1. Overview
+
+   This module provides various cryptography tools for use in Kamailio
+   configuration file.
+
+   It relies on OpenSSL libraries for cryptographic operations (libssl,
+   libcrypto).
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * none.
+
+2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * libcrypto - part of OpenSSL project
+
+3. Functions
+
+   3.1. crypto_aes_encrypt(text, key, res)
+   3.2. crypto_aes_decrypt(text, key, res)
+
+3.1. crypto_aes_encrypt(text, key, res)
+
+   Encrypts the text with the key using AES encryption algorithm. The
+   result is encoded in base64 format and stored in res. The parameter res
+   must be a read-write variables. The parameters text and key can be
+   static strings or strings with variables (dynamic strings).
+
+   This function can be used from ANY_ROUTE.
+
+   Example 1.1. crypto_aes_encrypt usage
+...
+crypto_aes_encrypt("$rb", "my-secret-key", "$var(encrypted)");
+...
+
+3.2. crypto_aes_decrypt(text, key, res)
+
+   Decrypts the text with the key using AES encryption algorithm. The text
+   has to be encoded in base64 format. The parameter res must be a
+   read-write variables. The parameters text and key can be static strings
+   or strings with variables (dynamic strings).
+
+   This function can be used from ANY_ROUTE.
+
+   Example 1.2. crypto_aes_decrypt usage
+...
+crypto_aes_decrypt("$var(encrypted)", "my-secret-key", "$var(text)");
+...

+ 383 - 0
modules/crypto/crypto_mod.c

@@ -0,0 +1,383 @@
+/**
+ * Copyright (C) 2011 Daniel-Constantin Mierla (asipto.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../mod_fix.h"
+#include "../../pvapi.h"
+#include "../../lvalue.h"
+#include "../../basex.h"
+
+#include <openssl/evp.h>
+
+#define AES_BLOCK_SIZE 256
+
+MODULE_VERSION
+
+int crypto_aes_init(unsigned char *key_data, int key_data_len,
+		unsigned char *salt, EVP_CIPHER_CTX *e_ctx, EVP_CIPHER_CTX *d_ctx);
+unsigned char *crypto_aes_encrypt(EVP_CIPHER_CTX *e, unsigned char *plaintext,
+		int *len);
+unsigned char *crypto_aes_decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext,
+		int *len);
+
+static int  mod_init(void);
+static int  child_init(int);
+static void mod_destroy(void);
+
+static int w_crypto_aes_encrypt(sip_msg_t* msg, char* inb, char* keyb, char* outb);
+static int fixup_crypto_aes_encrypt(void** param, int param_no);
+static int w_crypto_aes_decrypt(sip_msg_t* msg, char* inb, char* keyb, char* outb);
+static int fixup_crypto_aes_decrypt(void** param, int param_no);
+
+
+static cmd_export_t cmds[]={
+	{"crypto_aes_encrypt", (cmd_function)w_crypto_aes_encrypt, 3,
+		fixup_crypto_aes_encrypt, 0, ANY_ROUTE},
+	{"crypto_aes_decrypt", (cmd_function)w_crypto_aes_decrypt, 3,
+		fixup_crypto_aes_decrypt, 0, ANY_ROUTE},
+	{0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[]={
+	{0, 0, 0}
+};
+
+struct module_exports exports = {
+	"crypto",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,
+	params,
+	0,
+	0,              /* exported MI functions */
+	0,              /* exported pseudo-variables */
+	0,              /* extra processes */
+	mod_init,       /* module initialization function */
+	0,              /* response function */
+	mod_destroy,    /* destroy function */
+	child_init      /* per child init function */
+};
+
+
+/**
+ * @brief Initialize crypto module function
+ */
+static int mod_init(void)
+{
+	return 0;
+}
+
+/**
+ * @brief Initialize crypto module children
+ */
+static int child_init(int rank)
+{
+	return 0;
+}
+
+/**
+ * destroy module function
+ */
+static void mod_destroy(void)
+{
+	return;
+}
+
+/**
+ *
+ */
+static int w_crypto_aes_encrypt(sip_msg_t* msg, char* inb, char* keyb, char* outb)
+{
+	str ins;
+	str keys;
+	pv_spec_t *dst;
+	pv_value_t val;
+	EVP_CIPHER_CTX en;
+	str etext;
+	unsigned char salt[] = {1,2,3,4,5,6,7,8};
+
+	if (fixup_get_svalue(msg, (gparam_t*)inb, &ins) != 0) {
+		LM_ERR("cannot get input value\n");
+		return -1;
+	}
+	if (fixup_get_svalue(msg, (gparam_t*)keyb, &keys) != 0) {
+		LM_ERR("cannot get key value\n");
+		return -1;
+	}
+	dst = (pv_spec_t*)outb;
+
+	/* gen key and iv. init the cipher ctx object */
+	if (crypto_aes_init((unsigned char *)keys.s, keys.len, salt, &en, NULL)) {
+		LM_ERR("couldn't initialize AES cipher\n");
+		return -1;
+	}
+	etext.len = ins.len;
+	etext.s = (char *)crypto_aes_encrypt(&en, (unsigned char *)ins.s, &etext.len);
+
+	memset(&val, 0, sizeof(pv_value_t));
+	val.rs.s = pv_get_buffer();
+	val.rs.len = base64_enc((unsigned char *)etext.s, etext.len,
+					(unsigned char *)val.rs.s, pv_get_buffer_size()-1);
+	if (val.rs.len < 0) {
+		LM_ERR("base64 output of encrypted value is too large (need %d)\n",
+				-val.rs.len);
+		goto error;
+	}
+	LM_DBG("base64 encrypted result: [%.*s]\n", val.rs.len, val.rs.s);
+	val.flags = PV_VAL_STR;
+	dst->setf(msg, &dst->pvp, (int)EQ_T, &val);
+
+	free(etext.s);
+	EVP_CIPHER_CTX_cleanup(&en);
+	return 1;
+
+error:
+	free(etext.s);
+	EVP_CIPHER_CTX_cleanup(&en);
+	return -1;
+}
+
+/**
+ *
+ */
+static int fixup_crypto_aes_encrypt(void** param, int param_no)
+{
+	if(param_no==1 || param_no==2) {
+		if(fixup_spve_null(param, 1)<0)
+			return -1;
+		return 0;
+	} else if(param_no==3) {
+		if (fixup_pvar_null(param, 1) != 0) {
+			LM_ERR("failed to fixup result pvar\n");
+			return -1;
+		}
+		if (((pv_spec_t *)(*param))->setf == NULL) {
+			LM_ERR("result pvar is not writeble\n");
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/**
+ *
+ */
+static int w_crypto_aes_decrypt(sip_msg_t* msg, char* inb, char* keyb, char* outb)
+{
+	return 1;
+}
+
+/**
+ *
+ */
+static int fixup_crypto_aes_decrypt(void** param, int param_no)
+{
+	if(param_no==1 || param_no==2) {
+		if(fixup_spve_null(param, 1)<0)
+			return -1;
+		return 0;
+	} else if(param_no==3) {
+		if (fixup_pvar_null(param, 1) != 0) {
+			LM_ERR("failed to fixup result pvar\n");
+			return -1;
+		}
+		if (((pv_spec_t *)(*param))->setf == NULL) {
+			LM_ERR("result pvar is not writeble\n");
+			return -1;
+		}
+	}
+	return 0;
+}
+
+
+/**
+ * Create an 256 bit key and IV using the supplied key_data and salt.
+ * Fills in the encryption and decryption ctx objects and returns 0 on success
+ */
+int crypto_aes_init(unsigned char *key_data, int key_data_len,
+		unsigned char *salt, EVP_CIPHER_CTX *e_ctx, EVP_CIPHER_CTX *d_ctx)
+{
+	int i, nrounds = 5;
+	unsigned char key[32], iv[32];
+
+	/*
+	 * Gen key & IV for AES 256 CBC mode. A SHA1 digest is used to hash
+	 * the supplied key material.
+	 * nrounds is the number of times the we hash the material. More rounds
+	 * are more secure but slower.
+	 */
+	i = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), salt,
+			key_data, key_data_len, nrounds, key, iv);
+	if (i != 32) {
+		LM_ERR("key size is %d bits - should be 256 bits\n", i);
+		return -1;
+	}
+
+	for(int x = 0; x<32; ++x)
+		LM_DBG("key: %x iv: %x \n", key[x], iv[x]);
+
+	for(int x = 0; x<8; ++x)
+		LM_DBG("salt: %x\n", salt[x]);
+
+	if(e_ctx) {
+		EVP_CIPHER_CTX_init(e_ctx);
+		EVP_EncryptInit_ex(e_ctx, EVP_aes_256_cbc(), NULL, key, iv);
+	}
+	if(d_ctx) {
+		EVP_CIPHER_CTX_init(d_ctx);
+		EVP_DecryptInit_ex(d_ctx, EVP_aes_256_cbc(), NULL, key, iv);
+	}
+
+	return 0;
+}
+
+/*
+ * Encrypt *len bytes of data
+ * All data going in & out is considered binary (unsigned char[])
+ */
+unsigned char *crypto_aes_encrypt(EVP_CIPHER_CTX *e, unsigned char *plaintext,
+		int *len)
+{
+	/* max ciphertext len for a n bytes of plaintext is
+	 * n + AES_BLOCK_SIZE -1 bytes */
+	int c_len = *len + AES_BLOCK_SIZE - 1, f_len = 0;
+	unsigned char *ciphertext = (unsigned char *)malloc(c_len);
+
+	/* allows reusing of 'e' for multiple encryption cycles */
+	if(!EVP_EncryptInit_ex(e, NULL, NULL, NULL, NULL)){
+		LM_ERR("failure in EVP_EncryptInit_ex \n");
+		return NULL;
+	}
+
+	/* update ciphertext, c_len is filled with the length of ciphertext
+	 * generated, *len is the size of plaintext in bytes */
+	if(!EVP_EncryptUpdate(e, ciphertext, &c_len, plaintext, *len)){
+		LM_ERR("failure in EVP_EncryptUpdate \n");
+		return NULL;
+	}
+
+	/* update ciphertext with the final remaining bytes */
+	if(!EVP_EncryptFinal_ex(e, ciphertext+c_len, &f_len)){
+		LM_ERR("failure in EVP_EncryptFinal_ex \n");
+		return NULL;
+	}
+
+	*len = c_len + f_len;
+	return ciphertext;
+}
+
+/*
+ * Decrypt *len bytes of ciphertext
+ */
+unsigned char *crypto_aes_decrypt(EVP_CIPHER_CTX *e, unsigned char *ciphertext,
+		int *len)
+{
+	/* plaintext will always be equal to or lesser than length of ciphertext*/
+	int p_len = *len, f_len = 0;
+	unsigned char *plaintext = (unsigned char *)malloc(p_len);
+
+	if(!EVP_DecryptInit_ex(e, NULL, NULL, NULL, NULL)){
+		LM_ERR("failure in EVP_DecryptInit_ex \n");
+		return NULL;
+	}
+
+	if(!EVP_DecryptUpdate(e, plaintext, &p_len, ciphertext, *len)){
+		LM_ERR("failure in EVP_DecryptUpdate\n");
+		return NULL;
+	}
+
+	if(!EVP_DecryptFinal_ex(e, plaintext+p_len, &f_len)){
+		LM_ERR("failure in EVP_DecryptFinal_ex\n");
+		return NULL;
+	}
+
+	*len = p_len + f_len;
+	return plaintext;
+}
+
+/**
+ * testing function
+ */
+int crypto_aes_test(void)
+{
+	/* "opaque" encryption, decryption ctx structures
+	 * that libcrypto uses to record status of enc/dec operations */
+	EVP_CIPHER_CTX en, de;
+
+
+	/* The salt paramter is used as a salt in the derivation:
+	 * it should point to an 8 byte buffer or NULL if no salt is used. */
+	unsigned char salt[] = {1,2,3,4,5,6,7,8};
+
+	unsigned char *key_data;
+	int key_data_len, i;
+	char *input[] = {"Kamailio - The Open Source SIP Server",
+		"Thank you for flying Kamailio!",
+		"100 Trying\nYour call is important to us",
+		NULL
+	};
+
+	/* the key_data for testing */
+	key_data = (unsigned char *)"kamailio-sip-server";
+	key_data_len = strlen((const char *)key_data);
+
+	/* gen key and iv. init the cipher ctx object */
+	if (crypto_aes_init(key_data, key_data_len, salt, &en, &de)) {
+		LM_ERR("couldn't initialize AES cipher\n");
+		return -1;
+	}
+
+	/* encrypt and decrypt each input string and compare with the original */
+	for (i = 0; input[i]; i++) {
+		char *plaintext;
+		unsigned char *ciphertext;
+		int olen, len;
+
+		/* The enc/dec functions deal with binary data and not C strings.
+		 * strlen() will return length of the string without counting the '\0'
+		 * string marker. We always pass in the marker byte to the
+		 * encrypt/decrypt functions so that after decryption we end up with
+		 * a legal C string */
+		olen = len = strlen(input[i])+1;
+
+		ciphertext = crypto_aes_encrypt(&en, (unsigned char *)input[i], &len);
+		plaintext = (char *)crypto_aes_decrypt(&de, ciphertext, &len);
+
+		if (strncmp(plaintext, input[i], olen))
+			LM_ERR("FAIL: enc/dec failed for \"%s\"\n", input[i]);
+		else
+			LM_NOTICE("OK: enc/dec ok for \"%s\"\n", plaintext);
+
+		free(ciphertext);
+		free(plaintext);
+	}
+
+	EVP_CIPHER_CTX_cleanup(&de);
+	EVP_CIPHER_CTX_cleanup(&en);
+
+	return 0;
+}

+ 4 - 0
modules/crypto/doc/Makefile

@@ -0,0 +1,4 @@
+docs = crypto.xml
+
+docbook_dir = ../../../docbook
+include $(docbook_dir)/Makefile.module

+ 37 - 0
modules/crypto/doc/crypto.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+	<title>CRYPTO Module</title>
+	<productname class="trade">sip-router.org</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<email>[email protected]</email>
+	    </author>
+	    <editor>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<email>[email protected]</email>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2016</year>
+	    <holder>asipto.com</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+
+    <xi:include href="crypto_admin.xml"/>
+
+
+</book>

+ 107 - 0
modules/crypto/doc/crypto_admin.xml

@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module User's Guide -->
+
+<chapter>
+
+	<title>&adminguide;</title>
+
+	<section>
+	<title>Overview</title>
+	<para>
+		This module provides various cryptography tools for use
+		in &kamailio; configuration file.
+	</para>
+	<para>
+		It relies on OpenSSL libraries for cryptographic operations
+		(libssl, libcrypto).
+	</para>
+	</section>
+
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>none</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>libcrypto</emphasis> - part of OpenSSL project
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+
+	<section>
+	<title>Functions</title>
+	<section id="async.f.crypto_aes_encrypt">
+	    <title>
+		<function moreinfo="none">crypto_aes_encrypt(text, key, res)</function>
+	    </title>
+	    <para>
+		Encrypts the text with the key using AES encryption algorithm. The
+		result is encoded in base64 format and stored in res. The parameter
+		res must be a read-write variables. The parameters text and key can
+		be static strings or strings with variables (dynamic strings).
+		</para>
+		<para>
+		This function can be used from ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>crypto_aes_encrypt</function> usage</title>
+		<programlisting format="linespecific">
+...
+crypto_aes_encrypt("$rb", "my-secret-key", "$var(encrypted)");
+...
+</programlisting>
+	    </example>
+	</section>
+
+	<section id="async.f.crypto_aes_decrypt">
+	    <title>
+		<function moreinfo="none">crypto_aes_decrypt(text, key, res)</function>
+	    </title>
+	    <para>
+		Decrypts the text with the key using AES encryption algorithm. The
+		text has to be encoded in base64 format. The parameter
+		res must be a read-write variables. The parameters text and key can
+		be static strings or strings with variables (dynamic strings).
+		</para>
+		<para>
+		This function can be used from ANY_ROUTE.
+		</para>
+		<example>
+		<title><function>crypto_aes_decrypt</function> usage</title>
+		<programlisting format="linespecific">
+...
+crypto_aes_decrypt("$var(encrypted)", "my-secret-key", "$var(text)");
+...
+</programlisting>
+	    </example>
+	</section>
+	</section>
+</chapter>

+ 4 - 0
modules/curl/TODO.txt

@@ -48,8 +48,12 @@ The curl module - todo
 
 
 
 
 - Async - event_route when done
 - Async - event_route when done
+	(with and without suspending transaction)
 	Need background task process to run curl request
 	Need background task process to run curl request
 	event_route[curl:connectioncomplete]
 	event_route[curl:connectioncomplete]
 	$curlcon == connection
 	$curlcon == connection
 	$curl == URL
 	$curl == URL
 	$curlres == result
 	$curlres == result
+
+- API for other modules - done by Hugh Waite
+	- Needs documentation

+ 28 - 1
modules/curl/curl_api.h

@@ -1,4 +1,31 @@
-#ifndef _CURL_CPI_H_
+/*
+ * Copyright (C) 2015 Hugh Waite
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+/*!
+ * \file
+ * \brief Kamailio curl :: Core API include file
+ * \ingroup curl
+ * Module: \ref curl
+ */
+#ifndef _CURL_API_H_
 #define _CURL_API_H_
 #define _CURL_API_H_
 
 
 #include "../../sr_module.h"
 #include "../../sr_module.h"

+ 7 - 3
modules/db_mysql/km_dbase.c

@@ -80,9 +80,13 @@ static int db_mysql_submit_query(const db1_con_t* _h, const str* _s)
 	if (my_ping_interval) {
 	if (my_ping_interval) {
 		t = time(0);
 		t = time(0);
 		if ((t - CON_TIMESTAMP(_h)) > my_ping_interval) {
 		if ((t - CON_TIMESTAMP(_h)) > my_ping_interval) {
-			if (mysql_ping(CON_CONNECTION(_h))) {
-				LM_WARN("driver error on ping: %s\n", mysql_error(CON_CONNECTION(_h)));
-				counter_inc(mysql_cnts_h.driver_err);
+			for (i=0; i < (db_mysql_auto_reconnect ? 3 : 1); i++) {
+				if (mysql_ping(CON_CONNECTION(_h))) {
+					LM_WARN("driver error on ping: %s\n", mysql_error(CON_CONNECTION(_h)));
+					counter_inc(mysql_cnts_h.driver_err);
+				} else {
+					break;
+				}
 			}
 			}
 		}
 		}
 		/*
 		/*

+ 129 - 0
modules/db_text/dbt_base.c

@@ -617,3 +617,132 @@ error:
 	return -1;
 	return -1;
 }
 }
 
 
+int dbt_replace(db1_con_t* _h, db_key_t* _k, db_val_t* _v,
+	      int _n, int _nk, int _m)
+{
+	dbt_table_p _tbc = NULL;
+	dbt_row_p _drp = NULL;
+	int i, j;
+	int *lkey=NULL, *lres=NULL;
+
+	if (!_h || !CON_TABLE(_h) || _nk <= 0)
+	{
+		LM_ERR("invalid parameters\n");
+		return -1;
+	}
+
+	((dbt_con_p)_h->tail)->affected = 0;
+
+	/* lock database */
+	_tbc = dbt_db_get_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h));
+	if(!_tbc)
+	{
+		LM_ERR("table %.*s does not exist!\n", CON_TABLE(_h)->len, CON_TABLE(_h)->s);
+		return -1;
+	}
+
+	if(_k)
+	{
+		lkey = dbt_get_refs(_tbc, _k, _nk);
+		if(!lkey)
+			goto error;
+	}
+	lres = dbt_get_refs(_tbc, _k, _n);
+	if(!lres)
+		goto error;
+	_drp = _tbc->rows;
+	while(_drp)
+	{
+		if(dbt_row_match(_tbc, _drp, lkey, NULL, _v, _nk))
+		{ // update fields
+			for(i=0; i<_n; i++)
+			{
+				if(dbt_is_neq_type(_tbc->colv[lres[i]]->type, _v[i].type))
+				{
+					LM_ERR("incompatible types!\n");
+					goto error;
+				}
+
+				if(dbt_row_update_val(_drp, &(_v[i]),
+							_tbc->colv[lres[i]]->type, lres[i]))
+				{
+					LM_ERR("cannot set v[%d] in c[%d]!\n",
+							i, lres[i]);
+					goto error;
+				}
+			}
+
+			((dbt_con_p)_h->tail)->affected++;
+
+		}
+		_drp = _drp->next;
+	}
+
+	if(((dbt_con_p)_h->tail)->affected == 0) {
+		_drp = dbt_row_new(_tbc->nrcols);
+		if(!_drp)
+		{
+			LM_ERR("no shm memory for a new row!!\n");
+			goto error;
+		}
+
+		for(i=0; i<_n; i++)
+		{
+			j = lres[i];
+			if(dbt_is_neq_type(_tbc->colv[j]->type, _v[i].type))
+			{
+				LM_ERR("incompatible types v[%d] - c[%d]!\n", i, j);
+				goto error;
+			}
+			if(_v[i].type == DB1_STRING && !_v[i].nul)
+				_v[i].val.str_val.len = strlen(_v[i].val.string_val);
+			if(dbt_row_set_val(_drp, &(_v[i]), _tbc->colv[j]->type, j))
+			{
+				LM_ERR("cannot set v[%d] in c[%d]!\n", i, j);
+				goto error;
+			}
+		}
+
+		if(dbt_table_add_row(_tbc, _drp))
+		{
+			LM_ERR("cannot insert the new row!!\n");
+			goto error;
+		}
+
+		((dbt_con_p)_h->tail)->affected = 1;
+
+	}
+
+	if( ((dbt_con_p)_h->tail)->affected )
+		dbt_table_update_flags(_tbc, DBT_TBFL_MODI, DBT_FL_SET, 1);
+
+	/* dbt_print_table(_tbc, NULL); */
+
+	/* unlock database */
+	dbt_release_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h));
+
+	if(lkey)
+		pkg_free(lkey);
+	if(lres)
+		pkg_free(lres);
+
+    return 0;
+
+error:
+
+	if(lkey)
+		pkg_free(lkey);
+	if(lres)
+		pkg_free(lres);
+	if(_drp) // free row
+		dbt_row_free(_tbc, _drp);
+
+	/* unlock database */
+	dbt_release_table(DBT_CON_CONNECTION(_h), CON_TABLE(_h));
+
+	LM_ERR("failed to update the table!\n");
+
+	return -1;
+}
+
+

+ 2 - 2
modules/db_text/dbt_lib.c

@@ -443,13 +443,13 @@ int dbt_is_neq_type(db_type_t _t0, db_type_t _t1)
 		case DB1_DOUBLE:
 		case DB1_DOUBLE:
 			break;
 			break;
 		case DB1_STRING:
 		case DB1_STRING:
-			if(_t0==DB1_STR)
+			if(_t0==DB1_STR || _t0==DB1_BLOB)
 				return 0;
 				return 0;
 		case DB1_STR:
 		case DB1_STR:
 			if(_t0==DB1_STRING || _t0==DB1_BLOB)
 			if(_t0==DB1_STRING || _t0==DB1_BLOB)
 				return 0;
 				return 0;
 		case DB1_BLOB:
 		case DB1_BLOB:
-			if(_t0==DB1_STR)
+			if(_t0==DB1_STR || _t0==DB1_STRING)
 				return 0;
 				return 0;
 		case DB1_BITMAP:
 		case DB1_BITMAP:
 			if (_t0==DB1_INT)
 			if (_t0==DB1_INT)

+ 1 - 1
modules/db_text/dbt_raw_util.c

@@ -216,7 +216,7 @@ int dbt_build_where(char* where, db_key_t** _k, db_op_t** _o, db_val_t** _v)
 		//      needs changes in dbt_query / dbt_row_match
 		//      needs changes in dbt_query / dbt_row_match
 
 
 		l = matches[2].rm_eo - matches[2].rm_so;
 		l = matches[2].rm_eo - matches[2].rm_so;
-		_k1[idx] = pkg_malloc(sizeof(db_key_t));
+		_k1[idx] = pkg_malloc(sizeof(str));
 		_k1[idx]->len = l;
 		_k1[idx]->len = l;
 		_k1[idx]->s = pkg_malloc(sizeof(char) * (l+1));
 		_k1[idx]->s = pkg_malloc(sizeof(char) * (l+1));
 		strncpy(_k1[idx]->s, buffer+matches[2].rm_so, l);
 		strncpy(_k1[idx]->s, buffer+matches[2].rm_so, l);

+ 2 - 1
modules/db_text/dbtext.c

@@ -125,9 +125,10 @@ int dbt_bind_api(db_func_t *dbb)
 	dbb->insert      = (db_insert_f)dbt_insert;
 	dbb->insert      = (db_insert_f)dbt_insert;
 	dbb->delete      = (db_delete_f)dbt_delete; 
 	dbb->delete      = (db_delete_f)dbt_delete; 
 	dbb->update      = (db_update_f)dbt_update;
 	dbb->update      = (db_update_f)dbt_update;
+	dbb->replace     = (db_replace_f)dbt_replace;
 	dbb->affected_rows = (db_affected_rows_f) dbt_affected_rows;
 	dbb->affected_rows = (db_affected_rows_f) dbt_affected_rows;
 	dbb->raw_query   = (db_raw_query_f) dbt_raw_query;
 	dbb->raw_query   = (db_raw_query_f) dbt_raw_query;
-	dbb->cap         = DB_CAP_ALL | DB_CAP_AFFECTED_ROWS | DB_CAP_RAW_QUERY;
+	dbb->cap         = DB_CAP_ALL | DB_CAP_AFFECTED_ROWS | DB_CAP_RAW_QUERY | DB_CAP_REPLACE;
 
 
 	return 0;
 	return 0;
 }
 }

+ 6 - 0
modules/db_text/dbtext.h

@@ -81,6 +81,12 @@ int dbt_delete(db1_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v, int _n);
 int dbt_update(db1_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v,
 int dbt_update(db1_con_t* _h, db_key_t* _k, db_op_t* _o, db_val_t* _v,
 	      db_key_t* _uk, db_val_t* _uv, int _n, int _un);
 	      db_key_t* _uk, db_val_t* _uv, int _n, int _un);
 
 
+/*
+ * replace a row in table
+ */
+int dbt_replace(db1_con_t* _h, db_key_t* _k, db_val_t* _v,
+	      int _n, int _nk, int _m);
+
 /*
 /*
  * Affected rows
  * Affected rows
  */
  */

+ 1 - 0
modules/debugger/Makefile

@@ -10,4 +10,5 @@ DEFS+=-DKAMAILIO_MOD_INTERFACE
 
 
 SERLIBPATH=../../lib
 SERLIBPATH=../../lib
 SER_LIBS+=$(SERLIBPATH)/srutils/srutils
 SER_LIBS+=$(SERLIBPATH)/srutils/srutils
+SER_LIBS+=$(SERLIBPATH)/kmi/kmi
 include ../../Makefile.modules
 include ../../Makefile.modules

+ 31 - 14
modules/debugger/README

@@ -41,6 +41,7 @@ Daniel-Constantin Mierla
               3.14. log_assign (int)
               3.14. log_assign (int)
               3.15. cfgpkgcheck (int)
               3.15. cfgpkgcheck (int)
               3.16. reset_msgid (int)
               3.16. reset_msgid (int)
+              3.17. cfgtest (int)
 
 
         4. Functions
         4. Functions
 
 
@@ -76,9 +77,10 @@ Daniel-Constantin Mierla
    1.14. Set log_assign parameter
    1.14. Set log_assign parameter
    1.15. Set cfgpkgcheck parameter
    1.15. Set cfgpkgcheck parameter
    1.16. Set reset_msgid parameter
    1.16. Set reset_msgid parameter
-   1.17. dbg_breakpoint usage
-   1.18. dbg_pv_dump usage
-   1.19. dbg_sip_msg usage
+   1.17. Set cfgtest parameter
+   1.18. dbg_breakpoint usage
+   1.19. dbg_pv_dump usage
+   1.20. dbg_sip_msg usage
 
 
 Chapter 1. Admin Guide
 Chapter 1. Admin Guide
 
 
@@ -108,6 +110,7 @@ Chapter 1. Admin Guide
         3.14. log_assign (int)
         3.14. log_assign (int)
         3.15. cfgpkgcheck (int)
         3.15. cfgpkgcheck (int)
         3.16. reset_msgid (int)
         3.16. reset_msgid (int)
+        3.17. cfgtest (int)
 
 
    4. Functions
    4. Functions
 
 
@@ -183,6 +186,7 @@ Chapter 1. Admin Guide
    3.14. log_assign (int)
    3.14. log_assign (int)
    3.15. cfgpkgcheck (int)
    3.15. cfgpkgcheck (int)
    3.16. reset_msgid (int)
    3.16. reset_msgid (int)
+   3.17. cfgtest (int)
 
 
 3.1. cfgtrace (int)
 3.1. cfgtrace (int)
 
 
@@ -389,13 +393,26 @@ modparam("debugger", "cfgpkgcheck", 1)
 modparam("debugger", "reset_msgid", 1)
 modparam("debugger", "reset_msgid", 1)
 ...
 ...
 
 
+3.17. cfgtest (int)
+
+   Control whether the cfgt module is enabled or disabled at startup.
+   Module cfgt needs to be loaded before.
+
+   Default value is "0" (disabled).
+
+   Example 1.17. Set cfgtest parameter
+...
+loadmodule "cfgt.so"
+modparam("debugger", "cfgtest", 1)
+...
+
 4. Functions
 4. Functions
 
 
    4.1. dbg_breakpoint(mode)
    4.1. dbg_breakpoint(mode)
    4.2. dbg_pv_dump([mask] [, level])
    4.2. dbg_pv_dump([mask] [, level])
    4.3. dbg_sip_msg([log_level], [facility])
    4.3. dbg_sip_msg([log_level], [facility])
 
 
-4.1. dbg_breakpoint(mode)
+4.1.  dbg_breakpoint(mode)
 
 
    Anchor a breakpoint at the current line of the config (the one on which
    Anchor a breakpoint at the current line of the config (the one on which
    this function is called). The 'mode' specifies whether the breakpoint
    this function is called). The 'mode' specifies whether the breakpoint
@@ -404,13 +421,13 @@ modparam("debugger", "reset_msgid", 1)
    Note that this version of the module does not export this anchors to
    Note that this version of the module does not export this anchors to
    RPC for interactive debugging (temporarily disabled).
    RPC for interactive debugging (temporarily disabled).
 
 
-   Example 1.17. dbg_breakpoint usage
+   Example 1.18. dbg_breakpoint usage
 ...
 ...
 if($si=="10.0.0.10")
 if($si=="10.0.0.10")
         dbg_breakpoint("1");
         dbg_breakpoint("1");
 ...
 ...
 
 
-4.2. dbg_pv_dump([mask] [, level])
+4.2.  dbg_pv_dump([mask] [, level])
 
 
    Prints the content of pv_cache on json format. Defaults are mask=31 and
    Prints the content of pv_cache on json format. Defaults are mask=31 and
    level = "L_DBG"
    level = "L_DBG"
@@ -432,7 +449,7 @@ if($si=="10.0.0.10")
      * L_INFO - log level 2
      * L_INFO - log level 2
      * L_DBG - log level 3
      * L_DBG - log level 3
 
 
-   Example 1.18. dbg_pv_dump usage
+   Example 1.19. dbg_pv_dump usage
 ...
 ...
 $var(temp) = 1;
 $var(temp) = 1;
 $avp(s:more_avp) = 2;
 $avp(s:more_avp) = 2;
@@ -455,7 +472,7 @@ vp(x)":[{"different":["foo"]},{"other":[2,1],"more":["hi","bye"]}],"$T_branch_id
 x":0,"$var(empty)":0}
 x":0,"$var(empty)":0}
  ...
  ...
 
 
-4.3. dbg_sip_msg([log_level], [facility])
+4.3.  dbg_sip_msg([log_level], [facility])
 
 
    Prints how the sip message would look like if it would be sent out at
    Prints how the sip message would look like if it would be sent out at
    that point in the config(i.e. if the current lump lists would have been
    that point in the config(i.e. if the current lump lists would have been
@@ -475,7 +492,7 @@ x":0,"$var(empty)":0}
    force the lump application using msg_apply_changes() function from
    force the lump application using msg_apply_changes() function from
    textopsx module.
    textopsx module.
 
 
-   Example 1.19. dbg_sip_msg usage
+   Example 1.20. dbg_sip_msg usage
 ...
 ...
     dbg_sip_msg();
     dbg_sip_msg();
     dbg_sip_msg("L_ERR");
     dbg_sip_msg("L_ERR");
@@ -512,7 +529,7 @@ P-Hint: My hint
    5.4. dbg.mod_level
    5.4. dbg.mod_level
    5.5. dbg.reset_msgid
    5.5. dbg.reset_msgid
 
 
-5.1. dbg.ls
+5.1.  dbg.ls
 
 
    List Kamailio processes with info related to interactive debugging.
    List Kamailio processes with info related to interactive debugging.
 
 
@@ -526,7 +543,7 @@ P-Hint: My hint
                 dbg.ls
                 dbg.ls
                 dbg.ls 1234
                 dbg.ls 1234
 
 
-5.2. dbg.trace
+5.2.  dbg.trace
 
 
    Control config script running trace.
    Control config script running trace.
 
 
@@ -543,7 +560,7 @@ P-Hint: My hint
                 dbg.trace off
                 dbg.trace off
                 dbg.trace on 1234
                 dbg.trace on 1234
 
 
-5.3. dbg.bp
+5.3.  dbg.bp
 
 
    Control breakpoints and config execution.
    Control breakpoints and config execution.
 
 
@@ -581,7 +598,7 @@ P-Hint: My hint
                 dbg.bp eval 1234 $fu
                 dbg.bp eval 1234 $fu
                 dbg.bp move 1234
                 dbg.bp move 1234
 
 
-5.4. dbg.mod_level
+5.4.  dbg.mod_level
 
 
    Specify module log level.
    Specify module log level.
 
 
@@ -595,7 +612,7 @@ P-Hint: My hint
                 dbg.mod_level core 3
                 dbg.mod_level core 3
                 dbg.mod_level tm 3
                 dbg.mod_level tm 3
 
 
-5.5. dbg.reset_msgid
+5.5.  dbg.reset_msgid
 
 
    Resets the message sequence ($mi). Internally there is no real change.
    Resets the message sequence ($mi). Internally there is no real change.
    This can be useful for unit test cases in order to be able to replicate
    This can be useful for unit test cases in order to be able to replicate

+ 148 - 12
modules/debugger/debugger_api.c

@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <unistd.h>
 
 
+#include "../cfgt/cfgt.h"
 #include "../../dprint.h"
 #include "../../dprint.h"
 #include "../../events.h"
 #include "../../events.h"
 #include "../../locking.h"
 #include "../../locking.h"
@@ -70,6 +71,7 @@ str *dbg_get_state_name(int t)
 #define DBG_CFGTRACE_ON	(1<<0)
 #define DBG_CFGTRACE_ON	(1<<0)
 #define DBG_ABKPOINT_ON	(1<<1)
 #define DBG_ABKPOINT_ON	(1<<1)
 #define DBG_LBKPOINT_ON	(1<<2)
 #define DBG_LBKPOINT_ON	(1<<2)
+#define DBG_CFGTEST_ON	(1<<3)
 
 
 static str _dbg_status_list[] = {
 static str _dbg_status_list[] = {
 	str_init("cfgtrace-on"),
 	str_init("cfgtrace-on"),
@@ -78,6 +80,8 @@ static str _dbg_status_list[] = {
 	str_init("abkpoint-off"),
 	str_init("abkpoint-off"),
 	str_init("lbkpoint-on"),
 	str_init("lbkpoint-on"),
 	str_init("lbkpoint-off"),
 	str_init("lbkpoint-off"),
+	str_init("cfgtest-on"),
+	str_init("cfgtest-off"),
 	{0, 0}
 	{0, 0}
 };
 };
 
 
@@ -89,6 +93,8 @@ str *dbg_get_status_name(int t)
 		return &_dbg_status_list[2];
 		return &_dbg_status_list[2];
 	if(t&DBG_LBKPOINT_ON)
 	if(t&DBG_LBKPOINT_ON)
 		return &_dbg_status_list[4];
 		return &_dbg_status_list[4];
+	if(t&DBG_CFGTEST_ON)
+		return &_dbg_status_list[6];
 
 
 	return &_dbg_state_list[0];
 	return &_dbg_state_list[0];
 }
 }
@@ -188,6 +194,12 @@ int _dbg_step_loops = 200;
  */
  */
 int _dbg_reset_msgid = 0;
 int _dbg_reset_msgid = 0;
 
 
+/**
+ * disabled by default
+ */
+int _dbg_cfgtest = 0;
+cfgt_api_t _dbg_cfgt;
+
 /**
 /**
  *
  *
  */
  */
@@ -356,6 +368,11 @@ int dbg_cfg_trace(void *data)
 				);
 				);
 		}
 		}
 	}
 	}
+	if(_dbg_pid_list[process_no].set&DBG_CFGTEST_ON)
+	{
+		if(_dbg_cfgt.cfgt_process_route(msg, a)<0)
+				LM_ERR("Error processing route\n");
+	}
 	if(!(_dbg_pid_list[process_no].set&DBG_ABKPOINT_ON))
 	if(!(_dbg_pid_list[process_no].set&DBG_ABKPOINT_ON))
 	{
 	{
 		/* no breakpoints to be considered */
 		/* no breakpoints to be considered */
@@ -590,6 +607,8 @@ int dbg_init_mypid(void)
 		_dbg_pid_list[process_no].set |= DBG_ABKPOINT_ON;
 		_dbg_pid_list[process_no].set |= DBG_ABKPOINT_ON;
 	if(_dbg_cfgtrace==1)
 	if(_dbg_cfgtrace==1)
 		_dbg_pid_list[process_no].set |= DBG_CFGTRACE_ON;
 		_dbg_pid_list[process_no].set |= DBG_CFGTRACE_ON;
+	if(_dbg_cfgtest==1)
+		_dbg_pid_list[process_no].set |= DBG_CFGTEST_ON;
 	if(_dbg_reset_msgid==1)
 	if(_dbg_reset_msgid==1)
 	{
 	{
 		LM_DBG("[%d] create locks\n", process_no);
 		LM_DBG("[%d] create locks\n", process_no);
@@ -962,12 +981,12 @@ static void  dbg_rpc_trace(rpc_t* rpc, void* ctx)
 /**
 /**
  *
  *
  */
  */
-static const char* dbg_rpc_mod_level_doc[2] = {
-	"Specify module log level",
+static const char* dbg_rpc_set_mod_level_doc[2] = {
+	"Set module log level",
 	0
 	0
 };
 };
 
 
-static void dbg_rpc_mod_level(rpc_t* rpc, void* ctx){
+static void dbg_rpc_set_mod_level(rpc_t* rpc, void* ctx){
 	int l;
 	int l;
 	str value = {0,0};
 	str value = {0,0};
 
 
@@ -988,12 +1007,12 @@ static void dbg_rpc_mod_level(rpc_t* rpc, void* ctx){
 /**
 /**
  *
  *
  */
  */
-static const char* dbg_rpc_mod_facility_doc[2] = {
-	"Specify module log facility",
+static const char* dbg_rpc_set_mod_facility_doc[2] = {
+	"Set module log facility",
 	0
 	0
 };
 };
 
 
-static void dbg_rpc_mod_facility(rpc_t* rpc, void* ctx) {
+static void dbg_rpc_set_mod_facility(rpc_t* rpc, void* ctx) {
 	int fl;
 	int fl;
 	str value = {0, 0};
 	str value = {0, 0};
 	str facility = {0, 0};
 	str facility = {0, 0};
@@ -1017,6 +1036,55 @@ static void dbg_rpc_mod_facility(rpc_t* rpc, void* ctx) {
 	rpc->add(ctx, "s", "200 ok");
 	rpc->add(ctx, "s", "200 ok");
 }
 }
 
 
+/**
+ *
+ */
+static const char* dbg_rpc_get_mod_level_doc[2] = {
+	"Get module log level",
+	0
+};
+
+static void dbg_rpc_get_mod_level(rpc_t* rpc, void* ctx){
+	int l;
+	str value = {0,0};
+
+	if (rpc->scan(ctx, "S", &value) < 1)
+	{
+		rpc->fault(ctx, 500, "invalid parameters");
+		return;
+	}
+
+	l = get_debug_level(value.s, value.len);
+
+	rpc->add(ctx, "d", l);
+}
+
+/**
+ *
+ */
+static const char* dbg_rpc_get_mod_facility_doc[2] = {
+	"Get module log facility",
+	0
+};
+
+static void dbg_rpc_get_mod_facility(rpc_t* rpc, void* ctx) {
+	int fl;
+	str value = {0, 0};
+	str facility = {0, 0};
+
+	if (rpc->scan(ctx, "S", &value) < 1)
+	{
+	    rpc->fault(ctx, 500, "invalid parameters");
+	    return;
+	}
+
+	fl = get_debug_facility(value.s, value.len);
+	facility.s = facility2str(fl, &facility.len);
+
+	rpc->add(ctx, "S", &facility);
+}
+
+
 /**
 /**
  *
  *
  */
  */
@@ -1057,8 +1125,10 @@ rpc_export_t dbg_rpc[] = {
 	{"dbg.bp",        dbg_rpc_bp,        dbg_rpc_bp_doc,        0},
 	{"dbg.bp",        dbg_rpc_bp,        dbg_rpc_bp_doc,        0},
 	{"dbg.ls",        dbg_rpc_list,      dbg_rpc_list_doc,      0},
 	{"dbg.ls",        dbg_rpc_list,      dbg_rpc_list_doc,      0},
 	{"dbg.trace",     dbg_rpc_trace,     dbg_rpc_trace_doc,     0},
 	{"dbg.trace",     dbg_rpc_trace,     dbg_rpc_trace_doc,     0},
-	{"dbg.mod_level", dbg_rpc_mod_level, dbg_rpc_mod_level_doc, 0},
-	{"dbg.mod_facility", dbg_rpc_mod_facility, dbg_rpc_mod_facility_doc, 0},
+	{"dbg.set_mod_level", dbg_rpc_set_mod_level, dbg_rpc_set_mod_level_doc, 0},
+	{"dbg.set_mod_facility", dbg_rpc_set_mod_facility, dbg_rpc_set_mod_facility_doc, 0},
+	{"dbg.get_mod_level", dbg_rpc_get_mod_level, dbg_rpc_get_mod_level_doc, 0},
+	{"dbg.get_mod_facility", dbg_rpc_get_mod_facility, dbg_rpc_get_mod_facility_doc, 0},
 	{"dbg.reset_msgid", dbg_rpc_reset_msgid, dbg_rpc_reset_msgid_doc, 0},
 	{"dbg.reset_msgid", dbg_rpc_reset_msgid, dbg_rpc_reset_msgid_doc, 0},
 	{0, 0, 0, 0}
 	{0, 0, 0, 0}
 };
 };
@@ -1119,6 +1189,7 @@ int dbg_init_mod_levels(int dbg_mod_hash_size)
 		return -1;
 		return -1;
 	}
 	}
 	memset(_dbg_mod_table, 0, _dbg_mod_table_size*sizeof(dbg_mod_slot_t));
 	memset(_dbg_mod_table, 0, _dbg_mod_table_size*sizeof(dbg_mod_slot_t));
+	LM_DBG("Created _dbg_mod_table, size %d\n", _dbg_mod_table_size);
 
 
 	for(i=0; i<_dbg_mod_table_size; i++)
 	for(i=0; i<_dbg_mod_table_size; i++)
 	{
 	{
@@ -1141,6 +1212,63 @@ int dbg_init_mod_levels(int dbg_mod_hash_size)
 	return 0;
 	return 0;
 }
 }
 
 
+/**
+ *
+ */
+int dbg_destroy_mod_levels()
+{
+	int i;
+	dbg_mod_level_t *itl = NULL;
+	dbg_mod_level_t *itlp = NULL;
+
+	dbg_mod_facility_t *itf = NULL;
+	dbg_mod_facility_t *itfp = NULL;
+
+	if (_dbg_mod_table_size <= 0)
+		return 0;
+
+	if (_dbg_mod_table == NULL)
+		return 0;
+
+	for (i = 0; i < _dbg_mod_table_size; i++) {
+		// destroy level list
+		lock_get(&_dbg_mod_table[i].lock);
+		itl = _dbg_mod_table[i].first;
+		while (itl) {
+			itlp = itl;
+			itl = itl->next;
+			shm_free(itlp);
+		}
+		lock_release(&_dbg_mod_table[i].lock);
+
+		// destroy facility list
+		lock_get(&_dbg_mod_table[i].lock_ft);
+		itf = _dbg_mod_table[i].first_ft;
+		while (itf) {
+			itfp = itf;
+			itf = itf->next;
+			shm_free(itfp);
+		}
+		lock_release(&_dbg_mod_table[i].lock_ft);
+
+		// destroy locks
+		lock_destroy(&_dbg_mod_table[i].lock);
+		lock_destroy(&_dbg_mod_table[i].lock_ft);
+
+		// reset all
+		_dbg_mod_table[i].first = NULL;
+		_dbg_mod_table[i].first_ft = NULL;
+	}
+
+	// free table
+	shm_free(_dbg_mod_table);
+	_dbg_mod_table = NULL;
+
+	LM_DBG("Destroyed _dbg_mod_table, size %d\n", _dbg_mod_table_size);
+
+	return 0;
+}
+
 /*
 /*
  * case insensitive hashing - clone here to avoid usage of LOG*()
  * case insensitive hashing - clone here to avoid usage of LOG*()
  * - s1 - str to hash
  * - s1 - str to hash
@@ -1216,15 +1344,14 @@ int dbg_set_mod_debug_level(char *mname, int mnlen, int *mlevel)
 		itp = it;
 		itp = it;
 		it = it->next;
 		it = it->next;
 	}
 	}
+	lock_release(&_dbg_mod_table[idx].lock);
 	/* not found - add */
 	/* not found - add */
 	if(mlevel==NULL) {
 	if(mlevel==NULL) {
-		lock_release(&_dbg_mod_table[idx].lock);
 		return 0;
 		return 0;
 	}
 	}
 	itn = (dbg_mod_level_t*)shm_malloc(sizeof(dbg_mod_level_t) + (mnlen+1)*sizeof(char));
 	itn = (dbg_mod_level_t*)shm_malloc(sizeof(dbg_mod_level_t) + (mnlen+1)*sizeof(char));
 	if(itn==NULL) {
 	if(itn==NULL) {
 		LM_ERR("no more shm\n");
 		LM_ERR("no more shm\n");
-		lock_release(&_dbg_mod_table[idx].lock);
 		return -1;
 		return -1;
 	}
 	}
 	memset(itn, 0, sizeof(dbg_mod_level_t) + (mnlen+1)*sizeof(char));
 	memset(itn, 0, sizeof(dbg_mod_level_t) + (mnlen+1)*sizeof(char));
@@ -1235,6 +1362,7 @@ int dbg_set_mod_debug_level(char *mname, int mnlen, int *mlevel)
 	strncpy(itn->name.s, mname, mnlen);
 	strncpy(itn->name.s, mname, mnlen);
 	itn->name.s[itn->name.len] = '\0';
 	itn->name.s[itn->name.len] = '\0';
 
 
+	lock_get(&_dbg_mod_table[idx].lock);
 	if(itp==NULL) {
 	if(itp==NULL) {
 		itn->next = _dbg_mod_table[idx].first;
 		itn->next = _dbg_mod_table[idx].first;
 		_dbg_mod_table[idx].first = itn;
 		_dbg_mod_table[idx].first = itn;
@@ -1292,15 +1420,14 @@ int dbg_set_mod_debug_facility(char *mname, int mnlen, int *mfacility)
 		itp = it;
 		itp = it;
 		it = it->next;
 		it = it->next;
 	}
 	}
+	lock_release(&_dbg_mod_table[idx].lock_ft);
 	/* not found - add */
 	/* not found - add */
 	if(mfacility==NULL) {
 	if(mfacility==NULL) {
-		lock_release(&_dbg_mod_table[idx].lock_ft);
 		return 0;
 		return 0;
 	}
 	}
 	itn = (dbg_mod_facility_t*)shm_malloc(sizeof(dbg_mod_facility_t) + (mnlen+1)*sizeof(char));
 	itn = (dbg_mod_facility_t*)shm_malloc(sizeof(dbg_mod_facility_t) + (mnlen+1)*sizeof(char));
 	if(itn==NULL) {
 	if(itn==NULL) {
 		LM_ERR("no more shm\n");
 		LM_ERR("no more shm\n");
-		lock_release(&_dbg_mod_table[idx].lock_ft);
 		return -1;
 		return -1;
 	}
 	}
 	memset(itn, 0, sizeof(dbg_mod_facility_t) + (mnlen+1)*sizeof(char));
 	memset(itn, 0, sizeof(dbg_mod_facility_t) + (mnlen+1)*sizeof(char));
@@ -1311,6 +1438,7 @@ int dbg_set_mod_debug_facility(char *mname, int mnlen, int *mfacility)
 	strncpy(itn->name.s, mname, mnlen);
 	strncpy(itn->name.s, mname, mnlen);
 	itn->name.s[itn->name.len] = '\0';
 	itn->name.s[itn->name.len] = '\0';
 
 
+	lock_get(&_dbg_mod_table[idx].lock_ft);
 	if(itp==NULL) {
 	if(itp==NULL) {
 		itn->next = _dbg_mod_table[idx].first_ft;
 		itn->next = _dbg_mod_table[idx].first_ft;
 		_dbg_mod_table[idx].first_ft = itn;
 		_dbg_mod_table[idx].first_ft = itn;
@@ -1335,6 +1463,10 @@ int dbg_get_mod_debug_level(char *mname, int mnlen, int *mlevel)
 	if(_dbg_mod_table==NULL)
 	if(_dbg_mod_table==NULL)
 		return -1;
 		return -1;
 
 
+	if (!dbg_cfg) {
+		return -1;
+	}
+
 	if(cfg_get(dbg, dbg_cfg, mod_level_mode)==0)
 	if(cfg_get(dbg, dbg_cfg, mod_level_mode)==0)
 		return -1;
 		return -1;
 
 
@@ -1378,6 +1510,10 @@ int dbg_get_mod_debug_facility(char *mname, int mnlen, int *mfacility)
 	if(_dbg_mod_table==NULL)
 	if(_dbg_mod_table==NULL)
 		return -1;
 		return -1;
 
 
+	if (!dbg_cfg) {
+		return -1;
+	}
+
 	if(cfg_get(dbg, dbg_cfg, mod_facility_mode)==0)
 	if(cfg_get(dbg, dbg_cfg, mod_facility_mode)==0)
 		return -1;
 		return -1;
 
 

+ 1 - 0
modules/debugger/debugger_api.h

@@ -34,6 +34,7 @@ int dbg_init_mypid(void);
 int dbg_init_rpc(void);
 int dbg_init_rpc(void);
 
 
 int dbg_init_mod_levels(int _dbg_mod_hash_size);
 int dbg_init_mod_levels(int _dbg_mod_hash_size);
+int dbg_destroy_mod_levels();
 int dbg_set_mod_debug_level(char *mname, int mnlen, int *mlevel);
 int dbg_set_mod_debug_level(char *mname, int mnlen, int *mlevel);
 int dbg_set_mod_debug_facility(char *mname, int mnlen, int *mfacility);
 int dbg_set_mod_debug_facility(char *mname, int mnlen, int *mfacility);
 void dbg_enable_mod_levels(void);
 void dbg_enable_mod_levels(void);

+ 472 - 154
modules/debugger/debugger_mod.c

@@ -27,6 +27,9 @@
 #include <stdlib.h>
 #include <stdlib.h>
 #include <string.h>
 #include <string.h>
 
 
+#include "../cfgt/cfgt.h"
+#include "../../lib/kmi/mi.h"
+#include "../../lib/kmi/tree.h"
 #include "../../sr_module.h"
 #include "../../sr_module.h"
 #include "../../dprint.h"
 #include "../../dprint.h"
 #include "../../ut.h"
 #include "../../ut.h"
@@ -60,6 +63,12 @@ static int w_dbg_sip_msg(struct sip_msg* msg, char *level, char *facility);
 
 
 extern char* dump_lump_list(struct lump *list, int s_offset, char *s_buf);
 extern char* dump_lump_list(struct lump *list, int s_offset, char *s_buf);
 
 
+/* mi commands */
+static struct mi_root* mi_set_dbg_mod_level(struct mi_root *cmd_tree, void *param);
+static struct mi_root* mi_set_dbg_mod_facility(struct mi_root *cmd_tree, void *param);
+static struct mi_root* mi_get_dbg_mod_level(struct mi_root *cmd_tree, void *param);
+static struct mi_root* mi_get_dbg_mod_facility(struct mi_root *cmd_tree, void *param);
+
 /* parameters */
 /* parameters */
 extern int _dbg_cfgtrace;
 extern int _dbg_cfgtrace;
 extern int _dbg_cfgpkgcheck;
 extern int _dbg_cfgpkgcheck;
@@ -71,6 +80,10 @@ extern char *_dbg_cfgtrace_lname;
 extern int _dbg_step_usleep;
 extern int _dbg_step_usleep;
 extern int _dbg_step_loops;
 extern int _dbg_step_loops;
 extern int _dbg_reset_msgid;
 extern int _dbg_reset_msgid;
+extern int _dbg_cfgtest;
+
+/* cfgt api */
+extern cfgt_api_t _dbg_cfgt;
 
 
 static int _dbg_sip_msg_cline;
 static int _dbg_sip_msg_cline;
 static char * _dbg_cfgtrace_facility_str = 0;
 static char * _dbg_cfgtrace_facility_str = 0;
@@ -85,12 +98,12 @@ static cmd_export_t cmds[]={
 		fixup_dbg_pv_dump, 0, ANY_ROUTE},
 		fixup_dbg_pv_dump, 0, ANY_ROUTE},
 	{"dbg_pv_dump", (cmd_function)w_dbg_dump, 2,
 	{"dbg_pv_dump", (cmd_function)w_dbg_dump, 2,
 		fixup_dbg_pv_dump, 0, ANY_ROUTE},
 		fixup_dbg_pv_dump, 0, ANY_ROUTE},
-    {"dbg_sip_msg", (cmd_function)w_dbg_sip_msg, 0,
-        fixup_dbg_sip_msg, 0, REQUEST_ROUTE},
-    {"dbg_sip_msg", (cmd_function)w_dbg_sip_msg, 1,
-        fixup_dbg_sip_msg, 0, REQUEST_ROUTE},
-    {"dbg_sip_msg", (cmd_function)w_dbg_sip_msg, 2,
-        fixup_dbg_sip_msg, 0, REQUEST_ROUTE},
+	{"dbg_sip_msg", (cmd_function)w_dbg_sip_msg, 0,
+		fixup_dbg_sip_msg, 0, REQUEST_ROUTE|ONREPLY_ROUTE},
+	{"dbg_sip_msg", (cmd_function)w_dbg_sip_msg, 1,
+		fixup_dbg_sip_msg, 0, REQUEST_ROUTE|ONREPLY_ROUTE},
+	{"dbg_sip_msg", (cmd_function)w_dbg_sip_msg, 2,
+		fixup_dbg_sip_msg, 0, REQUEST_ROUTE|ONREPLY_ROUTE},
 	{0, 0, 0, 0, 0, 0}
 	{0, 0, 0, 0, 0, 0}
 };
 };
 
 
@@ -111,16 +124,25 @@ static param_export_t params[]={
 	{"mod_facility",      PARAM_STRING|USE_FUNC_PARAM, (void*)dbg_mod_facility_param},
 	{"mod_facility",      PARAM_STRING|USE_FUNC_PARAM, (void*)dbg_mod_facility_param},
 	{"reset_msgid",       INT_PARAM, &_dbg_reset_msgid},
 	{"reset_msgid",       INT_PARAM, &_dbg_reset_msgid},
 	{"cfgpkgcheck",       INT_PARAM, &_dbg_cfgpkgcheck},
 	{"cfgpkgcheck",       INT_PARAM, &_dbg_cfgpkgcheck},
+	{"cfgtest",           INT_PARAM, &_dbg_cfgtest},
 	{0, 0, 0}
 	{0, 0, 0}
 };
 };
 
 
+static mi_export_t mi_cmds[] = {
+	{"set_dbg_mod_level",         mi_set_dbg_mod_level, 0, 0, 0},
+	{"set_dbg_mod_facility",      mi_set_dbg_mod_facility, 0, 0, 0},
+	{"get_dbg_mod_level",         mi_get_dbg_mod_level, 0, 0, 0},
+	{"get_dbg_mod_facility",      mi_get_dbg_mod_facility, 0, 0, 0},
+	{ 0, 0, 0, 0, 0}
+};
+
 struct module_exports exports = {
 struct module_exports exports = {
 	"debugger",
 	"debugger",
 	DEFAULT_DLFLAGS, /* dlopen flags */
 	DEFAULT_DLFLAGS, /* dlopen flags */
 	cmds,
 	cmds,
 	params,
 	params,
 	0,
 	0,
-	0,              /* exported MI functions */
+	mi_cmds,        /* exported MI functions */
 	0,              /* exported pseudo-variables */
 	0,              /* exported pseudo-variables */
 	0,              /* extra processes */
 	0,              /* extra processes */
 	mod_init,       /* module initialization function */
 	mod_init,       /* module initialization function */
@@ -130,12 +152,267 @@ struct module_exports exports = {
 };
 };
 
 
 
 
+static struct mi_root* mi_set_dbg_mod_level(struct mi_root *cmd_tree, void *param) {
+	struct mi_node *node;
+	str mod_str, level_str;
+	int l;
+
+	/* get first param */
+	node = cmd_tree->node.kids;
+	if (node == NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	if (node->value.s == NULL || node->value.len == 0) {
+		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+	}
+
+	/* get module str */
+	mod_str = node->value;
+
+	/* get second param */
+	node = node->next;
+	if (node == NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	if (node->value.s == NULL || node->value.len == 0) {
+		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+	}
+
+	/* get level str */
+	level_str = node->value;
+
+	/* no further params expected */
+	node = node->next;
+	if (node != NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	/* get level int */
+	if (str2sint(&level_str, &l) < 0) {
+		LM_ERR("invalid parameter - level value: %.*s\n",
+			level_str.len, level_str.s);
+		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+	}
+
+	/* set level int */
+	if (default_dbg_cfg.mod_hash_size <= 0 || default_dbg_cfg.mod_level_mode <= 0) {
+		LM_ERR("can't set level for module=%.*s; enable mod_hash_size and mod_level_mode config parameters!\n",
+			mod_str.len, mod_str.s);
+		return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
+	} else if (dbg_set_mod_debug_level(mod_str.s, mod_str.len, &l) < 0) {
+		LM_ERR("failed set level for module=%.*s\n", mod_str.len, mod_str.s);
+		return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
+	} else {
+		LM_DBG("module=%.*s level_str=%.*s level_int=%d\n",
+			mod_str.len, mod_str.s, level_str.len, level_str.s, l);
+	}
+
+	return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+}
+
+static struct mi_root* mi_set_dbg_mod_facility(struct mi_root *cmd_tree, void *param) {
+	struct mi_node *node;
+	str mod_str, facility_str;
+	int fl;
+
+	/* get first param */
+	node = cmd_tree->node.kids;
+	if (node == NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	if (node->value.s == NULL || node->value.len == 0) {
+		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+	}
+
+	/* get module str */
+	mod_str = node->value;
+
+	/* get second param */
+	node = node->next;
+	if (node == NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	if (node->value.s == NULL || node->value.len == 0) {
+		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+	}
+
+	/* get facility str */
+	facility_str = node->value;
+
+	/* no further params expected */
+	node = node->next;
+	if (node != NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	/* get facility int */
+	facility_str.s[facility_str.len] = '\0';
+	if ((fl = str2facility(facility_str.s)) == -1) {
+		LM_ERR("invalid parameter - facility value: %.*s\n",
+			facility_str.len, facility_str.s);
+		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+	}
+
+	/* set facility int */
+	if (default_dbg_cfg.mod_hash_size <= 0 || default_dbg_cfg.mod_facility_mode <= 0) {
+		LM_ERR("can't set facility for module=%.*s; enable mod_hash_size and mod_facility_mode config parameters!\n",
+			mod_str.len, mod_str.s);
+		return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
+	} else if (dbg_set_mod_debug_facility(mod_str.s, mod_str.len, &fl) < 0) {
+		LM_ERR("failed set facility for module=%.*s\n", mod_str.len, mod_str.s);
+		return init_mi_tree(500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
+	} else {
+		LM_DBG("module=%.*s facility_str=%.*s facility_int=%d\n",
+			mod_str.len, mod_str.s, facility_str.len, facility_str.s, fl);
+	}
+
+	return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+}
+
+static struct mi_root* mi_get_dbg_mod_level(struct mi_root *cmd_tree, void *param) {
+	struct mi_node *node, *crt_node;
+	struct mi_root *root = NULL;
+	struct mi_attr *attr;
+	int l;
+	str mod_str, level_str;
+	str level_attr = {"level", strlen("level")};
+
+	/* get first param */
+	node = cmd_tree->node.kids;
+	if (node == NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	if (node->value.s == NULL || node->value.len == 0) {
+		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+	}
+
+	/* get module str */
+	mod_str = node->value;
+
+	/* no further params expected */
+	node = node->next;
+	if (node != NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	/* get module log level */
+	l = get_debug_level(mod_str.s, mod_str.len);
+	level_str.s = sint2str(l, &level_str.len);
+	LM_DBG("module=%.*s level_str=%.*s level_int=%d\n",
+		mod_str.len, mod_str.s, level_str.len, level_str.s, l);
+
+	/* return module log level */
+	root = init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+	if (!root) {
+		LM_ERR("the MI tree cannot be initialized!\n");
+		goto error;
+	}
+	node = &root->node;
+
+	if (!(crt_node = add_mi_node_child(node, 0, mod_str.s, mod_str.len, 0, 0)) ) {
+		LM_ERR("cannot add the child node to the tree\n");
+		goto error;
+	}
+
+	if ((attr = add_mi_attr(crt_node, MI_DUP_VALUE,
+		level_attr.s, level_attr.len,
+		level_str.s, level_str.len)) == 0) {
+		LM_ERR("cannot add attributes to the node\n");
+		goto error;
+	}
+
+	return root;
+
+error:
+	if (root) {
+		free_mi_tree(root);
+	}
+
+	return NULL;
+}
+
+static struct mi_root* mi_get_dbg_mod_facility(struct mi_root *cmd_tree, void *param) {
+	struct mi_node *node, *crt_node;
+	struct mi_root *root = NULL;
+	struct mi_attr *attr;
+	int fl;
+	str mod_str, facility_str;
+	str facility_attr = {"facility", strlen("facility")};
+
+	/* get first param */
+	node = cmd_tree->node.kids;
+	if (node == NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	if (node->value.s == NULL || node->value.len == 0) {
+		return init_mi_tree(400, MI_BAD_PARM_S, MI_BAD_PARM_LEN);
+	}
+
+	/* get module str */
+	mod_str = node->value;
+
+	/* no further params expected */
+	node = node->next;
+	if (node != NULL) {
+		return init_mi_tree(400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+	}
+
+	/* get module log facility */
+	fl = get_debug_facility(mod_str.s, mod_str.len);
+	facility_str.s = facility2str(fl, &facility_str.len);
+	LM_DBG("module=%.*s facility_str=%.*s facility_int=%d\n",
+		mod_str.len, mod_str.s, facility_str.len, facility_str.s, fl);
+
+	/* return module log facility */
+	root = init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+	if (!root) {
+		LM_ERR("the MI tree cannot be initialized!\n");
+		goto error;
+	}
+	node = &root->node;
+
+	if (!(crt_node = add_mi_node_child(node, 0, mod_str.s, mod_str.len, 0, 0)) ) {
+		LM_ERR("cannot add the child node to the tree\n");
+		goto error;
+	}
+
+	if ((attr = add_mi_attr(crt_node, MI_DUP_VALUE,
+		facility_attr.s, facility_attr.len,
+		facility_str.s, facility_str.len)) == 0) {
+		LM_ERR("cannot add attributes to the node\n");
+		goto error;
+	}
+
+	return root;
+
+error:
+	if (root) {
+		free_mi_tree(root);
+	}
+
+	return NULL;
+}
+
 /**
 /**
  * init module function
  * init module function
  */
  */
 static int mod_init(void)
 static int mod_init(void)
 {
 {
 	int fl;
 	int fl;
+	bind_cfgt_t bind_cfgt;
+
+	if (register_mi_mod(exports.name, mi_cmds) != 0)
+	{
+		LM_ERR("failed to register MI commands\n");
+		return -1;
+	}
+
 	if (_dbg_cfgtrace_facility_str!=NULL)
 	if (_dbg_cfgtrace_facility_str!=NULL)
 	{
 	{
 		fl = str2facility(_dbg_cfgtrace_facility_str);
 		fl = str2facility(_dbg_cfgtrace_facility_str);
@@ -159,6 +436,12 @@ static int mod_init(void)
 		LM_ERR("Fail to declare the configuration\n");
 		LM_ERR("Fail to declare the configuration\n");
 		return -1;
 		return -1;
 	}
 	}
+
+	/* anyhow, should fail before */
+	if (!dbg_cfg) {
+		return -1;
+	}
+
 	LM_DBG("cfg level_mode:%d facility_mode:%d hash_size:%d\n",
 	LM_DBG("cfg level_mode:%d facility_mode:%d hash_size:%d\n",
 		cfg_get(dbg, dbg_cfg, mod_level_mode),
 		cfg_get(dbg, dbg_cfg, mod_level_mode),
 		cfg_get(dbg, dbg_cfg, mod_facility_mode),
 		cfg_get(dbg, dbg_cfg, mod_facility_mode),
@@ -187,6 +470,19 @@ static int mod_init(void)
 			return -1;
 			return -1;
 		}
 		}
 	}
 	}
+	if(_dbg_cfgtest==1)
+	{
+		bind_cfgt = (bind_cfgt_t)find_export("cfgt_bind_cfgt", 1, 0);
+		if (!bind_cfgt) {
+			LM_ERR("can't find cfgt module\n");
+			return -1;
+		}
+
+		if (bind_cfgt(&_dbg_cfgt) < 0) {
+			return -1;
+		}
+		LM_INFO("bind to cfgt module\n");
+	}
 	return dbg_init_bp_list();
 	return dbg_init_bp_list();
 }
 }
 
 
@@ -210,6 +506,8 @@ static int child_init(int rank)
  */
  */
 static void mod_destroy(void)
 static void mod_destroy(void)
 {
 {
+	dbg_cfg = NULL;
+	dbg_destroy_mod_levels();
 }
 }
 
 
 /**
 /**
@@ -258,7 +556,7 @@ static int fixup_dbg_pv_dump(void** param, int param_no)
 		break;
 		break;
 	}
 	}
 
 
-    return 0;
+	return 0;
 }
 }
 
 
 /**
 /**
@@ -306,7 +604,7 @@ static int fixup_dbg_breakpoint(void** param, int param_no)
 	a = dbg_fixup_get_action(param, param_no);
 	a = dbg_fixup_get_action(param, param_no);
 	p = (char*)(*param);
 	p = (char*)(*param);
 
 
-    return dbg_add_breakpoint(a, (*p=='0')?0:1);
+	return dbg_add_breakpoint(a, (*p=='0')?0:1);
 }
 }
 
 
 static int dbg_mod_level_param(modparam_t type, void *val)
 static int dbg_mod_level_param(modparam_t type, void *val)
@@ -331,19 +629,27 @@ static int dbg_mod_level_param(modparam_t type, void *val)
 	}
 	}
 	s.s = (char*)val;
 	s.s = (char*)val;
 	s.len = p - s.s;
 	s.len = p - s.s;
+
+	if (!dbg_cfg) {
+		return -1;
+	}
+
 	LM_DBG("cfg level_mode:%d hash_size:%d\n",
 	LM_DBG("cfg level_mode:%d hash_size:%d\n",
 		cfg_get(dbg, dbg_cfg, mod_level_mode),
 		cfg_get(dbg, dbg_cfg, mod_level_mode),
 		cfg_get(dbg, dbg_cfg, mod_hash_size));
 		cfg_get(dbg, dbg_cfg, mod_hash_size));
+
 	if(dbg_init_mod_levels(cfg_get(dbg, dbg_cfg, mod_hash_size))<0)
 	if(dbg_init_mod_levels(cfg_get(dbg, dbg_cfg, mod_hash_size))<0)
 	{
 	{
 		LM_ERR("failed to init per module log level\n");
 		LM_ERR("failed to init per module log level\n");
 		return -1;
 		return -1;
 	}
 	}
+
 	if(dbg_set_mod_debug_level(s.s, s.len, &l)<0)
 	if(dbg_set_mod_debug_level(s.s, s.len, &l)<0)
 	{
 	{
 		LM_ERR("cannot store parameter: %s\n", (char*)val);
 		LM_ERR("cannot store parameter: %s\n", (char*)val);
 		return -1;
 		return -1;
 	}
 	}
+
 	return 0;
 	return 0;
 
 
 }
 }
@@ -371,65 +677,85 @@ static int dbg_mod_facility_param(modparam_t type, void *val)
 
 
 	s.s = (char*)val;
 	s.s = (char*)val;
 	s.len = p - s.s;
 	s.len = p - s.s;
+
+	if (!dbg_cfg) {
+		return -1;
+	}
+
 	LM_DBG("cfg facility_mode:%d hash_size:%d\n",
 	LM_DBG("cfg facility_mode:%d hash_size:%d\n",
 		cfg_get(dbg, dbg_cfg, mod_facility_mode),
 		cfg_get(dbg, dbg_cfg, mod_facility_mode),
 		cfg_get(dbg, dbg_cfg, mod_hash_size));
 		cfg_get(dbg, dbg_cfg, mod_hash_size));
+
 	if(dbg_init_mod_levels(cfg_get(dbg, dbg_cfg, mod_hash_size))<0)
 	if(dbg_init_mod_levels(cfg_get(dbg, dbg_cfg, mod_hash_size))<0)
 	{
 	{
 		LM_ERR("failed to init per module log level\n");
 		LM_ERR("failed to init per module log level\n");
 		return -1;
 		return -1;
 	}
 	}
+
 	if(dbg_set_mod_debug_facility(s.s, s.len, &fl)<0)
 	if(dbg_set_mod_debug_facility(s.s, s.len, &fl)<0)
 	{
 	{
 		LM_ERR("cannot store parameter: %s\n", (char*)val);
 		LM_ERR("cannot store parameter: %s\n", (char*)val);
 		return -1;
 		return -1;
 	}
 	}
+
 	return 0;
 	return 0;
 }
 }
 
 
 static int fixup_dbg_sip_msg(void** param, int param_no)
 static int fixup_dbg_sip_msg(void** param, int param_no)
 {
 {
-    int facility;
-    int level;
-    struct action *dbg_sip_msg_action;
-
-    switch(param_no)
-    {
-        case 2:
-            facility = str2facility((char*)*(param));
-            if (facility == -1) {
-                LM_ERR("invalid log facility configured");
-                return E_UNSPEC;
-            }
-
-                         *param = (void*)(long)facility;    
-        break;
-        case 1:
-            switch(((char*)(*param))[2])
-            {
-                /* add L_OFFSET because L_WARN is consdered null pointer */
-                case 'A': level = L_ALERT + L_OFFSET; break;
-                case 'B': level = L_BUG + L_OFFSET; break;
-                case 'C': level = L_CRIT2 + L_OFFSET; break;
-                case 'E': level = L_ERR + L_OFFSET; break;
-                case 'W': level = L_WARN + L_OFFSET; break;
-                case 'N': level = L_NOTICE + L_OFFSET; break;
-                case 'I': level = L_INFO + L_OFFSET; break;
-                case 'D': level = L_DBG + L_OFFSET; break;
-                default:
-                    LM_ERR("unknown log level\n");
-                    return E_UNSPEC;
-            }
-
-            *param = (void*)(long)level;
-        break;
-    }
-
-    /* save the config line where this config function was called */
-    dbg_sip_msg_action = dbg_fixup_get_action(param, param_no);
-    _dbg_sip_msg_cline = dbg_sip_msg_action->cline;
-
-     return 0;
+	int facility;
+	int level;
+	struct action *dbg_sip_msg_action;
+
+	LM_DBG("dbg_sip_msg() called with %d params\n", param_no);
+
+	switch(param_no)
+	{
+		case 2:
+			facility = str2facility((char*)*(param));
+			if (facility == -1) {
+				LM_ERR("invalid log facility configured");
+				return E_UNSPEC;
+			}
+
+			*param = (void*)(long)facility;
+			break;
+
+		case 1:
+			switch(((char*)(*param))[2])
+			{
+				/* add L_OFFSET because L_WARN is consdered null pointer */
+				case 'A': level = L_ALERT + L_OFFSET; break;
+				case 'B': level = L_BUG + L_OFFSET; break;
+				case 'C': level = L_CRIT2 + L_OFFSET; break;
+				case 'E': level = L_ERR + L_OFFSET; break;
+				case 'W': level = L_WARN + L_OFFSET; break;
+				case 'N': level = L_NOTICE + L_OFFSET; break;
+				case 'I': level = L_INFO + L_OFFSET; break;
+				case 'D': level = L_DBG + L_OFFSET; break;
+				default:
+					LM_ERR("unknown log level\n");
+					return E_UNSPEC;
+			}
+
+			*param = (void*)(long)level;
+			break;
+
+		case 0:
+			_dbg_sip_msg_cline = -1;
+			return 0;
+
+		default:
+			// should not reach here
+			_dbg_sip_msg_cline = -1;
+			return -1;
+	}
+
+	/* save the config line where this config function was called */
+	dbg_sip_msg_action = dbg_fixup_get_action(param, param_no);
+	_dbg_sip_msg_cline = dbg_sip_msg_action->cline;
+
+	return 0;
 }
 }
 
 
 /**
 /**
@@ -438,108 +764,100 @@ static int fixup_dbg_sip_msg(void** param, int param_no)
   */
   */
 static int w_dbg_sip_msg(struct sip_msg* msg, char *level, char *facility)
 static int w_dbg_sip_msg(struct sip_msg* msg, char *level, char *facility)
 {
 {
-    int ilevel = cfg_get(core, core_cfg, debug);
-    int ifacility= cfg_get(core, core_cfg, log_facility);
-    int flag = FLAG_MSG_LUMPS_ONLY; // copy lumps only, not the whole message
-    unsigned int new_buf_offs=0, orig_offs = 0;
-    char *hdr_lumps = NULL;
-    char *bdy_lumps = NULL;
-    const char *start_txt = "------------------------- START OF SIP message debug --------------------------\n";
-    const char *hdr_txt =   "------------------------------ SIP header diffs -------------------------------\n";
-    const char *bdy_txt =   "------------------------------- SIP body diffs --------------------------------\n";
-    const char *end_txt =   "-------------------------- END OF SIP message debug ---------------------------\n\n";
-    struct dest_info send_info;
-    str obuf;
-
-    if (level != NULL) {
-        /* substract L_OFFSET previously added */
-        ilevel = (int)(long)level - L_OFFSET;
-    }
-
-    if (facility != NULL) {
-        ifacility = (int)(long)facility;
-    }
-
-    /* msg_apply_changes_f code needed to get the current msg */
-    init_dest_info(&send_info);
-    send_info.proto = PROTO_UDP;
-    if(msg->first_line.type == SIP_REPLY) {
-        obuf.s = generate_res_buf_from_sip_res(msg,
-                (unsigned int*)&obuf.len, BUILD_NO_VIA1_UPDATE);
-    } else {
-        obuf.s = build_req_buf_from_sip_req(msg,
-                (unsigned int*)&obuf.len, &send_info,
-                BUILD_NO_PATH|BUILD_NO_LOCAL_VIA|BUILD_NO_VIA1_UPDATE);
-    }
-
-    if(obuf.s == NULL)
-    {
-        LM_ERR("couldn't update msg buffer content\n");
-        return -1;
-    }
-
-    if(obuf.len >= BUF_SIZE)
-    {
-        LM_ERR("new buffer overflow (%d)\n", obuf.len);
-        pkg_free(obuf.s);
-        return -1;
-    }
-
-    /* skip original uri */
-    if (msg->new_uri.s){
-        orig_offs=msg->first_line.u.request.uri.s - msg->buf;
-        orig_offs=msg->first_line.u.request.uri.len;
-    }
-
-    /* alloc private mem and copy lumps */
-    hdr_lumps = pkg_malloc(BUF_SIZE);
-    bdy_lumps = pkg_malloc(BUF_SIZE);
-
-    new_buf_offs = 0;
-    process_lumps(msg, msg->add_rm, hdr_lumps, &new_buf_offs, &orig_offs, &send_info, flag);
-
-    new_buf_offs = 0;
-    process_lumps(msg, msg->body_lumps, bdy_lumps, &new_buf_offs, &orig_offs, &send_info, flag);
-
-    /* do the print */
-    if (hdr_lumps != NULL && bdy_lumps != NULL) {
-        LOG_FC(ifacility, ilevel, "CONFIG LINE %d\n%s%.*s%s%s%s%s%s",
-            _dbg_sip_msg_cline,
-            start_txt,
-            obuf.len, obuf.s,
-            hdr_txt, hdr_lumps,
-            bdy_txt, bdy_lumps,
-            end_txt);
-    } else if (hdr_lumps != NULL) {
-        LOG_FC(ifacility, ilevel, "CONFIG LINE %d\n%s%.*s%s%s%s",
-            _dbg_sip_msg_cline,
-            start_txt,
-            obuf.len, obuf.s,
-            hdr_txt, hdr_lumps,
-            end_txt);
-    } else if (bdy_lumps != NULL) {
-        LOG_FC(ifacility, ilevel, "CONFIG LINE %d\n%s%.*s%s%s%s",
-            _dbg_sip_msg_cline,
-            start_txt,
-            obuf.len, obuf.s,
-            bdy_txt, bdy_lumps,
-            end_txt);
-    } else {
-        LOG_FC(ifacility, ilevel, "CONFIG LINE %d\n%s%.*s%s",
-            _dbg_sip_msg_cline,
-            start_txt,
-            obuf.len, obuf.s,
-            end_txt);
-    }
-
-    /* free lumps */
-    if (hdr_lumps) {
-        pkg_free(hdr_lumps);
-    }
-
-    if (bdy_lumps) {
-        pkg_free(bdy_lumps);
-    }
-
-    return 1;
+	int ilevel = cfg_get(core, core_cfg, debug);
+	int ifacility= cfg_get(core, core_cfg, log_facility);
+	int flag = FLAG_MSG_LUMPS_ONLY; // copy lumps only, not the whole message
+	unsigned int new_buf_offs=0, orig_offs = 0;
+	char *hdr_lumps = NULL;
+	char *bdy_lumps = NULL;
+	const char *start_txt = "------------------------- START OF SIP message debug --------------------------\n";
+	const char *hdr_txt =   "------------------------------ SIP header diffs -------------------------------\n";
+	const char *bdy_txt =   "------------------------------- SIP body diffs --------------------------------\n";
+	const char *end_txt =   "-------------------------- END OF SIP message debug ---------------------------\n\n";
+	struct dest_info send_info;
+	str obuf;
+
+	if (msg->first_line.type != SIP_REPLY && get_route_type() != REQUEST_ROUTE) {
+		LM_ERR("invalid usage - not in request route\n");
+		return -1;
+	}
+
+	if (level != NULL) {
+		/* substract L_OFFSET previously added */
+		ilevel = (int)(long)level - L_OFFSET;
+	}
+
+	if (facility != NULL) {
+		ifacility = (int)(long)facility;
+	}
+
+	/* msg_apply_changes_f code needed to get the current msg */
+	init_dest_info(&send_info);
+	send_info.proto = PROTO_UDP;
+	if(msg->first_line.type == SIP_REPLY) {
+		obuf.s = generate_res_buf_from_sip_res(msg,
+				(unsigned int*)&obuf.len, BUILD_NO_VIA1_UPDATE);
+	} else {
+		obuf.s = build_req_buf_from_sip_req(msg,
+				(unsigned int*)&obuf.len, &send_info,
+				BUILD_NO_PATH|BUILD_NO_LOCAL_VIA|BUILD_NO_VIA1_UPDATE);
+	}
+
+	if(obuf.s == NULL)
+	{
+		LM_ERR("couldn't update msg buffer content\n");
+		return -1;
+	}
+
+	if(obuf.len >= BUF_SIZE)
+	{
+		LM_ERR("new buffer overflow (%d)\n", obuf.len);
+		pkg_free(obuf.s);
+		return -1;
+	}
+
+	/* skip original uri */
+	if (msg->new_uri.s){
+		orig_offs=msg->first_line.u.request.uri.s - msg->buf;
+		orig_offs=msg->first_line.u.request.uri.len;
+	}
+
+	/* alloc private mem and copy lumps */
+	hdr_lumps = pkg_malloc(BUF_SIZE);
+	bdy_lumps = pkg_malloc(BUF_SIZE);
+
+	new_buf_offs = 0;
+	process_lumps(msg, msg->add_rm, hdr_lumps, &new_buf_offs, &orig_offs, &send_info, flag);
+
+	new_buf_offs = 0;
+	process_lumps(msg, msg->body_lumps, bdy_lumps, &new_buf_offs, &orig_offs, &send_info, flag);
+
+	/* do the print */
+	if (_dbg_sip_msg_cline < 0 ) {
+		LOG_FC(ifacility, ilevel, "CONFIG LINE unknown\n%s%.*s%s%s%s%s%s",
+			start_txt,
+			obuf.len, obuf.s,
+			hdr_txt, hdr_lumps,
+			bdy_txt, bdy_lumps,
+			end_txt);
+	} else {
+		LOG_FC(ifacility, ilevel, "CONFIG LINE %d\n%s%.*s%s%s%s%s%s",
+			_dbg_sip_msg_cline,
+			start_txt,
+			obuf.len, obuf.s,
+			hdr_txt, hdr_lumps,
+			bdy_txt, bdy_lumps,
+			end_txt);
+	}
+
+	/* free lumps */
+	if (hdr_lumps) {
+		pkg_free(hdr_lumps);
+	}
+
+	if (bdy_lumps) {
+		pkg_free(bdy_lumps);
+	}
+
+	return 1;
 }
 }

+ 181 - 0
modules/debugger/doc/debugger_admin.xml

@@ -57,6 +57,10 @@
 		    </listitem>
 		    </listitem>
 	    	</itemizedlist>
 	    	</itemizedlist>
 	    </para>
 	    </para>
+	    <para>
+		NOTE: Due to the debugger module child_init() function, one should load the module first in the module sequence in order to initialize _dbg_pid_list.
+		Otherwise, another module (i.e. p_usrloc) forking a process with rank != PROC_INIT will fail.
+	    </para>
 	</section>
 	</section>
 	<section>
 	<section>
 	    <title>External Libraries or Applications</title>
 	    <title>External Libraries or Applications</title>
@@ -418,6 +422,28 @@ modparam("debugger", "reset_msgid", 1)
 	    </example>
 	    </example>
 	</section>
 	</section>
 
 
+	<section id="dbg.p.cfgtest">
+	    <title><varname>cfgtest</varname> (int)</title>
+	    <para>
+			Control whether the cfgt module is enabled or disabled
+			at startup. Module cfgt needs to be loaded before.
+	    </para>
+	    <para>
+		<emphasis>
+		    Default value is <quote>0</quote> (disabled).
+		</emphasis>
+	    </para>
+	    <example>
+		<title>Set <varname>cfgtest</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+loadmodule "cfgt.so"
+modparam("debugger", "cfgtest", 1)
+...
+</programlisting>
+	    </example>
+	</section>
+
 	</section>
 	</section>
 
 
     <section>
     <section>
@@ -594,6 +620,83 @@ P-Hint: My hint
     </section>
     </section>
 
 
 
 
+
+	<section>
+		<title>Exported MI Functions</title>
+
+		<section id="debugger.m.set_dbg_mod_level mod_name level">
+			<title><function moreinfo="none">set_dbg_mod_level mod_name level</function></title>
+			<para>
+				Set the module log level.
+				If module does not exist in kamailio, the entry in the level hashtable is still added for the bogus module.
+			</para>
+			<example>
+				<title><function moreinfo="none">set_dbg_mod_level</function> usage</title>
+				<programlisting format="linespecific">
+...
+$ &ctltool; fifo set_dbg_mod_level core 2
+$ &ctltool; fifo set_dbg_mod_level debugger 3
+...
+				</programlisting>
+			</example>
+		</section>
+
+		<section id="debugger.m.set_dbg_mod_facility mod_name facility">
+			<title><function moreinfo="none">set_dbg_mod_facility mod_name facility</function></title>
+			<para>
+				Set the mod_name log facility.
+				If mod_name does not exist in kamailio, the entry in the facility hashtable is still added for the bogus mod_name.
+			</para>
+			<example>
+				<title><function moreinfo="none">set_dbg_mod_facility</function> usage</title>
+				<programlisting format="linespecific">
+...
+$ &ctltool; fifo set_dbg_mod_facility core LOG_LOCAL1
+$ &ctltool; fifo set_dbg_mod_facility debugger LOG_LOCAL0
+...
+				</programlisting>
+			</example>
+		</section>
+
+		<section id="debugger.m.get_dbg_mod_level mod_name">
+			<title><function moreinfo="none">get_dbg_mod_level mod_name</function></title>
+			<para>
+				Get the mod_name log level.
+				If mod_name does not exist in the level hashtable, returns the config file value.
+			</para>
+			<example>
+				<title><function moreinfo="none">get_dbg_mod_level</function> usage</title>
+				<programlisting format="linespecific">
+...
+$ &ctltool; fifo get_dbg_mod_level core
+$ &ctltool; fifo get_dbg_mod_level debugger
+...
+				</programlisting>
+			</example>
+		</section>
+
+		<section id="debugger.m.get_dbg_mod_facility mod_name">
+			<title><function moreinfo="none">get_dbg_mod_facility mod_name</function></title>
+			<para>
+				Get the mod_name log facility.
+				If mod_name does not exist in the facility hashtable, returns the config file value.
+			</para>
+			<example>
+				<title><function moreinfo="none">get_dbg_mod_facility</function> usage</title>
+				<programlisting format="linespecific">
+...
+$ &ctltool; fifo get_dbg_mod_facility core
+$ &ctltool; fifo get_dbg_mod_facility debugger
+...
+				</programlisting>
+			</example>
+		</section>
+
+	</section>
+
+
+
+
 	<section>
 	<section>
 		<title>Exported RPC Functions</title>
 		<title>Exported RPC Functions</title>
 
 
@@ -775,7 +878,85 @@ P-Hint: My hint
 		</programlisting>
 		</programlisting>
     </section>
     </section>
 
 
+	<section id="dbg.r.set_mod_level">
+		<title>
+			<function moreinfo="none">dbg.set_mod_level</function>
+		</title>
+		<para>
+			Set the module log level.
+			If module does not exist in kamailio, the entry in the level hashtable is still added for the bogus module.
+		</para>
+		<para>
+			Name: <emphasis>dbg.set_mod_level</emphasis>
+		</para>
+		<para>
+			Examples of use with &sercmd;:
+		</para>
+		<programlisting  format="linespecific">
+			dbg.set_mod_level core 1
+		</programlisting>
+	</section>
+
+	<section id="dbg.r.set_mod_facility">
+		<title>
+			<function moreinfo="none">dbg.set_mod_facility</function>
+		</title>
+		<para>
+			Set the module log facility.
+			If module does not exist in kamailio, the entry in the facility hashtable is still added for the bogus module.
+		</para>
+		<para>
+			Name: <emphasis>dbg.set_mod_facility</emphasis>
+		</para>
+		<para>
+			Examples of use with &sercmd;:
+		</para>
+		<programlisting  format="linespecific">
+			dbg.set_mod_facility core LOG_LOCAL1
+		</programlisting>
+	</section>
+
+	<section id="dbg.r.get_mod_level">
+		<title>
+			<function moreinfo="none">dbg.get_mod_level</function>
+		</title>
+		<para>
+			Get the module log level.
+			If mod_name does not exist in the level hashtable, returns the config file value.
+		</para>
+		<para>
+			Name: <emphasis>dbg.get_mod_level</emphasis>
+		</para>
+		<para>
+			Examples of use with &sercmd;:
+		</para>
+		<programlisting  format="linespecific">
+			dbg.get_mod_level core
+		</programlisting>
+	</section>
+
+	<section id="dbg.r.get_mod_facility">
+		<title>
+			<function moreinfo="none">dbg.get_mod_facility</function>
+		</title>
+		<para>
+			Get the module log facility.
+			If mod_name does not exist in the facility hashtable, returns the config file value.
+		</para>
+		<para>
+			Name: <emphasis>dbg.get_mod_facility</emphasis>
+		</para>
+		<para>
+			Examples of use with &sercmd;:
+		</para>
+		<programlisting  format="linespecific">
+			dbg.get_mod_facility core
+		</programlisting>
+	</section>
+
     </section>
     </section>
+
+
 	<section>
 	<section>
 		<title>Usage</title>
 		<title>Usage</title>
 		<para>
 		<para>

+ 1 - 0
modules/dispatcher/README

@@ -1373,6 +1373,7 @@ modparam("dispatcher", "flags", 2)
 modparam("dispatcher", "dst_avp", "$avp(AVP_DST)")
 modparam("dispatcher", "dst_avp", "$avp(AVP_DST)")
 modparam("dispatcher", "grp_avp", "$avp(AVP_GRP)")
 modparam("dispatcher", "grp_avp", "$avp(AVP_GRP)")
 modparam("dispatcher", "cnt_avp", "$avp(AVP_CNT)")
 modparam("dispatcher", "cnt_avp", "$avp(AVP_CNT)")
+modparam("dispatcher", "sock_avp", "$avp(AVP_SOCK)")
 
 
 ####### Routing Logic ########
 ####### Routing Logic ########
 
 

+ 13 - 0
modules/dispatcher/dispatch.c

@@ -255,6 +255,7 @@ int add_dest2list(int id, str uri, int flags, int priority, str *attrs,
 	int orig_id = 0, orig_nr = 0;
 	int orig_id = 0, orig_nr = 0;
 	str host;
 	str host;
 	int port, proto;
 	int port, proto;
+	char c = 0;
 	ds_set_t *orig_ds_lists = ds_lists[list_idx];
 	ds_set_t *orig_ds_lists = ds_lists[list_idx];
 
 
 	/* check uri */
 	/* check uri */
@@ -329,11 +330,23 @@ int add_dest2list(int id, str uri, int flags, int priority, str *attrs,
 
 
 	/* check socket attribute */
 	/* check socket attribute */
 	if (dp->attrs.socket.s && dp->attrs.socket.len > 0) {
 	if (dp->attrs.socket.s && dp->attrs.socket.len > 0) {
+		/* parse_phostport(...) expects 0-terminated string
+		 * - after socket parameter is either ';' or '\0' */
+		if(dp->attrs.socket.s[dp->attrs.socket.len]!='\0') {
+			c = dp->attrs.socket.s[dp->attrs.socket.len];
+			dp->attrs.socket.s[dp->attrs.socket.len] = '\0';
+		}
 		if (parse_phostport(dp->attrs.socket.s, &host.s, &host.len,
 		if (parse_phostport(dp->attrs.socket.s, &host.s, &host.len,
 				&port, &proto)!=0) {
 				&port, &proto)!=0) {
 			LM_ERR("bad socket <%.*s>\n", dp->attrs.socket.len, dp->attrs.socket.s);
 			LM_ERR("bad socket <%.*s>\n", dp->attrs.socket.len, dp->attrs.socket.s);
+			if(c!=0) {
+				dp->attrs.socket.s[dp->attrs.socket.len] = c;
+			}
 			goto err;
 			goto err;
 		}
 		}
+		if(c!=0) {
+			dp->attrs.socket.s[dp->attrs.socket.len] = c;
+		}
 		dp->sock = grep_sock_info( &host, (unsigned short)port, proto);
 		dp->sock = grep_sock_info( &host, (unsigned short)port, proto);
 		if (dp->sock==0) {
 		if (dp->sock==0) {
 			LM_ERR("non-local socket <%.*s>\n", dp->attrs.socket.len, dp->attrs.socket.s);
 			LM_ERR("non-local socket <%.*s>\n", dp->attrs.socket.len, dp->attrs.socket.s);

+ 1 - 0
modules/dispatcher/doc/dispatcher.cfg

@@ -115,6 +115,7 @@ modparam("dispatcher", "flags", 2)
 modparam("dispatcher", "dst_avp", "$avp(AVP_DST)")
 modparam("dispatcher", "dst_avp", "$avp(AVP_DST)")
 modparam("dispatcher", "grp_avp", "$avp(AVP_GRP)")
 modparam("dispatcher", "grp_avp", "$avp(AVP_GRP)")
 modparam("dispatcher", "cnt_avp", "$avp(AVP_CNT)")
 modparam("dispatcher", "cnt_avp", "$avp(AVP_CNT)")
+modparam("dispatcher", "sock_avp", "$avp(AVP_SOCK)")
 
 
 ####### Routing Logic ########
 ####### Routing Logic ########
 
 

+ 7 - 4
modules/dmq_usrloc/usrloc_sync.c

@@ -56,10 +56,13 @@ static int add_contact(str aor, ucontact_info_t* ci)
 	str contact;
 	str contact;
 	int res;
 	int res;
 
 
-        if (dmq_ul.get_udomain("location", &_d) < 0) {
-                LM_ERR("Failed to get domain\n");
-                return -1;
-        }
+	if (dmq_ul.get_udomain("location", &_d) < 0) {
+		LM_ERR("Failed to get domain\n");
+		return -1;
+	}
+
+	dmq_ul.lock_udomain(_d, &aor);
+
 	res = dmq_ul.get_urecord(_d, &aor, &r);
 	res = dmq_ul.get_urecord(_d, &aor, &r);
 	if (res < 0) {
 	if (res < 0) {
 		LM_ERR("failed to retrieve record from usrloc\n");
 		LM_ERR("failed to retrieve record from usrloc\n");

+ 1 - 1
modules/htable/htable.c

@@ -1125,7 +1125,7 @@ static void  htable_rpc_stats(rpc_t* rpc, void* c)
 			ht_slot_unlock(ht, i);
 			ht_slot_unlock(ht, i);
 		}
 		}
 
 
-		if(rpc->struct_add(th, "Sddd",
+		if(rpc->struct_add(th, "Sdddd",
 						"name", &ht->name,	/* str */
 						"name", &ht->name,	/* str */
 						"slots", (int)ht->htsize,	/* uint */
 						"slots", (int)ht->htsize,	/* uint */
 						"all", (int)all,	/* uint */
 						"all", (int)all,	/* uint */

+ 3 - 1
modules/ims_auth/cxdx_mar.c

@@ -137,7 +137,7 @@ void async_cdp_callback(int is_timeout, void *param, AAAMessage *maa, long elaps
     if (tmb.t_lookup_ident(&t, data->tindex, data->tlabel) < 0) {
     if (tmb.t_lookup_ident(&t, data->tindex, data->tlabel) < 0) {
         LM_ERR("t_continue: transaction not found\n");
         LM_ERR("t_continue: transaction not found\n");
         result = CSCF_RETURN_ERROR;
         result = CSCF_RETURN_ERROR;
-        goto error;
+        goto error1;
     }
     }
 
 
     /* get the private_identity */
     /* get the private_identity */
@@ -485,6 +485,8 @@ error:
         tmb.unref_cell(t);
         tmb.unref_cell(t);
     }
     }
     tmb.t_continue(data->tindex, data->tlabel, data->act);
     tmb.t_continue(data->tindex, data->tlabel, data->act);
+    
+error1:    
     free_saved_transaction_data(data);
     free_saved_transaction_data(data);
 }
 }
 
 

+ 37 - 99
modules/ims_charging/dialog.c

@@ -1,27 +1,31 @@
 #include "mod.h"
 #include "mod.h"
 #include "dialog.h"
 #include "dialog.h"
 #include "ro_session_hash.h"
 #include "ro_session_hash.h"
-#include "../ims_usrloc_scscf/usrloc.h"
-#include "../ims_usrloc_scscf/udomain.h"
 #include "ro_db_handler.h"
 #include "ro_db_handler.h"
+#include "ims_charging_stats.h"
 
 
 struct cdp_binds cdpb;
 struct cdp_binds cdpb;
 
 
-extern usrloc_api_t ul;
 extern int ro_db_mode;
 extern int ro_db_mode;
 extern char *domain;
 extern char *domain;
 extern struct dlg_binds dlgb;
 extern struct dlg_binds dlgb;
+extern struct ims_charging_counters_h ims_charging_cnts_h;
 
 
 void dlg_callback_received(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) {
 void dlg_callback_received(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) {
     LM_DBG("Received dialog callback event [%d]\n", type);
     LM_DBG("Received dialog callback event [%d]\n", type);
+    unsigned int termcode = 0;
     switch (type) {
     switch (type) {
         case DLGCB_CONFIRMED:
         case DLGCB_CONFIRMED:
             dlg_answered(dlg, type, _params);
             dlg_answered(dlg, type, _params);
             break;
             break;
         case DLGCB_TERMINATED:
         case DLGCB_TERMINATED:
+            dlg_terminated(dlg, type, termcode, "normal call clearing", _params);
+            break;
         case DLGCB_FAILED:
         case DLGCB_FAILED:
+            dlg_terminated(dlg, type, termcode, "call failed", _params);
+            break;
         case DLGCB_EXPIRED:
         case DLGCB_EXPIRED:
-            dlg_terminated(dlg, type, _params);
+            dlg_terminated(dlg, type, termcode, "dialog timeout", _params);
             break;
             break;
         default:
         default:
             LM_WARN("Received unknown dialog callback [%d]\n", type);
             LM_WARN("Received unknown dialog callback [%d]\n", type);
@@ -29,7 +33,6 @@ void dlg_callback_received(struct dlg_cell *dlg, int type, struct dlg_cb_params
 }
 }
 
 
 void dlg_answered(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) {
 void dlg_answered(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) {
-    struct sip_msg *reply;
     struct ro_session* session = 0;
     struct ro_session* session = 0;
     struct ro_session_entry* ro_session_entry;
     struct ro_session_entry* ro_session_entry;
     time_t now = get_current_time_micro();
     time_t now = get_current_time_micro();
@@ -114,19 +117,38 @@ void dlg_answered(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params)
 
 
 }
 }
 
 
-void dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) {
+void dlg_terminated(struct dlg_cell *dlg, int type, unsigned int termcode, char* reason, struct dlg_cb_params *_params) {
 	//int i;
 	//int i;
 	int unref = 0;
 	int unref = 0;
 	struct ro_session *ro_session = 0;
 	struct ro_session *ro_session = 0;
 	struct ro_session_entry *ro_session_entry;
 	struct ro_session_entry *ro_session_entry;
 	struct sip_msg *request;
 	struct sip_msg *request;
+        str s_reason;
+        
+        s_reason.s = reason;
+        s_reason.len = strlen(reason);
 	
 	
-	LM_DBG("dialog [%p] terminated, lets send stop record\n", dlg);
+	LM_DBG("dialog [%p] terminated on type [%d], lets send stop record\n", dlg, type);
 
 
 	if (!_params) {
 	if (!_params) {
 		return;
 		return;
 	}
 	}
 	
 	
+	LM_DBG("Direction is %d\n", _params->direction);
+	if (_params->req) {
+		if (_params->req->first_line.u.request.method_value == METHOD_BYE) {
+			if (_params->direction == DLG_DIR_DOWNSTREAM) {
+				LM_DBG("Dialog ended by Caller\n");
+			} else {
+				LM_DBG("Dialog ended by Callee\n");
+			}
+		} else {
+			LM_DBG("Request is %.*s\n", _params->req->first_line.u.request.method.len, _params->req->first_line.u.request.method.s);
+		}
+	} else if (_params->rpl) {
+		LM_DBG("Reply is [%d - %.*s]", _params->rpl->first_line.u.reply.statuscode, _params->rpl->first_line.u.reply.reason.len, _params->rpl->first_line.u.reply.reason.s);
+	}
+	
 	ro_session = (struct ro_session*)*_params->param;
 	ro_session = (struct ro_session*)*_params->param;
 	if (!ro_session) {
 	if (!ro_session) {
 		LM_ERR("Ro Session object is NULL...... aborting\n");
 		LM_ERR("Ro Session object is NULL...... aborting\n");
@@ -153,14 +175,14 @@ void dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *_param
 				//double processing for various dialog_terminated callback events.
 				//double processing for various dialog_terminated callback events.
 				//If however, the call was never answered, then we can continue as normal
 				//If however, the call was never answered, then we can continue as normal
 				ro_session_lock(ro_session_table, ro_session_entry);
 				ro_session_lock(ro_session_table, ro_session_entry);
-				if (!ro_session->active && (ro_session->start_time != 0)) {
-					unref_ro_session(ro_session,1);
-					LM_ERR("Ro Session is not active, but may have been answered [%d]\n", (int)ro_session->start_time);
+                                
+                                LM_DBG("processing dlg_terminated in Ro and session [%.*s] has active = %d", ro_session->ro_session_id.len, ro_session->ro_session_id.s, ro_session->active);
+				if ((!ro_session->active && (ro_session->start_time != 0)) || (ro_session->ccr_sent == 1)) {
+					unref_ro_session_unsafe(ro_session,1,ro_session_entry);
+					LM_ERR("CCR already sent or Ro Session is not active, but may have been answered [%d]\n", (int)ro_session->start_time);
 					ro_session_unlock(ro_session_table, ro_session_entry);
 					ro_session_unlock(ro_session_table, ro_session_entry);
 					return;
 					return;
 				}
 				}
-			
-	
 
 
 				if (ro_session->active) { // if the call was never activated, there's no timer to remove
 				if (ro_session->active) { // if the call was never activated, there's no timer to remove
 					int ret = remove_ro_timer(&ro_session->ro_tl);
 					int ret = remove_ro_timer(&ro_session->ro_tl);
@@ -176,8 +198,10 @@ void dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *_param
 				}
 				}
 
 
 				LM_DBG("Sending CCR STOP on Ro_Session [%p]\n", ro_session);
 				LM_DBG("Sending CCR STOP on Ro_Session [%p]\n", ro_session);
-				send_ccr_stop(ro_session);
+				send_ccr_stop_with_param(ro_session, termcode, &s_reason);
 				ro_session->active = -1;    //deleted.... terminated ....
 				ro_session->active = -1;    //deleted.... terminated ....
+                                ro_session->ccr_sent = 1;
+//                                counter_add(ims_charging_cnts_h.active_ro_sessions, -1);
 				
 				
 				if (ro_db_mode == DB_MODE_REALTIME) {
 				if (ro_db_mode == DB_MODE_REALTIME) {
 				    ro_session->flags |= RO_SESSION_FLAG_DELETED;
 				    ro_session->flags |= RO_SESSION_FLAG_DELETED;
@@ -194,89 +218,3 @@ void dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *_param
 		//}
 		//}
 	}
 	}
 }
 }
-
-void remove_dlg_data_from_contact(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) {
-        
-    struct impu_data *impu_data;
-    impurecord_t* implicit_impurecord = 0;
-    struct ucontact* ucontact;
-    str callid = {0, 0};
-    str path = {0, 0};
-    udomain_t* domain_t;
-    
-    LM_DBG("dialog [%p] terminated, lets remove dlg data from contact\n", dlg);
-    
-    if (ul.register_udomain(domain, &domain_t) < 0) {
-	    LM_ERR("Unable to register usrloc domain....aborting\n");
-	    return;
-    }
-    
-    if(_params && _params->param){
-	impu_data = (struct impu_data*)*_params->param;
-	if (!impu_data) {
-		LM_ERR("IMPU data object is NULL...... aborting\n");
-		return;
-	}
-	
-	LM_DBG("IMPU data is present, contact: <%.*s> identity <%.*s>", impu_data->contact.len, impu_data->contact.s, impu_data->identity.len, impu_data->identity.s);
-	LM_DBG("IMPU data domain <%.*s>", domain_t->name->len, domain_t->name->s);
-	
-	ul.lock_udomain(domain_t, &impu_data->identity);
-	if (ul.get_impurecord(domain_t, &impu_data->identity, &implicit_impurecord) != 0) {
-	    LM_DBG("usrloc does not have imprecord for implicity IMPU, ignore\n");
-	}else {
-	    if (ul.get_ucontact(&impu_data->contact, &callid, &path, 0/*cseq*/,  &ucontact) != 0) { //contact does not exist
-		LM_DBG("This contact: <%.*s> is not in usrloc, ignore - NOTE: You need S-CSCF usrloc set to match_mode CONTACT_PORT_IP_ONLY\n", impu_data->contact.len, impu_data->contact.s);
-	    } else {//contact exists so add dialog data to it
-		ul.remove_dialog_data_from_contact(ucontact, dlg->h_entry, dlg->h_id);
-		ul.release_ucontact(ucontact);
-	    }
-	}
-	ul.unlock_udomain(domain_t, &impu_data->identity);
-	free_impu_data(impu_data);
-    }
-    
-    //we referenced the dialog when we registered for callbacks on it...
-    dlgb.release_dlg(dlg);
-}
-
-void add_dlg_data_to_contact(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params) {
-    
-    struct impu_data *impu_data;
-    impurecord_t* implicit_impurecord = 0;
-    struct ucontact* ucontact;
-    str callid = {0, 0};
-    str path = {0, 0};
-    udomain_t* domain_t;
-    
-    LM_DBG("dialog [%p] confirmed, lets add dlg data to contact\n", dlg);
-    
-    if (ul.register_udomain(domain, &domain_t) < 0) {
-	    LM_ERR("Unable to register usrloc domain....aborting\n");
-	    return;
-    }
-    
-    if(_params && _params->param){
-	impu_data = (struct impu_data*)*_params->param;
-	if (!impu_data) {
-		LM_ERR("IMPU data object is NULL...... aborting\n");
-		return;
-	}
-	
-	LM_DBG("IMPU data is present, contact: <%.*s> identity <%.*s>", impu_data->contact.len, impu_data->contact.s, impu_data->identity.len, impu_data->identity.s);
-	LM_DBG("IMPU data domain <%.*s>", domain_t->name->len, domain_t->name->s);
-	
-	ul.lock_udomain(domain_t, &impu_data->identity);
-	if (ul.get_impurecord(domain_t, &impu_data->identity, &implicit_impurecord) != 0) {
-	    LM_DBG("usrloc does not have imprecord for implicity IMPU, ignore\n");
-	}else {
-	    if (ul.get_ucontact(&impu_data->contact, &callid, &path, 0/*cseq*/,  &ucontact) != 0) { //contact does not exist
-		LM_DBG("This contact: <%.*s> is not in usrloc, ignore - NOTE: You need S-CSCF usrloc set to match_mode CONTACT_PORT_IP_ONLY\n", impu_data->contact.len, impu_data->contact.s);
-	    } else {//contact exists so add dialog data to it
-		ul.add_dialog_data_to_contact(ucontact, dlg->h_entry, dlg->h_id);
-		ul.release_ucontact(ucontact);
-	    }
-	}
-	ul.unlock_udomain(domain_t, &impu_data->identity);
-    }
-} 

+ 1 - 3
modules/ims_charging/dialog.h

@@ -9,9 +9,7 @@
 extern int ro_timer_buffer;
 extern int ro_timer_buffer;
 
 
 void dlg_callback_received(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params);
 void dlg_callback_received(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params);
-void dlg_terminated(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params);
+void dlg_terminated(struct dlg_cell *dlg, int type, unsigned int termcode, char* reason, struct dlg_cb_params *_params);
 void dlg_answered(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params);
 void dlg_answered(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params);
-void add_dlg_data_to_contact(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params);
-void remove_dlg_data_from_contact(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params);
 
 
 #endif
 #endif

+ 69 - 5
modules/ims_charging/ims_ro.c

@@ -42,6 +42,8 @@ extern cdp_avp_bind_t *cdp_avp;
 extern str ro_forced_peer;
 extern str ro_forced_peer;
 extern int ro_db_mode;
 extern int ro_db_mode;
 extern struct ims_charging_counters_h ims_charging_cnts_h;
 extern struct ims_charging_counters_h ims_charging_cnts_h;
+extern int vendor_specific_id;
+extern int vendor_specific_chargeinfo;
 
 
 struct session_setup_data {
 struct session_setup_data {
     struct ro_session *ro_session;
     struct ro_session *ro_session;
@@ -180,6 +182,21 @@ inline int Ro_add_termination_cause(AAAMessage *msg, unsigned int term_code) {
     return Ro_add_avp(msg, s.s, s.len, AVP_Termination_Cause, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
     return Ro_add_avp(msg, s.s, s.len, AVP_Termination_Cause, AAA_AVP_FLAG_MANDATORY, 0, AVP_DUPLICATE_DATA, __FUNCTION__);
 }
 }
 
 
+inline int Ro_add_vendor_specific_termination_cause(AAAMessage *msg, unsigned int term_code) {
+    char x[4];
+    str s = {x, 4};
+    uint32_t code = htonl(term_code);
+    memcpy(x, &code, sizeof (uint32_t));
+
+    return Ro_add_avp(msg, s.s, s.len, VS_TERMCODE, AAA_AVP_FLAG_VENDOR_SPECIFIC, 10, AVP_DUPLICATE_DATA, __FUNCTION__);
+}
+
+inline int Ro_add_vendor_specific_termination_reason(AAAMessage *msg, str* reason) {
+    return Ro_add_avp(msg, reason->s, reason->len, VS_TERMREASON, AAA_AVP_FLAG_VENDOR_SPECIFIC, 10, AVP_DUPLICATE_DATA, __FUNCTION__);
+}
+
+
+
 /* called only when building stop record AVPS */
 /* called only when building stop record AVPS */
 inline int Ro_add_multiple_service_credit_Control_stop(AAAMessage *msg, int used_unit, int active_rating_group, int active_service_identifier) {
 inline int Ro_add_multiple_service_credit_Control_stop(AAAMessage *msg, int used_unit, int active_rating_group, int active_service_identifier) {
     char x[4];
     char x[4];
@@ -721,6 +738,7 @@ static void resume_on_interim_ccr(int is_timeout, void *param, AAAMessage *cca,
     goto success;
     goto success;
 
 
 error:
 error:
+    counter_inc(ims_charging_cnts_h.failed_interim_ccr);  
     if (ro_cca_data)
     if (ro_cca_data)
         Ro_free_CCA(ro_cca_data);
         Ro_free_CCA(ro_cca_data);
 
 
@@ -744,7 +762,7 @@ long get_current_time_micro() {
     return tv.tv_sec*1000000 + tv.tv_usec;
     return tv.tv_sec*1000000 + tv.tv_usec;
 }
 }
 
 
-void send_ccr_stop(struct ro_session *ro_session) {
+void send_ccr_stop_with_param(struct ro_session *ro_session, unsigned int code, str* reason) {
     AAASession * auth = 0;
     AAASession * auth = 0;
     Ro_CCR_t * ro_ccr_data = 0;
     Ro_CCR_t * ro_ccr_data = 0;
     AAAMessage * ccr = 0;
     AAAMessage * ccr = 0;
@@ -755,9 +773,29 @@ void send_ccr_stop(struct ro_session *ro_session) {
     long used = 0;
     long used = 0;
     str user_name = {0, 0};
     str user_name = {0, 0};
     int ret = 0;
     int ret = 0;
+    time_t stop_time;
+    time_t actual_time_micros;
+    int actual_time_seconds;
+
+    stop_time = get_current_time_micro();
+
+    if (ro_session->start_time == 0)
+        actual_time_micros = 0;
+    else
+        actual_time_micros = stop_time - ro_session->start_time;
+
+    actual_time_seconds = (actual_time_micros + (1000000 - 1)) / (float) 1000000;
 
 
     if (ro_session->event_type != pending) {
     if (ro_session->event_type != pending) {
-        used = rint((get_current_time_micro() - ro_session->last_event_timestamp)/(float)1000000);
+        used = rint((stop_time - ro_session->last_event_timestamp) / (float) 1000000);
+        LM_DBG("Final used number of seconds for session is %ld\n", used);
+    }
+
+    LM_DBG("Call started at %ld and ended at %ld and lasted %d seconds and so far we have billed for %ld seconds\n", ro_session->start_time, stop_time,
+            actual_time_seconds, ro_session->billed + used);
+    if (ro_session->billed + used < actual_time_seconds) {
+        LM_DBG("Making adjustment by adding %ld seconds\n", actual_time_seconds - (ro_session->billed + used));
+        used += actual_time_seconds - (ro_session->billed + used);
     }
     }
 
 
     counter_add(ims_charging_cnts_h.billed_secs, (int)used);
     counter_add(ims_charging_cnts_h.billed_secs, (int)used);
@@ -869,6 +907,16 @@ void send_ccr_stop(struct ro_session *ro_session) {
         LM_ERR("problem add Termination cause AVP to STOP record.\n");
         LM_ERR("problem add Termination cause AVP to STOP record.\n");
     }
     }
 
 
+    if (vendor_specific_chargeinfo) {
+        if (!Ro_add_vendor_specific_termination_cause(ccr, code)) {
+            LM_ERR("problem add Termination cause AVP to STOP record.\n");
+        }
+
+        if (!Ro_add_vendor_specific_termination_reason(ccr, reason)) {
+            LM_ERR("problem add Termination cause AVP to STOP record.\n");
+        }
+    }
+
     cdpb.AAASessionsUnlock(auth->hash);
     cdpb.AAASessionsUnlock(auth->hash);
 
 
     if (ro_forced_peer.len > 0) {
     if (ro_forced_peer.len > 0) {
@@ -884,7 +932,7 @@ void send_ccr_stop(struct ro_session *ro_session) {
     Ro_free_CCR(ro_ccr_data);
     Ro_free_CCR(ro_ccr_data);
 
 
     counter_inc(ims_charging_cnts_h.final_ccrs);
     counter_inc(ims_charging_cnts_h.final_ccrs);
-    counter_add(ims_charging_cnts_h.active_ro_sessions, -1);
+//    counter_add(ims_charging_cnts_h.active_ro_sessions, -1);
     return;
     return;
 
 
 error1:
 error1:
@@ -915,6 +963,7 @@ static void resume_on_termination_ccr(int is_timeout, void *param, AAAMessage *c
 
 
     if (!cca) {
     if (!cca) {
         LM_ERR("Error in termination CCR.\n");
         LM_ERR("Error in termination CCR.\n");
+        counter_inc(ims_charging_cnts_h.failed_final_ccrs); 
         return;
         return;
     }
     }
 
 
@@ -922,6 +971,7 @@ static void resume_on_termination_ccr(int is_timeout, void *param, AAAMessage *c
 
 
     if (ro_cca_data == NULL) {
     if (ro_cca_data == NULL) {
         LM_DBG("Could not parse CCA message response.\n");
         LM_DBG("Could not parse CCA message response.\n");
+        counter_inc(ims_charging_cnts_h.failed_final_ccrs); 
         return;
         return;
     }
     }
 
 
@@ -933,8 +983,14 @@ static void resume_on_termination_ccr(int is_timeout, void *param, AAAMessage *c
     }
     }
 
 
     counter_inc(ims_charging_cnts_h.successful_final_ccrs);
     counter_inc(ims_charging_cnts_h.successful_final_ccrs);
+    Ro_free_CCA(ro_cca_data);
+    if (!is_timeout && cca) {
+        cdpb.AAAFreeMessage(&cca);
+    }
+    return;
 
 
 error:
 error:
+    counter_inc(ims_charging_cnts_h.failed_final_ccrs);      
     Ro_free_CCA(ro_cca_data);
     Ro_free_CCA(ro_cca_data);
     if (!is_timeout && cca) {
     if (!is_timeout && cca) {
         cdpb.AAAFreeMessage(&cca);
         cdpb.AAAFreeMessage(&cca);
@@ -1138,6 +1194,7 @@ int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir, int reservat
     LM_DBG("new CC Ro Session ID: [%.*s] stored in shared memory address [%p]\n", cc_acc_session->id.len, cc_acc_session->id.s, new_session);
     LM_DBG("new CC Ro Session ID: [%.*s] stored in shared memory address [%p]\n", cc_acc_session->id.len, cc_acc_session->id.s, new_session);
 
 
     LM_DBG("Sending CCR Diameter message.\n");
     LM_DBG("Sending CCR Diameter message.\n");
+//    new_session->ccr_sent = 1;      //assume we will send successfully
     cdpb.AAASessionsUnlock(cc_acc_session->hash);
     cdpb.AAASessionsUnlock(cc_acc_session->hash);
 
 
     if (ro_forced_peer.len > 0) {
     if (ro_forced_peer.len > 0) {
@@ -1150,6 +1207,7 @@ int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir, int reservat
 
 
     if (ret != 1) {
     if (ret != 1) {
         LM_ERR("Failed to send Diameter CCR\n");
         LM_ERR("Failed to send Diameter CCR\n");
+//        new_session->ccr_sent = 0;
         goto error;
         goto error;
     }
     }
 
 
@@ -1164,6 +1222,7 @@ int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir, int reservat
     }
     }
 
 
     counter_inc(ims_charging_cnts_h.initial_ccrs);
     counter_inc(ims_charging_cnts_h.initial_ccrs);
+    counter_inc(ims_charging_cnts_h.active_ro_sessions);
 
 
     if (free_called_asserted_identity) shm_free(called_asserted_identity.s); // shm_malloc in cscf_get_public_identity_from_requri	
     if (free_called_asserted_identity) shm_free(called_asserted_identity.s); // shm_malloc in cscf_get_public_identity_from_requri	
     return RO_RETURN_BREAK;
     return RO_RETURN_BREAK;
@@ -1255,13 +1314,17 @@ static void resume_on_initial_ccr(int is_timeout, void *param, AAAMessage *cca,
     ssd->ro_session->event_type = pending;
     ssd->ro_session->event_type = pending;
     ssd->ro_session->reserved_secs = ro_cca_data->mscc->granted_service_unit->cc_time;
     ssd->ro_session->reserved_secs = ro_cca_data->mscc->granted_service_unit->cc_time;
     ssd->ro_session->valid_for = ro_cca_data->mscc->validity_time;
     ssd->ro_session->valid_for = ro_cca_data->mscc->validity_time;
+    ssd->ro_session->is_final_allocation = 0;
+
+    if (ro_cca_data->mscc->final_unit_action && (ro_cca_data->mscc->final_unit_action->action == 0))
+        ssd->ro_session->is_final_allocation = 1;
 
 
     Ro_free_CCA(ro_cca_data);
     Ro_free_CCA(ro_cca_data);
 
 
     LM_DBG("Freeing CCA message\n");
     LM_DBG("Freeing CCA message\n");
     cdpb.AAAFreeMessage(&cca);
     cdpb.AAAFreeMessage(&cca);
 
 
-    link_ro_session(ssd->ro_session, 1); /* create extra ref for the fact that dialog has a handle in the callbacks */
+    link_ro_session(ssd->ro_session, 0); 
 
 
     if (ro_db_mode == DB_MODE_REALTIME) {
     if (ro_db_mode == DB_MODE_REALTIME) {
         ssd->ro_session->flags |= RO_SESSION_FLAG_NEW;
         ssd->ro_session->flags |= RO_SESSION_FLAG_NEW;
@@ -1281,7 +1344,6 @@ static void resume_on_initial_ccr(int is_timeout, void *param, AAAMessage *cca,
     shm_free(ssd);
     shm_free(ssd);
 
 
     counter_inc(ims_charging_cnts_h.successful_initial_ccrs);
     counter_inc(ims_charging_cnts_h.successful_initial_ccrs);
-    counter_inc(ims_charging_cnts_h.active_ro_sessions);
 
 
     return;
     return;
 
 
@@ -1290,6 +1352,8 @@ error1:
 
 
 error0:
 error0:
     LM_DBG("Trying to reserve credit on initial INVITE failed on cdp callback\n");
     LM_DBG("Trying to reserve credit on initial INVITE failed on cdp callback\n");
+//    counter_add(ims_charging_cnts_h.active_ro_sessions, -1); /*we bumped active on the original initial ccr sent */
+    counter_inc(ims_charging_cnts_h.failed_initial_ccrs);      /* drop by one as theoretically this is failed initial ccr */
     create_cca_return_code(error_code);
     create_cca_return_code(error_code);
 
 
     if (!is_timeout && cca) {
     if (!is_timeout && cca) {

+ 6 - 1
modules/ims_charging/ims_ro.h

@@ -6,6 +6,11 @@
 #include "../dialog_ng/dlg_hash.h"
 #include "../dialog_ng/dlg_hash.h"
 #include "ro_session_hash.h"
 #include "ro_session_hash.h"
 
 
+typedef enum {
+    VS_TERMCODE = 3,
+    VS_TERMREASON = 2
+} vs_term_avp;
+
 struct interim_ccr {
 struct interim_ccr {
 	struct ro_session* ro_session;
 	struct ro_session* ro_session;
 	int new_credit;
 	int new_credit;
@@ -19,7 +24,7 @@ int Ro_Send_CCR(struct sip_msg *msg, struct dlg_cell *dlg, int dir, int reservat
 	    str *incoming_trunk_id, str *outgoing_trunk_id, str *enb_cell_id, cfg_action_t* action, unsigned int tindex, unsigned int tlabel);
 	    str *incoming_trunk_id, str *outgoing_trunk_id, str *enb_cell_id, cfg_action_t* action, unsigned int tindex, unsigned int tlabel);
 long get_current_time_micro();
 long get_current_time_micro();
 void send_ccr_interim(struct ro_session* ro_session, unsigned int used, unsigned int reserve);
 void send_ccr_interim(struct ro_session* ro_session, unsigned int used, unsigned int reserve);
-void send_ccr_stop(struct ro_session *ro_session);
+void send_ccr_stop_with_param(struct ro_session *ro_session, unsigned int code, str* reason);
 int get_direction_as_int(str* direction);
 int get_direction_as_int(str* direction);
 
 
 #endif /* CLIENT_RF_IMS_RO_H */
 #endif /* CLIENT_RF_IMS_RO_H */

+ 101 - 24
modules/ims_charging/mod.c

@@ -17,10 +17,11 @@
 #include "ims_ro.h"
 #include "ims_ro.h"
 #include "config.h"
 #include "config.h"
 #include "dialog.h"
 #include "dialog.h"
-#include "../ims_usrloc_scscf/usrloc.h"
 #include "../../lib/ims/ims_getters.h"
 #include "../../lib/ims/ims_getters.h"
 #include "ro_db_handler.h"
 #include "ro_db_handler.h"
 #include "ims_charging_stats.h"
 #include "ims_charging_stats.h"
+#include "ro_session_hash.h"
+#include "ims_charging_stats.h"
 
 
 MODULE_VERSION
 MODULE_VERSION
 
 
@@ -31,6 +32,9 @@ char* ro_service_context_id_ext_s = "ext";
 char* ro_service_context_id_mnc_s = "01";
 char* ro_service_context_id_mnc_s = "01";
 char* ro_service_context_id_mcc_s = "001";
 char* ro_service_context_id_mcc_s = "001";
 char* ro_service_context_id_release_s = "8";
 char* ro_service_context_id_release_s = "8";
+int	termination_code = 0;
+int vendor_specific_id = 10;
+int vendor_specific_chargeinfo = 0;
 static int ro_session_hash_size = 4096;
 static int ro_session_hash_size = 4096;
 int ro_timer_buffer = 5;
 int ro_timer_buffer = 5;
 int interim_request_credits = 30;
 int interim_request_credits = 30;
@@ -57,13 +61,12 @@ client_ro_cfg cfg = { str_init("scscf.ims.smilecoms.com"),
     0
     0
 };
 };
 
 
+extern struct ims_charging_counters_h ims_charging_cnts_h;
 struct cdp_binds cdpb;
 struct cdp_binds cdpb;
 struct dlg_binds dlgb;
 struct dlg_binds dlgb;
 cdp_avp_bind_t *cdp_avp;
 cdp_avp_bind_t *cdp_avp;
 struct tm_binds tmb;
 struct tm_binds tmb;
 
 
-usrloc_api_t ul; /*!< Structure containing pointers to usrloc functions*/
-
 char* rx_dest_realm_s = "ims.smilecoms.com";
 char* rx_dest_realm_s = "ims.smilecoms.com";
 str rx_dest_realm;
 str rx_dest_realm;
 /* Only used if we want to force the Ro peer usually this is configured at a stack level and the first request uses realm routing */
 /* Only used if we want to force the Ro peer usually this is configured at a stack level and the first request uses realm routing */
@@ -81,6 +84,7 @@ static int mod_child_init(int);
 static void mod_destroy(void);
 static void mod_destroy(void);
 
 
 static int w_ro_ccr(struct sip_msg *msg, char* route_name, char* direction, int reservation_units, char* incoming_trunk_id, char* outgoing_trunk_id);
 static int w_ro_ccr(struct sip_msg *msg, char* route_name, char* direction, int reservation_units, char* incoming_trunk_id, char* outgoing_trunk_id);
+static int w_ro_ccr_stop(struct sip_msg *msg, char* direction, char* _code, char* _reason);
 //void ro_session_ontimeout(struct ro_tl *tl);
 //void ro_session_ontimeout(struct ro_tl *tl);
 
 
 
 
@@ -88,10 +92,12 @@ int create_response_avp_string(char* name, str* val);
 static int w_ro_set_session_id_avp(struct sip_msg *msg, char *str1, char *str2);
 static int w_ro_set_session_id_avp(struct sip_msg *msg, char *str1, char *str2);
 
 
 static int ro_fixup(void **param, int param_no);
 static int ro_fixup(void **param, int param_no);
+static int ro_fixup_stop(void **param, int param_no);
 
 
 static cmd_export_t cmds[] = {
 static cmd_export_t cmds[] = {
 		{ "Ro_CCR", 	(cmd_function) w_ro_ccr, 5, ro_fixup, 0, REQUEST_ROUTE },
 		{ "Ro_CCR", 	(cmd_function) w_ro_ccr, 5, ro_fixup, 0, REQUEST_ROUTE },
-                { "Ro_set_session_id_avp", 	(cmd_function) w_ro_set_session_id_avp, 0, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE },
+		{ "Ro_CCR_Stop",(cmd_function) w_ro_ccr_stop, 3, ro_fixup_stop, 0, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE},
+        { "Ro_set_session_id_avp", 	(cmd_function) w_ro_set_session_id_avp, 0, 0, 0, REQUEST_ROUTE | ONREPLY_ROUTE },
 		{ 0, 0, 0, 0, 0, 0 }
 		{ 0, 0, 0, 0, 0, 0 }
 };
 };
 
 
@@ -124,6 +130,8 @@ static param_export_t params[] = {
 		{ "db_mode",			INT_PARAM,			&ro_db_mode_param		},
 		{ "db_mode",			INT_PARAM,			&ro_db_mode_param		},
 		{ "db_url",			PARAM_STRING,			&db_url 			},
 		{ "db_url",			PARAM_STRING,			&db_url 			},
 		{ "db_update_period",		INT_PARAM,			&db_update_period		},
 		{ "db_update_period",		INT_PARAM,			&db_update_period		},
+		{ "vendor_specific_chargeinfo",		INT_PARAM,	&vendor_specific_chargeinfo		}, /* VSI for extra charing info in Ro */
+		{ "vendor_specific_id",		INT_PARAM,			&vendor_specific_id		}, /* VSI for extra charing info in Ro */
 		{ 0, 0, 0 }
 		{ 0, 0, 0 }
 };
 };
 
 
@@ -173,7 +181,6 @@ int fix_parameters() {
 static int mod_init(void) {
 static int mod_init(void) {
 	int n;
 	int n;
 	load_tm_f load_tm;
 	load_tm_f load_tm;
-	bind_usrloc_t bind_usrloc;
 
 
 	if (!fix_parameters()) {
 	if (!fix_parameters()) {
 		LM_ERR("unable to set Ro configuration parameters correctly\n");
 		LM_ERR("unable to set Ro configuration parameters correctly\n");
@@ -204,16 +211,6 @@ static int mod_init(void) {
 		goto error;
 		goto error;
 	}
 	}
         
         
-        bind_usrloc = (bind_usrloc_t) find_export("ul_bind_usrloc", 1, 0);
-	if (!bind_usrloc) {
-	    LM_ERR("can't bind usrloc\n");
-	    return -1;
-	}
-	
-	if (bind_usrloc(&ul) < 0) {
-	    return -1;
-	}
-
 	/* init timer lists*/
 	/* init timer lists*/
 	if (init_ro_timer(ro_session_ontimeout) != 0) {
 	if (init_ro_timer(ro_session_ontimeout) != 0) {
 		LM_ERR("cannot init timer list\n");
 		LM_ERR("cannot init timer list\n");
@@ -240,15 +237,6 @@ static int mod_init(void) {
 		LM_ERR("failed to register timer \n");
 		LM_ERR("failed to register timer \n");
 		return -1;
 		return -1;
 	}
 	}
-
-	
-
-	/*Register for callback of URECORD being deleted - so we can send a SAR*/
-
-	if (ul.register_ulcb == NULL) {
-	    LM_ERR("Could not import ul_register_ulcb\n");
-	    return -1;
-	}
 	
 	
 	if (ims_charging_init_counters() != 0) {
 	if (ims_charging_init_counters() != 0) {
 	    LM_ERR("Failed to register counters for ims_charging module\n");
 	    LM_ERR("Failed to register counters for ims_charging module\n");
@@ -352,11 +340,93 @@ static int w_ro_set_session_id_avp(struct sip_msg *msg, char *str1, char *str2)
     //set avp response with session id
     //set avp response with session id
     res = create_response_avp_string("ro_session_id", &ro_session->ro_session_id);
     res = create_response_avp_string("ro_session_id", &ro_session->ro_session_id);
     dlgb.release_dlg(dlg);
     dlgb.release_dlg(dlg);
+    unref_ro_session(ro_session, 1);
     return res;
     return res;
 }
 }
 
 
+static int w_ro_ccr_stop(struct sip_msg *msg, char* c_direction, char* _code, char* _reason) {
+    struct ro_session* ro_session;
+    struct ro_session_entry *ro_session_entry;
+    unsigned int h_entry;
+    str s_code, s_reason;
+    unsigned int code;
+    int dir = 0; /*any side*/
+
+    LM_DBG("Inside Ro_CCR_Stop with direction [%s]\n", c_direction);
+    if (strlen(c_direction) == 4) {
+        if (c_direction[0] == 'O' || c_direction[0] == 'o') {
+            dir = RO_ORIG_DIRECTION;
+        } else {
+            dir = RO_TERM_DIRECTION;
+        }
+    } else {
+        LM_ERR("Unknown direction [%s] to terminate\n", c_direction);
+        return RO_RETURN_FALSE;
+    }
+    struct dlg_cell* dlg = dlgb.get_dlg(msg);
+    if (!dlg) {
+        LM_ERR("Unable to find dialog to send CCR STOP record\n");
+        return RO_RETURN_ERROR;
+    }
+
+    if (get_str_fparam(&s_code, msg, (fparam_t*) _code) < 0) {
+        LM_ERR("failed to get code\n");
+        return RO_RETURN_ERROR;
+    }
+    LM_DBG("Code is [%.*s]\n", s_code.len, s_code.s);
+    if (get_str_fparam(&s_reason, msg, (fparam_t*) _reason) < 0) {
+        LM_ERR("failed to get reason\n");
+        return RO_RETURN_ERROR;
+    }
+
+    if (str2int(&s_code, &code) != 0) {
+        LM_ERR("Bad response code: [%.*s]\n", s_code.len, s_code.s);
+        return RO_RETURN_FALSE;
+    }
 
 
+//    switch (code) {
+//        case 486:
+//            termcode = VS_TERMCODE_BUSYHERE;
+//            break;
+//        case 487:
+//            termcode = VS_TERMCODE_CANCELLED;
+//            break;
+//        case 480:
+//        case 408:
+//            /* subscriber not available */
+//            termcode = VS_TERMCODE_NOTFOUND;
+//            break;
+//    }
+
+    LM_DBG("Sending Stop record with code [%d] and reason [%.*s]\n", code, s_reason.len, s_reason.s);
+
+    LM_DBG("Found DLG [%d : %d]\n", dlg->h_id, dlg->h_entry);
+
+    ro_session = lookup_ro_session(dlg->h_entry, &dlg->callid, dir, 0);
+    if (ro_session == NULL) {
+        LM_DBG("no ro_session - ignoring\n");
+        return RO_RETURN_TRUE;
+    }
+    h_entry = ro_session->h_entry;
+    ro_session_entry = &(ro_session_table->entries[h_entry]);
 
 
+    ro_session_lock(ro_session_table, ro_session_entry);
+    
+    if (ro_session->ccr_sent == 1) {
+        LM_DBG("Ro CCR already sent for session [%.*s]\n", ro_session->ro_session_id.len, ro_session->ro_session_id.s);
+        goto done;
+    }
+    send_ccr_stop_with_param(ro_session, code, &s_reason);
+    //TODO = check the CCR was sent successfully.
+    LM_DBG("Setting Ro session [%.*s] ccr_sent to 1\n", ro_session->ro_session_id.len, ro_session->ro_session_id.s);
+    ro_session->ccr_sent = 1;
+    ro_session->active = -1;
+//    counter_add(ims_charging_cnts_h.active_ro_sessions, -1);
+done:
+    unref_ro_session_unsafe(ro_session, 1, ro_session_entry);
+    ro_session_unlock(ro_session_table, ro_session_entry);
+    return RO_RETURN_TRUE;
+}
 
 
 static int w_ro_ccr(struct sip_msg *msg, char* c_route_name, char* c_direction, int reservation_units, char* c_incoming_trunk_id, char* c_outgoing_trunk_id) {
 static int w_ro_ccr(struct sip_msg *msg, char* c_route_name, char* c_direction, int reservation_units, char* c_incoming_trunk_id, char* c_outgoing_trunk_id) {
 	/* PSEUDOCODE/NOTES
 	/* PSEUDOCODE/NOTES
@@ -541,3 +611,10 @@ static int ro_fixup(void **param, int param_no) {
 	
 	
 	return 0;
 	return 0;
 }
 }
+
+static int ro_fixup_stop(void **param, int param_no) {
+	if (param_no == 2 || param_no == 3) {
+		return fixup_var_pve_12(param, param_no);
+	}
+	return 0;
+}

+ 1 - 1
modules/ims_charging/ro_session_hash.c

@@ -227,7 +227,7 @@ struct ro_session* build_new_ro_session(int direction, int auth_appid, int auth_
 
 
     new_ro_session->h_entry = dlg_h_entry; /* we will use the same entry ID as the dlg - saves us using our own hash function */
     new_ro_session->h_entry = dlg_h_entry; /* we will use the same entry ID as the dlg - saves us using our own hash function */
     new_ro_session->h_id = 0;
     new_ro_session->h_id = 0;
-    new_ro_session->ref = 0;
+    new_ro_session->ref = 1;
     
     
     new_ro_session->rating_group = active_rating_group;
     new_ro_session->rating_group = active_rating_group;
     new_ro_session->service_identifier = active_service_identifier;
     new_ro_session->service_identifier = active_service_identifier;

+ 8 - 1
modules/ims_charging/ro_session_hash.h

@@ -10,7 +10,7 @@
 
 
 #include "ro_timer.h"
 #include "ro_timer.h"
 #include "../../mem/shm_mem.h"
 #include "../../mem/shm_mem.h"
-#include "../ims_usrloc_scscf/usrloc.h"
+#include "ims_charging_stats.h"
 #include <stdlib.h>
 #include <stdlib.h>
 
 
 
 
@@ -22,6 +22,8 @@
 
 
 #define MAX_PANI_LEN 100
 #define MAX_PANI_LEN 100
 
 
+extern struct ims_charging_counters_h ims_charging_cnts_h;
+
 enum ro_session_event_type {
 enum ro_session_event_type {
     pending,
     pending,
     answered,
     answered,
@@ -71,6 +73,9 @@ struct ro_session {
     str mac;
     str mac;
     int rating_group;
     int rating_group;
     int service_identifier;
     int service_identifier;
+    unsigned int is_final_allocation;
+    long billed;
+    unsigned int ccr_sent;
 };
 };
 
 
 /*! entries in the main ro_session table */
 /*! entries in the main ro_session table */
@@ -161,6 +166,8 @@ static inline void unlink_unsafe_ro_session(struct ro_session_entry *ro_session_
         ro_session_entry->first = ro_session->next;
         ro_session_entry->first = ro_session->next;
 
 
     ro_session->next = ro_session->prev = 0;
     ro_session->next = ro_session->prev = 0;
+    
+    counter_add(ims_charging_cnts_h.active_ro_sessions, -1);
 
 
     return;
     return;
 }
 }

+ 21 - 0
modules/ims_charging/ro_timer.c

@@ -380,6 +380,7 @@ void resume_ro_session_ontimeout(struct interim_ccr *i_req) {
 void ro_session_ontimeout(struct ro_tl *tl) {
 void ro_session_ontimeout(struct ro_tl *tl) {
     time_t now, call_time;
     time_t now, call_time;
     long used_secs;
     long used_secs;
+	int adjustment;
 
 
     LM_DBG("We have a fired timer [p=%p] and tl=[%i].\n", tl, tl->timeout);
     LM_DBG("We have a fired timer [p=%p] and tl=[%i].\n", tl, tl->timeout);
 
 
@@ -400,12 +401,31 @@ void ro_session_ontimeout(struct ro_tl *tl) {
     //		return;
     //		return;
     //	}
     //	}
 
 
+
+    if(ro_session->is_final_allocation) {
+        now = get_current_time_micro();
+        used_secs = now - ro_session->last_event_timestamp;
+        if((ro_session->reserved_secs - used_secs) > 0) {
+            update_ro_timer(&ro_session->ro_tl, (ro_session->reserved_secs - used_secs));
+            return;
+        }
+        else {
+            ro_session->event_type = no_more_credit;
+        }
+    }
+
     switch (ro_session->event_type) {
     switch (ro_session->event_type) {
         case answered:
         case answered:
             now = get_current_time_micro();
             now = get_current_time_micro();
             used_secs = rint((now - ro_session->last_event_timestamp) / (float) 1000000);
             used_secs = rint((now - ro_session->last_event_timestamp) / (float) 1000000);
             call_time = rint((now - ro_session->start_time) / (float) 1000000);
             call_time = rint((now - ro_session->start_time) / (float) 1000000);
 
 
+			if ((used_secs + ro_session->billed) < (call_time)) {
+				adjustment = call_time - (used_secs + ro_session->billed);
+				LM_DBG("Making adjustment for Ro interim timer by adding %d seconds\n", adjustment);
+				used_secs += adjustment;
+			}
+			
             counter_add(ims_charging_cnts_h.billed_secs, used_secs);
             counter_add(ims_charging_cnts_h.billed_secs, used_secs);
 
 
             if (ro_session->callid.s != NULL
             if (ro_session->callid.s != NULL
@@ -431,6 +451,7 @@ void ro_session_ontimeout(struct ro_tl *tl) {
                 // Apply for more credit.
                 // Apply for more credit.
                 //
                 //
                 // The function call will return immediately and we will receive the reply asynchronously via a callback
                 // The function call will return immediately and we will receive the reply asynchronously via a callback
+				ro_session->billed += used_secs;
                 send_ccr_interim(ro_session, (unsigned int) used_secs, interim_request_credits);
                 send_ccr_interim(ro_session, (unsigned int) used_secs, interim_request_credits);
                 return;
                 return;
             } else {
             } else {

+ 1 - 1
modules/ims_qos/rx_str.c

@@ -107,7 +107,7 @@ int rx_send_str(str *rx_session_id) {
         // so just wait for STA or for Grace Timout to happen
         // so just wait for STA or for Grace Timout to happen
         LM_DBG("Hmmm, auth session already in disconnected state\n");
         LM_DBG("Hmmm, auth session already in disconnected state\n");
         cdpb.AAASessionsUnlock(auth->hash);
         cdpb.AAASessionsUnlock(auth->hash);
-		CSCF_RETURN_FALSE;
+        return CSCF_RETURN_FALSE;
     }
     }
 
 
     LM_DBG("Creating STR\n");
     LM_DBG("Creating STR\n");

+ 50 - 68
modules/ims_registrar_scscf/registrar_notify.c

@@ -57,6 +57,7 @@
 #include <libxml/parser.h>
 #include <libxml/parser.h>
 
 
 #include "../../lib/ims/useful_defs.h"
 #include "../../lib/ims/useful_defs.h"
+#include "../ims_usrloc_scscf/udomain.h"
 
 
 #define STATE_ACTIVE 1
 #define STATE_ACTIVE 1
 #define STATE_TERMINATED 0
 #define STATE_TERMINATED 0
@@ -437,8 +438,6 @@ error:
  * return 0 on success. anything else failure
  * return 0 on success. anything else failure
  */
  */
 int event_reg(udomain_t* _d, impurecord_t* r_passed, int event_type, str *presentity_uri, str *watcher_contact) {
 int event_reg(udomain_t* _d, impurecord_t* r_passed, int event_type, str *presentity_uri, str *watcher_contact) {
-
-    str content = {0, 0};
     impurecord_t* r;
     impurecord_t* r;
     int num_impus;
     int num_impus;
     str* impu_list;
     str* impu_list;
@@ -471,7 +470,7 @@ int event_reg(udomain_t* _d, impurecord_t* r_passed, int event_type, str *presen
 
 
             //get IMPU set from the presentity's subscription
             //get IMPU set from the presentity's subscription
             res = ul.get_impus_from_subscription_as_string(_d, r,
             res = ul.get_impus_from_subscription_as_string(_d, r,
-                    0/*all unbarred impus*/, &impu_list, &num_impus);
+                    0/*all unbarred impus*/, &impu_list, &num_impus, 0/*pkg*/);
             if (res != 0) {
             if (res != 0) {
                 LM_WARN("failed to get IMPUs from subscription\n");
                 LM_WARN("failed to get IMPUs from subscription\n");
                 ul.unlock_udomain(_d, presentity_uri);
                 ul.unlock_udomain(_d, presentity_uri);
@@ -481,19 +480,9 @@ int event_reg(udomain_t* _d, impurecord_t* r_passed, int event_type, str *presen
                 return 1;
                 return 1;
             }
             }
             ul.unlock_udomain((udomain_t*) _d, presentity_uri);
             ul.unlock_udomain((udomain_t*) _d, presentity_uri);
-
-            content = generate_reginfo_full(_d, impu_list,
-                    num_impus, 0, 0);
-
-            if (impu_list) {
-                pkg_free(impu_list);
-            }
-
             LM_DBG("About to ceate notification");
             LM_DBG("About to ceate notification");
 
 
-            create_notifications(_d, r_passed, presentity_uri, watcher_contact, content, event_type);
-            if (content.s) pkg_free(content.s);
-            //            if (send_now) notification_timer(0, 0);
+            create_notifications(_d, r_passed, presentity_uri, watcher_contact, &impu_list, num_impus, event_type);
             return 0;
             return 0;
             break;
             break;
 
 
@@ -507,12 +496,9 @@ int event_reg(udomain_t* _d, impurecord_t* r_passed, int event_type, str *presen
                 LM_ERR("this is a contact change passed from ul callback: r_passed and c_passed should both be valid and presentity_uri, watcher_contact and _d should be 0 for ul callback");
                 LM_ERR("this is a contact change passed from ul callback: r_passed and c_passed should both be valid and presentity_uri, watcher_contact and _d should be 0 for ul callback");
                 return 0;
                 return 0;
             }
             }
-
-            //content = get_reginfo_partial(r_passed, c_passed, event_type);
-
             //this is a ulcallback so r_passed domain is already locked
             //this is a ulcallback so r_passed domain is already locked
             res = ul.get_impus_from_subscription_as_string(_d, r_passed,
             res = ul.get_impus_from_subscription_as_string(_d, r_passed,
-                    0/*all unbarred impus*/, &impu_list, &num_impus);
+                    0/*all unbarred impus*/, &impu_list, &num_impus, 0/*pkg*/);
             if (res != 0) {
             if (res != 0) {
                 LM_WARN("failed to get IMPUs from subscription\n");
                 LM_WARN("failed to get IMPUs from subscription\n");
                 if (impu_list) {
                 if (impu_list) {
@@ -526,19 +512,9 @@ int event_reg(udomain_t* _d, impurecord_t* r_passed, int event_type, str *presen
                 LM_ERR("Unable to register usrloc domain....aborting\n");
                 LM_ERR("Unable to register usrloc domain....aborting\n");
                 return 0;
                 return 0;
             }
             }
-            //	    
-            content = generate_reginfo_full(udomain, impu_list,
-                    num_impus, &r_passed->public_identity, 1);
-            //	    
-            if (impu_list) {
-                pkg_free(impu_list);
-            }
-
             LM_DBG("About to ceate notification");
             LM_DBG("About to ceate notification");
 
 
-            create_notifications(_d, r_passed, presentity_uri, watcher_contact, content, event_type);
-            if (content.s) pkg_free(content.s);
-            //                        if (send_now) notification_timer(0, 0);
+            create_notifications(_d, r_passed, presentity_uri, watcher_contact, &impu_list, num_impus, event_type);
             return 1;
             return 1;
 
 
         default:
         default:
@@ -1391,7 +1367,7 @@ static str subs_active = {"active;expires=", 15};
  * @param content - the body content
  * @param content - the body content
  * @param expires - the remaining subcription expiration time in seconds
  * @param expires - the remaining subcription expiration time in seconds
  */
  */
-void create_notifications(udomain_t* _t, impurecord_t* r_passed, str *presentity_uri, str *watcher_contact, str content, int event_type) {
+void create_notifications(udomain_t* _t, impurecord_t* r_passed, str *presentity_uri, str *watcher_contact, str** impus, int num_impus, int event_type) {
 
 
     reg_notification *n;
     reg_notification *n;
     reg_subscriber *s;
     reg_subscriber *s;
@@ -1432,7 +1408,7 @@ void create_notifications(udomain_t* _t, impurecord_t* r_passed, str *presentity
 
 
         if (s->expires > act_time) {
         if (s->expires > act_time) {
             LM_DBG("Expires is greater than current time!");
             LM_DBG("Expires is greater than current time!");
-            subscription_state.s = pkg_malloc(32);
+            subscription_state.s = (char*) pkg_malloc(32 * sizeof (char*));
             subscription_state.len = 0;
             subscription_state.len = 0;
             if (subscription_state.s) {
             if (subscription_state.s) {
 
 
@@ -1462,8 +1438,9 @@ void create_notifications(udomain_t* _t, impurecord_t* r_passed, str *presentity
                 version = s->version + 1;
                 version = s->version + 1;
                 ul.update_subscriber(r, &s, 0, &local_cseq, &version);
                 ul.update_subscriber(r, &s, 0, &local_cseq, &version);
 
 
-                n = new_notification(subscription_state, content_type, content, s);
+                n = new_notification(subscription_state, content_type, impus, num_impus, s);
                 if (n) {
                 if (n) {
+                    n->_d = _t;
                     LM_DBG("Notification exists - about to add it");
                     LM_DBG("Notification exists - about to add it");
                     add_notification(n);
                     add_notification(n);
                 } else {
                 } else {
@@ -1488,15 +1465,16 @@ void create_notifications(udomain_t* _t, impurecord_t* r_passed, str *presentity
                 version = s->version + 1;
                 version = s->version + 1;
                 ul.update_subscriber(r, &s, 0, &local_cseq, &version);
                 ul.update_subscriber(r, &s, 0, &local_cseq, &version);
 
 
-                n = new_notification(subscription_state, content_type, content, s);
-                if (n) {
-                    LM_DBG("Notification exists - about to add it");
-                    add_notification(n);
+            n = new_notification(subscription_state, content_type, impus, num_impus, s);
+            if (n) {
+                n->_d = _t;
+                LM_DBG("Notification exists - about to add it");
+                add_notification(n);
 
 
-                } else {
-                    LM_DBG("Notification does not exist");
-                }
-//            }
+            } else {
+                LM_DBG("Notification does not exist");
+            }
+            //            }
         }
         }
         s = s->next;
         s = s->next;
 
 
@@ -1655,7 +1633,7 @@ static void process_xml_for_contact(str* buf, str* pad, ucontact_t* ptr) {
  * @returns the str with the XML content
  * @returns the str with the XML content
  * if its a new subscription we do things like subscribe to updates on IMPU, etc
  * if its a new subscription we do things like subscribe to updates on IMPU, etc
  */
  */
-str generate_reginfo_full(udomain_t* _t, str* impu_list, int num_impus, str *primary_impu, int primary_locked) {
+str generate_reginfo_full(udomain_t* _t, str* impu_list, int num_impus) {
     str x = {0, 0};
     str x = {0, 0};
     str buf, pad;
     str buf, pad;
     char bufc[MAX_REGINFO_SIZE], padc[MAX_REGINFO_SIZE];
     char bufc[MAX_REGINFO_SIZE], padc[MAX_REGINFO_SIZE];
@@ -1927,12 +1905,23 @@ static int free_tm_dlg(dlg_t * td) {
 
 
 void send_notification(reg_notification * n) {
 void send_notification(reg_notification * n) {
     str h = {0, 0};
     str h = {0, 0};
-
+    str content = {0, 0};
     uac_req_t uac_r;
     uac_req_t uac_r;
     dlg_t* td = NULL;
     dlg_t* td = NULL;
 
 
+    struct udomain* domain = (struct udomain*) n->_d;
+    if (!domain) {
+        ul.register_udomain("location", &domain);
+    }
+
+    LM_DBG("Have a notification to send for the following IMPUs using domain [%.*s]\n", domain->name->len, domain->name->s);
+
+
+    content = generate_reginfo_full(domain, n->impus, n->num_impus);
+
     str method = {"NOTIFY", 6};
     str method = {"NOTIFY", 6};
 
 
+    LM_DBG("Notification content: [%.*s]", content.len, content.s);
     LM_DBG("DBG:send_notification: NOTIFY about <%.*s>\n", n->watcher_uri.len, n->watcher_uri.s);
     LM_DBG("DBG:send_notification: NOTIFY about <%.*s>\n", n->watcher_uri.len, n->watcher_uri.s);
 
 
     h.len = 0;
     h.len = 0;
@@ -1975,14 +1964,12 @@ void send_notification(reg_notification * n) {
         return;
         return;
     }
     }
 
 
-
-    if (n->content.len) {
-
+    if (content.len) {
         LM_DBG("Notification content exists - about to send notification with subscription state: [%.*s] content_type: [%.*s] content: [%.*s] : presentity_uri: [%.*s] watcher_uri: [%.*s]",
         LM_DBG("Notification content exists - about to send notification with subscription state: [%.*s] content_type: [%.*s] content: [%.*s] : presentity_uri: [%.*s] watcher_uri: [%.*s]",
-                n->subscription_state.len, n->subscription_state.s, n->content_type.len, n->content_type.s, n->content.len, n->content.s,
+                n->subscription_state.len, n->subscription_state.s, n->content_type.len, n->content_type.s, content.len, content.s,
                 n->presentity_uri.len, n->presentity_uri.s, n->watcher_uri.len, n->watcher_uri.s);
                 n->presentity_uri.len, n->presentity_uri.s, n->watcher_uri.len, n->watcher_uri.s);
 
 
-        set_uac_req(&uac_r, &method, &h, &n->content, td, TMCB_LOCAL_COMPLETED,
+        set_uac_req(&uac_r, &method, &h, &content, td, TMCB_LOCAL_COMPLETED,
                 uac_request_cb, 0);
                 uac_request_cb, 0);
         tmb.t_request_within(&uac_r);
         tmb.t_request_within(&uac_r);
     } else {
     } else {
@@ -2012,26 +1999,17 @@ void send_notification(reg_notification * n) {
  * @returns the r_notification or NULL on error
  * @returns the r_notification or NULL on error
  */
  */
 reg_notification * new_notification(str subscription_state,
 reg_notification * new_notification(str subscription_state,
-        str content_type, str content, reg_subscriber * r) {
-
+        str content_type, str** impus, int num_impus, reg_subscriber * r) {
+    int i;
     reg_notification *n = 0;
     reg_notification *n = 0;
-
-    str buf;
-    char bufc[MAX_REGINFO_SIZE];
-
-    if (content.len > MAX_REGINFO_SIZE) {
-        LM_ERR("content size (%d) exceeds MAX_REGINFO_SIZE (%d)!\n", content.len, MAX_REGINFO_SIZE);
-        return 0;
-    }
-
-    buf.s = bufc;
-    buf.len = snprintf(buf.s, MAX_REGINFO_SIZE, content.s, r->version);
-
     int len;
     int len;
     char *p;
     char *p;
 
 
     len = sizeof (reg_notification) + r->call_id.len + r->from_tag.len + r->to_tag.len + r->watcher_uri.len + r->watcher_contact.len +
     len = sizeof (reg_notification) + r->call_id.len + r->from_tag.len + r->to_tag.len + r->watcher_uri.len + r->watcher_contact.len +
-            r->record_route.len + r->sockinfo_str.len + r->presentity_uri.len + subscription_state.len + content_type.len + buf.len;
+            r->record_route.len + r->sockinfo_str.len + r->presentity_uri.len + subscription_state.len + content_type.len + (num_impus*sizeof(str)); // + buf.len;
+    for (i=0; i<num_impus; i++) {
+        len += impus[i]->len;
+    }
 
 
     LM_DBG("Creating new notification");
     LM_DBG("Creating new notification");
 
 
@@ -2106,12 +2084,16 @@ reg_notification * new_notification(str subscription_state,
     p += content_type.len;
     p += content_type.len;
     LM_DBG("Notification content type: [%.*s]", n->content_type.len, n->content_type.s);
     LM_DBG("Notification content type: [%.*s]", n->content_type.len, n->content_type.s);
 
 
-    n->content.s = p;
-    n->content.len = buf.len;
-    memcpy(p, buf.s, buf.len);
-    p += buf.len;
-    LM_DBG("Notification content: [%.*s]", n->content.len, n->content.s);
-
+    n->impus = p;
+    p += sizeof(str)*num_impus;
+    for (i=0; i<num_impus; i++) {
+        n->impus[i].s = p;
+        memcpy(p, impus[i]->s, impus[i]->len);
+        n->impus[i].len = impus[i]->len;
+        p += impus[i]->len;
+    }
+    n->num_impus = num_impus;
+    
     if (p != (((char*) n) + len)) {
     if (p != (((char*) n) + len)) {
         LM_CRIT("buffer overflow\n");
         LM_CRIT("buffer overflow\n");
         free_notification(n);
         free_notification(n);

+ 6 - 4
modules/ims_registrar_scscf/registrar_notify.h

@@ -62,12 +62,14 @@ typedef struct _reg_notification {
     
     
     str subscription_state; /**< Subscription-state header value*/
     str subscription_state; /**< Subscription-state header value*/
     str content_type; /**< content type					*/
     str content_type; /**< content type					*/
-    str content; /**< content						*/
     
     
     str watcher_contact;
     str watcher_contact;
     str watcher_uri;
     str watcher_uri;
     str presentity_uri;
     str presentity_uri;
     
     
+    struct udomain* _d;
+    str* impus;
+    int num_impus;
     
     
     unsigned int local_cseq;
     unsigned int local_cseq;
     str call_id;
     str call_id;
@@ -126,11 +128,11 @@ int subscribe_reply(struct sip_msg *msg, int code, char *text, int *expires, str
 int event_reg(udomain_t* _d, impurecord_t* r_passed, int event_type, str *presentity_uri, str *watcher_contact);
 int event_reg(udomain_t* _d, impurecord_t* r_passed, int event_type, str *presentity_uri, str *watcher_contact);
 
 
 
 
-str generate_reginfo_full(udomain_t* _t, str* impu_list, int new_subscription, str *primary_impu, int primary_locked);
+str generate_reginfo_full(udomain_t* _t, str* impu_list, int new_subscription);
 
 
 str get_reginfo_partial(impurecord_t *r, ucontact_t *c, int event_type);
 str get_reginfo_partial(impurecord_t *r, ucontact_t *c, int event_type);
 
 
-void create_notifications(udomain_t* _t, impurecord_t* r_passed, str *presentity_uri, str *watcher_contact, str content, int event_type);
+void create_notifications(udomain_t* _t, impurecord_t* r_passed, str *presentity_uri, str *watcher_contact, str** impus, int num_impus, int event_type);
 
 
 void notification_event_process();
 void notification_event_process();
 
 
@@ -141,7 +143,7 @@ void send_notification(reg_notification * n);
 void add_notification(reg_notification *n);
 void add_notification(reg_notification *n);
 
 
 reg_notification* new_notification(str subscription_state,
 reg_notification* new_notification(str subscription_state,
-        str content_type, str content, reg_subscriber* r);
+        str content_type, str** impus, int num_impus, reg_subscriber* r);
 
 
 dlg_t* build_dlg_t_from_notification(reg_notification* n);
 dlg_t* build_dlg_t_from_notification(reg_notification* n);
 
 

+ 10 - 3
modules/ims_usrloc_scscf/udomain.c

@@ -626,7 +626,7 @@ int delete_impurecord(udomain_t* _d, str* _aor, struct impurecord* _r) {
  * barring-(-1) get all records
  * barring-(-1) get all records
  * NB. Remember to free the block of memory pointed to by impus (pkg_malloc)
  * NB. Remember to free the block of memory pointed to by impus (pkg_malloc)
  */
  */
-int get_impus_from_subscription_as_string(udomain_t* _d, impurecord_t* impu_rec, int barring, str** impus, int* num_impus) {
+int get_impus_from_subscription_as_string(udomain_t* _d, impurecord_t* impu_rec, int barring, str** impus, int* num_impus, int is_shm) {
     int i, j, count;
     int i, j, count;
     *num_impus = 0;
     *num_impus = 0;
     *impus = 0;
     *impus = 0;
@@ -669,9 +669,16 @@ int get_impus_from_subscription_as_string(udomain_t* _d, impurecord_t* impu_rec,
     LM_DBG("num of records returned is %d and we need %d bytes\n", *num_impus, bytes_needed);
     LM_DBG("num of records returned is %d and we need %d bytes\n", *num_impus, bytes_needed);
 
 
     len = (sizeof (str)*(*num_impus)) + bytes_needed;
     len = (sizeof (str)*(*num_impus)) + bytes_needed;
-    *impus = (str*) pkg_malloc(len); //TODO: rather put this on the stack... dont' fragment pkg....
+    if (is_shm)
+        *impus = (str*) shm_malloc(len); 
+    else 
+        *impus = (str*) pkg_malloc(len); //TODO: rather put this on the stack... dont' fragment pkg....
+    
     if (*impus == 0) {
     if (*impus == 0) {
-        LM_ERR("no more pkg_mem\n");
+        if (is_shm)
+            LM_ERR("no more shm_mem\n");
+        else 
+            LM_ERR("no more pkg_mem\n");
         return 1;
         return 1;
     }
     }
     char* ptr = (char*) (*impus + *num_impus);
     char* ptr = (char*) (*impus + *num_impus);

+ 1 - 1
modules/ims_usrloc_scscf/udomain.h

@@ -226,7 +226,7 @@ int delete_impurecord(udomain_t* _d, str* _aor, struct impurecord* _r);
  * barring-0 get all unbarred
  * barring-0 get all unbarred
  * barring-(-1) get all records
  * barring-(-1) get all records
  */
  */
-int get_impus_from_subscription_as_string(udomain_t* _d, impurecord_t* impu_rec, int barring, str** impus, int* num_impus);
+int get_impus_from_subscription_as_string(udomain_t* _d, impurecord_t* impu_rec, int barring, str** impus, int* num_impus, int is_shm);
 
 
 int get_subscription(str* impi_s, ims_subscription** s, int leave_slot_locked);
 int get_subscription(str* impi_s, ims_subscription** s, int leave_slot_locked);
 void add_subscription(ims_subscription* s);
 void add_subscription(ims_subscription* s);

+ 1 - 1
modules/ims_usrloc_scscf/usrloc.h

@@ -496,7 +496,7 @@ typedef int (*get_subscriber_t)(impurecord_t* urec, str *watcher_contact, str *p
 typedef int (*add_subscriber_t)(impurecord_t* urec,
 typedef int (*add_subscriber_t)(impurecord_t* urec,
 		subscriber_data_t* subscriber_data, reg_subscriber** _reg_subscriber, int db_load);
 		subscriber_data_t* subscriber_data, reg_subscriber** _reg_subscriber, int db_load);
 
 
-typedef int (*get_impus_from_subscription_as_string_t)(udomain_t* _d, impurecord_t* impu_rec, int barring, str** impus, int* num_impus);
+typedef int (*get_impus_from_subscription_as_string_t)(udomain_t* _d, impurecord_t* impu_rec, int barring, str** impus, int* num_impus, int is_shm);
 
 
 typedef str (*get_presentity_from_subscriber_dialog_t)(str *callid, str *to_tag, str *from_tag);
 typedef str (*get_presentity_from_subscriber_dialog_t)(str *callid, str *to_tag, str *from_tag);
 
 

+ 3 - 3
modules/matrix/README

@@ -139,11 +139,11 @@ Chapter 1. Admin Guide
 
 
    The URL for the database connection.
    The URL for the database connection.
 
 
-   Default value is "mysql://openserro:openserro@localhost/openser".
+   Default value is "mysql://kamailioro:kamailioro@localhost/kamailio".
 
 
    Example 1.1. Set db_url parameter
    Example 1.1. Set db_url parameter
 ...
 ...
-modparam("matrix", "db_url", "mysql://openserro:openserro@localhost/openser")
+modparam("matrix", "db_url", "mysql://kamailioro:kamailioro@localhost/kamailio")
 ...
 ...
 
 
 3.2. matrix_table (string)
 3.2. matrix_table (string)
@@ -264,7 +264,7 @@ Chapter 2. Module parameter for database access.
 
 
    URL to the database containing the data.
    URL to the database containing the data.
 
 
-   Default value is "mysql://openserro:openserro@localhost/openser".
+   Default value is "mysql://kamailioro:kamailioro@localhost/kamailio".
 
 
    Example 2.1. Set db_url parameter
    Example 2.1. Set db_url parameter
 ...
 ...

+ 6 - 6
modules/matrix/doc/matrix_admin.xml

@@ -62,7 +62,7 @@
 
 
 <section>
 <section>
 	<title>Parameters</title>
 	<title>Parameters</title>
-	<section>
+	<section id="matrix.p.db_url">
 		<title><varname>db_url</varname> (string)</title>
 		<title><varname>db_url</varname> (string)</title>
 	  <para>
 	  <para>
 			The URL for the database connection.
 			The URL for the database connection.
@@ -81,7 +81,7 @@ modparam("matrix", "db_url", "&defaultrodb;")
 		  </programlisting>
 		  </programlisting>
 	  </example>
 	  </example>
   </section>
   </section>
-	<section>
+	<section id="matrix.p.matrix_table">
 		<title><varname>matrix_table</varname> (string)</title>
 		<title><varname>matrix_table</varname> (string)</title>
 	  <para>
 	  <para>
 			The name of the table containing the matrix data.
 			The name of the table containing the matrix data.
@@ -100,7 +100,7 @@ modparam("matrix", "matrix_table", "matrix")
 		  </programlisting>
 		  </programlisting>
 	  </example>
 	  </example>
   </section>
   </section>
-	<section>
+	<section id="matrix.p.matrix_first_col">
 		<title><varname>matrix_first_col</varname> (string)</title>
 		<title><varname>matrix_first_col</varname> (string)</title>
 	  <para>
 	  <para>
 			The name of the column containing the first row in the matrix.
 			The name of the column containing the first row in the matrix.
@@ -119,7 +119,7 @@ modparam("matrix", "matrix_first_col", "first")
 		  </programlisting>
 		  </programlisting>
 	  </example>
 	  </example>
   </section>
   </section>
-	<section>
+	<section id="matrix.p.matrix_second_col">
 		<title><varname>matrix_second_col</varname> (string)</title>
 		<title><varname>matrix_second_col</varname> (string)</title>
 	  <para>
 	  <para>
 			The name of the column containing the second row in the matrix.
 			The name of the column containing the second row in the matrix.
@@ -138,7 +138,7 @@ modparam("matrix", "matrix_second_col", "second")
 		  </programlisting>
 		  </programlisting>
 	  </example>
 	  </example>
   </section>
   </section>
-	<section>
+	<section id="matrix.p.matrix_res_col">
 		<title><varname>matrix_res_col</varname> (string)</title>
 		<title><varname>matrix_res_col</varname> (string)</title>
 	  <para>
 	  <para>
 			The name of the column containing the result ID to be used.
 			The name of the column containing the result ID to be used.
@@ -160,7 +160,7 @@ modparam("matrix", "matrix_res_col", "res")
 </section>
 </section>
 <section>
 <section>
 	<title>Functions</title>
 	<title>Functions</title>
-	<section>
+	<section id="matrix.f.matrix">
 	  <title>
 	  <title>
 			<function moreinfo="none">matrix (string first, string second, string dstavp)</function>
 			<function moreinfo="none">matrix (string first, string second, string dstavp)</function>
 		</title>
 		</title>

+ 34 - 38
modules/nat_traversal/nat_traversal.c

@@ -34,6 +34,7 @@
    traversal for SIP signaling. 
    traversal for SIP signaling. 
  */
  */
 
 
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <unistd.h>
@@ -98,12 +99,7 @@ MODULE_VERSION
 #define STR_HAS_IPREFIX(str, prefix) ((str).len>(prefix).len && strncasecmp((prefix).s, (str).s, (prefix).len)==0)
 #define STR_HAS_IPREFIX(str, prefix) ((str).len>(prefix).len && strncasecmp((prefix).s, (str).s, (prefix).len)==0)
 
 
 
 
-typedef int Bool;
-#define True  1
-#define False 0
-
-
-typedef Bool (*NatTestFunction)(struct sip_msg *msg);
+typedef bool (*NatTestFunction)(struct sip_msg *msg);
 
 
 typedef enum {
 typedef enum {
     NTNone=0,
     NTNone=0,
@@ -162,7 +158,7 @@ typedef struct Dialog_Param {
     char *caller_uri;
     char *caller_uri;
     char *callee_uri;
     char *callee_uri;
     time_t expire;
     time_t expire;
-    Bool confirmed;
+    bool confirmed;
     gen_lock_t lock;
     gen_lock_t lock;
     struct {
     struct {
         char **uri;
         char **uri;
@@ -194,9 +190,9 @@ static int NAT_Keepalive(struct sip_msg *msg);
 static int FixContact(struct sip_msg *msg);
 static int FixContact(struct sip_msg *msg);
 static int ClientNatTest(struct sip_msg *msg, unsigned int tests);
 static int ClientNatTest(struct sip_msg *msg, unsigned int tests);
 
 
-static Bool test_private_contact(struct sip_msg *msg);
-static Bool test_source_address(struct sip_msg *msg);
-static Bool test_private_via(struct sip_msg *msg);
+static bool test_private_contact(struct sip_msg *msg);
+static bool test_source_address(struct sip_msg *msg);
+static bool test_private_via(struct sip_msg *msg);
 
 
 static INLINE char* shm_strdup(char *source);
 static INLINE char* shm_strdup(char *source);
 
 
@@ -215,7 +211,7 @@ static int pv_get_source_uri(struct sip_msg *msg, pv_param_t *param, pv_value_t
 //
 //
 static HashTable *nat_table = NULL;
 static HashTable *nat_table = NULL;
 
 
-static Bool keepalive_disabled = False;
+static bool keepalive_disabled = false;
 
 
 static unsigned int keepalive_interval = 60;
 static unsigned int keepalive_interval = 60;
 
 
@@ -225,7 +221,7 @@ static Keepalive_Params keepalive_params = {"NOTIFY", NULL, "", "", 0, 0, ""};
 
 
 struct tm_binds  tm_api;
 struct tm_binds  tm_api;
 struct dlg_binds dlg_api;
 struct dlg_binds dlg_api;
-Bool have_dlg_api = False;
+bool have_dlg_api = false;
 
 
 static int dialog_flag = -1;
 static int dialog_flag = -1;
 static unsigned dialog_default_timeout = 12*3600;  // 12 hours
 static unsigned dialog_default_timeout = 12*3600;  // 12 hours
@@ -466,7 +462,7 @@ NAT_Contact_del(NAT_Contact *contact)
 }
 }
 
 
 
 
-static Bool
+static bool
 NAT_Contact_match(NAT_Contact *contact, const char *uri)
 NAT_Contact_match(NAT_Contact *contact, const char *uri)
 {
 {
     return strcmp(contact->uri, uri)==0;
     return strcmp(contact->uri, uri)==0;
@@ -674,24 +670,24 @@ Dialog_Param_del(Dialog_Param *param)
 
 
 // This function assumes the caller has locked the Dialog_Param while operating on it
 // This function assumes the caller has locked the Dialog_Param while operating on it
 //
 //
-static Bool
+static bool
 Dialog_Param_has_candidate(Dialog_Param *param, char *candidate)
 Dialog_Param_has_candidate(Dialog_Param *param, char *candidate)
 {
 {
     int i;
     int i;
 
 
     for (i=0; i<param->callee_candidates.count; i++) {
     for (i=0; i<param->callee_candidates.count; i++) {
         if (strcmp(candidate, param->callee_candidates.uri[i])==0) {
         if (strcmp(candidate, param->callee_candidates.uri[i])==0) {
-            return True;
+            return true;
         }
         }
     }
     }
 
 
-    return False;
+    return false;
 }
 }
 
 
 
 
 // This function assumes the caller has locked the Dialog_Param while operating on it
 // This function assumes the caller has locked the Dialog_Param while operating on it
 //
 //
-static Bool
+static bool
 Dialog_Param_add_candidate(Dialog_Param *param, char *candidate)
 Dialog_Param_add_candidate(Dialog_Param *param, char *candidate)
 {
 {
     char **new_uri, *new_candidate;
     char **new_uri, *new_candidate;
@@ -703,7 +699,7 @@ Dialog_Param_add_candidate(Dialog_Param *param, char *candidate)
         new_uri = shm_realloc(param->callee_candidates.uri, new_size * sizeof(char*));
         new_uri = shm_realloc(param->callee_candidates.uri, new_size * sizeof(char*));
         if (!new_uri) {
         if (!new_uri) {
             LM_ERR("failed to grow callee_candidates uri list\n");
             LM_ERR("failed to grow callee_candidates uri list\n");
-            return False;
+            return false;
         }
         }
         param->callee_candidates.uri = new_uri;
         param->callee_candidates.uri = new_uri;
         param->callee_candidates.size = new_size;
         param->callee_candidates.size = new_size;
@@ -712,13 +708,13 @@ Dialog_Param_add_candidate(Dialog_Param *param, char *candidate)
     new_candidate = shm_strdup(candidate);
     new_candidate = shm_strdup(candidate);
     if (!new_candidate) {
     if (!new_candidate) {
         LM_ERR("cannot allocate shared memory for new candidate uri\n");
         LM_ERR("cannot allocate shared memory for new candidate uri\n");
-        return False;
+        return false;
     }
     }
 
 
     param->callee_candidates.uri[param->callee_candidates.count] = new_candidate;
     param->callee_candidates.uri[param->callee_candidates.count] = new_candidate;
     param->callee_candidates.count++;
     param->callee_candidates.count++;
 
 
-    return True;
+    return true;
 }
 }
 
 
 
 
@@ -774,30 +770,30 @@ shm_strdup(char *source)
 }
 }
 
 
 
 
-static Bool
+static bool
 get_contact_uri(struct sip_msg* msg, struct sip_uri *uri, contact_t **_c)
 get_contact_uri(struct sip_msg* msg, struct sip_uri *uri, contact_t **_c)
 {
 {
 
 
     if ((parse_headers(msg, HDR_CONTACT_F, 0) == -1) || !msg->contact)
     if ((parse_headers(msg, HDR_CONTACT_F, 0) == -1) || !msg->contact)
-        return False;
+        return false;
 
 
     if (!msg->contact->parsed && parse_contact(msg->contact) < 0) {
     if (!msg->contact->parsed && parse_contact(msg->contact) < 0) {
         LM_ERR("cannot parse the Contact header\n");
         LM_ERR("cannot parse the Contact header\n");
-        return False;
+        return false;
     }
     }
 
 
     *_c = ((contact_body_t*)msg->contact->parsed)->contacts;
     *_c = ((contact_body_t*)msg->contact->parsed)->contacts;
 
 
     if (*_c == NULL) {
     if (*_c == NULL) {
-        return False;
+        return false;
     }
     }
 
 
     if (parse_uri((*_c)->uri.s, (*_c)->uri.len, uri) < 0 || uri->host.len <= 0) {
     if (parse_uri((*_c)->uri.s, (*_c)->uri.len, uri) < 0 || uri->host.len <= 0) {
         LM_ERR("cannot parse the Contact URI\n");
         LM_ERR("cannot parse the Contact URI\n");
-        return False;
+        return false;
     }
     }
 
 
-    return True;
+    return true;
 }
 }
 
 
 
 
@@ -828,10 +824,10 @@ rfc1918address(str *address)
 
 
 
 
 // Test if address of signaling is different from address in 1st Via field
 // Test if address of signaling is different from address in 1st Via field
-static Bool
+static bool
 test_source_address(struct sip_msg *msg)
 test_source_address(struct sip_msg *msg)
 {
 {
-    Bool different_ip, different_port;
+    bool different_ip, different_port;
     int via1_port;
     int via1_port;
 
 
     different_ip = received_via_test(msg);
     different_ip = received_via_test(msg);
@@ -843,21 +839,21 @@ test_source_address(struct sip_msg *msg)
 
 
 
 
 // Test if Contact field contains a private IP address as defined in RFC1918
 // Test if Contact field contains a private IP address as defined in RFC1918
-static Bool
+static bool
 test_private_contact(struct sip_msg *msg)
 test_private_contact(struct sip_msg *msg)
 {
 {
     struct sip_uri uri;
     struct sip_uri uri;
     contact_t* contact;
     contact_t* contact;
 
 
     if (!get_contact_uri(msg, &uri, &contact))
     if (!get_contact_uri(msg, &uri, &contact))
-        return False;
+        return false;
 
 
     return is_private_address(&(uri.host));
     return is_private_address(&(uri.host));
 }
 }
 
 
 
 
 // Test if top Via field contains a private IP address as defined in RFC1918
 // Test if top Via field contains a private IP address as defined in RFC1918
-static Bool
+static bool
 test_private_via(struct sip_msg *msg)
 test_private_via(struct sip_msg *msg)
 {
 {
     return is_private_address(&(msg->via1->host));
     return is_private_address(&(msg->via1->host));
@@ -898,7 +894,7 @@ get_register_expire(struct sip_msg *request, struct sip_msg *reply)
     param_t *expires_param;
     param_t *expires_param;
     time_t now, expire=0;
     time_t now, expire=0;
     unsigned exp;
     unsigned exp;
-    Bool matched;
+    bool matched;
 
 
     if (!request->contact)
     if (!request->contact)
         return 0;
         return 0;
@@ -935,7 +931,7 @@ get_register_expire(struct sip_msg *request, struct sip_msg *reply)
         }
         }
 
 
         for (contact=contact_body->contacts; contact; contact=contact->next) {
         for (contact=contact_body->contacts; contact; contact=contact->next) {
-            for (r_hdr=reply->contact, matched=False; r_hdr && !matched; r_hdr=next_sibling_hdr(r_hdr)) {
+            for (r_hdr=reply->contact, matched=false; r_hdr && !matched; r_hdr=next_sibling_hdr(r_hdr)) {
                 if (!r_hdr->parsed && parse_contact(r_hdr) < 0) {
                 if (!r_hdr->parsed && parse_contact(r_hdr) < 0) {
                     LM_ERR("failed to parse the Contact header body in reply\n");
                     LM_ERR("failed to parse the Contact header body in reply\n");
                     continue;
                     continue;
@@ -946,7 +942,7 @@ get_register_expire(struct sip_msg *request, struct sip_msg *reply)
                         expires_param = r_contact->expires;
                         expires_param = r_contact->expires;
                         if (expires_param && expires_param->body.len && str2int(&expires_param->body, &exp) == 0)
                         if (expires_param && expires_param->body.len && str2int(&expires_param->body, &exp) == 0)
                             expire = max(expire, exp);
                             expire = max(expire, exp);
-                        matched = True;
+                        matched = true;
                         break;
                         break;
                     }
                     }
                 }
                 }
@@ -1092,7 +1088,7 @@ __dialog_confirmed(struct dlg_cell *dlg, int type, struct dlg_cb_params *_params
 
 
     lock_get(&param->lock);
     lock_get(&param->lock);
 
 
-    param->confirmed = True;
+    param->confirmed = true;
 
 
     callee_uri = get_source_uri(_params->rpl);
     callee_uri = get_source_uri(_params->rpl);
 
 
@@ -1684,7 +1680,7 @@ restore_keepalive_state(void)
 
 
     res = fscanf(f, STATE_FILE_HEADER); // skip header
     res = fscanf(f, STATE_FILE_HEADER); // skip header
 
 
-    while (True) {
+    while (true) {
         res = fscanf(f, "%63s %63s %ld %ld", uri, socket, &rtime, &stime);
         res = fscanf(f, "%63s %63s %ld %ld", uri, socket, &rtime, &stime);
         if (res == EOF) {
         if (res == EOF) {
             if (ferror(f))
             if (ferror(f))
@@ -1734,7 +1730,7 @@ mod_init(void)
 
 
     if (keepalive_interval <= 0) {
     if (keepalive_interval <= 0) {
         LM_NOTICE("keepalive functionality is disabled from the configuration\n");
         LM_NOTICE("keepalive functionality is disabled from the configuration\n");
-        keepalive_disabled = True;
+        keepalive_disabled = true;
         return 0;
         return 0;
     }
     }
 
 
@@ -1764,7 +1760,7 @@ mod_init(void)
         param = find_param_export(find_module_by_name("dialog"),
         param = find_param_export(find_module_by_name("dialog"),
 				"dlg_flag", INT_PARAM, &type);
 				"dlg_flag", INT_PARAM, &type);
         if (param) {
         if (param) {
-		have_dlg_api = True;
+		have_dlg_api = true;
 
 
 		dialog_flag = *param;
 		dialog_flag = *param;
 
 

+ 1 - 1
modules/ndb_mongodb/README

@@ -105,7 +105,7 @@ Chapter 1. Admin Guide
 
 
 3.1. server (str)
 3.1. server (str)
 
 
-   Specify the details to connect to REDIS server. It takes a list of
+   Specify the details to connect to MongoDB server. It takes a list of
    attribute=value separated by semicolon, the attributes can be name and
    attribute=value separated by semicolon, the attributes can be name and
    uri. Name is a generic identifier to be used with module functions. The
    uri. Name is a generic identifier to be used with module functions. The
    uri parameter must be a valid MongoDB database connection string.
    uri parameter must be a valid MongoDB database connection string.

+ 1 - 1
modules/ndb_mongodb/doc/ndb_mongodb_admin.xml

@@ -62,7 +62,7 @@
 	<section id="ndb_mongodb.p.server">
 	<section id="ndb_mongodb.p.server">
 		<title><varname>server</varname> (str)</title>
 		<title><varname>server</varname> (str)</title>
 		<para>
 		<para>
-			Specify the details to connect to REDIS server. It takes a list of
+			Specify the details to connect to MongoDB server. It takes a list of
 			attribute=value separated by semicolon, the attributes can be
 			attribute=value separated by semicolon, the attributes can be
 			name and uri. Name is a generic identifier to be used
 			name and uri. Name is a generic identifier to be used
 			with module functions. The uri parameter must be a valid
 			with module functions. The uri parameter must be a valid

+ 36 - 21
modules/permissions/README

@@ -63,6 +63,7 @@ Emmanuel Schmidbauer
               3.19. priority_col (string)
               3.19. priority_col (string)
               3.20. peer_tag_avp (AVP string)
               3.20. peer_tag_avp (AVP string)
               3.21. peer_tag_mode (integer)
               3.21. peer_tag_mode (integer)
+              3.22. max_subnets (int)
 
 
         4. Functions
         4. Functions
 
 
@@ -119,17 +120,18 @@ Emmanuel Schmidbauer
    1.19. Set priority_col parameter
    1.19. Set priority_col parameter
    1.20. Set peer_tag_avp parameter
    1.20. Set peer_tag_avp parameter
    1.21. Set peer_tag_mode parameter
    1.21. Set peer_tag_mode parameter
-   1.22. allow_routing usage
-   1.23. allow_routing(basename) usage
-   1.24. allow_routing(allow_file, deny_file) usage
-   1.25. allow_register(basename) usage
-   1.26. allow_register(allow_file, deny_file) usage
-   1.27. allow_uri(basename, pvar) usage
-   1.28. allow_address() usage
-   1.29. allow_source_address(group_id) usage
-   1.30. allow_source_address_group() usage
+   1.22. Set max_subnets parameter
+   1.23. allow_routing usage
+   1.24. allow_routing(basename) usage
+   1.25. allow_routing(allow_file, deny_file) usage
+   1.26. allow_register(basename) usage
+   1.27. allow_register(allow_file, deny_file) usage
+   1.28. allow_uri(basename, pvar) usage
+   1.29. allow_address() usage
+   1.30. allow_source_address(group_id) usage
    1.31. allow_source_address_group() usage
    1.31. allow_source_address_group() usage
-   1.32. allow_trusted() usage
+   1.32. allow_source_address_group() usage
+   1.33. allow_trusted() usage
 
 
 Chapter 1. Admin Guide
 Chapter 1. Admin Guide
 
 
@@ -171,6 +173,7 @@ Chapter 1. Admin Guide
         3.19. priority_col (string)
         3.19. priority_col (string)
         3.20. peer_tag_avp (AVP string)
         3.20. peer_tag_avp (AVP string)
         3.21. peer_tag_mode (integer)
         3.21. peer_tag_mode (integer)
+        3.22. max_subnets (int)
 
 
    4. Functions
    4. Functions
 
 
@@ -403,6 +406,7 @@ Chapter 1. Admin Guide
    3.19. priority_col (string)
    3.19. priority_col (string)
    3.20. peer_tag_avp (AVP string)
    3.20. peer_tag_avp (AVP string)
    3.21. peer_tag_mode (integer)
    3.21. peer_tag_mode (integer)
+   3.22. max_subnets (int)
 
 
 3.1. default_allow_file (string)
 3.1. default_allow_file (string)
 
 
@@ -683,6 +687,17 @@ modparam("permissions", "peer_tag_avp", "$avp(i:707)")
 modparam("permissions", "peer_tag_mode", 1)
 modparam("permissions", "peer_tag_mode", 1)
 ...
 ...
 
 
+3.22. max_subnets (int)
+
+   The maximum number of subnet addresses to be loaded from address table.
+
+   Default value is "512".
+
+   Example 1.22. Set max_subnets parameter
+...
+modparam("permissions", "max_subnets", 1024)
+...
+
 4. Functions
 4. Functions
 
 
    4.1. allow_routing()
    4.1. allow_routing()
@@ -706,7 +721,7 @@ modparam("permissions", "peer_tag_mode", 1)
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
 
-   Example 1.22. allow_routing usage
+   Example 1.23. allow_routing usage
 ...
 ...
 if (allow_routing()) {
 if (allow_routing()) {
         t_relay();
         t_relay();
@@ -729,7 +744,7 @@ if (allow_routing()) {
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
 
-   Example 1.23. allow_routing(basename) usage
+   Example 1.24. allow_routing(basename) usage
 ...
 ...
 if (allow_routing("basename")) {
 if (allow_routing("basename")) {
         t_relay();
         t_relay();
@@ -754,7 +769,7 @@ if (allow_routing("basename")) {
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
 
-   Example 1.24. allow_routing(allow_file, deny_file) usage
+   Example 1.25. allow_routing(allow_file, deny_file) usage
 ...
 ...
 if (allow_routing("rules.allow", "rules.deny")) {
 if (allow_routing("rules.allow", "rules.deny")) {
         t_relay();
         t_relay();
@@ -777,7 +792,7 @@ if (allow_routing("rules.allow", "rules.deny")) {
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
 
-   Example 1.25. allow_register(basename) usage
+   Example 1.26. allow_register(basename) usage
 ...
 ...
 if (method=="REGISTER") {
 if (method=="REGISTER") {
         if (allow_register("register")) {
         if (allow_register("register")) {
@@ -807,7 +822,7 @@ if (method=="REGISTER") {
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
 
-   Example 1.26. allow_register(allow_file, deny_file) usage
+   Example 1.27. allow_register(allow_file, deny_file) usage
 ...
 ...
 if (method=="REGISTER") {
 if (method=="REGISTER") {
         if (allow_register("register.allow", "register.deny")) {
         if (allow_register("register.allow", "register.deny")) {
@@ -836,7 +851,7 @@ if (method=="REGISTER") {
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
 
-   Example 1.27. allow_uri(basename, pvar) usage
+   Example 1.28. allow_uri(basename, pvar) usage
 ...
 ...
 if (allow_uri("basename", "$rt")) {  // Check Refer-To URI
 if (allow_uri("basename", "$rt")) {  // Check Refer-To URI
         t_relay();
         t_relay();
@@ -862,7 +877,7 @@ if (allow_uri("basename", "$avp(i:705)") {  // Check URI stored in $avp(i:705)
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
 
-   Example 1.28. allow_address() usage
+   Example 1.29. allow_address() usage
 ...
 ...
 
 
 // Check if source address/port is in group 1
 // Check if source address/port is in group 1
@@ -884,7 +899,7 @@ if (!allow_address("2", "$avp(dst_adr)", "$avp(dst_port)") {
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
 
-   Example 1.29. allow_source_address(group_id) usage
+   Example 1.30. allow_source_address(group_id) usage
 ...
 ...
 
 
 // Check source address/port of request
 // Check source address/port of request
@@ -901,7 +916,7 @@ if (!allow_source_address("1")) {
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
 
-   Example 1.30. allow_source_address_group() usage
+   Example 1.31. allow_source_address_group() usage
 ...
 ...
 
 
 $var(group) = allow_source_address_group();
 $var(group) = allow_source_address_group();
@@ -919,7 +934,7 @@ if ($var(group) != -1) {
 
 
    This function can be used from ANY_ROUTE.
    This function can be used from ANY_ROUTE.
 
 
-   Example 1.31. allow_source_address_group() usage
+   Example 1.32. allow_source_address_group() usage
 ...
 ...
 
 
 $var(group) = allow_address_group("1.2.3.4", "5060");
 $var(group) = allow_address_group("1.2.3.4", "5060");
@@ -944,7 +959,7 @@ if ($var(group) != -1) {
 
 
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
    This function can be used from REQUEST_ROUTE, FAILURE_ROUTE.
 
 
-   Example 1.32. allow_trusted() usage
+   Example 1.33. allow_trusted() usage
 ...
 ...
 if (allow_trusted()) {
 if (allow_trusted()) {
         t_relay();
         t_relay();

+ 20 - 0
modules/permissions/doc/permissions_admin.xml

@@ -769,6 +769,26 @@ modparam("permissions", "peer_tag_avp", "$avp(i:707)")
 ...
 ...
 modparam("permissions", "peer_tag_mode", 1)
 modparam("permissions", "peer_tag_mode", 1)
 ...
 ...
+</programlisting>
+		</example>
+	</section>
+	<section id ="permissions.p.max_subnets">
+		<title><varname>max_subnets</varname> (int)</title>
+		<para>
+			The maximum number of subnet addresses to be loaded from
+			address table.
+		</para>
+		<para>
+		<emphasis>
+		Default value is <quote>512</quote>.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>max_subnets</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("permissions", "max_subnets", 1024)
+...
 </programlisting>
 </programlisting>
 		</example>
 		</example>
 	</section>
 	</section>

+ 3 - 0
modules/permissions/hash.c

@@ -44,6 +44,9 @@ static int_str tag_avp;
 extern int peer_tag_mode;
 extern int peer_tag_mode;
 
 
 
 
+extern int _perm_max_subnets;
+
+#define PERM_MAX_SUBNETS _perm_max_subnets
 
 
 /*
 /*
  * Parse and set tag AVP specs
  * Parse and set tag AVP specs

+ 0 - 2
modules/permissions/hash.h

@@ -176,8 +176,6 @@ int addr_hash_table_rpc_print(struct addr_list** table, rpc_t* rpc, void* c);
 void empty_addr_hash_table(struct addr_list** hash_table);
 void empty_addr_hash_table(struct addr_list** hash_table);
 
 
 
 
-#define PERM_MAX_SUBNETS 128 
-
 
 
 /*
 /*
  * Structure used to store a subnet
  * Structure used to store a subnet

+ 3 - 0
modules/permissions/permissions.c

@@ -88,6 +88,8 @@ str port_col = str_init("port");           /* Name of port column */
 static int check_all_branches = 1;
 static int check_all_branches = 1;
 
 
 
 
+int _perm_max_subnets = 512;
+
 /*  
 /*  
  * Convert the name of the files into table index
  * Convert the name of the files into table index
  */
  */
@@ -178,6 +180,7 @@ static param_export_t params[] = {
 	{"ip_addr_col",        PARAM_STR, &ip_addr_col     },
 	{"ip_addr_col",        PARAM_STR, &ip_addr_col     },
 	{"mask_col",           PARAM_STR, &mask_col        },
 	{"mask_col",           PARAM_STR, &mask_col        },
 	{"port_col",           PARAM_STR, &port_col        },
 	{"port_col",           PARAM_STR, &port_col        },
+	{"max_subnets",        PARAM_INT, &_perm_max_subnets },
 	{0, 0, 0}
 	{0, 0, 0}
 };
 };
 
 

+ 17 - 5
modules/presence/notify.c

@@ -1470,6 +1470,7 @@ int send_notify_request(subs_t* subs, subs_t * watcher_subs,
 	c_back_param *cb_param= NULL;
 	c_back_param *cb_param= NULL;
 	str* final_body= NULL;
 	str* final_body= NULL;
 	uac_req_t uac_r;
 	uac_req_t uac_r;
+	str* aux_body = NULL;
 	
 	
 	LM_DBG("dialog info:\n");
 	LM_DBG("dialog info:\n");
 	printf_subs(subs);
 	printf_subs(subs);
@@ -1532,7 +1533,11 @@ int send_notify_request(subs_t* subs, subs_t * watcher_subs,
 					/* call aux_body_processing if exists */
 					/* call aux_body_processing if exists */
 					if(subs->event->aux_body_processing)
 					if(subs->event->aux_body_processing)
 					{
 					{
-						subs->event->aux_body_processing(subs, notify_body);
+						aux_body = subs->event->aux_body_processing(subs, notify_body);
+						if(aux_body) {
+							free_notify_body(notify_body, subs->event);
+							notify_body = aux_body;
+						}
 					}
 					}
 
 
 					/* apply authorization rules if exists */
 					/* apply authorization rules if exists */
@@ -1607,11 +1612,12 @@ jump_over_body:
 	}
 	}
 
 
 	LM_GEN1(pres_local_log_level,
 	LM_GEN1(pres_local_log_level,
-		"NOTIFY %.*s via %.*s on behalf of %.*s for event %.*s\n",
+		"NOTIFY %.*s via %.*s on behalf of %.*s for event %.*s : %.*s\n",
 		td->rem_uri.len, td->rem_uri.s, td->hooks.next_hop->len,
 		td->rem_uri.len, td->rem_uri.s, td->hooks.next_hop->len,
 		td->hooks.next_hop->s,
 		td->hooks.next_hop->s,
-		td->loc_uri.len, td->loc_uri.s, subs->event->name.len,
-		subs->event->name.s);
+		td->loc_uri.len, td->loc_uri.s,
+		subs->event->name.len, subs->event->name.s,
+		subs->callid.len, subs->callid.s);
 
 
 	ps_free_tm_dlg(td);
 	ps_free_tm_dlg(td);
 	
 	
@@ -2854,7 +2860,13 @@ int process_dialogs(int round, int presence_winfo)
 
 
 		if (dialog->n > 1)
 		if (dialog->n > 1)
 		{
 		{
-			LM_ERR("multiple records found\n");
+			LM_ERR("multiple records found for %.*s, ci : %.*s, tt : %.*s, ft : %.*s, ev : %.*s\n",
+					sub.pres_uri.len, sub.pres_uri.s,
+					sub.callid.len, sub.callid.s,
+					sub.to_tag.len, sub.to_tag.s,
+					sub.from_tag.len, sub.from_tag.s,
+					ev_sname.len, ev_sname.s
+					);
 			goto delete_dialog;
 			goto delete_dialog;
 		}
 		}
 
 

+ 1 - 1
modules/presence/publish.c

@@ -509,7 +509,7 @@ error:
 int update_hard_presentity(str *pres_uri, pres_ev_t *event, str *file_uri, str *filename)
 int update_hard_presentity(str *pres_uri, pres_ev_t *event, str *file_uri, str *filename)
 {
 {
 	int ret = -1, new_t, pidf_result;
 	int ret = -1, new_t, pidf_result;
-	str *pidf_doc;
+	str *pidf_doc = 0;
 	char *sphere = NULL;
 	char *sphere = NULL;
 	presentity_t *pres = NULL;
 	presentity_t *pres = NULL;
 	struct sip_uri parsed_uri;
 	struct sip_uri parsed_uri;

+ 1 - 0
modules/presence_dialoginfo/add_events.c

@@ -57,6 +57,7 @@ int dlginfo_add_events(void)
 
 
 	/* modify XML body for each watcher to set the correct "version" */
 	/* modify XML body for each watcher to set the correct "version" */
     event.aux_body_processing = dlginfo_body_setversion;
     event.aux_body_processing = dlginfo_body_setversion;
+    event.aux_free_body = free_xml_body;
 
 
 	
 	
     if (pres_add_event(&event) < 0) {
     if (pres_add_event(&event) < 0) {

+ 36 - 2
modules/presence_dialoginfo/notify_body.c

@@ -538,6 +538,7 @@ str *dlginfo_body_setversion(subs_t *subs, str *body) {
 	char *version_start=0;
 	char *version_start=0;
 	char version[MAX_INT_LEN + 2]; /* +2 becasue of trailing " and \0 */
 	char version[MAX_INT_LEN + 2]; /* +2 becasue of trailing " and \0 */
 	int version_len;
 	int version_len;
+	str* aux_body = NULL;
 
 
 	if (!body) {
 	if (!body) {
 		return NULL;
 		return NULL;
@@ -564,9 +565,29 @@ str *dlginfo_body_setversion(subs_t *subs, str *body) {
 	version_len = snprintf(version, MAX_INT_LEN + 2,"%d\"", subs->version);
 	version_len = snprintf(version, MAX_INT_LEN + 2,"%d\"", subs->version);
 	if (version_len >= MAX_INT_LEN + 2) {
 	if (version_len >= MAX_INT_LEN + 2) {
 		LM_ERR("failed to convert 'version' to string\n");
 		LM_ERR("failed to convert 'version' to string\n");
-		memcpy(version_start, "00000000000\"", 12);
 		return NULL;
 		return NULL;
 	}
 	}
+
+	aux_body= (str*)pkg_malloc(sizeof(str));
+	if(aux_body== NULL)
+	{
+		LM_ERR("error allocating memory for aux body str\n");
+		return NULL;
+	}
+	memset(aux_body, 0, sizeof(str));
+	aux_body->s= (char*)pkg_malloc( body->len * sizeof(char));
+	if(aux_body->s== NULL)
+	{
+		pkg_free(aux_body);
+		LM_ERR("error allocating memory for aux body buffer\n");
+		return NULL;
+	}
+	memcpy(aux_body->s, body->s, body->len);
+	aux_body->len= body->len;
+
+	/* again but on the copied str, no checks needed */
+	version_start = strstr(aux_body->s + 34, "version=");
+	version_start += 9;
 	/* Replace the placeholder 00000000000 with the version.
 	/* Replace the placeholder 00000000000 with the version.
 	 * Put the padding behind the ""
 	 * Put the padding behind the ""
 	 */
 	 */
@@ -574,5 +595,18 @@ str *dlginfo_body_setversion(subs_t *subs, str *body) {
 	memcpy(version_start, version, version_len);
 	memcpy(version_start, version, version_len);
 	memset(version_start + version_len, ' ', 12 - version_len);
 	memset(version_start + version_len, ' ', 12 - version_len);
 
 
-	return NULL;
+	xmlDocPtr doc = xmlReadMemory(aux_body->s, aux_body->len, "noname.xml", NULL, 0);
+        if (doc == NULL) {
+		LM_ERR("error allocation xmldoc\n");
+		pkg_free(aux_body->s);
+		pkg_free(aux_body);
+		return NULL;
+	}
+	pkg_free(aux_body->s);
+        xmlDocDumpFormatMemory(doc,(xmlChar**)(void*)&aux_body->s, &aux_body->len, 1);
+	xmlFreeDoc(doc);
+        xmlCleanupParser();
+        xmlMemoryDump();
+
+	return aux_body;
 }
 }

+ 33 - 10
modules/presence_xml/notify_body.c

@@ -58,27 +58,51 @@ void free_xml_body(char* body)
 	body= NULL;
 	body= NULL;
 }
 }
 
 
-#define PRESENCE_EMPTY_BODY_SIZE 512
+#define PRESENCE_EMPTY_BODY_SIZE 1024
 
 
-#define PRESENCE_EMPTY_BODY "<presence> \
-<tuple id=\"615293b33c62dec073e05d9421e9f48b\">\
+#define PRESENCE_EMPTY_BODY  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\
+<presence xmlns=\"urn:ietf:params:xml:ns:pidf\" xmlns:dm=\"urn:ietf:params:xml:ns:pidf:data-model\" xmlns:rpid=\"urn:ietf:params:xml:ns:pidf:rpid\" xmlns:c=\"urn:ietf:params:xml:ns:pidf:cipid\" entity=\"%.*s\"> \
+<tuple xmlns=\"urn:ietf:params:xml:ns:pidf\" id=\"615293b33c62dec073e05d9421e9f48b\">\
 <status>\
 <status>\
 <basic>open</basic>\
 <basic>open</basic>\
 </status>\
 </status>\
 </tuple>\
 </tuple>\
 <note xmlns=\"urn:ietf:params:xml:ns:pidf\">Available</note>\
 <note xmlns=\"urn:ietf:params:xml:ns:pidf\">Available</note>\
+<dm:person xmlns:dm=\"urn:ietf:params:xml:ns:pidf:data-model\" xmlns:rpid=\"urn:ietf:params:xml:ns:pidf:rpid\" id=\"1\">\
+<rpid:activities/>\
+<dm:note>Available</dm:note>\
+</dm:person>\
 </presence>"
 </presence>"
 
 
 str* pres_agg_nbody_empty(str* pres_user, str* pres_domain)
 str* pres_agg_nbody_empty(str* pres_user, str* pres_domain)
 {
 {
 	str* n_body= NULL;
 	str* n_body= NULL;
 
 
+	str* body_array;
+	char* body;
+
 	LM_DBG("creating empty presence for [pres_user]=%.*s [pres_domain]= %.*s\n",
 	LM_DBG("creating empty presence for [pres_user]=%.*s [pres_domain]= %.*s\n",
 			pres_user->len, pres_user->s, pres_domain->len, pres_domain->s);
 			pres_user->len, pres_user->s, pres_domain->len, pres_domain->s);
 
 
-	str* body_array = (str*)pkg_malloc(sizeof(str));
-	char* body = (char*)pkg_malloc(PRESENCE_EMPTY_BODY_SIZE);
-	sprintf(body, PRESENCE_EMPTY_BODY);
+	if(pres_user->len+sizeof(PRESENCE_EMPTY_BODY)
+			>= PRESENCE_EMPTY_BODY_SIZE - 1) {
+		LM_ERR("insufficient buffer to add user (its len is: %d)\n",
+				pres_user->len);
+		return NULL;
+	}
+	body_array = (str*)pkg_malloc(sizeof(str));
+	if(body_array==NULL) {
+		LM_ERR("no more pkg\n");
+		return NULL;
+	}
+
+	body = (char*)pkg_malloc(PRESENCE_EMPTY_BODY_SIZE);
+	if(body_array==NULL) {
+		LM_ERR("no more pkg\n");
+		pkg_free(body_array);
+		return NULL;
+	}
+	snprintf(body, PRESENCE_EMPTY_BODY_SIZE, PRESENCE_EMPTY_BODY, pres_user->len, pres_user->s);
 	body_array->s = body;
 	body_array->s = body;
 	body_array->len = strlen(body);
 	body_array->len = strlen(body);
 
 
@@ -86,11 +110,10 @@ str* pres_agg_nbody_empty(str* pres_user, str* pres_domain)
 	n_body= agregate_xmls(pres_user, pres_domain, &body_array, 1);
 	n_body= agregate_xmls(pres_user, pres_domain, &body_array, 1);
 	LM_DBG("[n_body]=%p\n", n_body);
 	LM_DBG("[n_body]=%p\n", n_body);
 	if(n_body) {
 	if(n_body) {
-		LM_DBG("[*n_body]=%.*s\n",n_body->len, n_body->s);
+		LM_DBG("[*n_body]=%.*s\n", n_body->len, n_body->s);
 	}
 	}
-	if(n_body== NULL)
-	{
-		LM_ERR("while aggregating body\n");
+	if(n_body== NULL) {
+		LM_ERR("while aggregating body for: %.*s\n", pres_user->len, pres_user->s);
 	}
 	}
 
 
 	pkg_free(body);
 	pkg_free(body);

+ 6 - 1
modules/registrar/reg_mod.c

@@ -362,8 +362,13 @@ static int mod_init(void)
 			sock_hdr_name.len = 0;
 			sock_hdr_name.len = 0;
 			sock_flag = -1;
 			sock_flag = -1;
 		}
 		}
+	} else if (reg_xavp_cfg.s) {
+		if (reg_xavp_cfg.len == 0 || sock_flag == -1) {
+			LM_WARN("empty reg_xavp_cfg or sock_flag no set -> resetting\n");
+			sock_flag = -1;
+		}
 	} else if (sock_flag!=-1) {
 	} else if (sock_flag!=-1) {
-		LM_WARN("sock_flag defined but no sock_hdr_name -> reseting flag\n");
+		LM_WARN("sock_flag defined but no sock_hdr_name or no reg_xavp_cfg -> resetting flag\n");
 		sock_flag = -1;
 		sock_flag = -1;
 	}
 	}
 
 

+ 122 - 3
modules/rtpengine/doc/rtpengine_admin.xml

@@ -73,6 +73,38 @@
 		If the set was selected using setid_avp, the avp needs to be
 		If the set was selected using setid_avp, the avp needs to be
 		set only once before rtpengine_offer() or rtpengine_manage() call.
 		set only once before rtpengine_offer() or rtpengine_manage() call.
 	</para>
 	</para>
+	<para>
+		From the current implementation point of view, the sets of rtpproxy nodes
+		are shared memory(shm), so all processes can see a common list of nodes.
+		There is no locking when setting the nodes enabled/disabled (to keep the
+		memory access as fast as possible). Thus, problems related to node state
+		might appear for concurent processes that might set the nodes
+		enabled/disabled(e.g. by fifo command). This robustness problems are overcomed as follows.
+	</para>
+
+	<para>
+		If the current process sees the selected node as disabled, the node is
+		<emphasis>force tested</emphasis> before the current process actually
+		takes the disabled decision. If the test succeeds, the process will set
+		the node as enabled (but other concurrent process might still see it as disabled).
+.
+	</para>
+
+	<para>
+		If the current process sees the selected node as enabled, it does no additional checks
+		and sends the command which will fail in case the machine is actually broken.
+		The process will set the node as disabled (but other concurrent process might still see it as enabled).
+	</para>
+
+	<para>
+		The 'kamctl fifo' commands (including rtpengin ones) are executed by an exclusive
+		process which operate on the same shared memory node list.
+	</para>
+
+	<para>
+		All the nodes are pinged in the beginning by all the processes,
+		even if the node list is shared memory.
+	</para>
 	</section>
 	</section>
 
 
 	<section>
 	<section>
@@ -175,6 +207,30 @@ modparam("rtpengine", "rtpengine_disable_tout", 20)
 ...
 ...
 modparam("rtpengine", "rtpengine_tout_ms", 2000)
 modparam("rtpengine", "rtpengine_tout_ms", 2000)
 ...
 ...
+</programlisting>
+		</example>
+	</section>
+	<section id="rtpengine.p.rtpengine_allow_op">
+		<title><varname>rtpengine_allow_op</varname> (integer)</title>
+		<para>
+		Enable this to allow finishing the current sessions while denying new sessions for the
+		<emphasis>manually deactivated nodes </emphasis> via kamctl command i.e. "disabled(permanent)" nodes.
+		Probably the manually deactivated machine is still running(did not crash).
+		</para>
+		<para>
+		This is <emphasis>useful</emphasis> when deactivating a node for maintanance and reject new sessions but allow current ones to finish.
+		</para>
+		<para>
+		<emphasis>
+		Default value is <quote>0</quote> to keep the current behaviour.
+		</emphasis>
+		</para>
+		<example>
+		<title>Set <varname>rtpengine_allow_op</varname> parameter</title>
+		<programlisting format="linespecific">
+...
+modparam("rtpengine", "rtpengine_allow_op", 1)
+...
 </programlisting>
 </programlisting>
 		</example>
 		</example>
 	</section>
 	</section>
@@ -305,7 +361,7 @@ modparam("rtpengine", "read_sdp_pv", "$var(sdp)")
 route {
 route {
 	...
 	...
 	$var(sdp) = $rb + "a=foo:bar\r\n";
 	$var(sdp) = $rb + "a=foo:bar\r\n";
-	rtpproxy_manage();
+	rtpengine_manage();
 }
 }
 </programlisting>
 </programlisting>
 		</example>
 		</example>
@@ -330,7 +386,7 @@ modparam("rtpengine", "write_sdp_pv", "$avp(sdp)")
 ...
 ...
 route {
 route {
 	...
 	...
-	rtpproxy_manage();
+	rtpengine_manage();
 	set_body("$avp(sdp)a=baz123\r\n", "application/sdp");
 	set_body("$avp(sdp)a=baz123\r\n", "application/sdp");
 }
 }
 </programlisting>
 </programlisting>
@@ -350,12 +406,58 @@ route {
 		<title>Set <varname>rtp_inst_pvar</varname> parameter</title>
 		<title>Set <varname>rtp_inst_pvar</varname> parameter</title>
 <programlisting format="linespecific">
 <programlisting format="linespecific">
 ...
 ...
-modparam("rtpproxy", "rtp_inst_pvar", "$avp(RTP_INSTANCE)")
+modparam("rtpengine", "rtp_inst_pvar", "$avp(RTP_INSTANCE)")
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section id="rtpengine.p.hash_table_size">
+		<title><varname>hash_table_size</varname> (integer)</title>
+		<para>
+			Size of the hash table. Default value is 256.
+		</para>
+		<para>
+			NOTE: If configured size is <emphasis>less than</emphasis> 1, the size will be defaulted to 1.
+		</para>
+		<example>
+		<title>Set <varname>hash_table_size</varname> parameter</title>
+<programlisting format="linespecific">
+...
+modparam("rtpengine", "hash_table_size", "123")
 ...
 ...
 </programlisting>
 </programlisting>
 		</example>
 		</example>
 	</section>
 	</section>
 
 
+	<section id="rtpengine.p.hash_table_tout">
+		<title><varname>hash_table_tout</varname> (integer)</title>
+		<para>
+			Number of seconds after an rtpengine hash table entry is marked for deletion.
+			By default, this parameter is set to 3600 (seconds).
+		</para>
+		<para>
+			To maintain information about a selected rtp machine node, for a given call, entries are added in a hashtable of (callid, node) pairs.
+			When command comes, lookup callid. If found, return chosen node. If not found, choose a new node, insert it in the hastable and return the chosen node.
+		</para>
+		<para>
+			NOTE: In the current implementation, the actual deletion happens <emphasis>on the fly</emphasis>,
+			while insert/remove/lookup the hastable, <emphasis>only</emphasis> for the entries in the insert/remove/lookup path.
+		</para>
+		<para>
+			NOTE: When configuring this parameter, one should consider maximum call time VS share memory for unfinished calls.
+		</para>
+		<example>
+		<title>Set <varname>hash_table_tout</varname> parameter</title>
+<programlisting format="linespecific">
+...
+modparam("rtpengine", "hash_table_tout", "300")
+...
+</programlisting>
+		</example>
+	</section>
+
+
 	</section>
 	</section>
 
 
 	<section>
 	<section>
@@ -964,6 +1066,23 @@ $ &ctltool; fifo nh_ping_rtpp all
 			</programlisting>
 			</programlisting>
 			</example>
 			</example>
 		</section>
 		</section>
+
+	    <section id="rtpengine.m.nh_show_hash_total">
+			<title><function moreinfo="none">nh_show_hash_total</function></title>
+			<para>
+				Print the total number of hash entries in the hash table at a given moment.
+			</para>
+			<example>
+			<title>
+				<function moreinfo="none">nh_show_hash_total</function> usage</title>
+			<programlisting format="linespecific">
+...
+$ &ctltool; fifo nh_show_hash_total
+...
+			</programlisting>
+			</example>
+		</section>
+
 	</section>
 	</section>
 
 
 </chapter>
 </chapter>

+ 250 - 48
modules/rtpengine/rtpengine.c

@@ -78,6 +78,7 @@
 #include "../../modules/tm/tm_load.h"
 #include "../../modules/tm/tm_load.h"
 #include "rtpengine.h"
 #include "rtpengine.h"
 #include "rtpengine_funcs.h"
 #include "rtpengine_funcs.h"
+#include "rtpengine_hash.h"
 #include "bencode.h"
 #include "bencode.h"
 
 
 MODULE_VERSION
 MODULE_VERSION
@@ -108,6 +109,7 @@ MODULE_VERSION
 #define MI_ENABLE_RTP_PROXY			"nh_enable_rtpp"
 #define MI_ENABLE_RTP_PROXY			"nh_enable_rtpp"
 #define MI_SHOW_RTP_PROXIES			"nh_show_rtpp"
 #define MI_SHOW_RTP_PROXIES			"nh_show_rtpp"
 #define MI_PING_RTP_PROXY           "nh_ping_rtpp"
 #define MI_PING_RTP_PROXY           "nh_ping_rtpp"
+#define MI_SHOW_HASH_TOTAL          "nh_show_hash_total"
 
 
 #define MI_RTP_PROXY_NOT_FOUND		"RTP proxy not found"
 #define MI_RTP_PROXY_NOT_FOUND		"RTP proxy not found"
 #define MI_RTP_PROXY_NOT_FOUND_LEN	(sizeof(MI_RTP_PROXY_NOT_FOUND)-1)
 #define MI_RTP_PROXY_NOT_FOUND_LEN	(sizeof(MI_RTP_PROXY_NOT_FOUND)-1)
@@ -142,6 +144,10 @@ MODULE_VERSION
 #define MI_SUCCESS_LEN         		(sizeof(MI_SUCCESS)-1)
 #define MI_SUCCESS_LEN         		(sizeof(MI_SUCCESS)-1)
 #define MI_FAIL                     "fail"
 #define MI_FAIL                     "fail"
 #define MI_FAIL_LEN         		(sizeof(MI_FAIL)-1)
 #define MI_FAIL_LEN         		(sizeof(MI_FAIL)-1)
+#define MI_HASH_ENTRIES				"entries"
+#define MI_HASH_ENTRIES_LEN			(sizeof(MI_HASH_ENTRIES)-1)
+#define MI_HASH_ENTRIES_FAIL		"Fail to get entry details"
+#define MI_HASH_ENTRIES_FAIL_LEN	(sizeof(MI_HASH_ENTRIES_FAIL)-1)
 
 
 #define MI_FOUND_ALL                   2
 #define MI_FOUND_ALL                   2
 #define MI_FOUND_ONE                   1
 #define MI_FOUND_ONE                   1
@@ -187,7 +193,9 @@ static int rtpengine_offer_answer(struct sip_msg *msg, const char *flags, int op
 static int fixup_set_id(void ** param, int param_no);
 static int fixup_set_id(void ** param, int param_no);
 static int set_rtpengine_set_f(struct sip_msg * msg, char * str1, char * str2);
 static int set_rtpengine_set_f(struct sip_msg * msg, char * str1, char * str2);
 static struct rtpp_set * select_rtpp_set(int id_set);
 static struct rtpp_set * select_rtpp_set(int id_set);
-static struct rtpp_node *select_rtpp_node(str, int);
+static struct rtpp_node *select_rtpp_node_new(str, str, int);
+static struct rtpp_node *select_rtpp_node_old(str, str, int);
+static struct rtpp_node *select_rtpp_node(str, str, int);
 static char *send_rtpp_command(struct rtpp_node *, bencode_item_t *, int *);
 static char *send_rtpp_command(struct rtpp_node *, bencode_item_t *, int *);
 static int get_extra_id(struct sip_msg* msg, str *id_str);
 static int get_extra_id(struct sip_msg* msg, str *id_str);
 
 
@@ -210,17 +218,17 @@ static int rtpp_test_ping(struct rtpp_node *node);
 
 
 /* Pseudo-Variables */
 /* Pseudo-Variables */
 static int pv_get_rtpstat_f(struct sip_msg *, pv_param_t *, pv_value_t *);
 static int pv_get_rtpstat_f(struct sip_msg *, pv_param_t *, pv_value_t *);
+static int set_rtp_inst_pvar(struct sip_msg *msg, const str * const uri);
 
 
 /*mi commands*/
 /*mi commands*/
-static struct mi_root* mi_enable_rtp_proxy(struct mi_root* cmd_tree,
-		void* param );
-static struct mi_root* mi_show_rtp_proxy(struct mi_root* cmd_tree,
-		void* param);
-static struct mi_root* mi_ping_rtp_proxy(struct mi_root* cmd_tree,
-        void* param);
+static struct mi_root* mi_enable_rtp_proxy(struct mi_root* cmd_tree, void* param);
+static struct mi_root* mi_show_rtp_proxy(struct mi_root* cmd_tree, void* param);
+static struct mi_root* mi_ping_rtp_proxy(struct mi_root* cmd_tree, void* param);
+static struct mi_root* mi_show_hash_total(struct mi_root* cmd_tree, void* param);
 
 
 
 
 static int rtpengine_disable_tout = 60;
 static int rtpengine_disable_tout = 60;
+static int rtpengine_allow_op = 0;
 static int rtpengine_retr = 5;
 static int rtpengine_retr = 5;
 static int rtpengine_tout_ms = 1000;
 static int rtpengine_tout_ms = 1000;
 static int queried_nodes_limit = MAX_RTPP_TRIED_NODES;
 static int queried_nodes_limit = MAX_RTPP_TRIED_NODES;
@@ -228,6 +236,8 @@ static pid_t mypid;
 static unsigned int myseqn = 0;
 static unsigned int myseqn = 0;
 static str extra_id_pv_param = {NULL, 0};
 static str extra_id_pv_param = {NULL, 0};
 static char *setid_avp_param = NULL;
 static char *setid_avp_param = NULL;
+static int hash_table_tout = 3600;
+static int hash_table_size = 256;
 
 
 static char ** rtpp_strings=0;
 static char ** rtpp_strings=0;
 static int rtpp_sets=0; /*used in rtpengine_set_store()*/
 static int rtpp_sets=0; /*used in rtpengine_set_store()*/
@@ -326,6 +336,7 @@ static param_export_t params[] = {
 	{"rtpengine_disable_tout",INT_PARAM, &rtpengine_disable_tout },
 	{"rtpengine_disable_tout",INT_PARAM, &rtpengine_disable_tout },
 	{"rtpengine_retr",        INT_PARAM, &rtpengine_retr         },
 	{"rtpengine_retr",        INT_PARAM, &rtpengine_retr         },
 	{"rtpengine_tout_ms",     INT_PARAM, &rtpengine_tout_ms      },
 	{"rtpengine_tout_ms",     INT_PARAM, &rtpengine_tout_ms      },
+	{"rtpengine_allow_op",    INT_PARAM, &rtpengine_allow_op     },
 	{"queried_nodes_limit",   INT_PARAM, &queried_nodes_limit    },
 	{"queried_nodes_limit",   INT_PARAM, &queried_nodes_limit    },
 	{"db_url",                PARAM_STR, &rtpp_db_url },
 	{"db_url",                PARAM_STR, &rtpp_db_url },
 	{"table_name",            PARAM_STR, &rtpp_table_name },
 	{"table_name",            PARAM_STR, &rtpp_table_name },
@@ -336,6 +347,8 @@ static param_export_t params[] = {
 	{"rtp_inst_pvar",         PARAM_STR, &rtp_inst_pv_param },
 	{"rtp_inst_pvar",         PARAM_STR, &rtp_inst_pv_param },
 	{"write_sdp_pv",          PARAM_STR, &write_sdp_pvar_str          },
 	{"write_sdp_pv",          PARAM_STR, &write_sdp_pvar_str          },
 	{"read_sdp_pv",           PARAM_STR, &read_sdp_pvar_str          },
 	{"read_sdp_pv",           PARAM_STR, &read_sdp_pvar_str          },
+	{"hash_table_tout",       INT_PARAM, &hash_table_tout        },
+	{"hash_table_size",       INT_PARAM, &hash_table_size        },
 	{0, 0, 0}
 	{0, 0, 0}
 };
 };
 
 
@@ -343,6 +356,7 @@ static mi_export_t mi_cmds[] = {
 	{MI_ENABLE_RTP_PROXY,     mi_enable_rtp_proxy,  0,  0,  0},
 	{MI_ENABLE_RTP_PROXY,     mi_enable_rtp_proxy,  0,  0,  0},
 	{MI_SHOW_RTP_PROXIES,     mi_show_rtp_proxy,    0,  0,  0},
 	{MI_SHOW_RTP_PROXIES,     mi_show_rtp_proxy,    0,  0,  0},
 	{MI_PING_RTP_PROXY,       mi_ping_rtp_proxy,    0,  0,  0},
 	{MI_PING_RTP_PROXY,       mi_ping_rtp_proxy,    0,  0,  0},
+	{MI_SHOW_HASH_TOTAL,      mi_show_hash_total,    0,  0,  0},
 	{ 0, 0, 0, 0, 0}
 	{ 0, 0, 0, 0, 0}
 };
 };
 
 
@@ -1099,8 +1113,7 @@ error:
 	return -1;
 	return -1;
 }
 }
 
 
-static struct mi_root* mi_show_rtp_proxy(struct mi_root* cmd_tree,
-												void* param)
+static struct mi_root* mi_show_rtp_proxy(struct mi_root* cmd_tree, void* param)
 {
 {
 	struct mi_node *node;
 	struct mi_node *node;
 	struct mi_root *root = NULL;
 	struct mi_root *root = NULL;
@@ -1189,8 +1202,7 @@ error:
 	return init_mi_tree(404, MI_ERROR, MI_ERROR_LEN);
 	return init_mi_tree(404, MI_ERROR, MI_ERROR_LEN);
 }
 }
 
 
-static struct mi_root* mi_ping_rtp_proxy(struct mi_root* cmd_tree,
-												void* param)
+static struct mi_root* mi_ping_rtp_proxy(struct mi_root* cmd_tree, void* param)
 {
 {
 	struct mi_node *node, *crt_node;
 	struct mi_node *node, *crt_node;
 	struct mi_attr *attr;
 	struct mi_attr *attr;
@@ -1315,6 +1327,48 @@ error:
 }
 }
 
 
 
 
+static struct mi_root* mi_show_hash_total(struct mi_root* cmd_tree, void* param)
+{
+	struct mi_node *node, *crt_node;
+	struct mi_attr *attr;
+	struct mi_root *root = NULL;
+	unsigned int total;
+	str total_str;
+
+	// Init print tree
+	root = init_mi_tree(200, MI_OK_S, MI_OK_LEN);
+	if (!root) {
+		LM_ERR("the MI tree cannot be initialized!\n");
+		return 0;
+	}
+	node = &root->node;
+
+	// Create new node and add it to the roots's kids
+	if(!(crt_node = add_mi_node_child(node, MI_DUP_NAME, "total", strlen("total"), 0, 0))) {
+		LM_ERR("cannot add the child node to the tree\n");
+		goto error;
+	}
+
+	// Get total number of entries
+	total = rtpengine_hash_table_total();
+	total_str.s = int2str(total, &total_str.len);
+
+	// Add node attributes
+	if ((attr = add_mi_attr(crt_node, MI_DUP_VALUE, MI_HASH_ENTRIES, MI_HASH_ENTRIES_LEN, total_str.s, total_str.len)) == 0) {
+		LM_ERR("cannot add attributes to the node\n");
+		goto error;
+	}
+
+	return root;
+
+error:
+	if (root) {
+	    free_mi_tree(root);
+	}
+
+	return init_mi_tree(404, MI_HASH_ENTRIES_FAIL, MI_HASH_ENTRIES_FAIL_LEN);
+}
+
 
 
 static int
 static int
 mod_init(void)
 mod_init(void)
@@ -1440,6 +1494,14 @@ mod_init(void)
 		return -1;
 		return -1;
 	}
 	}
 
 
+	/* init the hastable which keeps the call-id <-> selected_node relation */
+	if (!rtpengine_hash_table_init(hash_table_size)) {
+		LM_ERR("rtpengine_hash_table_init(%d) failed!\n", hash_table_size);
+		return -1;
+	} else {
+		LM_DBG("rtpengine_hash_table_init(%d) success!\n", hash_table_size);
+	}
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1579,6 +1641,13 @@ static void mod_destroy(void)
 	}
 	}
 
 
 	shm_free(rtpp_set_list);
 	shm_free(rtpp_set_list);
+
+	/* destroy the hastable which keeps the call-id <-> selected_node relation */
+	if (!rtpengine_hash_table_destroy()) {
+		LM_ERR("rtpengine_hash_table_destroy() failed!\n");
+	} else {
+		LM_DBG("rtpengine_hash_table_destroy() success!\n");
+	}
 }
 }
 
 
 
 
@@ -1790,7 +1859,8 @@ static bencode_item_t *rtpp_function_call(bencode_buffer_t *bencbuf, struct sip_
 {
 {
 	struct ng_flags_parse ng_flags;
 	struct ng_flags_parse ng_flags;
 	bencode_item_t *item, *resp;
 	bencode_item_t *item, *resp;
-	str callid, from_tag, to_tag, body, viabranch, error;
+	str callid = STR_NULL, from_tag = STR_NULL, to_tag = STR_NULL, viabranch = STR_NULL;
+	str body = STR_NULL, error = STR_NULL;
 	int ret, queried_nodes;
 	int ret, queried_nodes;
 	struct rtpp_node *node;
 	struct rtpp_node *node;
 	char *cp;
 	char *cp;
@@ -1923,16 +1993,17 @@ select_node:
 			LM_ERR("queried nodes limit reached\n");
 			LM_ERR("queried nodes limit reached\n");
 			goto error;
 			goto error;
 		}
 		}
-		node = select_rtpp_node(callid, 1);
+		node = select_rtpp_node(callid, viabranch, 1);
 		if (!node) {
 		if (!node) {
 			LM_ERR("no available proxies\n");
 			LM_ERR("no available proxies\n");
 			goto error;
 			goto error;
 		}
 		}
+
 		cp = send_rtpp_command(node, ng_flags.dict, &ret);
 		cp = send_rtpp_command(node, ng_flags.dict, &ret);
-        if (cp == NULL) {
-    	    node->rn_disabled = 1;
-        	node->rn_recheck_ticks = get_ticks() + rtpengine_disable_tout;
-        }
+		if (cp == NULL) {
+			node->rn_disabled = 1;
+			node->rn_recheck_ticks = get_ticks() + rtpengine_disable_tout;
+		}
 	} while (cp == NULL);
 	} while (cp == NULL);
 	LM_DBG("proxy reply: %.*s\n", ret, cp);
 	LM_DBG("proxy reply: %.*s\n", ret, cp);
 
 
@@ -1944,12 +2015,13 @@ select_node:
 		LM_ERR("failed to decode bencoded reply from proxy: %.*s\n", ret, cp);
 		LM_ERR("failed to decode bencoded reply from proxy: %.*s\n", ret, cp);
 		goto error;
 		goto error;
 	}
 	}
+
 	if (!bencode_dictionary_get_strcmp(resp, "result", "error")) {
 	if (!bencode_dictionary_get_strcmp(resp, "result", "error")) {
 		if (!bencode_dictionary_get_str(resp, "error-reason", &error)) {
 		if (!bencode_dictionary_get_str(resp, "error-reason", &error)) {
 		    LM_ERR("proxy return error but didn't give an error reason: %.*s\n", ret, cp);
 		    LM_ERR("proxy return error but didn't give an error reason: %.*s\n", ret, cp);
 		}
 		}
 		else {
 		else {
-			if ((RTPENGINE_SESS_LIMIT_MSG_LEN == error.len) && 
+			if ((RTPENGINE_SESS_LIMIT_MSG_LEN == error.len) &&
 			    (strncmp(error.s, RTPENGINE_SESS_LIMIT_MSG, RTPENGINE_SESS_LIMIT_MSG_LEN) == 0))
 			    (strncmp(error.s, RTPENGINE_SESS_LIMIT_MSG, RTPENGINE_SESS_LIMIT_MSG_LEN) == 0))
 			{
 			{
 				LM_WARN("proxy %.*s: %.*s", node->rn_url.len, node->rn_url.s , error.len, error.s);
 				LM_WARN("proxy %.*s: %.*s", node->rn_url.len, node->rn_url.s , error.len, error.s);
@@ -1957,12 +2029,23 @@ select_node:
 			}
 			}
 			LM_ERR("proxy replied with error: %.*s\n", error.len, error.s);
 			LM_ERR("proxy replied with error: %.*s\n", error.len, error.s);
 		}
 		}
-        goto error;
+		goto error;
 	}
 	}
 
 
 	if (body_out)
 	if (body_out)
 		*body_out = body;
 		*body_out = body;
 
 
+	if (op == OP_DELETE) {
+		/* Delete the key<->value from the hashtable */
+		if (!rtpengine_hash_table_remove(callid, viabranch)) {
+			LM_ERR("rtpengine hash table failed to remove entry for callen=%d callid=%.*s viabranch=%.*s\n",
+				callid.len, callid.len, callid.s, viabranch.len, viabranch.s);
+		} else {
+			LM_DBG("rtpengine hash table remove entry for callen=%d callid=%.*s viabranch=%.*s\n",
+				callid.len, callid.len, callid.s, viabranch.len, viabranch.s);
+		}
+	}
+
 	return resp;
 	return resp;
 
 
 error:
 error:
@@ -2191,81 +2274,199 @@ static struct rtpp_set * select_rtpp_set(int id_set ){
 
 
 	return rtpp_list;
 	return rtpp_list;
 }
 }
+
 /*
 /*
- * Main balancing routine. This does not try to keep the same proxy for
- * the call if some proxies were disabled or enabled; proxy death considered
- * too rare. Otherwise we should implement "mature" HA clustering, which is
- * too expensive here.
+ * run the selection algorithm and return the new selected node
  */
  */
 static struct rtpp_node *
 static struct rtpp_node *
-select_rtpp_node(str callid, int do_test)
+select_rtpp_node_new(str callid, str viabranch, int do_test)
 {
 {
-	unsigned sum, sumcut, weight_sum;
 	struct rtpp_node* node;
 	struct rtpp_node* node;
-	int was_forced;
-
-	if(!active_rtpp_set){
-		LM_ERR("script error -no valid set selected\n");
-		return NULL;
-	}
-	/* Most popular case: 1 proxy, nothing to calculate */
-	if (active_rtpp_set->rtpp_node_count == 1) {
-		node = active_rtpp_set->rn_first;
-		if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks())
-			node->rn_disabled = rtpp_test(node, 1, 0);
-		return node->rn_disabled ? NULL : node;
-	}
+	unsigned i, sum, sumcut, weight_sum;
+	int was_forced = 0;
 
 
 	/* XXX Use quick-and-dirty hashing algo */
 	/* XXX Use quick-and-dirty hashing algo */
-	for(sum = 0; callid.len > 0; callid.len--)
-		sum += callid.s[callid.len - 1];
+	sum = 0;
+	for(i = 0; i < callid.len; i++)
+		sum += callid.s[i];
 	sum &= 0xff;
 	sum &= 0xff;
 
 
-	was_forced = 0;
 retry:
 retry:
 	weight_sum = 0;
 	weight_sum = 0;
-	for (node=active_rtpp_set->rn_first; node!=NULL; node=node->rn_next) {
 
 
+	for (node=active_rtpp_set->rn_first; node!=NULL; node=node->rn_next) {
+		/* Try to enable if it's time to try. */
 		if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks()){
 		if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks()){
-			/* Try to enable if it's time to try. */
 			node->rn_disabled = rtpp_test(node, 1, 0);
 			node->rn_disabled = rtpp_test(node, 1, 0);
 		}
 		}
-		if (!node->rn_disabled)
+
+		/* Select only between enabled machines */
+		if (!node->rn_disabled) {
 			weight_sum += node->rn_weight;
 			weight_sum += node->rn_weight;
+		}
 	}
 	}
+
+	/* No proxies? Force all to be redetected, if not yet */
 	if (weight_sum == 0) {
 	if (weight_sum == 0) {
-		/* No proxies? Force all to be redetected, if not yet */
-		if (was_forced)
+		if (was_forced) {
 			return NULL;
 			return NULL;
+		}
+
 		was_forced = 1;
 		was_forced = 1;
+
 		for(node=active_rtpp_set->rn_first; node!=NULL; node=node->rn_next) {
 		for(node=active_rtpp_set->rn_first; node!=NULL; node=node->rn_next) {
 			node->rn_disabled = rtpp_test(node, 1, 1);
 			node->rn_disabled = rtpp_test(node, 1, 1);
 		}
 		}
+
 		goto retry;
 		goto retry;
 	}
 	}
+
+	/* sumcut here lays from 0 to weight_sum-1 */
 	sumcut = sum % weight_sum;
 	sumcut = sum % weight_sum;
+
 	/*
 	/*
-	 * sumcut here lays from 0 to weight_sum-1.
 	 * Scan proxy list and decrease until appropriate proxy is found.
 	 * Scan proxy list and decrease until appropriate proxy is found.
 	 */
 	 */
 	for (node=active_rtpp_set->rn_first; node!=NULL; node=node->rn_next) {
 	for (node=active_rtpp_set->rn_first; node!=NULL; node=node->rn_next) {
+		/* Select only between enabled machines */
 		if (node->rn_disabled)
 		if (node->rn_disabled)
 			continue;
 			continue;
+
+		/* Found enabled machine */
 		if (sumcut < node->rn_weight)
 		if (sumcut < node->rn_weight)
 			goto found;
 			goto found;
+
+		/* Update sumcut if enabled machine */
 		sumcut -= node->rn_weight;
 		sumcut -= node->rn_weight;
 	}
 	}
+
 	/* No node list */
 	/* No node list */
 	return NULL;
 	return NULL;
+
 found:
 found:
 	if (do_test) {
 	if (do_test) {
 		node->rn_disabled = rtpp_test(node, node->rn_disabled, 0);
 		node->rn_disabled = rtpp_test(node, node->rn_disabled, 0);
 		if (node->rn_disabled)
 		if (node->rn_disabled)
 			goto retry;
 			goto retry;
 	}
 	}
+
+	/* build the entry */
+	struct rtpengine_hash_entry *entry = shm_malloc(sizeof(struct rtpengine_hash_entry));
+	if (!entry) {
+		LM_ERR("rtpengine hash table fail to create entry for calllen=%d callid=%.*s viabranch=%.*s\n",
+			callid.len, callid.len, callid.s, viabranch.len, viabranch.s);
+		return node;
+	}
+        memset(entry, 0, sizeof(struct rtpengine_hash_entry));
+
+	/* fill the entry */
+        if (callid.s && callid.len > 0) {
+		if (shm_str_dup(&entry->callid, &callid) < 0) {
+			LM_ERR("rtpengine hash table fail to duplicate calllen=%d callid=%.*s\n",
+				callid.len, callid.len, callid.s);
+			rtpengine_hash_table_free_entry(entry);
+			return node;
+		}
+	}
+        if (viabranch.s && viabranch.len > 0) {
+		if (shm_str_dup(&entry->viabranch, &viabranch) < 0) {
+			LM_ERR("rtpengine hash table fail to duplicate calllen=%d viabranch=%.*s\n",
+				callid.len, viabranch.len, viabranch.s);
+			rtpengine_hash_table_free_entry(entry);
+			return node;
+		}
+	}
+	entry->node = node;
+	entry->next = NULL;
+	entry->tout = get_ticks() + hash_table_tout;
+
+	/* insert the key<->entry from the hashtable */
+	if (!rtpengine_hash_table_insert(callid, viabranch, entry)) {
+		LM_ERR("rtpengine hash table fail to insert node=%.*s for calllen=%d callid=%.*s viabranch=%.*s\n",
+			node->rn_url.len, node->rn_url.s, callid.len, callid.len, callid.s, viabranch.len, viabranch.s);
+		rtpengine_hash_table_free_entry(entry);
+		return node;
+	} else {
+		LM_DBG("rtpengine hash table insert node=%.*s for calllen=%d callid=%.*s viabranch=%.*s\n",
+			node->rn_url.len, node->rn_url.s, callid.len, callid.len, callid.s, viabranch.len, viabranch.s);
+	}
+
+	/* return selected node */
 	return node;
 	return node;
 }
 }
 
 
+/*
+ * lookup the hastable (key=callid value=node) and get the old node (e.g. for answer/delete)
+ */
+static struct rtpp_node *
+select_rtpp_node_old(str callid, str viabranch, int do_test)
+{
+	struct rtpp_node *node = NULL;
+
+	node = rtpengine_hash_table_lookup(callid, viabranch);
+
+	if (!node) {
+		LM_NOTICE("rtpengine hash table lookup failed to find node for calllen=%d callid=%.*s viabranch=%.*s\n",
+			callid.len, callid.len, callid.s, viabranch.len, viabranch.s);
+		return NULL;
+	} else {
+		LM_DBG("rtpengine hash table lookup find node=%.*s for calllen=%d callid=%.*s viabranch=%.*s\n",
+			node->rn_url.len, node->rn_url.s, callid.len, callid.len, callid.s, viabranch.len, viabranch.s);
+	}
+
+	return node;
+}
+
+/*
+ * Main balancing routine. This DO try to keep the same proxy for
+ * the call if some proxies were disabled or enabled (e.g. kamctl command)
+ */
+static struct rtpp_node *
+select_rtpp_node(str callid, str viabranch, int do_test)
+{
+	struct rtpp_node *node = NULL;
+
+	if(!active_rtpp_set) {
+		LM_ERR("script error - no valid set selected\n");
+		return NULL;
+	}
+
+	// lookup node
+	node = select_rtpp_node_old(callid, viabranch, do_test);
+
+	// check node
+	if (!node) {
+		// run the selection algorithm
+		node = select_rtpp_node_new(callid, viabranch, do_test);
+
+		// check node
+		if (!node) {
+			LM_ERR("rtpengine failed to select new for calllen=%d callid=%.*s\n",
+				callid.len, callid.len, callid.s);
+			return NULL;
+		}
+	}
+
+	// if node enabled, return it
+	if (!node->rn_disabled) {
+		return node;
+	}
+
+	// if proper configuration and node manually or timeout disabled, return it
+	if (rtpengine_allow_op) {
+		if (node->rn_recheck_ticks == MI_MAX_RECHECK_TICKS) {
+			LM_DBG("node=%.*s for calllen=%d callid=%.*s is disabled(permanent) (probably still UP)! Return it\n",
+				node->rn_url.len, node->rn_url.s, callid.len, callid.len, callid.s);
+		} else {
+			LM_DBG("node=%.*s for calllen=%d callid=%.*s is disabled, either broke or timeout disabled! Return it\n",
+				node->rn_url.len, node->rn_url.s, callid.len, callid.len, callid.s);
+		}
+		return node;
+	}
+
+	return NULL;
+}
+
 static int
 static int
 get_extra_id(struct sip_msg* msg, str *id_str) {
 get_extra_id(struct sip_msg* msg, str *id_str) {
 	if(msg==NULL || extra_id_pv==NULL || id_str==NULL) {
 	if(msg==NULL || extra_id_pv==NULL || id_str==NULL) {
@@ -2682,7 +2883,8 @@ pv_get_rtpstat_f(struct sip_msg *msg, pv_param_t *param,
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_rtpstat_wrap, parms, 1);
 	return rtpengine_rtpp_set_wrap(msg, rtpengine_rtpstat_wrap, parms, 1);
 }
 }
 
 
-int set_rtp_inst_pvar(struct sip_msg *msg, const str * const uri) {
+static int
+set_rtp_inst_pvar(struct sip_msg *msg, const str * const uri) {
 	pv_value_t val;
 	pv_value_t val;
 
 
 	if (rtp_inst_pvar == NULL)
 	if (rtp_inst_pvar == NULL)

+ 0 - 1
modules/rtpengine/rtpengine.h

@@ -61,7 +61,6 @@ struct rtpp_set_head{
 struct rtpp_set *get_rtpp_set(int set_id);
 struct rtpp_set *get_rtpp_set(int set_id);
 int add_rtpengine_socks(struct rtpp_set * rtpp_list, char * rtpproxy);
 int add_rtpengine_socks(struct rtpp_set * rtpp_list, char * rtpproxy);
 
 
-int set_rtp_inst_pvar(struct sip_msg *msg, const str * const uri);
 
 
 int init_rtpproxy_db(void);
 int init_rtpproxy_db(void);
 
 

+ 544 - 0
modules/rtpengine/rtpengine_hash.c

@@ -0,0 +1,544 @@
+#include "rtpengine_hash.h"
+
+#include "../../str.h"
+#include "../../dprint.h"
+#include "../../mem/shm_mem.h"
+#include "../../locking.h"
+#include "../../timer.h"
+
+static void rtpengine_hash_table_free_row_lock(gen_lock_t *row_lock);
+
+
+static struct rtpengine_hash_table *rtpengine_hash_table;
+
+/* from sipwise rtpengine */
+static int str_cmp_str(const str a, const str b) {
+	if (a.len < b.len)
+		return -1;
+	if (a.len > b.len)
+		return 1;
+	if (a.len == 0 && b.len == 0)
+		return 0;
+	return memcmp(a.s, b.s, a.len);
+}
+
+/* from sipwise rtpengine */
+static int str_equal(str a, str b) {
+	return (str_cmp_str(a, b) == 0);
+}
+
+/* from sipwise rtpengine */
+static unsigned int str_hash(str s) {
+	unsigned int ret = 5381;
+	str it = s;
+
+	while (it.len > 0) {
+		ret = (ret << 5) + ret + *it.s;
+		it.s++;
+		it.len--;
+	}
+
+	return ret % rtpengine_hash_table->size;
+}
+
+/* rtpengine hash API */
+int rtpengine_hash_table_init(int size) {
+	int i;
+	int hash_table_size;
+
+	// init hash table size
+	if (size < 1) {
+		hash_table_size = 1;
+	} else {
+		hash_table_size = size;
+	}
+	LM_DBG("rtpengine_hash_table size = %d\n", hash_table_size);
+
+	// init hashtable
+	rtpengine_hash_table = shm_malloc(sizeof(struct rtpengine_hash_table));
+	if (!rtpengine_hash_table) {
+		LM_ERR("no shm left to create rtpengine_hash_table\n");
+		return 0;
+	}
+	memset(rtpengine_hash_table, 0, sizeof(struct rtpengine_hash_table));
+	rtpengine_hash_table->size = hash_table_size;
+
+	// init hashtable row_locks
+	rtpengine_hash_table->row_locks = shm_malloc(hash_table_size * sizeof(gen_lock_t*));
+	if (!rtpengine_hash_table->row_locks) {
+		LM_ERR("no shm left to create rtpengine_hash_table->row_locks\n");
+		rtpengine_hash_table_destroy();
+		return 0;
+	}
+	memset(rtpengine_hash_table->row_locks, 0, hash_table_size * sizeof(gen_lock_t*));
+
+	// init hashtable row_entry_list
+	rtpengine_hash_table->row_entry_list = shm_malloc(rtpengine_hash_table->size * sizeof(struct rtpengine_hash_entry*));
+	if (!rtpengine_hash_table->row_entry_list) {
+		LM_ERR("no shm left to create rtpengine_hash_table->row_entry_list\n");
+		rtpengine_hash_table_destroy();
+		return 0;
+	}
+	memset(rtpengine_hash_table->row_entry_list, 0, rtpengine_hash_table->size * sizeof(struct rtpengine_hash_entry*));
+
+	// init hashtable row_totals
+	rtpengine_hash_table->row_totals = shm_malloc(hash_table_size * sizeof(unsigned int));
+	if (!rtpengine_hash_table->row_totals) {
+		LM_ERR("no shm left to create rtpengine_hash_table->row_totals\n");
+		rtpengine_hash_table_destroy();
+		return 0;
+	}
+	memset(rtpengine_hash_table->row_totals, 0, hash_table_size * sizeof(unsigned int));
+
+	// init hashtable  row_locks[i], row_entry_list[i] and row_totals[i]
+	for (i = 0; i < hash_table_size; i++) {
+		// init hashtable row_locks[i]
+		rtpengine_hash_table->row_locks[i] = lock_alloc();
+		if (!rtpengine_hash_table->row_locks[i]) {
+			LM_ERR("no shm left to create rtpengine_hash_table->row_locks[%d]\n", i);
+			rtpengine_hash_table_destroy();
+			return 0;
+		}
+
+		// init hashtable row_entry_list[i]
+		rtpengine_hash_table->row_entry_list[i] = shm_malloc(sizeof(struct rtpengine_hash_entry));
+		if (!rtpengine_hash_table->row_entry_list[i]) {
+			LM_ERR("no shm left to create rtpengine_hash_table->row_entry_list[%d]\n", i);
+			rtpengine_hash_table_destroy();
+			return 0;
+		}
+		memset(rtpengine_hash_table->row_entry_list[i], 0, sizeof(struct rtpengine_hash_entry));
+
+		rtpengine_hash_table->row_entry_list[i]->tout = -1;
+		rtpengine_hash_table->row_entry_list[i]->next = NULL;
+
+		// init hashtable row_totals[i]
+		rtpengine_hash_table->row_totals[i] = 0;
+	}
+
+	return 1;
+}
+
+int rtpengine_hash_table_destroy() {
+	int i;
+
+	// check rtpengine hashtable
+	if (!rtpengine_hash_table) {
+		LM_ERR("NULL rtpengine_hash_table\n");
+		return 1;
+	}
+
+	// check rtpengine hashtable->row_locks
+	if (!rtpengine_hash_table->row_locks) {
+		LM_ERR("NULL rtpengine_hash_table->row_locks\n");
+		shm_free(rtpengine_hash_table);
+		rtpengine_hash_table = NULL;
+		return 1;
+	}
+
+	// destroy hashtable content
+	for (i = 0; i < rtpengine_hash_table->size; i++) {
+		// lock
+		if (!rtpengine_hash_table->row_locks[i]) {
+			LM_ERR("NULL rtpengine_hash_table->row_locks[%d]\n", i);
+			continue;
+		} else {
+			lock_get(rtpengine_hash_table->row_locks[i]);
+		}
+
+		// check rtpengine hashtable->row_entry_list
+		if (!rtpengine_hash_table->row_entry_list) {
+			LM_ERR("NULL rtpengine_hash_table->row_entry_list\n");
+		} else {
+			// destroy hashtable row_entry_list[i]
+			rtpengine_hash_table_free_row_entry_list(rtpengine_hash_table->row_entry_list[i]);
+			rtpengine_hash_table->row_entry_list[i] = NULL;
+		}
+
+		// unlock
+		lock_release(rtpengine_hash_table->row_locks[i]);
+
+		// destroy hashtable row_locks[i]
+		rtpengine_hash_table_free_row_lock(rtpengine_hash_table->row_locks[i]);
+		rtpengine_hash_table->row_locks[i] = NULL;
+	}
+
+	// destroy hashtable row_entry_list
+	if (!rtpengine_hash_table->row_entry_list) {
+		LM_ERR("NULL rtpengine_hash_table->row_entry_list\n");
+	} else {
+		shm_free(rtpengine_hash_table->row_entry_list);
+		rtpengine_hash_table->row_entry_list = NULL;
+	}
+
+	// destroy hashtable row_totals
+	if (!rtpengine_hash_table->row_totals) {
+		LM_ERR("NULL rtpengine_hash_table->row_totals\n");
+	} else {
+		shm_free(rtpengine_hash_table->row_totals);
+		rtpengine_hash_table->row_totals = NULL;
+	}
+
+	// destroy hashtable row_locks
+	if (!rtpengine_hash_table->row_locks) {
+		// should not be the case; just for code symmetry
+		LM_ERR("NULL rtpengine_hash_table->row_locks\n");
+	} else {
+		shm_free(rtpengine_hash_table->row_locks);
+		rtpengine_hash_table->row_locks = NULL;
+	}
+
+	// destroy hashtable
+	if (!rtpengine_hash_table) {
+		// should not be the case; just for code symmetry
+		LM_ERR("NULL rtpengine_hash_table\n");
+	} else {
+		shm_free(rtpengine_hash_table);
+		rtpengine_hash_table = NULL;
+	}
+
+	return 1;
+}
+
+int rtpengine_hash_table_insert(str callid, str viabranch, struct rtpengine_hash_entry *value) {
+	struct rtpengine_hash_entry *entry, *last_entry;
+	struct rtpengine_hash_entry *new_entry = (struct rtpengine_hash_entry *) value;
+	unsigned int hash_index;
+
+	// sanity checks
+	if (!rtpengine_hash_table_sanity_checks()) {
+		LM_ERR("sanity checks failed\n");
+		return 0;
+	}
+
+	// get entry list
+	hash_index = str_hash(callid);
+	entry = rtpengine_hash_table->row_entry_list[hash_index];
+	last_entry = entry;
+
+	// lock
+	if (rtpengine_hash_table->row_locks[hash_index]) {
+		lock_get(rtpengine_hash_table->row_locks[hash_index]);
+	} else {
+		LM_ERR("NULL rtpengine_hash_table->row_locks[%d]\n", hash_index);
+		return 0;
+	}
+
+	while (entry) {
+		// if found, don't add new entry
+		if (str_equal(entry->callid, new_entry->callid) &&
+		    str_equal(entry->viabranch, new_entry->viabranch)) {
+			// unlock
+			lock_release(rtpengine_hash_table->row_locks[hash_index]);
+			LM_NOTICE("callid=%.*s, viabranch=%.*s already in hashtable, ignore new value\n",
+				entry->callid.len, entry->callid.s,
+				entry->viabranch.len, entry->viabranch.s);
+			return 0;
+		}
+
+		// if expired entry discovered, delete it
+		if (entry->tout < get_ticks()) {
+			// set pointers; exclude entry
+			last_entry->next = entry->next;
+
+			// free current entry; entry points to unknown
+			rtpengine_hash_table_free_entry(entry);
+
+			// set pointers
+			entry = last_entry;
+
+			// update total
+			rtpengine_hash_table->row_totals[hash_index]--;
+		}
+
+		// next entry in the list
+		last_entry = entry;
+		entry = entry->next;
+	}
+
+	last_entry->next = new_entry;
+
+	// update total
+	rtpengine_hash_table->row_totals[hash_index]++;
+
+	// unlock
+	lock_release(rtpengine_hash_table->row_locks[hash_index]);
+
+	return 1;
+}
+
+int rtpengine_hash_table_remove(str callid, str viabranch) {
+	struct rtpengine_hash_entry *entry, *last_entry;
+	unsigned int hash_index;
+
+	// sanity checks
+	if (!rtpengine_hash_table_sanity_checks()) {
+		LM_ERR("sanity checks failed\n");
+		return 0;
+	}
+
+	// get first entry from entry list; jump over unused list head
+	hash_index = str_hash(callid);
+	entry = rtpengine_hash_table->row_entry_list[hash_index];
+	last_entry = entry;
+
+	// lock
+	if (rtpengine_hash_table->row_locks[hash_index]) {
+		lock_get(rtpengine_hash_table->row_locks[hash_index]);
+	} else {
+		LM_ERR("NULL rtpengine_hash_table->row_locks[%d]\n", hash_index);
+		return 0;
+	}
+
+	while (entry) {
+		// if callid found, delete entry
+		if (str_equal(entry->callid, callid) &&
+		    str_equal(entry->viabranch, viabranch)) {
+			// free entry
+			last_entry->next = entry->next;
+			rtpengine_hash_table_free_entry(entry);
+
+			// update total
+			rtpengine_hash_table->row_totals[hash_index]--;
+
+			// unlock
+			lock_release(rtpengine_hash_table->row_locks[hash_index]);
+
+			return 1;
+		}
+
+		// if expired entry discovered, delete it
+		if (entry->tout < get_ticks()) {
+			// set pointers; exclude entry
+			last_entry->next = entry->next;
+
+			// free current entry; entry points to unknown
+			rtpengine_hash_table_free_entry(entry);
+
+			// set pointers
+			entry = last_entry;
+
+			// update total
+			rtpengine_hash_table->row_totals[hash_index]--;
+		}
+
+		last_entry = entry;
+		entry = entry->next;
+	}
+
+	// unlock
+	lock_release(rtpengine_hash_table->row_locks[hash_index]);
+
+	return 0;
+}
+
+struct rtpp_node *rtpengine_hash_table_lookup(str callid, str viabranch) {
+	struct rtpengine_hash_entry *entry, *last_entry;
+	unsigned int hash_index;
+	struct rtpp_node *node;
+
+	// sanity checks
+	if (!rtpengine_hash_table_sanity_checks()) {
+		LM_ERR("sanity checks failed\n");
+		return 0;
+	}
+
+	// get first entry from entry list; jump over unused list head
+	hash_index = str_hash(callid);
+	entry = rtpengine_hash_table->row_entry_list[hash_index];
+	last_entry = entry;
+
+	// lock
+	if (rtpengine_hash_table->row_locks[hash_index]) {
+		lock_get(rtpengine_hash_table->row_locks[hash_index]);
+	} else {
+		LM_ERR("NULL rtpengine_hash_table->row_locks[%d]\n", hash_index);
+		return 0;
+	}
+
+	while (entry) {
+		// if callid found, return entry
+		if (str_equal(entry->callid, callid) &&
+		    str_equal(entry->viabranch, viabranch)) {
+			node = entry->node;
+
+			// unlock
+			lock_release(rtpengine_hash_table->row_locks[hash_index]);
+
+			return node;
+		}
+
+		// if expired entry discovered, delete it
+		if (entry->tout < get_ticks()) {
+			// set pointers; exclude entry
+			last_entry->next = entry->next;
+
+			// free current entry; entry points to unknown
+			rtpengine_hash_table_free_entry(entry);
+
+			// set pointers
+			entry = last_entry;
+
+			// update total
+			rtpengine_hash_table->row_totals[hash_index]--;
+		}
+
+		last_entry = entry;
+		entry = entry->next;
+	}
+
+	// unlock
+	lock_release(rtpengine_hash_table->row_locks[hash_index]);
+
+	return NULL;
+}
+
+// print hash table entries while deleting expired entries
+void rtpengine_hash_table_print() {
+	int i;
+	struct rtpengine_hash_entry *entry, *last_entry;
+
+	// sanity checks
+	if (!rtpengine_hash_table_sanity_checks()) {
+		LM_ERR("sanity checks failed\n");
+		return ;
+	}
+
+	// print hashtable
+	for (i = 0; i < rtpengine_hash_table->size; i++) {
+		// lock
+		if (rtpengine_hash_table->row_locks[i]) {
+			lock_get(rtpengine_hash_table->row_locks[i]);
+		} else {
+			LM_ERR("NULL rtpengine_hash_table->row_locks[%d]\n", i);
+			return ;
+		}
+
+		entry = rtpengine_hash_table->row_entry_list[i];
+		last_entry = entry;
+
+		while (entry) {
+			// if expired entry discovered, delete it
+			if (entry->tout < get_ticks()) {
+				// set pointers; exclude entry
+				last_entry->next = entry->next;
+
+				// free current entry; entry points to unknown
+				rtpengine_hash_table_free_entry(entry);
+
+				// set pointers
+				entry = last_entry;
+
+				// update total
+				rtpengine_hash_table->row_totals[i]--;
+			} else {
+				LM_DBG("hash_index=%d callid=%.*s tout=%u\n",
+					i, entry->callid.len, entry->callid.s, entry->tout - get_ticks());
+			}
+
+			last_entry = entry;
+			entry = entry->next;
+		}
+
+		// unlock
+		lock_release(rtpengine_hash_table->row_locks[i]);
+	}
+
+}
+
+unsigned int rtpengine_hash_table_total() {
+	int i;
+	unsigned int total = 0;
+
+	// sanity checks
+	if (!rtpengine_hash_table_sanity_checks()) {
+		LM_ERR("sanity checks failed\n");
+		return 0;
+	}
+
+	for (i = 0; i < rtpengine_hash_table->size; i++) {
+		total += rtpengine_hash_table->row_totals[i];
+	}
+
+	return total;
+}
+
+void rtpengine_hash_table_free_entry(struct rtpengine_hash_entry *entry) {
+	if (!entry) {
+		LM_ERR("try to free a NULL entry\n");
+		return ;
+	}
+
+	// free callid
+	if (entry->callid.s) {
+		shm_free(entry->callid.s);
+	}
+
+	// free viabranch
+	if (entry->viabranch.s) {
+		shm_free(entry->viabranch.s);
+	}
+
+	// free entry
+	shm_free(entry);
+
+	return ;
+}
+
+void rtpengine_hash_table_free_row_entry_list(struct rtpengine_hash_entry *row_entry_list) {
+	struct rtpengine_hash_entry *entry, *last_entry;
+
+	if (!row_entry_list) {
+		LM_ERR("try to free a NULL row_entry_list\n");
+		return ;
+	}
+
+	entry = row_entry_list;
+	while (entry) {
+		last_entry = entry;
+		entry = entry->next;
+		rtpengine_hash_table_free_entry(last_entry);
+		last_entry = NULL;
+	}
+
+	return ;
+}
+
+static void rtpengine_hash_table_free_row_lock(gen_lock_t *row_lock) {
+	if (!row_lock) {
+		LM_ERR("try to free a NULL lock\n");
+		return ;
+	}
+
+	lock_destroy(row_lock);
+
+	return ;
+}
+
+int rtpengine_hash_table_sanity_checks() {
+	// check rtpengine hashtable
+	if (!rtpengine_hash_table) {
+		LM_ERR("NULL rtpengine_hash_table\n");
+		return 0;
+	}
+
+	// check rtpengine hashtable->row_locks
+	if (!rtpengine_hash_table->row_locks) {
+		LM_ERR("NULL rtpengine_hash_table->row_locks\n");
+		return 0;
+	}
+
+	// check rtpengine hashtable->row_entry_list
+	if (!rtpengine_hash_table->row_entry_list) {
+		LM_ERR("NULL rtpengine_hash_table->row_entry_list\n");
+		return 0;
+	}
+
+	// check rtpengine hashtable->row_totals
+	if (!rtpengine_hash_table->row_totals) {
+		LM_ERR("NULL rtpengine_hash_table->row_totals\n");
+		return 0;
+	}
+
+	return 1;
+}

+ 40 - 0
modules/rtpengine/rtpengine_hash.h

@@ -0,0 +1,40 @@
+#ifndef _RTPENGINE_HASH_H
+#define _RTPENGINE_HASH_H
+
+#include "../../str.h"
+#include "../../locking.h"
+
+
+/* table entry */
+struct rtpengine_hash_entry {
+	str callid;				// call callid
+	str viabranch;				// call viabranch
+	struct rtpp_node *node;			// call selected node
+
+	unsigned int tout;			// call timeout
+	struct rtpengine_hash_entry *next;	// call next
+};
+
+/* table */
+struct rtpengine_hash_table {
+	struct rtpengine_hash_entry **row_entry_list;	// vector of size pointers to entry
+	gen_lock_t **row_locks;				// vector of size pointers to locks
+	unsigned int *row_totals;			// vector of size numbers of entries in the hashtable rows
+	unsigned int size;				// hash table size
+};
+
+
+int rtpengine_hash_table_init(int size);
+int rtpengine_hash_table_destroy();
+int rtpengine_hash_table_insert(str callid, str viabranch, struct rtpengine_hash_entry *value);
+int rtpengine_hash_table_remove(str callid, str viabranch);
+struct rtpp_node *rtpengine_hash_table_lookup(str callid, str viabranch);
+void rtpengine_hash_table_print();
+unsigned int rtpengine_hash_table_total();
+
+void rtpengine_hash_table_free_entry(struct rtpengine_hash_entry *entry);
+void rtpengine_hash_table_free_row_entry_list(struct rtpengine_hash_entry *row_entry_list);
+
+int rtpengine_hash_table_sanity_checks();
+
+#endif

+ 27 - 7
modules/siptrace/README

@@ -16,9 +16,9 @@ Daniel-Constantin Mierla
 
 
    <[email protected]>
    <[email protected]>
 
 
-   Copyright (c) 2010 asipto.com
+   Copyright © 2010 asipto.com
 
 
-   Copyright (c) 2006 voice-system.ro
+   Copyright © 2006 voice-system.ro
      __________________________________________________________________
      __________________________________________________________________
 
 
    Table of Contents
    Table of Contents
@@ -50,6 +50,7 @@ Daniel-Constantin Mierla
               3.15. hep_capture_id (integer)
               3.15. hep_capture_id (integer)
               3.16. trace_delayed (integer)
               3.16. trace_delayed (integer)
               3.17. force_send_sock (str)
               3.17. force_send_sock (str)
+              3.18. trace_mode (integer)
 
 
         4. Functions
         4. Functions
 
 
@@ -85,7 +86,8 @@ Daniel-Constantin Mierla
    1.15. Set hep_capture_id parameter
    1.15. Set hep_capture_id parameter
    1.16. Set trace_delayed parameter
    1.16. Set trace_delayed parameter
    1.17. Set force_send_sock parameter
    1.17. Set force_send_sock parameter
-   1.18. sip_trace() usage
+   1.18. Set trace_mode parameter
+   1.19. sip_trace() usage
 
 
 Chapter 1. Admin Guide
 Chapter 1. Admin Guide
 
 
@@ -116,6 +118,7 @@ Chapter 1. Admin Guide
         3.15. hep_capture_id (integer)
         3.15. hep_capture_id (integer)
         3.16. trace_delayed (integer)
         3.16. trace_delayed (integer)
         3.17. force_send_sock (str)
         3.17. force_send_sock (str)
+        3.18. trace_mode (integer)
 
 
    4. Functions
    4. Functions
 
 
@@ -190,6 +193,7 @@ Chapter 1. Admin Guide
    3.15. hep_capture_id (integer)
    3.15. hep_capture_id (integer)
    3.16. trace_delayed (integer)
    3.16. trace_delayed (integer)
    3.17. force_send_sock (str)
    3.17. force_send_sock (str)
+   3.18. trace_mode (integer)
 
 
 3.1. db_url (str)
 3.1. db_url (str)
 
 
@@ -420,11 +424,27 @@ modparam("siptrace", "trace_delayed", 1)
 modparam("siptrace", "force_send_sock", "sip:10.1.1.2:5000")
 modparam("siptrace", "force_send_sock", "sip:10.1.1.2:5000")
 ...
 ...
 
 
+3.18. trace_mode (integer)
+
+   If set to 1, the module uses core events triggered when receiving or
+   sending SIP traffic to mirror traffic to a SIP capture server using
+   HEP. It will automatically do the mirroring of all traffic, no need to
+   set the siptrace flag per request.
+
+   If set to 0, no automatic mirroring of SIP traffic via HEP.
+
+   Default value is 0.
+
+   Example 1.18. Set trace_mode parameter
+...
+modparam("siptrace", "trace_mode", 1)
+...
+
 4. Functions
 4. Functions
 
 
    4.1. sip_trace([address])
    4.1. sip_trace([address])
 
 
-4.1.  sip_trace([address])
+4.1. sip_trace([address])
 
 
    Store or forward the current processed SIP message in database. It is
    Store or forward the current processed SIP message in database. It is
    stored in the form prior applying changes made to it.
    stored in the form prior applying changes made to it.
@@ -438,7 +458,7 @@ modparam("siptrace", "force_send_sock", "sip:10.1.1.2:5000")
    ONREPLY_ROUTE, BRANCH_ROUTE.
    ONREPLY_ROUTE, BRANCH_ROUTE.
    Default value is "NULL".
    Default value is "NULL".
 
 
-   Example 1.18. sip_trace() usage
+   Example 1.19. sip_trace() usage
 ...
 ...
 sip_trace();
 sip_trace();
 ...
 ...
@@ -449,7 +469,7 @@ sip_trace("sip:10.1.1.2:5085");
 
 
    5.1. sip_trace
    5.1. sip_trace
 
 
-5.1.  sip_trace
+5.1. sip_trace
 
 
    Name: sip_trace
    Name: sip_trace
 
 
@@ -470,7 +490,7 @@ sip_trace("sip:10.1.1.2:5085");
 
 
    6.1. siptrace.status param
    6.1. siptrace.status param
 
 
-6.1.  siptrace.status param
+6.1. siptrace.status param
 
 
    Name: siptrace.status
    Name: siptrace.status
 
 

+ 24 - 0
modules/siptrace/doc/siptrace_admin.xml

@@ -466,6 +466,30 @@ modparam("siptrace", "trace_delayed", 1)
 ...
 ...
 modparam("siptrace", "force_send_sock", "sip:10.1.1.2:5000")
 modparam("siptrace", "force_send_sock", "sip:10.1.1.2:5000")
 ...
 ...
+</programlisting>
+                </example>
+        </section>
+	<section id="siptrace.p.trace_mode">
+                <title><varname>trace_mode</varname> (integer)</title>
+                <para>
+				If set to 1, the module uses core events triggered when receiving
+				or sending SIP traffic to mirror traffic to a SIP capture server
+				using HEP. It will automatically do the mirroring of all traffic,
+				no need to set the siptrace flag per request.
+                </para>
+                <para>
+                If set to 0, no automatic mirroring of SIP traffic via HEP.
+                </para>
+                <para>
+                Default value is <emphasis>0</emphasis>.
+                </para>
+                <example>
+                <title>Set <varname>trace_mode</varname>
+                parameter</title>
+                <programlisting format="linespecific">
+...
+modparam("siptrace", "trace_mode", 1)
+...
 </programlisting>
 </programlisting>
                 </example>
                 </example>
         </section>
         </section>

+ 225 - 128
modules/siptrace/siptrace.c

@@ -50,6 +50,7 @@
 #include "../../modules/sl/sl.h"
 #include "../../modules/sl/sl.h"
 #include "../../str.h"
 #include "../../str.h"
 #include "../../onsend.h"
 #include "../../onsend.h"
+#include "../../events.h"
 
 
 #include "../../modules/sipcapture/hep.h"
 #include "../../modules/sipcapture/hep.h"
 
 
@@ -106,6 +107,10 @@ static void trace_sl_ack_in(sl_cbp_t *slcb);
 static int trace_send_hep_duplicate(str *body, str *from, str *to, struct dest_info*);
 static int trace_send_hep_duplicate(str *body, str *from, str *to, struct dest_info*);
 static int pipport2su (char *pipport, union sockaddr_union *tmp_su, unsigned int *proto);
 static int pipport2su (char *pipport, union sockaddr_union *tmp_su, unsigned int *proto);
 
 
+int siptrace_net_data_recv(void *data);
+int siptrace_net_data_send(void *data);
+static int _siptrace_mode = 0;
+
 
 
 static struct mi_root* sip_trace_mi(struct mi_root* cmd, void* param );
 static struct mi_root* sip_trace_mi(struct mi_root* cmd, void* param );
 
 
@@ -174,7 +179,7 @@ db_func_t db_funcs;      		/*!< Database functions */
  */
  */
 static cmd_export_t cmds[] = {
 static cmd_export_t cmds[] = {
 	{"sip_trace", (cmd_function)sip_trace, 0, 0, 0, ANY_ROUTE},
 	{"sip_trace", (cmd_function)sip_trace, 0, 0, 0, ANY_ROUTE},
-    {"sip_trace", (cmd_function)sip_trace, 1, fixup_siptrace, 0, ANY_ROUTE},
+	{"sip_trace", (cmd_function)sip_trace, 1, fixup_siptrace, 0, ANY_ROUTE},
 	{0, 0, 0, 0, 0, 0}
 	{0, 0, 0, 0, 0, 0}
 };
 };
 
 
@@ -207,10 +212,11 @@ static param_export_t params[] = {
 	{"xheaders_write",     INT_PARAM, &xheaders_write       },
 	{"xheaders_write",     INT_PARAM, &xheaders_write       },
 	{"xheaders_read",      INT_PARAM, &xheaders_read        },
 	{"xheaders_read",      INT_PARAM, &xheaders_read        },
 	{"hep_mode_on",        INT_PARAM, &hep_mode_on          },	 
 	{"hep_mode_on",        INT_PARAM, &hep_mode_on          },	 
-    {"force_send_sock",    PARAM_STR, &force_send_sock_str	},
+	{"force_send_sock",    PARAM_STR, &force_send_sock_str	},
 	{"hep_version",        INT_PARAM, &hep_version          },
 	{"hep_version",        INT_PARAM, &hep_version          },
 	{"hep_capture_id",     INT_PARAM, &hep_capture_id       },	        
 	{"hep_capture_id",     INT_PARAM, &hep_capture_id       },	        
 	{"trace_delayed",      INT_PARAM, &trace_delayed        },
 	{"trace_delayed",      INT_PARAM, &trace_delayed        },
+	{"trace_mode",         PARAM_INT, &_siptrace_mode       },
 	{0, 0, 0}
 	{0, 0, 0}
 };
 };
 
 
@@ -297,8 +303,8 @@ static int mod_init(void)
 	*trace_to_database_flag = trace_to_database;
 	*trace_to_database_flag = trace_to_database;
 
 
 	if(hep_version != 1 && hep_version != 2) {
 	if(hep_version != 1 && hep_version != 2) {
-	    LM_ERR("unsupported version of HEP");
-	    return -1;
+		LM_ERR("unsupported version of HEP");
+		return -1;
 	}
 	}
 
 
 	/* Find a database module if needed */
 	/* Find a database module if needed */
@@ -316,12 +322,11 @@ static int mod_init(void)
 		}
 		}
 	}
 	}
 
 
-        if(hep_version != 1 && hep_version != 2) {
-  
-                  LM_ERR("unsupported version of HEP");
-                  return -1;
-        }                                          
+	if(hep_version != 1 && hep_version != 2) {
 
 
+		LM_ERR("unsupported version of HEP");
+		return -1;
+	}
 
 
 	trace_on_flag = (int*)shm_malloc(sizeof(int));
 	trace_on_flag = (int*)shm_malloc(sizeof(int));
 	if(trace_on_flag==NULL) {
 	if(trace_on_flag==NULL) {
@@ -391,19 +396,19 @@ static int mod_init(void)
 
 
 	if(force_send_sock_str.s!=0)
 	if(force_send_sock_str.s!=0)
 	{
 	{
-	    force_send_sock_str.len = strlen(force_send_sock_str.s);
-	    force_send_sock_uri = (struct sip_uri *)pkg_malloc(sizeof(struct sip_uri));
-	    if(force_send_sock_uri==0)
-	    {
-	        LM_ERR("no more pkg memory left\n");
-	        return -1;
-	    }
-	    memset(force_send_sock_uri, 0, sizeof(struct sip_uri));
-	    if(parse_uri(force_send_sock_str.s, force_send_sock_str.len, force_send_sock_uri)<0)
-	    {
-	        LM_ERR("bad dup uri\n");
-	        return -1;
-	    }
+		force_send_sock_str.len = strlen(force_send_sock_str.s);
+		force_send_sock_uri = (struct sip_uri *)pkg_malloc(sizeof(struct sip_uri));
+		if(force_send_sock_uri==0)
+		{
+			LM_ERR("no more pkg memory left\n");
+			return -1;
+		}
+		memset(force_send_sock_uri, 0, sizeof(struct sip_uri));
+		if(parse_uri(force_send_sock_str.s, force_send_sock_str.len, force_send_sock_uri)<0)
+		{
+			LM_ERR("bad dup uri\n");
+			return -1;
+		}
 	}
 	}
 
 
 	if(traced_user_avp_str.s && traced_user_avp_str.len > 0)
 	if(traced_user_avp_str.s && traced_user_avp_str.len > 0)
@@ -449,6 +454,10 @@ static int mod_init(void)
 		trace_table_avp_type = 0;
 		trace_table_avp_type = 0;
 	}
 	}
 
 
+	if(_siptrace_mode==1) {
+		sr_event_register_cb(SREV_NET_DATA_RECV, siptrace_net_data_recv);
+		sr_event_register_cb(SREV_NET_DATA_SEND, siptrace_net_data_send);
+	}
 	return 0;
 	return 0;
 }
 }
 
 
@@ -466,7 +475,7 @@ static int child_init(int rank)
 			return -1;
 			return -1;
 		}
 		}
 		if (db_check_table_version(&db_funcs, db_con, &siptrace_table,
 		if (db_check_table_version(&db_funcs, db_con, &siptrace_table,
-					   SIP_TRACE_TABLE_VERSION) < 0) {
+					SIP_TRACE_TABLE_VERSION) < 0) {
 			LM_ERR("error during table version check\n");
 			LM_ERR("error during table version check\n");
 			db_funcs.close(db_con);		
 			db_funcs.close(db_con);		
 			return -1;
 			return -1;
@@ -752,7 +761,7 @@ static int sip_trace_store(struct _siptrace_data *sto, struct dest_info *dst)
 		return -1;
 		return -1;
 
 
 	if(hep_mode_on) trace_send_hep_duplicate(&sto->body, &sto->fromip, &sto->toip, dst);
 	if(hep_mode_on) trace_send_hep_duplicate(&sto->body, &sto->fromip, &sto->toip, dst);
-    else trace_send_duplicate(sto->body.s, sto->body.len, dst);
+	else trace_send_duplicate(sto->body.s, sto->body.len, dst);
 
 
 	if (sip_trace_xheaders_free(sto) != 0)
 	if (sip_trace_xheaders_free(sto) != 0)
 		return -1;
 		return -1;
@@ -961,14 +970,14 @@ static int sip_trace(struct sip_msg *msg, struct dest_info * dst, char *dir)
 	struct onsend_info *snd_inf = NULL;
 	struct onsend_info *snd_inf = NULL;
 
 
 	if (dst){
 	if (dst){
-	    if (dst->send_sock == 0){
-	        dst->send_sock=get_send_socket(0, &dst->to, dst->proto);
-	        if (dst->send_sock==0){
-	            LM_ERR("can't forward to af %d, proto %d no corresponding"
-	                    " listening socket\n", dst->to.s.sa_family, dst->proto);
-	            return -1;
-	        }
-	    }
+		if (dst->send_sock == 0){
+			dst->send_sock=get_send_socket(0, &dst->to, dst->proto);
+			if (dst->send_sock==0){
+				LM_ERR("can't forward to af %d, proto %d no corresponding"
+						" listening socket\n", dst->to.s.sa_family, dst->proto);
+				return -1;
+			}
+		}
 	}
 	}
 
 
 	if(msg==NULL) {
 	if(msg==NULL) {
@@ -1584,23 +1593,23 @@ static int trace_send_duplicate(char *buf, int len, struct dest_info *dst2)
 	}
 	}
 
 
 	if (!dst2){
 	if (!dst2){
-	    init_dest_info(&dst);
-	    /* create a temporary proxy*/
-	    dst.proto = PROTO_UDP;
-	    p=mk_proxy(&dup_uri->host, (dup_uri->port_no)?dup_uri->port_no:SIP_PORT,
-	             dst.proto);
-	    if (p==0){
-	        LM_ERR("bad host name in uri\n");
-	        return -1;
-	    }
-	    hostent2su(&dst.to, &p->host, p->addr_idx, (p->port)?p->port:SIP_PORT);
-
-	    dst.send_sock=get_send_socket(0, &dst.to, dst.proto);
-	    if (dst.send_sock==0){
-	        LM_ERR("can't forward to af %d, proto %d no corresponding"
-	                " listening socket\n", dst.to.s.sa_family, dst.proto);
-	        goto error;
-	    }
+		init_dest_info(&dst);
+		/* create a temporary proxy*/
+		dst.proto = PROTO_UDP;
+		p=mk_proxy(&dup_uri->host, (dup_uri->port_no)?dup_uri->port_no:SIP_PORT,
+				dst.proto);
+		if (p==0){
+			LM_ERR("bad host name in uri\n");
+			return -1;
+		}
+		hostent2su(&dst.to, &p->host, p->addr_idx, (p->port)?p->port:SIP_PORT);
+
+		dst.send_sock=get_send_socket(0, &dst.to, dst.proto);
+		if (dst.send_sock==0){
+			LM_ERR("can't forward to af %d, proto %d no corresponding"
+					" listening socket\n", dst.to.s.sa_family, dst.proto);
+			goto error;
+		}
 	}
 	}
 
 
 	if (msg_send((dst2)?dst2:&dst, buf, len)<0)
 	if (msg_send((dst2)?dst2:&dst, buf, len)<0)
@@ -1610,15 +1619,15 @@ static int trace_send_duplicate(char *buf, int len, struct dest_info *dst2)
 	}
 	}
 
 
 	if (p){
 	if (p){
-	    free_proxy(p); /* frees only p content, not p itself */
-	    pkg_free(p);
+		free_proxy(p); /* frees only p content, not p itself */
+		pkg_free(p);
 	}
 	}
 	return 0;
 	return 0;
 error:
 error:
-    if (p){
-	free_proxy(p); /* frees only p content, not p itself */
-	pkg_free(p);
-    }
+	if (p){
+		free_proxy(p); /* frees only p content, not p itself */
+		pkg_free(p);
+	}
 	return -1;
 	return -1;
 }
 }
 
 
@@ -1637,7 +1646,7 @@ static int trace_send_hep_duplicate(str *body, str *from, str *to, struct dest_i
 	struct hep_timehdr hep_time;
 	struct hep_timehdr hep_time;
 	struct timeval tvb;
 	struct timeval tvb;
 	struct timezone tz;
 	struct timezone tz;
-	                 
+
 	struct hep_ip6hdr hep_ip6header;
 	struct hep_ip6hdr hep_ip6header;
 
 
 	if(body->s==NULL || body->len <= 0)
 	if(body->s==NULL || body->len <= 0)
@@ -1647,8 +1656,8 @@ static int trace_send_hep_duplicate(str *body, str *from, str *to, struct dest_i
 		return 0;
 		return 0;
 
 
 
 
-        gettimeofday( &tvb, &tz );
-        
+	gettimeofday( &tvb, &tz );
+
 
 
 	/* message length */
 	/* message length */
 	len = body->len 
 	len = body->len 
@@ -1667,50 +1676,50 @@ static int trace_send_hep_duplicate(str *body, str *from, str *to, struct dest_i
 
 
 	/* check if from and to are in the same family*/
 	/* check if from and to are in the same family*/
 	if(from_su.s.sa_family != to_su.s.sa_family) {
 	if(from_su.s.sa_family != to_su.s.sa_family) {
-		LOG(L_ERR, "ERROR: trace_send_hep_duplicate: interworking detected ?\n");
+		LM_ERR("interworking detected ?\n");
 		goto error;
 		goto error;
 	}
 	}
 
 
-    if (!dst2){
-	init_dest_info(&dst);
-	/* create a temporary proxy*/
-	dst.proto = PROTO_UDP;
-	p=mk_proxy(&dup_uri->host, (dup_uri->port_no)?dup_uri->port_no:SIP_PORT,
-			dst.proto);
-	if (p==0)
-	{
-		LM_ERR("bad host name in uri\n");
-		goto error;
+	if (!dst2){
+		init_dest_info(&dst);
+		/* create a temporary proxy*/
+		dst.proto = PROTO_UDP;
+		p=mk_proxy(&dup_uri->host, (dup_uri->port_no)?dup_uri->port_no:SIP_PORT,
+				dst.proto);
+		if (p==0)
+		{
+			LM_ERR("bad host name in uri\n");
+			goto error;
+		}
+
+		hostent2su(&dst.to, &p->host, p->addr_idx, (p->port)?p->port:SIP_PORT);
+		LM_DBG("setting up the socket_info\n");
+		dst_fin = &dst;
+	} else {
+		dst_fin = dst2;
+	}
+
+	if (force_send_sock_str.s) {
+		LM_DBG("force_send_sock activated, grep for the sock_info\n");
+		si = grep_sock_info(&force_send_sock_uri->host,
+				(force_send_sock_uri->port_no)?force_send_sock_uri->port_no:SIP_PORT,
+				PROTO_UDP);
+		if (!si) {
+			LM_WARN("cannot grep socket info\n");
+		} else {
+			LM_DBG("found socket while grep: [%.*s] [%.*s]\n", si->name.len, si->name.s, si->address_str.len, si->address_str.s);
+			dst_fin->send_sock = si;
+		}
 	}
 	}
 
 
-	hostent2su(&dst.to, &p->host, p->addr_idx, (p->port)?p->port:SIP_PORT);
-	LM_DBG("setting up the socket_info\n");
-	dst_fin = &dst;
-    } else {
-        dst_fin = dst2;
-    }
-
-    if (force_send_sock_str.s) {
-        LM_DBG("force_send_sock activated, grep for the sock_info\n");
-        si = grep_sock_info(&force_send_sock_uri->host,
-                (force_send_sock_uri->port_no)?force_send_sock_uri->port_no:SIP_PORT,
-                PROTO_UDP);
-        if (!si) {
-             LM_WARN("cannot grep socket info\n");
-        } else {
-            LM_DBG("found socket while grep: [%.*s] [%.*s]\n", si->name.len, si->name.s, si->address_str.len, si->address_str.s);
-            dst_fin->send_sock = si;
-        }
-    }
-
-    if (dst_fin->send_sock == 0) {
-        dst_fin->send_sock=get_send_socket(0, &dst_fin->to, dst_fin->proto);
-        if (dst_fin->send_sock == 0) {
-            LM_ERR("can't forward to af %d, proto %d no corresponding"
-                    " listening socket\n", dst_fin->to.s.sa_family, dst_fin->proto);
-            goto error;
-        }
-    }
+	if (dst_fin->send_sock == 0) {
+		dst_fin->send_sock=get_send_socket(0, &dst_fin->to, dst_fin->proto);
+		if (dst_fin->send_sock == 0) {
+			LM_ERR("can't forward to af %d, proto %d no corresponding"
+					" listening socket\n", dst_fin->to.s.sa_family, dst_fin->proto);
+			goto error;
+		}
+	}
 
 
 	/* Version && proto && length */
 	/* Version && proto && length */
 	hdr.hp_l = sizeof(struct hep_hdr);
 	hdr.hp_l = sizeof(struct hep_hdr);
@@ -1744,7 +1753,7 @@ static int trace_send_hep_duplicate(str *body, str *from, str *to, struct dest_i
 		len = sizeof(struct hep_ip6hdr);
 		len = sizeof(struct hep_ip6hdr);
 	}
 	}
 	else {
 	else {
-		LOG(L_ERR, "ERROR: trace_send_hep_duplicate: Unsupported protocol family\n");
+		LM_ERR("Unsupported protocol family\n");
 		goto error;;
 		goto error;;
 	}
 	}
 
 
@@ -1755,7 +1764,7 @@ static int trace_send_hep_duplicate(str *body, str *from, str *to, struct dest_i
 	len += sizeof(struct hep_hdr) + body->len;
 	len += sizeof(struct hep_hdr) + body->len;
 	buffer = (void *)pkg_malloc(len+1);
 	buffer = (void *)pkg_malloc(len+1);
 	if (buffer==0){
 	if (buffer==0){
-		LOG(L_ERR, "ERROR: trace_send_hep_duplicate: out of memory\n");
+		LM_ERR("out of memory\n");
 		goto error;
 		goto error;
 	}
 	}
 
 
@@ -1778,28 +1787,28 @@ static int trace_send_hep_duplicate(str *body, str *from, str *to, struct dest_i
 
 
 	if(hep_version == 2) {
 	if(hep_version == 2) {
 
 
-                hep_time.tv_sec = tvb.tv_sec;
-                hep_time.tv_usec = tvb.tv_usec;
-                hep_time.captid = hep_capture_id;
+		hep_time.tv_sec = tvb.tv_sec;
+		hep_time.tv_usec = tvb.tv_usec;
+		hep_time.captid = hep_capture_id;
 
 
-                memcpy((void*)buffer+buflen, &hep_time, sizeof(struct hep_timehdr));
-                buflen += sizeof(struct hep_timehdr);
-        }
+		memcpy((void*)buffer+buflen, &hep_time, sizeof(struct hep_timehdr));
+		buflen += sizeof(struct hep_timehdr);
+	}
 
 
 	/* PAYLOAD */
 	/* PAYLOAD */
 	memcpy((void*)(buffer + buflen) , (void*)body->s, body->len);
 	memcpy((void*)(buffer + buflen) , (void*)body->s, body->len);
 	buflen +=body->len;
 	buflen +=body->len;
 
 
-	if (msg_send(dst_fin, buffer, buflen)<0)
+	if (msg_send_buffer(dst_fin, buffer, buflen, 1)<0)
 	{
 	{
 		LM_ERR("cannot send hep duplicate message\n");
 		LM_ERR("cannot send hep duplicate message\n");
 		goto error;
 		goto error;
 	}
 	}
 
 
-    if (p) {
-	free_proxy(p); /* frees only p content, not p itself */
-	pkg_free(p);
-    }
+	if (p) {
+		free_proxy(p); /* frees only p content, not p itself */
+		pkg_free(p);
+	}
 	pkg_free(buffer);
 	pkg_free(buffer);
 	return 0;
 	return 0;
 error:
 error:
@@ -1843,14 +1852,14 @@ static int pipport2su (char *pipport, union sockaddr_union *tmp_su, unsigned int
 		LM_ERR("bad protocol %s\n", pipport);
 		LM_ERR("bad protocol %s\n", pipport);
 		return -1;
 		return -1;
 	}
 	}
-	
+
 	if((len = strlen(pipport)) >= 256) {
 	if((len = strlen(pipport)) >= 256) {
 		LM_ERR("too big pipport\n");
 		LM_ERR("too big pipport\n");
 		goto error;
 		goto error;
 	}
 	}
 
 
 	/* our tmp string */
 	/* our tmp string */
-        strncpy(tmp_piport, pipport, len+1);
+	strncpy(tmp_piport, pipport, len+1);
 
 
 	len = 0;
 	len = 0;
 
 
@@ -1867,19 +1876,19 @@ static int pipport2su (char *pipport, union sockaddr_union *tmp_su, unsigned int
 		port_no = 0;
 		port_no = 0;
 	}
 	}
 	else {
 	else {
-        	/*the address contains a port number*/
-        	*p = '\0';
-        	p++;
-        	port_str.s = p;
-        	port_str.len = strlen(p);
-        	LM_DBG("the port string is %s\n", p);
-        	if(str2int(&port_str, &port_no) != 0 ) {
-	        	LM_ERR("there is not a valid number port\n");
-	        	goto error;
-        	}
-        	*p = '\0';
-        }
-        
+		/*the address contains a port number*/
+		*p = '\0';
+		p++;
+		port_str.s = p;
+		port_str.len = strlen(p);
+		LM_DBG("the port string is %s\n", p);
+		if(str2int(&port_str, &port_no) != 0 ) {
+			LM_ERR("there is not a valid number port\n");
+			goto error;
+		}
+		*p = '\0';
+	}
+
 	/* now IPv6 address has no brakets. It should be fixed! */
 	/* now IPv6 address has no brakets. It should be fixed! */
 	if (host_s[0] == '[') {
 	if (host_s[0] == '[') {
 		len = strlen(host_s + 1) - 1;
 		len = strlen(host_s + 1) - 1;
@@ -1907,6 +1916,95 @@ error:
 	return -1;
 	return -1;
 }
 }
 
 
+
+/**
+ *
+ */
+int siptrace_net_data_recv(void *data)
+{
+	sr_net_info_t *nd;
+	struct _siptrace_data sto;
+
+	if(data==0)
+		return -1;
+
+	nd = (sr_net_info_t*)data;
+	if(nd->rcv==NULL || nd->data.s==NULL || nd->data.len<=0)
+		return -1;
+
+	memset(&sto, 0, sizeof(struct _siptrace_data));
+
+	sto.body.s   = nd->data.s;
+	sto.body.len = nd->data.len;
+
+	siptrace_copy_proto(nd->rcv->proto, sto.fromip_buff);
+	strcat(sto.fromip_buff, ip_addr2a(&nd->rcv->src_ip));
+	strcat(sto.fromip_buff,":");
+	strcat(sto.fromip_buff, int2str(nd->rcv->src_port, NULL));
+	sto.fromip.s = sto.fromip_buff;
+	sto.fromip.len = strlen(sto.fromip_buff);
+
+	siptrace_copy_proto(nd->rcv->proto, sto.toip_buff);
+	strcat(sto.toip_buff, ip_addr2a(&nd->rcv->dst_ip));
+	strcat(sto.toip_buff,":");
+	strcat(sto.toip_buff, int2str(nd->rcv->dst_port, NULL));
+	sto.toip.s = sto.toip_buff;
+	sto.toip.len = strlen(sto.toip_buff);
+
+	sto.dir = "in";
+
+	trace_send_hep_duplicate(&sto.body, &sto.fromip, &sto.toip, NULL);
+	return 0;
+
+}
+
+/**
+ *
+ */
+int siptrace_net_data_send(void *data)
+{
+	sr_net_info_t *nd;
+	struct dest_info new_dst;
+	struct _siptrace_data sto;
+
+	if(data==0)
+		return -1;
+
+	nd = (sr_net_info_t*)data;
+	if(nd->dst==NULL || nd->data.s==NULL || nd->data.len<=0)
+		return -1;
+
+	new_dst=*nd->dst;
+	new_dst.send_sock=get_send_socket(0, &nd->dst->to, nd->dst->proto);
+
+	memset(&sto, 0, sizeof(struct _siptrace_data));
+
+	sto.body.s   = nd->data.s;
+	sto.body.len = nd->data.len;
+
+	if (unlikely(new_dst.send_sock==0)) {
+		LM_WARN("no sending socket found\n");
+		strcpy(sto.fromip_buff, "any:255.255.255.255:5060");
+	} else {
+		strncpy(sto.fromip_buff, new_dst.send_sock->sock_str.s,
+			new_dst.send_sock->sock_str.len);
+	}
+	sto.fromip.s = sto.fromip_buff;
+	sto.fromip.len = strlen(sto.fromip_buff);
+
+	siptrace_copy_proto(new_dst.send_sock->proto, sto.toip_buff);
+	strcat(sto.toip_buff, suip2a(&new_dst.to, sizeof(new_dst.to)));
+	strcat(sto.toip_buff,":");
+	strcat(sto.toip_buff, int2str((int)su_getport(&new_dst.to), NULL));
+	sto.toip.s = sto.toip_buff;
+	sto.toip.len = strlen(sto.toip_buff);
+
+	sto.dir = "out";
+
+	trace_send_hep_duplicate(&sto.body, &sto.fromip, &sto.toip, NULL);
+	return 0;
+}
+
 static void siptrace_rpc_status (rpc_t* rpc, void* c) {
 static void siptrace_rpc_status (rpc_t* rpc, void* c) {
 	str status = {0, 0};
 	str status = {0, 0};
 
 
@@ -1939,8 +2037,8 @@ static void siptrace_rpc_status (rpc_t* rpc, void* c) {
 }
 }
 
 
 static const char* siptrace_status_doc[2] = {
 static const char* siptrace_status_doc[2] = {
-        "Get status or turn on/off siptrace. Parameters: on, off or check.",
-        0
+	"Get status or turn on/off siptrace. Parameters: on, off or check.",
+	0
 };
 };
 
 
 rpc_export_t siptrace_rpc[] = {
 rpc_export_t siptrace_rpc[] = {
@@ -1957,4 +2055,3 @@ static int siptrace_init_rpc(void)
 	}
 	}
 	return 0;
 	return 0;
 }
 }
-

+ 1 - 1
modules/smsops/doc/smsops_admin.xml

@@ -77,7 +77,7 @@ if (isRPDATA())
 </programlisting>
 </programlisting>
 		</example>
 		</example>
 		</section>
 		</section>
-		<section id="smsops.f.isRPDATA">
+		<section id="smsops.f.smsdump">
 		<title><function moreinfo="none">smsdump()</function></title>
 		<title><function moreinfo="none">smsdump()</function></title>
 		<para>Dumps the content of a 3GPP-SMS message to the Debug-Log.</para>
 		<para>Dumps the content of a 3GPP-SMS message to the Debug-Log.</para>
 		<para>Please make sure, to have debug-Log enabled. Otherwise, you won't see anything.</para>
 		<para>Please make sure, to have debug-Log enabled. Otherwise, you won't see anything.</para>

+ 19 - 19
modules/statsd/lib_statsd.c

@@ -24,13 +24,13 @@ static StatsConnection statsd_connection = {
     "8125"
     "8125"
 };
 };
 
 
-int statsd_connect(void){
+bool statsd_connect(void){
 
 
     struct addrinfo *serverAddr;
     struct addrinfo *serverAddr;
     int rc, error;
     int rc, error;
 
 
     if (statsd_socket.sock > 0){
     if (statsd_socket.sock > 0){
-        return True;
+        return true;
     }
     }
 
 
     error = getaddrinfo(
     error = getaddrinfo(
@@ -41,62 +41,62 @@ int statsd_connect(void){
         LM_ERR(
         LM_ERR(
             "Statsd: could not initiate server information (%s)\n",
             "Statsd: could not initiate server information (%s)\n",
             gai_strerror(error));
             gai_strerror(error));
-        return False;
+        return false;
     }
     }
 
 
     statsd_socket.sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
     statsd_socket.sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
     if (statsd_socket.sock == 0 ){
     if (statsd_socket.sock == 0 ){
         LM_ERR("Statsd: could not initiate a connect to statsd\n");
         LM_ERR("Statsd: could not initiate a connect to statsd\n");
-        return False;
+        return false;
     }
     }
 
 
     rc = connect(
     rc = connect(
         statsd_socket.sock, serverAddr->ai_addr, serverAddr->ai_addrlen);
         statsd_socket.sock, serverAddr->ai_addr, serverAddr->ai_addrlen);
     if (rc < 0){
     if (rc < 0){
         LM_ERR("Statsd: could not initiate a connect to statsd\n");
         LM_ERR("Statsd: could not initiate a connect to statsd\n");
-        return False;
+        return false;
     }
     }
-    return True;
+    return true;
 }
 }
 
 
-int send_command(char *command){
+bool send_command(char *command){
     int send_result;
     int send_result;
 
 
     if (!statsd_connect()){
     if (!statsd_connect()){
-        return False;
+        return false;
     }
     }
 
 
     send_result = send(statsd_socket.sock, command, strlen(command), 0);
     send_result = send(statsd_socket.sock, command, strlen(command), 0);
     if ( send_result < 0){
     if ( send_result < 0){
         LM_ERR("could not send the correct info to statsd (%i| %s)\n",
         LM_ERR("could not send the correct info to statsd (%i| %s)\n",
             send_result, strerror(errno));
             send_result, strerror(errno));
-        return True;
+        return true;
     }
     }
     LM_DBG("Sent to statsd (%s)", command);
     LM_DBG("Sent to statsd (%s)", command);
-    return True;
+    return true;
 }
 }
 
 
-int statsd_set(char *key, char *value){
+bool statsd_set(char *key, char *value){
    char* end = 0;
    char* end = 0;
    char command[254];
    char command[254];
    int val;
    int val;
    val = strtol(value, &end, 0);
    val = strtol(value, &end, 0);
    if (*end){
    if (*end){
        LM_ERR("statsd_count could not  use the provide value(%s)\n", value);
        LM_ERR("statsd_count could not  use the provide value(%s)\n", value);
-       return False;
+       return false;
    }
    }
    snprintf(command, sizeof command, "%s:%i|s\n", key, val);
    snprintf(command, sizeof command, "%s:%i|s\n", key, val);
    return send_command(command);
    return send_command(command);
 }
 }
 
 
 
 
-int statsd_gauge(char *key, char *value){
+bool statsd_gauge(char *key, char *value){
    char command[254];
    char command[254];
    snprintf(command, sizeof command, "%s:%s|g\n", key, value);
    snprintf(command, sizeof command, "%s:%s|g\n", key, value);
    return send_command(command);
    return send_command(command);
 }
 }
 
 
-int statsd_count(char *key, char *value){
+bool statsd_count(char *key, char *value){
    char* end = 0;
    char* end = 0;
    char command[254];
    char command[254];
    int val;
    int val;
@@ -104,19 +104,19 @@ int statsd_count(char *key, char *value){
    val = strtol(value, &end, 0);
    val = strtol(value, &end, 0);
    if (*end){
    if (*end){
        LM_ERR("statsd_count could not  use the provide value(%s)\n", value);
        LM_ERR("statsd_count could not  use the provide value(%s)\n", value);
-       return False;
+       return false;
    }
    }
    snprintf(command, sizeof command, "%s:%i|c\n", key, val);
    snprintf(command, sizeof command, "%s:%i|c\n", key, val);
    return send_command(command);
    return send_command(command);
 }
 }
 
 
-int statsd_timing(char *key, int value){
+bool statsd_timing(char *key, int value){
    char command[254];
    char command[254];
    snprintf(command, sizeof command, "%s:%i|ms\n", key, value);
    snprintf(command, sizeof command, "%s:%i|ms\n", key, value);
    return send_command(command);
    return send_command(command);
 }
 }
 
 
-int statsd_init(char *ip, char *port){
+bool statsd_init(char *ip, char *port){
 
 
     if (ip != NULL){
     if (ip != NULL){
         statsd_connection.ip = ip;
         statsd_connection.ip = ip;
@@ -127,7 +127,7 @@ int statsd_init(char *ip, char *port){
     return statsd_connect();
     return statsd_connect();
 }
 }
 
 
-int statsd_destroy(void){
+bool statsd_destroy(void){
     statsd_socket.sock = 0;
     statsd_socket.sock = 0;
-    return True;
+    return true;
 }
 }

+ 9 - 11
modules/statsd/lib_statsd.h

@@ -1,8 +1,6 @@
+#include <stdbool.h>
 
 
 #define BUFFER_SIZE 8192
 #define BUFFER_SIZE 8192
-typedef int Bool;
-#define True 1
-#define False 0
 
 
 typedef struct StatsConnection{
 typedef struct StatsConnection{
     char *ip;
     char *ip;
@@ -17,11 +15,11 @@ typedef struct StatsdSocket {
     char data[BUFFER_SIZE]; // buffer for the answer data
     char data[BUFFER_SIZE]; // buffer for the answer data
 } StatsdSocket;
 } StatsdSocket;
 
 
-int statsd_connect(void);
-int send_command(char *command);
-int statsd_set(char *key, char *value);
-int statsd_gauge(char *key, char *value);
-int statsd_count(char *key, char *value);
-int statsd_timing(char *key, int value);
-int statsd_init(char *ip, char *port);
-int statsd_destroy(void);
+bool statsd_connect(void);
+bool send_command(char *command);
+bool statsd_set(char *key, char *value);
+bool statsd_gauge(char *key, char *value);
+bool statsd_count(char *key, char *value);
+bool statsd_timing(char *key, int value);
+bool statsd_init(char *ip, char *port);
+bool statsd_destroy(void);

+ 45 - 0
modules/tcpops/tcpops.c

@@ -31,6 +31,8 @@
 #include "../../globals.h"
 #include "../../globals.h"
 #include "../../pass_fd.h"
 #include "../../pass_fd.h"
 #include "../../timer.h"
 #include "../../timer.h"
+#include "../../fmsg.h"
+#include "../../sr_module.h"
 
 
 /**
 /**
  * gets the fd of the current message source connection
  * gets the fd of the current message source connection
@@ -185,3 +187,46 @@ int tcpops_set_connection_lifetime(struct tcp_connection* con, int time) {
 	LM_DBG("new connection lifetime for conid=%d: %d\n", con->id, con->timeout);
 	LM_DBG("new connection lifetime for conid=%d: %d\n", con->id, con->timeout);
 	return 1;
 	return 1;
 }
 }
+
+static void tcpops_tcp_closed_run_route(struct tcp_connection *con)
+{
+	int rt, backup_rt;
+	struct run_act_ctx ctx;
+	sip_msg_t *fmsg;
+	LM_DBG("tcp_closed_run_route event_route[tcp:closed]\n");
+
+	rt = route_get(&event_rt, "tcp:closed");
+	if (rt < 0 || event_rt.rlist[rt] == NULL)
+	{
+		LM_DBG("route does not exist");
+		return;
+	}
+
+	if (faked_msg_init() < 0)
+	{
+		LM_ERR("faked_msg_init() failed\n");
+		return;
+	}
+	fmsg = faked_msg_next();
+	fmsg->rcv = con->rcv;
+
+	backup_rt = get_route_type();
+	set_route_type(EVENT_ROUTE);
+	init_run_actions_ctx(&ctx);
+	run_top_route(event_rt.rlist[rt], fmsg, 0);
+	set_route_type(backup_rt);
+}
+
+int tcpops_handle_tcp_closed(void *data)
+{
+	tcp_event_info_t *tev = (tcp_event_info_t *) data;
+
+	if (tev == NULL || tev->con == NULL) {
+		LM_WARN("received bad TCP closed event\n");
+		return -1;
+	}
+
+	tcpops_tcp_closed_run_route(tev->con);
+
+	return 0;
+}

+ 2 - 0
modules/tcpops/tcpops.h

@@ -25,11 +25,13 @@
 #define TCP_KEEPALIVE_H_
 #define TCP_KEEPALIVE_H_
 
 
 #include "../../tcp_conn.h"
 #include "../../tcp_conn.h"
+#include "../../events.h"
 
 
 int tcpops_get_current_fd(int conid, int *fd);
 int tcpops_get_current_fd(int conid, int *fd);
 int tcpops_acquire_fd_from_tcpmain(int conid, int *fd);
 int tcpops_acquire_fd_from_tcpmain(int conid, int *fd);
 int tcpops_keepalive_enable(int fd, int idle, int count, int interval, int closefd);
 int tcpops_keepalive_enable(int fd, int idle, int count, int interval, int closefd);
 int tcpops_keepalive_disable(int fd, int closefd);
 int tcpops_keepalive_disable(int fd, int closefd);
 int tcpops_set_connection_lifetime(struct tcp_connection* con, int time);
 int tcpops_set_connection_lifetime(struct tcp_connection* con, int time);
+int tcpops_handle_tcp_closed(void *data);
 
 
 #endif /* TCP_KEEPALIVE_H_ */
 #endif /* TCP_KEEPALIVE_H_ */

Some files were not shown because too many files changed in this diff